package rpc import ( "context" "encoding/binary" "errors" "sync" "testing" "time" "github.com/iamxvbaba/td/clock" "github.com/iamxvbaba/td/tg" "github.com/iamxvbaba/td/tgerr" "go.uber.org/zap/zaptest" "telesrv/internal/domain" "telesrv/internal/store" "telesrv/internal/store/memory" ) type unavailableResolveAuthService struct { *captureAuthService err error } func (s *unavailableResolveAuthService) ResolveAuthKey(context.Context, [8]byte) ([8]byte, bool, error) { return [8]byte{}, false, s.err } type unavailableAuthKeyInfoService struct { *captureAuthService err error } func (s *unavailableAuthKeyInfoService) AuthKeyClientInfo(context.Context, [8]byte) (domain.AuthKeyClientInfo, bool, error) { return domain.AuthKeyClientInfo{}, false, s.err } func TestResolveInheritedAuthKeyLayerUsesAuthKeyAuthorityOnly(t *testing.T) { tests := []struct { name string keyLayer int authorization int want int found bool }{ {name: "auth key primary", keyLayer: 225, authorization: 227, want: 225, found: true}, {name: "authorization mirror is not protocol evidence", keyLayer: 0, authorization: 225}, {name: "unsupported primary is authoritative unknown", keyLayer: 230, authorization: 227, found: true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { authKeyID := [8]byte{byte(tt.keyLayer), 0x71} auth := &captureAuthService{ authKeyClientInfos: map[[8]byte]domain.AuthKeyClientInfo{ authKeyID: {Layer: tt.keyLayer}, }, authorizations: []domain.Authorization{{AuthKeyID: authKeyID, UserID: 10, Layer: tt.authorization}}, } r := New(Config{DC: 2}, Deps{Auth: auth}, zaptest.NewLogger(t), clock.System) got, found, err := r.ResolveInheritedAuthKeyLayer(context.Background(), authKeyID) if err != nil { t.Fatal(err) } if got != tt.want || found != tt.found { t.Fatalf("resolved layer = (%d,%v), want (%d,%v)", got, found, tt.want, tt.found) } got, found, err = r.ResolveInheritedAuthKeyLayer(context.Background(), authKeyID) if err != nil || got != tt.want || found != tt.found { t.Fatalf("cached resolved layer = (%d,%v,%v), want (%d,%v,nil)", got, found, err, tt.want, tt.found) } if auth.authKeyInfoLookups != 1 { t.Fatalf("auth key info lookups = %d, want 1 after repeated resolve", auth.authKeyInfoLookups) } if auth.authorizationLookups != 0 { t.Fatalf("authorization Layer mirror lookups = %d, want 0", auth.authorizationLookups) } }) } } func TestResolveInheritedAuthKeyLayerNormalizesBoundTempToPermanent(t *testing.T) { rawAuthKeyID := [8]byte{0x31} permAuthKeyID := [8]byte{0x32} auth := &captureAuthService{ resolvedAuthKeyID: permAuthKeyID, hasResolved: true, authKeyClientInfos: map[[8]byte]domain.AuthKeyClientInfo{ rawAuthKeyID: {Layer: 225}, // stale shadow permAuthKeyID: {Layer: 227}, // canonical shared default }, } r := New(Config{DC: 2}, Deps{Auth: auth}, zaptest.NewLogger(t), clock.System) layer, found, err := r.ResolveInheritedAuthKeyLayer(context.Background(), rawAuthKeyID) if err != nil { t.Fatal(err) } if !found || layer != 227 { t.Fatalf("bound temp layer = (%d,%v), want (227,true)", layer, found) } if cached, ok := r.cachedAuthKeyLayerDefault(rawAuthKeyID); !ok || cached != 227 { t.Fatalf("raw in-memory shadow = (%d,%v), want (227,true)", cached, ok) } } func TestPositiveBindingResolutionIsSharedByLayerAndDispatchPaths(t *testing.T) { rawAuthKeyID := [8]byte{0x31, 1} permAuthKeyID := [8]byte{0x32, 1} const sessionID = int64(311) auth := &captureAuthService{ resolvedAuthKeyID: permAuthKeyID, hasResolved: true, authKeyClientInfos: map[[8]byte]domain.AuthKeyClientInfo{ permAuthKeyID: {Layer: 227}, }, } sessions := &captureSessions{} r := New(Config{DC: 2, TempKeyResolveCacheTTL: time.Minute}, Deps{ Auth: auth, Sessions: sessions, }, zaptest.NewLogger(t), clock.System) if layer, found, err := r.ResolveInheritedAuthKeyLayer(context.Background(), rawAuthKeyID); err != nil || !found || layer != 227 { t.Fatalf("inherited layer = (%d,%v,%v), want (227,true,nil)", layer, found, err) } if got, err := r.effectiveAuthKeyID(context.Background(), rawAuthKeyID, sessionID); err != nil || got != permAuthKeyID { t.Fatalf("effective auth key = (%x,%v), want (%x,nil)", got, err, permAuthKeyID) } freezeAndPublishLayer(t, r, rawAuthKeyID, sessionID, 10, 1, 227) if auth.resolveCount != 1 { t.Fatalf("ResolveAuthKey calls across inherited/dispatch/publication = %d, want 1", auth.resolveCount) } } func TestResolveInheritedAuthKeyLayerMarksOnlyAvailabilityFailures(t *testing.T) { boom := errors.New("postgres temporarily unavailable") for _, tt := range []struct { name string auth AuthService want error wantMarker bool }{ { name: "temporary binding lookup outage", auth: &unavailableResolveAuthService{ captureAuthService: &captureAuthService{}, err: boom, }, want: boom, wantMarker: true, }, { name: "auth key default lookup outage", auth: &unavailableAuthKeyInfoService{ captureAuthService: &captureAuthService{}, err: boom, }, want: boom, wantMarker: true, }, { name: "structural binding failure", auth: &unavailableResolveAuthService{ captureAuthService: &captureAuthService{}, err: store.ErrAuthKeyBindingInvalid, }, want: store.ErrAuthKeyBindingInvalid, }, } { t.Run(tt.name, func(t *testing.T) { r := New(Config{DC: 2}, Deps{Auth: tt.auth}, zaptest.NewLogger(t), clock.System) layer, found, err := r.ResolveInheritedAuthKeyLayer(context.Background(), [8]byte{0x33}) if layer != 0 || found || !errors.Is(err, tt.want) { t.Fatalf("resolution = (%d,%v,%v), want (0,false,%v)", layer, found, err, tt.want) } var marker interface{ LayerEvidenceDurabilityUnavailable() } if got := errors.As(err, &marker); got != tt.wantMarker { t.Fatalf("availability marker=%v, want %v", got, tt.wantMarker) } }) } } func TestResolveInheritedDurableAuthKeyLayerIgnoresAuthorizationMirror(t *testing.T) { authKeyID := [8]byte{0x39, 0x71} auth := &captureAuthService{ authKeyClientInfos: map[[8]byte]domain.AuthKeyClientInfo{ authKeyID: {}, }, authorizations: []domain.Authorization{{ AuthKeyID: authKeyID, UserID: 10, Layer: 227, }}, } r := New(Config{DC: 2}, Deps{ Auth: auth, AuthKeySessionLayers: memory.NewAuthKeyStore(), }, zaptest.NewLogger(t), clock.System) layer, found, err := r.ResolveInheritedAuthKeyLayer(context.Background(), authKeyID) if err != nil || found || layer != 0 { t.Fatalf("durable zero primary = (%d,%v,%v), want (0,false,nil)", layer, found, err) } if auth.authKeyInfoLookups != 1 || auth.authorizationLookups != 0 { t.Fatalf("durable lookups = auth_key:%d authorization:%d, want 1/0", auth.authKeyInfoLookups, auth.authorizationLookups) } } func TestExplicitLayerCorrectionUpdatesDefaultWithoutChangingSibling(t *testing.T) { authKeyID := [8]byte{0x41} auth := &captureAuthService{authKeyClientInfos: make(map[[8]byte]domain.AuthKeyClientInfo)} r := New(Config{DC: 2}, Deps{Auth: auth}, zaptest.NewLogger(t), clock.System) ctx1 := WithAuthKeyID(WithSessionID(WithRawAuthKeyID(context.Background(), authKeyID), 1), authKeyID) ctx2 := WithAuthKeyID(WithSessionID(WithRawAuthKeyID(context.Background(), authKeyID), 2), authKeyID) r.rememberClientLayer(ctx1, 225) r.rememberClientLayer(ctx2, 225) r.rememberClientLayer(ctx1, 227) if layer, ok := r.NegotiatedSessionLayer(authKeyID, 1); !ok || layer != 227 { t.Fatalf("corrected session = (%d,%v), want (227,true)", layer, ok) } if layer, ok := r.NegotiatedSessionLayer(authKeyID, 2); !ok || layer != 225 { t.Fatalf("sibling session = (%d,%v), want unchanged (225,true)", layer, ok) } if got := auth.authKeyClientInfos[authKeyID].Layer; got != 227 { t.Fatalf("durable shared default = %d, want 227", got) } layer, found, err := r.ResolveInheritedAuthKeyLayer(context.Background(), authKeyID) if err != nil || !found || layer != 227 { t.Fatalf("new session default = (%d,%v,%v), want (227,true,nil)", layer, found, err) } } type inheritedLayerSeedCall struct { rawAuthKeyID [8]byte layer int } type inheritedLayerCaptureSessions struct { captureSessions seeds []inheritedLayerSeedCall refreshes []inheritedLayerSeedCall clears [][8]byte explicitLayer int explicitMsgID int64 } func (s *inheritedLayerCaptureSessions) BindAuthKeyForRawAuthKey(rawAuthKeyID [8]byte, authKeyID [8]byte) int { s.BindAuthKeyForSession(rawAuthKeyID, 0, authKeyID) return 1 } func (s *inheritedLayerCaptureSessions) SeedInheritedLayerForRawAuthKey(rawAuthKeyID [8]byte, layer int) int { s.seeds = append(s.seeds, inheritedLayerSeedCall{rawAuthKeyID: rawAuthKeyID, layer: layer}) return 1 } func (s *inheritedLayerCaptureSessions) RefreshInheritedLayerForRawAuthKey(rawAuthKeyID [8]byte, layer int) int { s.refreshes = append(s.refreshes, inheritedLayerSeedCall{rawAuthKeyID: rawAuthKeyID, layer: layer}) return 1 } func (s *inheritedLayerCaptureSessions) ClearInheritedLayerForRawAuthKey(rawAuthKeyID [8]byte) int { s.clears = append(s.clears, rawAuthKeyID) return 1 } func (s *inheritedLayerCaptureSessions) ExplicitLayerEvidenceForAuthKey([8]byte, int64) (int, int64, bool) { return s.explicitLayer, s.explicitMsgID, s.explicitLayer != 0 } func TestBindTempAuthKeyLayerPrecedenceAndRawShadow(t *testing.T) { for _, tt := range []struct { name string explicitLayer int want int }{ {name: "inherit permanent", want: 225}, {name: "current explicit wins", explicitLayer: 227, want: 227}, } { t.Run(tt.name, func(t *testing.T) { rawAuthKeyID := [8]byte{0x51, byte(tt.want)} permAuthKeyID := [8]byte{0x52, byte(tt.want)} const sessionID = int64(99) auth := &captureAuthService{authKeyClientInfos: map[[8]byte]domain.AuthKeyClientInfo{ permAuthKeyID: {Layer: 225}, }} // Model the store-owned bind transaction. Bind returns the exact committed // tuple; the router must not re-read a later permanent row or derive a winner // from its process-local exact-session registry after Bind returns. auth.bindTempHook = func(domain.TempAuthKeyBinding) error { auth.authKeyClientInfos[rawAuthKeyID] = domain.AuthKeyClientInfo{Layer: tt.want, LayerObservationID: 42} auth.authKeyClientInfos[permAuthKeyID] = domain.AuthKeyClientInfo{Layer: tt.want, LayerObservationID: 42} return nil } sessions := &inheritedLayerCaptureSessions{} r := New(Config{DC: 2}, Deps{Auth: auth, Sessions: sessions}, zaptest.NewLogger(t), clock.System) if tt.explicitLayer != 0 { freezeAndPublishLayer(t, r, rawAuthKeyID, sessionID, 10, 1, tt.explicitLayer) sessions.seeds = nil } ctx := WithAuthKeyID(WithSessionID(WithRawAuthKeyID(context.Background(), rawAuthKeyID), sessionID), rawAuthKeyID) ok, err := r.onAuthBindTempAuthKey(ctx, &tg.AuthBindTempAuthKeyRequest{PermAuthKeyID: businessAuthKeyInt64(permAuthKeyID)}) if err != nil || !ok { t.Fatalf("bind = (%v,%v), want (true,nil)", ok, err) } if auth.authKeyInfoLookups != 0 || auth.authorizationLookups != 0 { t.Fatalf("bind re-read durable Layer state: key=%d authorization=%d", auth.authKeyInfoLookups, auth.authorizationLookups) } if got := auth.authKeyClientInfos[rawAuthKeyID].Layer; got != tt.want { t.Fatalf("raw shadow = %d, want %d", got, tt.want) } if got := auth.authKeyClientInfos[permAuthKeyID].Layer; got != tt.want { t.Fatalf("permanent default = %d, want %d", got, tt.want) } if len(sessions.refreshes) != 1 || sessions.refreshes[0] != (inheritedLayerSeedCall{rawAuthKeyID: rawAuthKeyID, layer: tt.want}) { t.Fatalf("refresh calls = %+v", sessions.refreshes) } if len(sessions.seeds) != 0 { t.Fatalf("bind used ordinary seed instead of identity refresh: %+v", sessions.seeds) } if cached, found := r.cachedAuthKeyLayerDefault(rawAuthKeyID); !found || cached != tt.want { t.Fatalf("raw cached default = (%d,%v), want (%d,true)", cached, found, tt.want) } r.clientInfoMu.RLock() rawCached := r.authInfo[rawAuthKeyID] permCached := r.authInfo[permAuthKeyID] r.clientInfoMu.RUnlock() if rawCached.layerObservationID != 42 || rawCached.layerObservationID != permCached.layerObservationID { t.Fatalf("bound observation shadow = raw:%d perm:%d, want 42/42", rawCached.layerObservationID, permCached.layerObservationID) } }) } } func TestResolveInheritedAuthKeyLayerCachesTerminalMissingRows(t *testing.T) { authKeyID := [8]byte{0x61} auth := &captureAuthService{authKeyClientInfos: make(map[[8]byte]domain.AuthKeyClientInfo)} r := New(Config{DC: 2}, Deps{Auth: auth}, zaptest.NewLogger(t), clock.System) for i := 0; i < 2; i++ { layer, found, err := r.ResolveInheritedAuthKeyLayer(context.Background(), authKeyID) if err != nil || found || layer != 0 { t.Fatalf("resolve %d = (%d,%v,%v), want (0,false,nil)", i, layer, found, err) } } if auth.authKeyInfoLookups != 1 || auth.authorizationLookups != 0 { t.Fatalf("terminal missing lookups = auth_key:%d authorization:%d, want 1/0", auth.authKeyInfoLookups, auth.authorizationLookups) } } func TestResolveInheritedBoundTempUnsupportedPermanentBlocksRawShadow(t *testing.T) { rawAuthKeyID := [8]byte{0x62} permAuthKeyID := [8]byte{0x63} auth := &captureAuthService{ resolvedAuthKeyID: permAuthKeyID, hasResolved: true, authKeyClientInfos: map[[8]byte]domain.AuthKeyClientInfo{ rawAuthKeyID: {Layer: 225}, permAuthKeyID: {Layer: 230}, }, } r := New(Config{DC: 2}, Deps{Auth: auth}, zaptest.NewLogger(t), clock.System) layer, authoritative, err := r.ResolveInheritedAuthKeyLayer(context.Background(), rawAuthKeyID) if err != nil || !authoritative || layer != 0 { t.Fatalf("bound future default = (%d,%v,%v), want (0,true,nil)", layer, authoritative, err) } if auth.authKeyInfoLookups != 1 { t.Fatalf("canonical auth-key lookups = %d, want 1", auth.authKeyInfoLookups) } } func TestBindTempAuthKeyFuturePermanentClearsInheritedUntilFreshExplicit(t *testing.T) { rawAuthKeyID := [8]byte{0x62, 1} permAuthKeyID := [8]byte{0x62, 2} const sessionID = int64(621) auth := &captureAuthService{ resolvedAuthKeyID: permAuthKeyID, hasResolved: true, authKeyClientInfos: map[[8]byte]domain.AuthKeyClientInfo{ rawAuthKeyID: {Layer: 225}, permAuthKeyID: {Layer: 230, LayerObservationID: 44}, }, } auth.bindTempHook = func(domain.TempAuthKeyBinding) error { auth.authKeyClientInfos[rawAuthKeyID] = domain.AuthKeyClientInfo{Layer: 230, LayerObservationID: 44} return nil } sessions := &inheritedLayerCaptureSessions{} r := New(Config{DC: 2}, Deps{Auth: auth, Sessions: sessions}, zaptest.NewLogger(t), clock.System) r.setAuthKeyLayerDefaults(225, rawAuthKeyID) ctx := WithAuthKeyID(WithSessionID(WithRawAuthKeyID(context.Background(), rawAuthKeyID), sessionID), rawAuthKeyID) ok, err := r.onAuthBindTempAuthKey(ctx, &tg.AuthBindTempAuthKeyRequest{PermAuthKeyID: businessAuthKeyInt64(permAuthKeyID)}) if err != nil || !ok { t.Fatalf("bind to future permanent = (%v,%v)", ok, err) } if len(sessions.clears) != 1 || sessions.clears[0] != rawAuthKeyID { t.Fatalf("inherited clear calls = %x", sessions.clears) } if len(sessions.refreshes) != 0 { t.Fatalf("future permanent refreshed stale inherited profile: %+v", sessions.refreshes) } r.clientInfoMu.RLock() blocked := r.authInfo[rawAuthKeyID] r.clientInfoMu.RUnlock() if blocked.layer != 0 || blocked.layerObservationID != 44 || !blocked.layerBlocked || !blocked.layerBlockedByAuthKey { t.Fatalf("raw blocked shadow = %+v", blocked) } // A later in-window explicit selector is new wire evidence and may correct // both the session and the permanent shared default. freezeAndPublishLayer(t, r, rawAuthKeyID, sessionID, 20, 1, 225) if got := auth.authKeyClientInfos[permAuthKeyID].Layer; got != 225 { t.Fatalf("fresh explicit did not self-heal future default: %d", got) } if got, found := r.cachedAuthKeyLayerDefault(rawAuthKeyID); !found || got != 225 { t.Fatalf("raw default after self-heal = (%d,%v)", got, found) } } func TestSupportedExplicitEvidenceClearsUnsupportedCacheState(t *testing.T) { authKeyID := [8]byte{0x63, 1} auth := &captureAuthService{authKeyClientInfos: map[[8]byte]domain.AuthKeyClientInfo{ authKeyID: {Layer: 230}, }} r := New(Config{DC: 2}, Deps{Auth: auth}, zaptest.NewLogger(t), clock.System) if layer, found, err := r.ResolveInheritedAuthKeyLayer(context.Background(), authKeyID); err != nil || !found || layer != 0 { t.Fatalf("future default = (%d,%v,%v), want (0,true,nil)", layer, found, err) } freezeAndPublishLayer(t, r, authKeyID, 1, 10, 1, 228) r.clientInfoMu.RLock() info := r.authInfo[authKeyID] r.clientInfoMu.RUnlock() if info.layer != 228 || info.layerBlocked || info.layerBlockedByAuthKey { t.Fatalf("supported and blocked cache state coexisted: %+v", info) } } type inheritedLayerResolution struct { layer int found bool err error } type blockingLayerReadAuthService struct { *captureAuthService started chan struct{} release chan struct{} once sync.Once stale domain.AuthKeyClientInfo } func (s *blockingLayerReadAuthService) AuthKeyClientInfo(ctx context.Context, _ [8]byte) (domain.AuthKeyClientInfo, bool, error) { s.authKeyInfoLookups++ s.once.Do(func() { close(s.started) }) select { case <-s.release: return s.stale, true, nil case <-ctx.Done(): return domain.AuthKeyClientInfo{}, false, ctx.Err() } } func TestResolveInheritedLayerDoesNotReturnStaleSingleflightRead(t *testing.T) { authKeyID := [8]byte{0x64} const sessionID = int64(640) auth := &blockingLayerReadAuthService{ captureAuthService: &captureAuthService{authKeyClientInfos: make(map[[8]byte]domain.AuthKeyClientInfo)}, started: make(chan struct{}), release: make(chan struct{}), stale: domain.AuthKeyClientInfo{Layer: 225}, } r := New(Config{DC: 2}, Deps{Auth: auth}, zaptest.NewLogger(t), clock.System) resolved := make(chan inheritedLayerResolution, 1) go func() { layer, found, err := r.ResolveInheritedAuthKeyLayer(context.Background(), authKeyID) resolved <- inheritedLayerResolution{layer: layer, found: found, err: err} }() <-auth.started freezeAndPublishLayer(t, r, authKeyID, sessionID, 100, 2, 227) close(auth.release) got := <-resolved if got.err != nil || !got.found || got.layer != 227 { t.Fatalf("resolver returned stale DB value = (%d,%v,%v), want (227,true,nil)", got.layer, got.found, got.err) } } func TestBoundDurableResolveCannotProjectStalePermanentLayerOverNewObservation(t *testing.T) { ctx := context.Background() keys := memory.NewAuthKeyStore() bindings := memory.NewTempAuthKeyBindingStore(keys) rawAuthKeyID := [8]byte{0x64, 1} permAuthKeyID := [8]byte{0x64, 2} const ( sessionID = int64(641) tempExpiry = 2_000_000_000 ) if err := keys.Save(ctx, store.AuthKeyData{ID: rawAuthKeyID, ExpiresAt: tempExpiry}); err != nil { t.Fatal(err) } if err := keys.Save(ctx, store.AuthKeyData{ ID: permAuthKeyID, Layer: 225, LayerObservationID: 1, }); err != nil { t.Fatal(err) } if err := bindings.Save(ctx, domain.TempAuthKeyBinding{ TempAuthKeyID: rawAuthKeyID, PermAuthKeyID: int64(binary.LittleEndian.Uint64(permAuthKeyID[:])), ExpiresAt: tempExpiry, }); err != nil { t.Fatal(err) } auth := &blockingLayerReadAuthService{ captureAuthService: &captureAuthService{ resolvedAuthKeyID: permAuthKeyID, hasResolved: true, }, started: make(chan struct{}), release: make(chan struct{}), stale: domain.AuthKeyClientInfo{Layer: 225, LayerObservationID: 1}, } sessions := &inheritedLayerCaptureSessions{} r := New(Config{DC: 2}, Deps{ Auth: auth, Sessions: sessions, AuthKeySessionLayers: keys, }, zaptest.NewLogger(t), clock.System) resolved := make(chan inheritedLayerResolution, 1) go func() { layer, found, err := r.ResolveInheritedAuthKeyLayer(ctx, rawAuthKeyID) resolved <- inheritedLayerResolution{layer: layer, found: found, err: err} }() <-auth.started msgID := int64((uint64(time.Now().UTC().Unix()) << 32) | 4) if layer, gotMsgID, publish, err := r.AdvanceNegotiatedSessionLayerEvidence( ctx, rawAuthKeyID, sessionID, 227, msgID, ); err != nil || layer != 227 || gotMsgID != msgID || !publish { t.Fatalf("advance while stale read blocked = (%d,%d,%v,%v)", layer, gotMsgID, publish, err) } if err := r.PublishAdmittedLayerProfileEvidence(ctx, rawAuthKeyID, sessionID, msgID, 2, 1, 227); err != nil { t.Fatal(err) } close(auth.release) got := <-resolved if got.err != nil || !got.found || got.layer != 227 { t.Fatalf("bound resolver returned stale permanent read = (%d,%v,%v)", got.layer, got.found, got.err) } permanent, found, err := keys.Get(ctx, permAuthKeyID) if err != nil || !found || permanent.Layer != 227 || permanent.LayerObservationID <= 1 { t.Fatalf("durable permanent tuple = (%+v,%v,%v)", permanent, found, err) } r.clientInfoMu.RLock() rawInfo := r.authInfo[rawAuthKeyID] permInfo := r.authInfo[permAuthKeyID] r.clientInfoMu.RUnlock() for id, info := range map[[8]byte]clientSessionInfo{ rawAuthKeyID: rawInfo, permAuthKeyID: permInfo, } { if info.layer != permanent.Layer || info.layerObservationID != permanent.LayerObservationID { t.Fatalf("cache %x = layer/obs %d/%d, durable %d/%d", id, info.layer, info.layerObservationID, permanent.Layer, permanent.LayerObservationID) } } } func TestAdmittedLayerSequenceOrdersSharedDefaultAcrossSessions(t *testing.T) { rawOld := [8]byte{0x65, 1} rawNew := [8]byte{0x65, 2} permAuthKeyID := [8]byte{0x65, 3} auth := &captureAuthService{ resolvedAuthKeyID: permAuthKeyID, hasResolved: true, authKeyClientInfos: make(map[[8]byte]domain.AuthKeyClientInfo), } r := New(Config{DC: 2}, Deps{Auth: auth}, zaptest.NewLogger(t), clock.System) if _, err := r.FreezeNegotiatedSessionLayerAt(rawOld, 1, 225, 10); err != nil { t.Fatal(err) } if _, err := r.FreezeNegotiatedSessionLayerAt(rawNew, 2, 227, 20); err != nil { t.Fatal(err) } if err := r.PublishAdmittedLayerProfileEvidence(context.Background(), rawNew, 2, 20, 2, 1, 227); err != nil { t.Fatal(err) } if err := r.PublishAdmittedLayerProfileEvidence(context.Background(), rawOld, 1, 10, 1, 1, 225); err != nil { t.Fatal(err) } if got := auth.authKeyClientInfos[permAuthKeyID].Layer; got != 227 { t.Fatalf("durable shared default rolled back = %d, want 227", got) } if got, ok := r.cachedAuthKeyLayerDefault(permAuthKeyID); !ok || got != 227 { t.Fatalf("memory shared default = (%d,%v), want (227,true)", got, ok) } if got, ok := r.NegotiatedSessionLayer(rawOld, 1); !ok || got != 225 { t.Fatalf("old live session changed = (%d,%v), want (225,true)", got, ok) } } func TestDurableObservationOrdersLocalDefaultWhenAdmissionCommitsInReverse(t *testing.T) { ctx := context.Background() keys := memory.NewAuthKeyStore() bindings := memory.NewTempAuthKeyBindingStore(keys) rawEarlierAdmission := [8]byte{0x65, 0x11} rawLaterAdmission := [8]byte{0x65, 0x12} permAuthKeyID := [8]byte{0x65, 0x13} const tempExpiry = 2_000_000_000 for _, key := range []store.AuthKeyData{ {ID: rawEarlierAdmission, ExpiresAt: tempExpiry}, {ID: rawLaterAdmission, ExpiresAt: tempExpiry}, {ID: permAuthKeyID}, } { if err := keys.Save(ctx, key); err != nil { t.Fatal(err) } } for index, raw := range [][8]byte{rawEarlierAdmission, rawLaterAdmission} { if err := bindings.Save(ctx, domain.TempAuthKeyBinding{ TempAuthKeyID: raw, PermAuthKeyID: int64(binary.LittleEndian.Uint64(permAuthKeyID[:])), Nonce: int64(index + 1), ExpiresAt: tempExpiry, }); err != nil { t.Fatal(err) } } auth := &captureAuthService{resolvedAuthKeyID: permAuthKeyID, hasResolved: true} sessions := &inheritedLayerCaptureSessions{} r := New(Config{DC: 2}, Deps{ Auth: auth, Sessions: sessions, AuthKeySessionLayers: keys, }, zaptest.NewLogger(t), clock.System) now := time.Now().UTC() msgLaterAdmission := int64((uint64(now.Unix()) << 32) | 4) msgEarlierAdmission := int64((uint64(now.Unix()) << 32) | 8) // Admission sequence 2 reaches the permanent identity gate and commits // first. It is temporarily the durable/local default. if layer, msgID, publish, err := r.AdvanceNegotiatedSessionLayerEvidence( ctx, rawLaterAdmission, 2, 227, msgLaterAdmission, ); err != nil || layer != 227 || msgID != msgLaterAdmission || !publish { t.Fatalf("later admission first commit = (%d,%d,%v,%v)", layer, msgID, publish, err) } if err := r.PublishAdmittedLayerProfileEvidence( ctx, rawLaterAdmission, 2, msgLaterAdmission, 2, 1, 227, ); err != nil { t.Fatal(err) } // Admission sequence 1 was allocated earlier but commits second. Database // observation order is authoritative, so local authInfo/session seeding must // move to 225 despite the lower process admission sequence. if layer, msgID, publish, err := r.AdvanceNegotiatedSessionLayerEvidence( ctx, rawEarlierAdmission, 1, 225, msgEarlierAdmission, ); err != nil || layer != 225 || msgID != msgEarlierAdmission || !publish { t.Fatalf("earlier admission late commit = (%d,%d,%v,%v)", layer, msgID, publish, err) } if err := r.PublishAdmittedLayerProfileEvidence( ctx, rawEarlierAdmission, 1, msgEarlierAdmission, 1, 1, 225, ); err != nil { t.Fatal(err) } permanent, found, err := keys.Get(ctx, permAuthKeyID) if err != nil || !found || permanent.Layer != 225 || permanent.LayerObservationID <= 0 { t.Fatalf("durable permanent default = (%+v,%v,%v)", permanent, found, err) } r.clientInfoMu.RLock() local := r.authInfo[permAuthKeyID] r.clientInfoMu.RUnlock() if local.layer != permanent.Layer || local.layerObservationID != permanent.LayerObservationID { t.Fatalf("local default = layer/obs %d/%d, durable %d/%d", local.layer, local.layerObservationID, permanent.Layer, permanent.LayerObservationID) } lastSeed := 0 for _, seed := range sessions.seeds { if seed.rawAuthKeyID == permAuthKeyID { lastSeed = seed.layer } } if lastSeed != 225 { t.Fatalf("SessionManager permanent seed = %d, want 225", lastSeed) } } type blockingLayerWriteAuthService struct { *captureAuthService mu sync.Mutex started chan struct{} release chan struct{} updates int blockFirst sync.Once } func (s *blockingLayerWriteAuthService) UpdateAuthKeyClientInfo(ctx context.Context, authKeyID [8]byte, info domain.AuthKeyClientInfo) error { s.mu.Lock() s.updates++ first := s.updates == 1 s.mu.Unlock() if first { s.blockFirst.Do(func() { close(s.started) }) select { case <-s.release: case <-ctx.Done(): return ctx.Err() } } s.mu.Lock() defer s.mu.Unlock() return s.captureAuthService.UpdateAuthKeyClientInfo(ctx, authKeyID, info) } func (s *blockingLayerWriteAuthService) layer(authKeyID [8]byte) int { s.mu.Lock() defer s.mu.Unlock() return s.authKeyClientInfos[authKeyID].Layer } func TestAdmittedLayerPersistentWritesFollowAdmissionSequence(t *testing.T) { rawOld := [8]byte{0x66, 1} rawNew := [8]byte{0x66, 2} permAuthKeyID := [8]byte{0x66, 3} auth := &blockingLayerWriteAuthService{ captureAuthService: &captureAuthService{ resolvedAuthKeyID: permAuthKeyID, hasResolved: true, authKeyClientInfos: make(map[[8]byte]domain.AuthKeyClientInfo), }, started: make(chan struct{}), release: make(chan struct{}), } r := New(Config{DC: 2}, Deps{Auth: auth}, zaptest.NewLogger(t), clock.System) if _, err := r.FreezeNegotiatedSessionLayerAt(rawOld, 1, 225, 10); err != nil { t.Fatal(err) } if _, err := r.FreezeNegotiatedSessionLayerAt(rawNew, 2, 227, 20); err != nil { t.Fatal(err) } errs := make(chan error, 2) go func() { errs <- r.PublishAdmittedLayerProfileEvidence(context.Background(), rawOld, 1, 10, 1, 1, 225) }() <-auth.started go func() { errs <- r.PublishAdmittedLayerProfileEvidence(context.Background(), rawNew, 2, 20, 2, 1, 227) }() close(auth.release) for i := 0; i < 2; i++ { if err := <-errs; err != nil { t.Fatal(err) } } if got := auth.layer(permAuthKeyID); got != 227 { t.Fatalf("ordered durable default = %d, want 227", got) } } func TestAuthLayerEvidenceCapacityUsesExactSafeFloor(t *testing.T) { fill := func(r *Router) { for i := uint64(1); i <= maxAuthLayerEvidenceEntries; i++ { var id [8]byte binary.LittleEndian.PutUint64(id[:], i) r.authLayerEvidence[id] = authLayerDefaultEvidence{layer: 225, sequence: 1} } } newID := [8]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff} t.Run("retired entry is evictable", func(t *testing.T) { r := New(Config{DC: 2}, Deps{}, zaptest.NewLogger(t), clock.System) fill(r) r.authLayerSafeEvictionFloor = 2 applied, err := r.claimAuthLayerDefaultEvidenceLocked(227, 2, false, newID) if err != nil || !applied { t.Fatalf("claim with retired capacity = (%v,%v), want (true,nil)", applied, err) } if len(r.authLayerEvidence) != maxAuthLayerEvidenceEntries { t.Fatalf("evidence entries = %d, want %d", len(r.authLayerEvidence), maxAuthLayerEvidenceEntries) } if got := r.authLayerEvidence[newID]; got != (authLayerDefaultEvidence{layer: 227, sequence: 2}) { t.Fatalf("new evidence = %+v", got) } }) t.Run("live entry is not evicted", func(t *testing.T) { r := New(Config{DC: 2}, Deps{}, zaptest.NewLogger(t), clock.System) fill(r) r.authLayerSafeEvictionFloor = 1 applied, err := r.claimAuthLayerDefaultEvidenceLocked(227, 2, false, newID) if err != nil || applied { t.Fatalf("claim with all-live capacity = (%v,%v), want (false,nil)", applied, err) } if _, exists := r.authLayerEvidence[newID]; exists { t.Fatal("capacity refusal installed new evidence") } }) } func TestAdmittedLayerSafeFloorIsMonotonic(t *testing.T) { r := New(Config{DC: 2}, Deps{}, zaptest.NewLogger(t), clock.System) first := [8]byte{0x67, 1} second := [8]byte{0x67, 2} if _, err := r.FreezeNegotiatedSessionLayerAt(first, 1, 225, 10); err != nil { t.Fatal(err) } if err := r.PublishAdmittedLayerProfileEvidence(context.Background(), first, 1, 10, 2, 2, 225); err != nil { t.Fatal(err) } if _, err := r.FreezeNegotiatedSessionLayerAt(second, 2, 227, 20); err != nil { t.Fatal(err) } if err := r.PublishAdmittedLayerProfileEvidence(context.Background(), second, 2, 20, 3, 1, 227); err != nil { t.Fatal(err) } r.clientInfoMu.RLock() floor := r.authLayerSafeEvictionFloor r.clientInfoMu.RUnlock() if floor != 2 { t.Fatalf("safe eviction floor regressed = %d, want 2", floor) } } func TestInvalidateAuthUserCachePreservesLayerEvidenceWatermark(t *testing.T) { rawOld := [8]byte{0x68, 1} permAuthKeyID := [8]byte{0x68, 2} auth := &captureAuthService{ resolvedAuthKeyID: permAuthKeyID, hasResolved: true, authKeyClientInfos: make(map[[8]byte]domain.AuthKeyClientInfo), } r := New(Config{DC: 2}, Deps{Auth: auth}, zaptest.NewLogger(t), clock.System) freezeAndPublishLayer(t, r, permAuthKeyID, 2, 20, 2, 227) r.invalidateAuthUserCache(permAuthKeyID) freezeAndPublishLayer(t, r, rawOld, 1, 10, 1, 225) if got := auth.authKeyClientInfos[permAuthKeyID].Layer; got != 227 { t.Fatalf("authorization invalidation erased protocol order = %d, want 227", got) } } func TestStaleExactCorrectionSkipsSharedPublicationWithoutFailingRPC(t *testing.T) { authKeyID := [8]byte{0x69} auth := &captureAuthService{authKeyClientInfos: make(map[[8]byte]domain.AuthKeyClientInfo)} r := New(Config{DC: 2}, Deps{Auth: auth}, zaptest.NewLogger(t), clock.System) if _, err := r.FreezeNegotiatedSessionLayerAt(authKeyID, 1, 225, 10); err != nil { t.Fatal(err) } if _, err := r.FreezeNegotiatedSessionLayerAt(authKeyID, 1, 227, 20); err != nil { t.Fatal(err) } if err := r.PublishAdmittedLayerProfileEvidence(context.Background(), authKeyID, 1, 10, 1, 1, 225); err != nil { t.Fatalf("stale publication rejected immutable old RPC: %v", err) } if _, exists := auth.authKeyClientInfos[authKeyID]; exists { t.Fatal("stale exact observation published durable default") } if err := r.PublishAdmittedLayerProfileEvidence(context.Background(), authKeyID, 1, 20, 2, 2, 227); err != nil { t.Fatal(err) } } func TestBindTempAuthKeyUsesLiveExplicitLayerAfterExactTTL(t *testing.T) { clk := &exactProfileTestClock{now: time.Unix(1_700_000_000, 0)} rawAuthKeyID := [8]byte{0x6a, 1} permAuthKeyID := [8]byte{0x6a, 2} const sessionID = int64(106) auth := &captureAuthService{authKeyClientInfos: map[[8]byte]domain.AuthKeyClientInfo{ permAuthKeyID: {Layer: 225}, }} auth.bindTempHook = func(domain.TempAuthKeyBinding) error { // The durable raw observation was published before the in-process exact // registry expired. Bind merges that observation into the permanent row. auth.authKeyClientInfos[rawAuthKeyID] = domain.AuthKeyClientInfo{Layer: 227} auth.authKeyClientInfos[permAuthKeyID] = domain.AuthKeyClientInfo{Layer: 227} return nil } sessions := &inheritedLayerCaptureSessions{explicitLayer: 227, explicitMsgID: 100} r := New(Config{DC: 2}, Deps{Auth: auth, Sessions: sessions}, zaptest.NewLogger(t), clk) freezeAndPublishLayer(t, r, rawAuthKeyID, sessionID, 100, 5, 227) sessions.seeds = nil clk.advance(exactSessionProfileTTL + time.Second) if _, ok := r.NegotiatedSessionLayer(rawAuthKeyID, sessionID); ok { t.Fatal("test setup retained expired exact registry entry") } ctx := WithAuthKeyID(WithSessionID(WithRawAuthKeyID(context.Background(), rawAuthKeyID), sessionID), rawAuthKeyID) ok, err := r.onAuthBindTempAuthKey(ctx, &tg.AuthBindTempAuthKeyRequest{PermAuthKeyID: businessAuthKeyInt64(permAuthKeyID)}) if err != nil || !ok { t.Fatalf("bind after exact TTL = (%v,%v)", ok, err) } if got := auth.authKeyClientInfos[permAuthKeyID].Layer; got != 227 { t.Fatalf("live explicit Layer was lost at bind = %d, want 227", got) } if len(sessions.refreshes) != 1 || sessions.refreshes[0].layer != 227 { t.Fatalf("bind refresh calls = %+v", sessions.refreshes) } } func TestNakedBindDoesNotRedateOlderExplicitEvidence(t *testing.T) { rawAuthKeyID := [8]byte{0x6b, 1} permAuthKeyID := [8]byte{0x6b, 2} const sessionID = int64(107) auth := &captureAuthService{authKeyClientInfos: make(map[[8]byte]domain.AuthKeyClientInfo)} auth.bindTempHook = func(domain.TempAuthKeyBinding) error { // The permanent observation is globally newer than the raw observation. // Model the transaction's observation_id merge before the router reloads. auth.authKeyClientInfos[rawAuthKeyID] = domain.AuthKeyClientInfo{Layer: 227} auth.authKeyClientInfos[permAuthKeyID] = domain.AuthKeyClientInfo{Layer: 227} return nil } sessions := &inheritedLayerCaptureSessions{explicitLayer: 225, explicitMsgID: 10} r := New(Config{DC: 2}, Deps{Auth: auth, Sessions: sessions}, zaptest.NewLogger(t), clock.System) freezeAndPublishLayer(t, r, rawAuthKeyID, sessionID, 10, 1, 225) freezeAndPublishLayer(t, r, permAuthKeyID, 2, 20, 2, 227) ctx := WithAuthKeyID(WithSessionID(WithRawAuthKeyID(context.Background(), rawAuthKeyID), sessionID), rawAuthKeyID) ctx = withLayerAdmissionSequence(ctx, 99) // naked bind order is not Layer evidence order. ok, err := r.onAuthBindTempAuthKey(ctx, &tg.AuthBindTempAuthKeyRequest{PermAuthKeyID: businessAuthKeyInt64(permAuthKeyID)}) if err != nil || !ok { t.Fatalf("naked bind = (%v,%v)", ok, err) } if got := auth.authKeyClientInfos[permAuthKeyID].Layer; got != 227 { t.Fatalf("naked bind re-dated old explicit Layer = %d, want 227", got) } if got := auth.authKeyClientInfos[rawAuthKeyID].Layer; got != 227 { t.Fatalf("raw inherited shadow did not normalize to permanent default = %d", got) } } func TestRequestBoundBindHasNoStoreCacheOrSessionSideEffects(t *testing.T) { rawAuthKeyID := [8]byte{0x6c, 1} permAuthKeyID := [8]byte{0x6c, 2} oldPermAuthKeyID := [8]byte{0x6c, 3} const sessionID = int64(108) auth := &captureAuthService{authKeyClientInfos: map[[8]byte]domain.AuthKeyClientInfo{ rawAuthKeyID: {Layer: 227, DeviceModel: "raw-before"}, permAuthKeyID: {Layer: 225}, }} sessions := &inheritedLayerCaptureSessions{explicitLayer: 227, explicitMsgID: 10} r := New(Config{DC: 2}, Deps{Auth: auth, Sessions: sessions}, zaptest.NewLogger(t), clock.System) sessions.BindAuthKeyForSession(rawAuthKeyID, sessionID, oldPermAuthKeyID) beforeSession := sessions.snapshot() now := time.Now() r.tempKeyResolveCache.Store(rawAuthKeyID, oldPermAuthKeyID, now.Add(time.Hour), now) r.setAuthUserCache(rawAuthKeyID, 101, true) r.setAuthUserCache(permAuthKeyID, 202, true) r.clientInfoMu.Lock() if r.authInfo == nil { r.authInfo = make(map[[8]byte]clientSessionInfo) } r.authInfo[rawAuthKeyID] = clientSessionInfo{ layer: 227, layerAdmissionSeq: 11, authKeyInfoChecked: true, authorizationChecked: true, hasClientInfo: true, clientInfo: ClientInfo{DeviceModel: "raw-cache"}, } r.authInfo[permAuthKeyID] = clientSessionInfo{ layer: 225, layerAdmissionSeq: 9, authKeyInfoChecked: true, authorizationChecked: true, hasClientInfo: true, clientInfo: ClientInfo{DeviceModel: "perm-cache"}, } beforeRawInfo := r.authInfo[rawAuthKeyID] beforePermInfo := r.authInfo[permAuthKeyID] r.clientInfoMu.Unlock() ctx := WithAuthKeyID(WithSessionID(WithRawAuthKeyID(context.Background(), rawAuthKeyID), sessionID), rawAuthKeyID) ctx = r.WithLayerRPCProfileEvidenceFresh(ctx, false) ok, err := r.onAuthBindTempAuthKey(ctx, &tg.AuthBindTempAuthKeyRequest{PermAuthKeyID: businessAuthKeyInt64(permAuthKeyID)}) if ok || !tgerr.Is(err, "TEMP_AUTH_KEY_EMPTY") { t.Fatalf("stale request-bound bind = (%v,%v), want (false,TEMP_AUTH_KEY_EMPTY)", ok, err) } if auth.bindTempCalls != 0 || auth.resolveCount != 0 || auth.authKeyInfoLookups != 0 || auth.authorizationLookups != 0 { t.Fatalf("stale bind touched auth store: bind=%d resolve=%d key=%d authorization=%d", auth.bindTempCalls, auth.resolveCount, auth.authKeyInfoLookups, auth.authorizationLookups) } if got := auth.authKeyClientInfos[rawAuthKeyID]; got != (domain.AuthKeyClientInfo{Layer: 227, DeviceModel: "raw-before"}) { t.Fatalf("stale bind changed raw durable fake: %+v", got) } if got := auth.authKeyClientInfos[permAuthKeyID]; got != (domain.AuthKeyClientInfo{Layer: 225}) { t.Fatalf("stale bind changed permanent durable fake: %+v", got) } if got := sessions.snapshot(); got != beforeSession { t.Fatalf("stale bind changed session binding: got %+v want %+v", got, beforeSession) } if len(sessions.seeds) != 0 || len(sessions.refreshes) != 0 || len(sessions.clears) != 0 { t.Fatalf("stale bind changed inherited session state: seeds=%+v refreshes=%+v clears=%x", sessions.seeds, sessions.refreshes, sessions.clears) } if got, found := r.tempKeyResolveCache.Get(rawAuthKeyID, oldPermAuthKeyID, now); !found || got != oldPermAuthKeyID { t.Fatalf("stale bind changed temp-key cache: got=%x found=%v", got, found) } r.authUserMu.RLock() rawUser := r.authUsers[rawAuthKeyID] permUser := r.authUsers[permAuthKeyID] r.authUserMu.RUnlock() if rawUser != (authUserCacheEntry{userID: 101, found: true}) || permUser != (authUserCacheEntry{userID: 202, found: true}) { t.Fatalf("stale bind changed auth-user cache: raw=%+v perm=%+v", rawUser, permUser) } r.clientInfoMu.RLock() afterRawInfo := r.authInfo[rawAuthKeyID] afterPermInfo := r.authInfo[permAuthKeyID] r.clientInfoMu.RUnlock() if afterRawInfo != beforeRawInfo || afterPermInfo != beforePermInfo { t.Fatalf("stale bind changed layer cache: raw=%+v/%+v perm=%+v/%+v", afterRawInfo, beforeRawInfo, afterPermInfo, beforePermInfo) } } func freezeAndPublishLayer(t *testing.T, r *Router, rawAuthKeyID [8]byte, sessionID, msgID int64, admissionSeq uint64, layer int) { t.Helper() if _, err := r.FreezeNegotiatedSessionLayerAt(rawAuthKeyID, sessionID, layer, msgID); err != nil { t.Fatal(err) } if err := r.PublishAdmittedLayerProfileEvidence(context.Background(), rawAuthKeyID, sessionID, msgID, admissionSeq, 1, layer); err != nil { t.Fatal(err) } }