package testkit import ( "fmt" "sync" "galaxy/authsession/internal/domain/common" "galaxy/authsession/internal/ports" ) // SequenceIDGenerator is a deterministic IDGenerator double that consumes // queued identifiers before falling back to monotonic generated ids. type SequenceIDGenerator struct { mu sync.Mutex // ChallengeIDs stores queued challenge identifiers returned by // NewChallengeID before generated ids are used. ChallengeIDs []common.ChallengeID // DeviceSessionIDs stores queued device-session identifiers returned by // NewDeviceSessionID before generated ids are used. DeviceSessionIDs []common.DeviceSessionID // ChallengeErr is returned directly from NewChallengeID when set. ChallengeErr error // DeviceSessionErr is returned directly from NewDeviceSessionID when set. DeviceSessionErr error ChallengePrefix string DeviceSessionPrefix string nextChallengeNumber int nextSessionNumber int } // NewChallengeID returns the next deterministic challenge identifier. func (g *SequenceIDGenerator) NewChallengeID() (common.ChallengeID, error) { if g.ChallengeErr != nil { return "", g.ChallengeErr } g.mu.Lock() defer g.mu.Unlock() if len(g.ChallengeIDs) > 0 { id := g.ChallengeIDs[0] g.ChallengeIDs = g.ChallengeIDs[1:] if err := id.Validate(); err != nil { return "", err } return id, nil } g.nextChallengeNumber++ prefix := g.ChallengePrefix if prefix == "" { prefix = "challenge-" } id := common.ChallengeID(fmt.Sprintf("%s%d", prefix, g.nextChallengeNumber)) if err := id.Validate(); err != nil { return "", err } return id, nil } // NewDeviceSessionID returns the next deterministic device-session // identifier. func (g *SequenceIDGenerator) NewDeviceSessionID() (common.DeviceSessionID, error) { if g.DeviceSessionErr != nil { return "", g.DeviceSessionErr } g.mu.Lock() defer g.mu.Unlock() if len(g.DeviceSessionIDs) > 0 { id := g.DeviceSessionIDs[0] g.DeviceSessionIDs = g.DeviceSessionIDs[1:] if err := id.Validate(); err != nil { return "", err } return id, nil } g.nextSessionNumber++ prefix := g.DeviceSessionPrefix if prefix == "" { prefix = "device-session-" } id := common.DeviceSessionID(fmt.Sprintf("%s%d", prefix, g.nextSessionNumber)) if err := id.Validate(); err != nil { return "", err } return id, nil } var _ ports.IDGenerator = (*SequenceIDGenerator)(nil)