148 lines
4.9 KiB
Go
148 lines
4.9 KiB
Go
package store
|
|
|
|
import (
|
|
"encoding/json"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/tinode/chat/server/store/types"
|
|
)
|
|
|
|
func TestForTenantRejectsZero(t *testing.T) {
|
|
if scoped, err := ForTenant(types.ZeroTenantID); err != types.ErrMalformed || scoped != nil {
|
|
t.Fatalf("ForTenant(0) = (%v, %v), want (nil, malformed)", scoped, err)
|
|
}
|
|
}
|
|
|
|
func TestValidateMessageHeadersProductContract(t *testing.T) {
|
|
valid := []types.KVMap{
|
|
{"x-im-schema": float64(1), "x-im-type": "text"},
|
|
{"x-im-schema": json.Number("1"), "x-im-type": "voice"},
|
|
{"x-im-schema": "1", "x-im-type": "contact_card"},
|
|
{"x-im-schema": 1, "type": "audio", "x-im-type": "voice"},
|
|
{"x-im-schema": 1, "x-im-type": "system_notice"},
|
|
{"x-im-schema": 1, "x-im-type": "system_action"},
|
|
{"type": "system"},
|
|
}
|
|
for _, head := range valid {
|
|
if err := validateMessageHeaders(head); err != nil {
|
|
t.Fatalf("validateMessageHeaders(%v) = %v, want nil", head, err)
|
|
}
|
|
}
|
|
|
|
invalid := []types.KVMap{
|
|
{"x-im-schema": float64(2), "x-im-type": "text"},
|
|
{"x-im-schema": true, "x-im-type": "text"},
|
|
{"x-im-schema": float64(1), "x-im-type": "unknown"},
|
|
{"type": "text", "x-im-type": "image"},
|
|
}
|
|
for _, head := range invalid {
|
|
if err := validateMessageHeaders(head); err != types.ErrMalformed {
|
|
t.Fatalf("validateMessageHeaders(%v) = %v, want malformed", head, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestVerificationCodeCachePreservesInternalFields(t *testing.T) {
|
|
tokenHash := "verify-token-hash"
|
|
now := time.Now().UTC().Round(time.Millisecond)
|
|
vc := &types.VerificationCode{
|
|
ID: 11,
|
|
TenantID: 7,
|
|
ChallengeID: "challenge-1",
|
|
Scene: "register",
|
|
Action: "signup",
|
|
PhoneHash: "phone-hash",
|
|
PhoneMask: "+861****000",
|
|
TargetUser: types.ParseUserId("usrR25C_ZgHzyg"),
|
|
CodeHash: "code-hash",
|
|
VerificationTokenHash: &tokenHash,
|
|
State: types.VerificationStateVerified,
|
|
Attempts: 2,
|
|
MaxAttempts: 5,
|
|
ExpiresAt: now.Add(5 * time.Minute),
|
|
ResendAfter: now.Add(time.Minute),
|
|
VerifiedAt: &now,
|
|
CreatedAt: now,
|
|
UpdatedAt: now,
|
|
}
|
|
|
|
publicPayload, err := json.Marshal(vc)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if strings.Contains(string(publicPayload), "code-hash") || strings.Contains(string(publicPayload), "phone-hash") {
|
|
t.Fatalf("public verification JSON leaked internal fields: %s", publicPayload)
|
|
}
|
|
|
|
cachePayload, err := json.Marshal(verificationCodeCacheFromType(vc))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !strings.Contains(string(cachePayload), `"code_hash":"code-hash"`) ||
|
|
!strings.Contains(string(cachePayload), `"phone_hash":"phone-hash"`) ||
|
|
!strings.Contains(string(cachePayload), `"target_user":"usrR25C_ZgHzyg"`) ||
|
|
!strings.Contains(string(cachePayload), `"max_attempts":5`) {
|
|
t.Fatalf("cache verification JSON missing internal fields: %s", cachePayload)
|
|
}
|
|
|
|
var cached verificationCodeCache
|
|
if err := json.Unmarshal(cachePayload, &cached); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
got := cached.toType()
|
|
if got.CodeHash != vc.CodeHash || got.PhoneHash != vc.PhoneHash ||
|
|
got.TargetUser != vc.TargetUser || got.State != vc.State || got.Attempts != vc.Attempts || got.MaxAttempts != vc.MaxAttempts ||
|
|
got.VerificationTokenHash == nil || *got.VerificationTokenHash != tokenHash {
|
|
t.Fatalf("cache round trip lost fields: got %#v", got)
|
|
}
|
|
}
|
|
|
|
func TestActionTokenCachePreservesInternalFields(t *testing.T) {
|
|
now := time.Now().UTC().Round(time.Millisecond)
|
|
consumedAt := now.Add(time.Minute)
|
|
token := &types.ActionToken{
|
|
ID: 9,
|
|
TenantID: 7,
|
|
UserID: types.ParseUserId("usrR25C_ZgHzyg"),
|
|
TokenID: "token-id",
|
|
Action: "close_youth_mode",
|
|
Target: "youth_mode",
|
|
TokenHash: "token-hash",
|
|
State: 1,
|
|
ExpiresAt: now.Add(5 * time.Minute),
|
|
ConsumedAt: &consumedAt,
|
|
CreatedAt: now,
|
|
UpdatedAt: consumedAt,
|
|
}
|
|
|
|
publicPayload, err := json.Marshal(token)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if strings.Contains(string(publicPayload), "token-hash") || strings.Contains(string(publicPayload), "token-id") {
|
|
t.Fatalf("public action token JSON leaked internal fields: %s", publicPayload)
|
|
}
|
|
|
|
cachePayload, err := json.Marshal(actionTokenCacheFromType(token))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !strings.Contains(string(cachePayload), `"token_hash":"token-hash"`) ||
|
|
!strings.Contains(string(cachePayload), `"token_id":"token-id"`) ||
|
|
!strings.Contains(string(cachePayload), `"state":1`) {
|
|
t.Fatalf("cache action token JSON missing internal fields: %s", cachePayload)
|
|
}
|
|
|
|
var cached actionTokenCache
|
|
if err := json.Unmarshal(cachePayload, &cached); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
got := cached.toType()
|
|
if got.TokenHash != token.TokenHash || got.TokenID != token.TokenID ||
|
|
got.UserID != token.UserID || got.State != token.State || got.Action != token.Action {
|
|
t.Fatalf("cache round trip lost fields: got %#v", got)
|
|
}
|
|
}
|