Improved test comments

master
oabrivard 1 month ago
parent 2fde7f1ff7
commit 05d950218c

@ -1,6 +1,6 @@
package tests package tests
// integration_http_test.go contains tests for backend behavior. // integration_http_test.go contains backend tests for package behavior, error paths, and regressions.
import ( import (
"context" "context"
@ -30,15 +30,15 @@ type inMemoryAuditRepo struct {
appendErr error appendErr error
} }
// newInMemoryAuditRepo is a test helper. // newInMemoryAuditRepo creates in-memory test doubles and deterministic fixtures.
func newInMemoryAuditRepo() *inMemoryAuditRepo { func newInMemoryAuditRepo() *inMemoryAuditRepo {
return &inMemoryAuditRepo{entries: make([]audit.Entry, 0)} return &inMemoryAuditRepo{entries: make([]audit.Entry, 0)}
} }
// EnsureSchema is a test helper. // EnsureSchema initializes schema state required before repository operations.
func (r *inMemoryAuditRepo) EnsureSchema(ctx context.Context) error { return nil } func (r *inMemoryAuditRepo) EnsureSchema(ctx context.Context) error { return nil }
// Append is a test helper. // Append appends audit records to the in-memory repository.
func (r *inMemoryAuditRepo) Append(ctx context.Context, e audit.Entry) error { func (r *inMemoryAuditRepo) Append(ctx context.Context, e audit.Entry) error {
if r.appendErr != nil { if r.appendErr != nil {
return r.appendErr return r.appendErr
@ -47,7 +47,7 @@ func (r *inMemoryAuditRepo) Append(ctx context.Context, e audit.Entry) error {
return nil return nil
} }
// List is a test helper. // List returns filtered collections from the in-memory repository.
func (r *inMemoryAuditRepo) List(ctx context.Context, limit, offset int) ([]audit.Entry, error) { func (r *inMemoryAuditRepo) List(ctx context.Context, limit, offset int) ([]audit.Entry, error) {
if r.listErr != nil { if r.listErr != nil {
return nil, r.listErr return nil, r.listErr
@ -64,7 +64,7 @@ func (r *inMemoryAuditRepo) List(ctx context.Context, limit, offset int) ([]audi
return out, nil return out, nil
} }
// Count is a test helper. // Count returns aggregate counts from the in-memory repository.
func (r *inMemoryAuditRepo) Count(ctx context.Context) (int64, error) { func (r *inMemoryAuditRepo) Count(ctx context.Context) (int64, error) {
if r.countErr != nil { if r.countErr != nil {
return 0, r.countErr return 0, r.countErr
@ -72,12 +72,12 @@ func (r *inMemoryAuditRepo) Count(ctx context.Context) (int64, error) {
return int64(len(r.entries)), nil return int64(len(r.entries)), nil
} }
// PruneBefore is a test helper. // PruneBefore supports prune before test setup and assertions.
func (r *inMemoryAuditRepo) PruneBefore(ctx context.Context, before time.Time) (int64, error) { func (r *inMemoryAuditRepo) PruneBefore(ctx context.Context, before time.Time) (int64, error) {
return 0, nil return 0, nil
} }
// setupApp is a test helper. // setupApp wires the test application with mocked dependencies.
func setupApp(t *testing.T, repo *inMemoryAuditRepo) *fiber.App { func setupApp(t *testing.T, repo *inMemoryAuditRepo) *fiber.App {
t.Helper() t.Helper()
@ -114,7 +114,7 @@ func setupApp(t *testing.T, repo *inMemoryAuditRepo) *fiber.App {
return app return app
} }
// TestAdminAuthRoute verifies expected behavior. // TestAdminAuthRoute ensures admin auth route behavior is handled correctly.
func TestAdminAuthRoute(t *testing.T) { func TestAdminAuthRoute(t *testing.T) {
repo := newInMemoryAuditRepo() repo := newInMemoryAuditRepo()
app := setupApp(t, repo) app := setupApp(t, repo)
@ -133,7 +133,7 @@ func TestAdminAuthRoute(t *testing.T) {
} }
} }
// TestDashboardRouteSuccessAndErrors verifies expected behavior. // TestDashboardRouteSuccessAndErrors ensures dashboard route success and errors behavior is handled correctly.
func TestDashboardRouteSuccessAndErrors(t *testing.T) { func TestDashboardRouteSuccessAndErrors(t *testing.T) {
t.Run("forbidden for non-admin", func(t *testing.T) { t.Run("forbidden for non-admin", func(t *testing.T) {
repo := newInMemoryAuditRepo() repo := newInMemoryAuditRepo()
@ -181,7 +181,7 @@ func TestDashboardRouteSuccessAndErrors(t *testing.T) {
}) })
} }
// TestAuditRouteSuccessAndErrors verifies expected behavior. // TestAuditRouteSuccessAndErrors ensures audit route success and errors behavior is handled correctly.
func TestAuditRouteSuccessAndErrors(t *testing.T) { func TestAuditRouteSuccessAndErrors(t *testing.T) {
t.Run("forbidden for non-admin", func(t *testing.T) { t.Run("forbidden for non-admin", func(t *testing.T) {
repo := newInMemoryAuditRepo() repo := newInMemoryAuditRepo()
@ -233,7 +233,7 @@ func TestAuditRouteSuccessAndErrors(t *testing.T) {
}) })
} }
// TestRegisterRoutesDoesNotPanic verifies expected behavior. // TestRegisterRoutesDoesNotPanic ensures register routes does not panic behavior is handled correctly.
func TestRegisterRoutesDoesNotPanic(t *testing.T) { func TestRegisterRoutesDoesNotPanic(t *testing.T) {
app := fiber.New() app := fiber.New()
repo := newInMemoryAuditRepo() repo := newInMemoryAuditRepo()

@ -1,6 +1,6 @@
package session package session
// service_test.go contains tests for backend behavior. // service_test.go contains backend tests for package behavior, error paths, and regressions.
import ( import (
"context" "context"
@ -19,7 +19,7 @@ type fakeRepo struct {
events []*domain.SessionEvent events []*domain.SessionEvent
} }
// newFakeRepo is a test helper. // newFakeRepo creates in-memory test doubles and deterministic fixtures.
func newFakeRepo() *fakeRepo { func newFakeRepo() *fakeRepo {
return &fakeRepo{ return &fakeRepo{
sessions: map[string]*domain.GameSession{}, sessions: map[string]*domain.GameSession{},
@ -28,10 +28,10 @@ func newFakeRepo() *fakeRepo {
} }
} }
// EnsureSchema is a test helper. // EnsureSchema initializes schema state required before repository operations.
func (r *fakeRepo) EnsureSchema(ctx context.Context) error { return nil } func (r *fakeRepo) EnsureSchema(ctx context.Context) error { return nil }
// CreateSession is a test helper. // CreateSession persists a new entity in the in-memory repository.
func (r *fakeRepo) CreateSession(ctx context.Context, session *domain.GameSession) (*domain.GameSession, error) { func (r *fakeRepo) CreateSession(ctx context.Context, session *domain.GameSession) (*domain.GameSession, error) {
session.ID = "sess-1" session.ID = "sess-1"
now := time.Now().UTC() now := time.Now().UTC()
@ -42,7 +42,7 @@ func (r *fakeRepo) CreateSession(ctx context.Context, session *domain.GameSessio
return &cp, nil return &cp, nil
} }
// GetSessionByID is a test helper. // GetSessionByID retrieves data from the in-memory repository.
func (r *fakeRepo) GetSessionByID(ctx context.Context, id string) (*domain.GameSession, error) { func (r *fakeRepo) GetSessionByID(ctx context.Context, id string) (*domain.GameSession, error) {
s, ok := r.sessions[id] s, ok := r.sessions[id]
if !ok { if !ok {
@ -52,7 +52,7 @@ func (r *fakeRepo) GetSessionByID(ctx context.Context, id string) (*domain.GameS
return &cp, nil return &cp, nil
} }
// GetActiveSessionByPlayerID is a test helper. // GetActiveSessionByPlayerID retrieves data from the in-memory repository.
func (r *fakeRepo) GetActiveSessionByPlayerID(ctx context.Context, playerID string) (*domain.GameSession, error) { func (r *fakeRepo) GetActiveSessionByPlayerID(ctx context.Context, playerID string) (*domain.GameSession, error) {
for _, s := range r.sessions { for _, s := range r.sessions {
if s.PlayerID == playerID && s.Status == domain.StatusActive { if s.PlayerID == playerID && s.Status == domain.StatusActive {
@ -63,7 +63,7 @@ func (r *fakeRepo) GetActiveSessionByPlayerID(ctx context.Context, playerID stri
return nil, domain.ErrSessionNotFound return nil, domain.ErrSessionNotFound
} }
// UpdateSession is a test helper. // UpdateSession updates an existing entity in the in-memory repository.
func (r *fakeRepo) UpdateSession(ctx context.Context, session *domain.GameSession) (*domain.GameSession, error) { func (r *fakeRepo) UpdateSession(ctx context.Context, session *domain.GameSession) (*domain.GameSession, error) {
if _, ok := r.sessions[session.ID]; !ok { if _, ok := r.sessions[session.ID]; !ok {
return nil, domain.ErrSessionNotFound return nil, domain.ErrSessionNotFound
@ -74,21 +74,21 @@ func (r *fakeRepo) UpdateSession(ctx context.Context, session *domain.GameSessio
return &cp, nil return &cp, nil
} }
// CreateAttempt is a test helper. // CreateAttempt persists a new entity in the in-memory repository.
func (r *fakeRepo) CreateAttempt(ctx context.Context, attempt *domain.SessionAttempt) error { func (r *fakeRepo) CreateAttempt(ctx context.Context, attempt *domain.SessionAttempt) error {
cp := *attempt cp := *attempt
r.attempts = append(r.attempts, &cp) r.attempts = append(r.attempts, &cp)
return nil return nil
} }
// CreateEvent is a test helper. // CreateEvent persists a new entity in the in-memory repository.
func (r *fakeRepo) CreateEvent(ctx context.Context, event *domain.SessionEvent) error { func (r *fakeRepo) CreateEvent(ctx context.Context, event *domain.SessionEvent) error {
cp := *event cp := *event
r.events = append(r.events, &cp) r.events = append(r.events, &cp)
return nil return nil
} }
// ListQuestionIDsForSession is a test helper. // ListQuestionIDsForSession returns filtered collections from the in-memory repository.
func (r *fakeRepo) ListQuestionIDsForSession(ctx context.Context, sessionID string) ([]string, error) { func (r *fakeRepo) ListQuestionIDsForSession(ctx context.Context, sessionID string) ([]string, error) {
seen := map[string]bool{} seen := map[string]bool{}
ids := make([]string, 0) ids := make([]string, 0)
@ -111,7 +111,7 @@ type fakeQuestionBank struct {
answerOK bool answerOK bool
} }
// GetRandomQuestion is a test helper. // GetRandomQuestion retrieves data from the in-memory repository.
func (f *fakeQuestionBank) GetRandomQuestion( func (f *fakeQuestionBank) GetRandomQuestion(
ctx context.Context, ctx context.Context,
exclusions []string, exclusions []string,
@ -131,7 +131,7 @@ func (f *fakeQuestionBank) GetRandomQuestion(
return nil, sharederrors.New(sharederrors.CodeNoQuestionsAvailable, "no questions available") return nil, sharederrors.New(sharederrors.CodeNoQuestionsAvailable, "no questions available")
} }
// GetQuestionByID is a test helper. // GetQuestionByID retrieves data from the in-memory repository.
func (f *fakeQuestionBank) GetQuestionByID(ctx context.Context, id string) (*SessionQuestion, error) { func (f *fakeQuestionBank) GetQuestionByID(ctx context.Context, id string) (*SessionQuestion, error) {
for _, q := range f.questions { for _, q := range f.questions {
if q.ID == id { if q.ID == id {
@ -142,7 +142,7 @@ func (f *fakeQuestionBank) GetQuestionByID(ctx context.Context, id string) (*Ses
return nil, sharederrors.New(sharederrors.CodeQuestionNotFound, "question not found") return nil, sharederrors.New(sharederrors.CodeQuestionNotFound, "question not found")
} }
// ValidateAnswer is a test helper. // ValidateAnswer supports validate answer test setup and assertions.
func (f *fakeQuestionBank) ValidateAnswer( func (f *fakeQuestionBank) ValidateAnswer(
ctx context.Context, ctx context.Context,
questionID, answer string, questionID, answer string,
@ -155,7 +155,7 @@ type fakeUserClient struct {
profile UserProfile profile UserProfile
} }
// GetUserProfile is a test helper. // GetUserProfile retrieves data from the in-memory repository.
func (f *fakeUserClient) GetUserProfile(ctx context.Context, userID, bearerToken string) (*UserProfile, error) { func (f *fakeUserClient) GetUserProfile(ctx context.Context, userID, bearerToken string) (*UserProfile, error) {
p := f.profile p := f.profile
if p.ID == "" { if p.ID == "" {
@ -171,7 +171,7 @@ type fakeStateStore struct {
locks map[string]bool locks map[string]bool
} }
// newFakeStateStore is a test helper. // newFakeStateStore creates in-memory test doubles and deterministic fixtures.
func newFakeStateStore() *fakeStateStore { func newFakeStateStore() *fakeStateStore {
return &fakeStateStore{ return &fakeStateStore{
active: map[string]string{}, active: map[string]string{},
@ -180,43 +180,43 @@ func newFakeStateStore() *fakeStateStore {
} }
} }
// GetActiveSession is a test helper. // GetActiveSession retrieves data from the in-memory repository.
func (s *fakeStateStore) GetActiveSession(ctx context.Context, playerID string) (string, bool) { func (s *fakeStateStore) GetActiveSession(ctx context.Context, playerID string) (string, bool) {
id, ok := s.active[playerID] id, ok := s.active[playerID]
return id, ok return id, ok
} }
// SetActiveSession is a test helper. // SetActiveSession stores ephemeral state in the fake cache or state store.
func (s *fakeStateStore) SetActiveSession(ctx context.Context, playerID, sessionID string, ttl time.Duration) error { func (s *fakeStateStore) SetActiveSession(ctx context.Context, playerID, sessionID string, ttl time.Duration) error {
s.active[playerID] = sessionID s.active[playerID] = sessionID
return nil return nil
} }
// ClearActiveSession is a test helper. // ClearActiveSession removes ephemeral state from the fake cache or state store.
func (s *fakeStateStore) ClearActiveSession(ctx context.Context, playerID string) error { func (s *fakeStateStore) ClearActiveSession(ctx context.Context, playerID string) error {
delete(s.active, playerID) delete(s.active, playerID)
return nil return nil
} }
// SetTimer is a test helper. // SetTimer stores ephemeral state in the fake cache or state store.
func (s *fakeStateStore) SetTimer(ctx context.Context, sessionID string, expiresAt time.Time, ttl time.Duration) error { func (s *fakeStateStore) SetTimer(ctx context.Context, sessionID string, expiresAt time.Time, ttl time.Duration) error {
s.timers[sessionID] = expiresAt s.timers[sessionID] = expiresAt
return nil return nil
} }
// GetTimer is a test helper. // GetTimer retrieves data from the in-memory repository.
func (s *fakeStateStore) GetTimer(ctx context.Context, sessionID string) (time.Time, bool) { func (s *fakeStateStore) GetTimer(ctx context.Context, sessionID string) (time.Time, bool) {
t, ok := s.timers[sessionID] t, ok := s.timers[sessionID]
return t, ok return t, ok
} }
// ClearTimer is a test helper. // ClearTimer removes ephemeral state from the fake cache or state store.
func (s *fakeStateStore) ClearTimer(ctx context.Context, sessionID string) error { func (s *fakeStateStore) ClearTimer(ctx context.Context, sessionID string) error {
delete(s.timers, sessionID) delete(s.timers, sessionID)
return nil return nil
} }
// AcquireLock is a test helper. // AcquireLock simulates distributed lock coordination for concurrent test flows.
func (s *fakeStateStore) AcquireLock(ctx context.Context, sessionID string, ttl time.Duration) bool { func (s *fakeStateStore) AcquireLock(ctx context.Context, sessionID string, ttl time.Duration) bool {
if s.locks[sessionID] { if s.locks[sessionID] {
return false return false
@ -225,12 +225,12 @@ func (s *fakeStateStore) AcquireLock(ctx context.Context, sessionID string, ttl
return true return true
} }
// ReleaseLock is a test helper. // ReleaseLock simulates distributed lock coordination for concurrent test flows.
func (s *fakeStateStore) ReleaseLock(ctx context.Context, sessionID string) { func (s *fakeStateStore) ReleaseLock(ctx context.Context, sessionID string) {
delete(s.locks, sessionID) delete(s.locks, sessionID)
} }
// TestStartSessionCreatesActiveSessionAndFirstQuestion verifies start initializes active session state. // TestStartSessionCreatesActiveSessionAndFirstQuestion ensures start session creates active session and first question behavior is handled correctly.
func TestStartSessionCreatesActiveSessionAndFirstQuestion(t *testing.T) { func TestStartSessionCreatesActiveSessionAndFirstQuestion(t *testing.T) {
svc := NewService( svc := NewService(
newFakeRepo(), newFakeRepo(),
@ -255,7 +255,7 @@ func TestStartSessionCreatesActiveSessionAndFirstQuestion(t *testing.T) {
} }
} }
// TestSubmitAnswerScoresAndAdvances verifies correct answer awards score and advances question. // TestSubmitAnswerScoresAndAdvances ensures submit answer scores and advances behavior is handled correctly.
func TestSubmitAnswerScoresAndAdvances(t *testing.T) { func TestSubmitAnswerScoresAndAdvances(t *testing.T) {
repo := newFakeRepo() repo := newFakeRepo()
svc := NewService( svc := NewService(
@ -297,7 +297,7 @@ func TestSubmitAnswerScoresAndAdvances(t *testing.T) {
} }
} }
// TestRequestHintOnlyOnce verifies hint can only be requested once per current question. // TestRequestHintOnlyOnce ensures request hint only once behavior is handled correctly.
func TestRequestHintOnlyOnce(t *testing.T) { func TestRequestHintOnlyOnce(t *testing.T) {
svc := NewService( svc := NewService(
newFakeRepo(), newFakeRepo(),
@ -325,7 +325,7 @@ func TestRequestHintOnlyOnce(t *testing.T) {
} }
} }
// TestSubmitAnswerRejectsRapidAnswers verifies anti-cheat latency guard blocks rapid submissions. // TestSubmitAnswerRejectsRapidAnswers ensures submit answer rejects rapid answers behavior is handled correctly.
func TestSubmitAnswerRejectsRapidAnswers(t *testing.T) { func TestSubmitAnswerRejectsRapidAnswers(t *testing.T) {
repo := newFakeRepo() repo := newFakeRepo()
svc := NewService( svc := NewService(
@ -349,7 +349,7 @@ func TestSubmitAnswerRejectsRapidAnswers(t *testing.T) {
} }
} }
// TestSubmitAnswerTimeoutTransition verifies expired sessions transition to timed_out on mutation. // TestSubmitAnswerTimeoutTransition ensures submit answer timeout transition behavior is handled correctly.
func TestSubmitAnswerTimeoutTransition(t *testing.T) { func TestSubmitAnswerTimeoutTransition(t *testing.T) {
svc := NewService( svc := NewService(
newFakeRepo(), newFakeRepo(),
@ -370,7 +370,7 @@ func TestSubmitAnswerTimeoutTransition(t *testing.T) {
} }
} }
// TestSubmitAnswerAdvancesAfterMaxAttempts verifies incorrect answer flow advances after max attempts. // TestSubmitAnswerAdvancesAfterMaxAttempts ensures submit answer advances after max attempts behavior is handled correctly.
func TestSubmitAnswerAdvancesAfterMaxAttempts(t *testing.T) { func TestSubmitAnswerAdvancesAfterMaxAttempts(t *testing.T) {
svc := NewService( svc := NewService(
newFakeRepo(), newFakeRepo(),

@ -1,6 +1,6 @@
package http package http
// handler_unit_test.go contains tests for backend behavior. // handler_unit_test.go contains backend tests for package behavior, error paths, and regressions.
import ( import (
"bytes" "bytes"
@ -13,7 +13,7 @@ import (
"knowfoolery/backend/shared/infra/utils/validation" "knowfoolery/backend/shared/infra/utils/validation"
) )
// TestUnauthorizedBranches verifies expected behavior. // TestUnauthorizedBranches ensures unauthorized branches behavior is handled correctly.
func TestUnauthorizedBranches(t *testing.T) { func TestUnauthorizedBranches(t *testing.T) {
h := NewHandler(nil, validation.NewValidator(), nil, nil) h := NewHandler(nil, validation.NewValidator(), nil, nil)
app := fiber.New() app := fiber.New()
@ -48,7 +48,7 @@ func TestUnauthorizedBranches(t *testing.T) {
} }
} }
// TestStartAndEndValidationBranches verifies expected behavior. // TestStartAndEndValidationBranches ensures start and end validation branches behavior is handled correctly.
func TestStartAndEndValidationBranches(t *testing.T) { func TestStartAndEndValidationBranches(t *testing.T) {
h := NewHandler(nil, validation.NewValidator(), nil, nil) h := NewHandler(nil, validation.NewValidator(), nil, nil)
app := fiber.New() app := fiber.New()
@ -107,7 +107,7 @@ func TestStartAndEndValidationBranches(t *testing.T) {
} }
// TestBearerTokenAndClaimsHelpers verifies expected behavior. // TestBearerTokenAndClaimsHelpers ensures bearer token and claims helpers behavior is handled correctly.
func TestBearerTokenAndClaimsHelpers(t *testing.T) { func TestBearerTokenAndClaimsHelpers(t *testing.T) {
app := fiber.New() app := fiber.New()
app.Use(func(c fiber.Ctx) error { app.Use(func(c fiber.Ctx) error {

@ -29,7 +29,7 @@ type inMemoryRepo struct {
attempts []*domain.SessionAttempt attempts []*domain.SessionAttempt
} }
// newInMemoryRepo is a test helper. // newInMemoryRepo creates in-memory test doubles and deterministic fixtures.
func newInMemoryRepo() *inMemoryRepo { func newInMemoryRepo() *inMemoryRepo {
return &inMemoryRepo{ return &inMemoryRepo{
sessions: map[string]*domain.GameSession{}, sessions: map[string]*domain.GameSession{},
@ -37,10 +37,10 @@ func newInMemoryRepo() *inMemoryRepo {
} }
} }
// EnsureSchema is a test helper. // EnsureSchema initializes schema state required before repository operations.
func (r *inMemoryRepo) EnsureSchema(ctx context.Context) error { return nil } func (r *inMemoryRepo) EnsureSchema(ctx context.Context) error { return nil }
// CreateSession is a test helper. // CreateSession persists a new entity in the in-memory repository.
func (r *inMemoryRepo) CreateSession(ctx context.Context, session *domain.GameSession) (*domain.GameSession, error) { func (r *inMemoryRepo) CreateSession(ctx context.Context, session *domain.GameSession) (*domain.GameSession, error) {
session.ID = "sess-1" session.ID = "sess-1"
now := time.Now().UTC() now := time.Now().UTC()
@ -51,7 +51,7 @@ func (r *inMemoryRepo) CreateSession(ctx context.Context, session *domain.GameSe
return &cp, nil return &cp, nil
} }
// GetSessionByID is a test helper. // GetSessionByID retrieves data from the in-memory repository.
func (r *inMemoryRepo) GetSessionByID(ctx context.Context, id string) (*domain.GameSession, error) { func (r *inMemoryRepo) GetSessionByID(ctx context.Context, id string) (*domain.GameSession, error) {
s, ok := r.sessions[id] s, ok := r.sessions[id]
if !ok { if !ok {
@ -61,7 +61,7 @@ func (r *inMemoryRepo) GetSessionByID(ctx context.Context, id string) (*domain.G
return &cp, nil return &cp, nil
} }
// GetActiveSessionByPlayerID is a test helper. // GetActiveSessionByPlayerID retrieves data from the in-memory repository.
func (r *inMemoryRepo) GetActiveSessionByPlayerID(ctx context.Context, playerID string) (*domain.GameSession, error) { func (r *inMemoryRepo) GetActiveSessionByPlayerID(ctx context.Context, playerID string) (*domain.GameSession, error) {
for _, s := range r.sessions { for _, s := range r.sessions {
if s.PlayerID == playerID && s.Status == domain.StatusActive { if s.PlayerID == playerID && s.Status == domain.StatusActive {
@ -72,7 +72,7 @@ func (r *inMemoryRepo) GetActiveSessionByPlayerID(ctx context.Context, playerID
return nil, domain.ErrSessionNotFound return nil, domain.ErrSessionNotFound
} }
// UpdateSession is a test helper. // UpdateSession updates an existing entity in the in-memory repository.
func (r *inMemoryRepo) UpdateSession(ctx context.Context, session *domain.GameSession) (*domain.GameSession, error) { func (r *inMemoryRepo) UpdateSession(ctx context.Context, session *domain.GameSession) (*domain.GameSession, error) {
cp := *session cp := *session
cp.UpdatedAt = time.Now().UTC() cp.UpdatedAt = time.Now().UTC()
@ -80,17 +80,17 @@ func (r *inMemoryRepo) UpdateSession(ctx context.Context, session *domain.GameSe
return &cp, nil return &cp, nil
} }
// CreateAttempt is a test helper. // CreateAttempt persists a new entity in the in-memory repository.
func (r *inMemoryRepo) CreateAttempt(ctx context.Context, attempt *domain.SessionAttempt) error { func (r *inMemoryRepo) CreateAttempt(ctx context.Context, attempt *domain.SessionAttempt) error {
cp := *attempt cp := *attempt
r.attempts = append(r.attempts, &cp) r.attempts = append(r.attempts, &cp)
return nil return nil
} }
// CreateEvent is a test helper. // CreateEvent persists a new entity in the in-memory repository.
func (r *inMemoryRepo) CreateEvent(ctx context.Context, event *domain.SessionEvent) error { return nil } func (r *inMemoryRepo) CreateEvent(ctx context.Context, event *domain.SessionEvent) error { return nil }
// ListQuestionIDsForSession is a test helper. // ListQuestionIDsForSession returns filtered collections from the in-memory repository.
func (r *inMemoryRepo) ListQuestionIDsForSession(ctx context.Context, sessionID string) ([]string, error) { func (r *inMemoryRepo) ListQuestionIDsForSession(ctx context.Context, sessionID string) ([]string, error) {
seen := map[string]bool{} seen := map[string]bool{}
ids := make([]string, 0) ids := make([]string, 0)
@ -112,7 +112,7 @@ type fakeQuestionBank struct {
questions []appsession.SessionQuestion questions []appsession.SessionQuestion
} }
// GetRandomQuestion is a test helper. // GetRandomQuestion retrieves data from the in-memory repository.
func (f *fakeQuestionBank) GetRandomQuestion( func (f *fakeQuestionBank) GetRandomQuestion(
ctx context.Context, ctx context.Context,
exclusions []string, exclusions []string,
@ -132,7 +132,7 @@ func (f *fakeQuestionBank) GetRandomQuestion(
return nil, domain.ErrSessionNotFound return nil, domain.ErrSessionNotFound
} }
// GetQuestionByID is a test helper. // GetQuestionByID retrieves data from the in-memory repository.
func (f *fakeQuestionBank) GetQuestionByID(ctx context.Context, id string) (*appsession.SessionQuestion, error) { func (f *fakeQuestionBank) GetQuestionByID(ctx context.Context, id string) (*appsession.SessionQuestion, error) {
for _, q := range f.questions { for _, q := range f.questions {
if q.ID == id { if q.ID == id {
@ -143,7 +143,7 @@ func (f *fakeQuestionBank) GetQuestionByID(ctx context.Context, id string) (*app
return nil, domain.ErrSessionNotFound return nil, domain.ErrSessionNotFound
} }
// ValidateAnswer is a test helper. // ValidateAnswer supports validate answer test setup and assertions.
func (f *fakeQuestionBank) ValidateAnswer( func (f *fakeQuestionBank) ValidateAnswer(
ctx context.Context, ctx context.Context,
questionID, answer string, questionID, answer string,
@ -157,7 +157,7 @@ func (f *fakeQuestionBank) ValidateAnswer(
// fakeUserClient returns a verified user profile for tests. // fakeUserClient returns a verified user profile for tests.
type fakeUserClient struct{} type fakeUserClient struct{}
// GetUserProfile is a test helper. // GetUserProfile retrieves data from the in-memory repository.
func (f *fakeUserClient) GetUserProfile( func (f *fakeUserClient) GetUserProfile(
ctx context.Context, ctx context.Context,
userID, bearerToken string, userID, bearerToken string,
@ -175,7 +175,7 @@ type fakeStateStore struct {
locks map[string]bool locks map[string]bool
} }
// newFakeStateStore is a test helper. // newFakeStateStore creates in-memory test doubles and deterministic fixtures.
func newFakeStateStore() *fakeStateStore { func newFakeStateStore() *fakeStateStore {
return &fakeStateStore{ return &fakeStateStore{
active: map[string]string{}, active: map[string]string{},
@ -183,38 +183,38 @@ func newFakeStateStore() *fakeStateStore {
} }
} }
// GetActiveSession is a test helper. // GetActiveSession retrieves data from the in-memory repository.
func (s *fakeStateStore) GetActiveSession(ctx context.Context, playerID string) (string, bool) { func (s *fakeStateStore) GetActiveSession(ctx context.Context, playerID string) (string, bool) {
id, ok := s.active[playerID] id, ok := s.active[playerID]
return id, ok return id, ok
} }
// SetActiveSession is a test helper. // SetActiveSession stores ephemeral state in the fake cache or state store.
func (s *fakeStateStore) SetActiveSession(ctx context.Context, playerID, sessionID string, ttl time.Duration) error { func (s *fakeStateStore) SetActiveSession(ctx context.Context, playerID, sessionID string, ttl time.Duration) error {
s.active[playerID] = sessionID s.active[playerID] = sessionID
return nil return nil
} }
// ClearActiveSession is a test helper. // ClearActiveSession removes ephemeral state from the fake cache or state store.
func (s *fakeStateStore) ClearActiveSession(ctx context.Context, playerID string) error { func (s *fakeStateStore) ClearActiveSession(ctx context.Context, playerID string) error {
delete(s.active, playerID) delete(s.active, playerID)
return nil return nil
} }
// SetTimer is a test helper. // SetTimer stores ephemeral state in the fake cache or state store.
func (s *fakeStateStore) SetTimer(ctx context.Context, sessionID string, expiresAt time.Time, ttl time.Duration) error { func (s *fakeStateStore) SetTimer(ctx context.Context, sessionID string, expiresAt time.Time, ttl time.Duration) error {
return nil return nil
} }
// GetTimer is a test helper. // GetTimer retrieves data from the in-memory repository.
func (s *fakeStateStore) GetTimer(ctx context.Context, sessionID string) (time.Time, bool) { func (s *fakeStateStore) GetTimer(ctx context.Context, sessionID string) (time.Time, bool) {
return time.Time{}, false return time.Time{}, false
} }
// ClearTimer is a test helper. // ClearTimer removes ephemeral state from the fake cache or state store.
func (s *fakeStateStore) ClearTimer(ctx context.Context, sessionID string) error { return nil } func (s *fakeStateStore) ClearTimer(ctx context.Context, sessionID string) error { return nil }
// AcquireLock is a test helper. // AcquireLock simulates distributed lock coordination for concurrent test flows.
func (s *fakeStateStore) AcquireLock(ctx context.Context, sessionID string, ttl time.Duration) bool { func (s *fakeStateStore) AcquireLock(ctx context.Context, sessionID string, ttl time.Duration) bool {
if s.locks[sessionID] { if s.locks[sessionID] {
return false return false
@ -223,12 +223,12 @@ func (s *fakeStateStore) AcquireLock(ctx context.Context, sessionID string, ttl
return true return true
} }
// ReleaseLock is a test helper. // ReleaseLock simulates distributed lock coordination for concurrent test flows.
func (s *fakeStateStore) ReleaseLock(ctx context.Context, sessionID string) { func (s *fakeStateStore) ReleaseLock(ctx context.Context, sessionID string) {
delete(s.locks, sessionID) delete(s.locks, sessionID)
} }
// setupApp wires a test Fiber app with in-memory dependencies and auth middleware. // setupApp wires the test application with mocked dependencies.
func setupApp(t *testing.T) *fiber.App { func setupApp(t *testing.T) *fiber.App {
t.Helper() t.Helper()
@ -280,7 +280,7 @@ func setupApp(t *testing.T) *fiber.App {
return app return app
} }
// TestSessionFlowEndpoints validates start, session read, question read, answer, hint, and end flows. // TestSessionFlowEndpoints ensures session flow endpoints behavior is handled correctly.
func TestSessionFlowEndpoints(t *testing.T) { func TestSessionFlowEndpoints(t *testing.T) {
app := setupApp(t) app := setupApp(t)
@ -328,7 +328,7 @@ func TestSessionFlowEndpoints(t *testing.T) {
} }
} }
// TestSessionUnauthorizedAndForbidden validates auth and ownership guards on session endpoints. // TestSessionUnauthorizedAndForbidden ensures session unauthorized and forbidden behavior is handled correctly.
func TestSessionUnauthorizedAndForbidden(t *testing.T) { func TestSessionUnauthorizedAndForbidden(t *testing.T) {
app := setupApp(t) app := setupApp(t)
@ -363,7 +363,7 @@ func TestSessionUnauthorizedAndForbidden(t *testing.T) {
} }
} }
// TestMetricsEndpoint verifies /metrics is reachable. // TestMetricsEndpoint ensures metrics endpoint behavior is handled correctly.
func TestMetricsEndpoint(t *testing.T) { func TestMetricsEndpoint(t *testing.T) {
app := setupApp(t) app := setupApp(t)
req := httptest.NewRequest(http.MethodGet, "/metrics", nil) req := httptest.NewRequest(http.MethodGet, "/metrics", nil)
@ -372,7 +372,7 @@ func TestMetricsEndpoint(t *testing.T) {
assertStatus(t, resp, http.StatusOK, "metrics failed") assertStatus(t, resp, http.StatusOK, "metrics failed")
} }
// mustJSONRequest is a test helper. // mustJSONRequest builds request fixtures and fails fast on malformed setup.
func mustJSONRequest( func mustJSONRequest(
t *testing.T, t *testing.T,
app *fiber.App, app *fiber.App,
@ -399,7 +399,7 @@ func mustJSONRequest(
return sharedhttpx.MustTest(t, app, req) return sharedhttpx.MustTest(t, app, req)
} }
// assertStatus is a test helper. // assertStatus asserts HTTP status codes and response payload invariants.
func assertStatus(t *testing.T, resp *http.Response, want int, msg string) { func assertStatus(t *testing.T, resp *http.Response, want int, msg string) {
t.Helper() t.Helper()
if resp.StatusCode != want { if resp.StatusCode != want {
@ -407,7 +407,7 @@ func assertStatus(t *testing.T, resp *http.Response, want int, msg string) {
} }
} }
// decodeDataMap is a test helper. // decodeDataMap decodes response data into assertion-friendly structures.
func decodeDataMap(t *testing.T, resp *http.Response) map[string]any { func decodeDataMap(t *testing.T, resp *http.Response) map[string]any {
t.Helper() t.Helper()
@ -421,7 +421,7 @@ func decodeDataMap(t *testing.T, resp *http.Response) map[string]any {
return payload.Data return payload.Data
} }
// asMap is a test helper. // asMap decodes response data into assertion-friendly structures.
func asMap(t *testing.T, v any) map[string]any { func asMap(t *testing.T, v any) map[string]any {
t.Helper() t.Helper()
m, ok := v.(map[string]any) m, ok := v.(map[string]any)
@ -431,7 +431,7 @@ func asMap(t *testing.T, v any) map[string]any {
return m return m
} }
// asString is a test helper. // asString decodes response data into assertion-friendly structures.
func asString(t *testing.T, v any) string { func asString(t *testing.T, v any) string {
t.Helper() t.Helper()
s, ok := v.(string) s, ok := v.(string)
@ -441,7 +441,7 @@ func asString(t *testing.T, v any) string {
return s return s
} }
// decodeAny is a test helper. // decodeAny decodes response data into assertion-friendly structures.
func decodeAny(t *testing.T, resp *http.Response) map[string]any { func decodeAny(t *testing.T, resp *http.Response) map[string]any {
t.Helper() t.Helper()

@ -1,6 +1,6 @@
package middleware package middleware
// middleware_test.go contains tests for backend behavior. // middleware_test.go contains backend tests for package behavior, error paths, and regressions.
import ( import (
"net/http" "net/http"
@ -18,7 +18,7 @@ import (
"knowfoolery/backend/shared/infra/observability/logging" "knowfoolery/backend/shared/infra/observability/logging"
) )
// TestCORSMiddleware verifies expected behavior. // TestCORSMiddleware ensures cors middleware behavior is handled correctly.
func TestCORSMiddleware(t *testing.T) { func TestCORSMiddleware(t *testing.T) {
app := fiber.New() app := fiber.New()
app.Use(CORS(gconfig.CORSConfig{ app.Use(CORS(gconfig.CORSConfig{
@ -69,7 +69,7 @@ func TestCORSMiddleware(t *testing.T) {
} }
} }
// TestCORSAllowAll verifies expected behavior. // TestCORSAllowAll ensures cors allow all behavior is handled correctly.
func TestCORSAllowAll(t *testing.T) { func TestCORSAllowAll(t *testing.T) {
app := fiber.New() app := fiber.New()
app.Use(CORS(gconfig.CORSConfig{AllowedOrigins: []string{"*"}})) app.Use(CORS(gconfig.CORSConfig{AllowedOrigins: []string{"*"}}))
@ -87,7 +87,7 @@ func TestCORSAllowAll(t *testing.T) {
} }
} }
// TestSecurityHeadersMiddleware verifies expected behavior. // TestSecurityHeadersMiddleware ensures security headers middleware behavior is handled correctly.
func TestSecurityHeadersMiddleware(t *testing.T) { func TestSecurityHeadersMiddleware(t *testing.T) {
app := fiber.New() app := fiber.New()
app.Use(SecurityHeaders(gconfig.SecurityHeadersConfig{ app.Use(SecurityHeaders(gconfig.SecurityHeadersConfig{
@ -123,7 +123,7 @@ func TestSecurityHeadersMiddleware(t *testing.T) {
} }
} }
// TestSecurityHeadersNoHSTSOnHTTP verifies expected behavior. // TestSecurityHeadersNoHSTSOnHTTP ensures security headers no hsts on http behavior is handled correctly.
func TestSecurityHeadersNoHSTSOnHTTP(t *testing.T) { func TestSecurityHeadersNoHSTSOnHTTP(t *testing.T) {
app := fiber.New() app := fiber.New()
app.Use(SecurityHeaders(gconfig.SecurityHeadersConfig{ app.Use(SecurityHeaders(gconfig.SecurityHeadersConfig{
@ -143,7 +143,7 @@ func TestSecurityHeadersNoHSTSOnHTTP(t *testing.T) {
} }
} }
// TestRequestContextMiddlewareAndRequestID verifies expected behavior. // TestRequestContextMiddlewareAndRequestID ensures request context middleware and request id behavior is handled correctly.
func TestRequestContextMiddlewareAndRequestID(t *testing.T) { func TestRequestContextMiddlewareAndRequestID(t *testing.T) {
app := fiber.New() app := fiber.New()
app.Use(RequestContext(nil)) app.Use(RequestContext(nil))
@ -179,7 +179,7 @@ func TestRequestContextMiddlewareAndRequestID(t *testing.T) {
} }
} }
// TestRequestContextWithLoggerAndInvalidRequestIDLocal verifies expected behavior. // TestRequestContextWithLoggerAndInvalidRequestIDLocal ensures request context with logger and invalid request id local behavior is handled correctly.
func TestRequestContextWithLoggerAndInvalidRequestIDLocal(t *testing.T) { func TestRequestContextWithLoggerAndInvalidRequestIDLocal(t *testing.T) {
logger := logging.NewLogger(logging.DefaultConfig()) logger := logging.NewLogger(logging.DefaultConfig())
app := fiber.New() app := fiber.New()
@ -203,7 +203,7 @@ func TestRequestContextWithLoggerAndInvalidRequestIDLocal(t *testing.T) {
} }
} }
// TestRateLimitMiddlewareDegradedModeAndHelpers verifies expected behavior. // TestRateLimitMiddlewareDegradedModeAndHelpers ensures rate limit middleware degraded mode and helpers behavior is handled correctly.
func TestRateLimitMiddlewareDegradedModeAndHelpers(t *testing.T) { func TestRateLimitMiddlewareDegradedModeAndHelpers(t *testing.T) {
app := fiber.New() app := fiber.New()
mw := RateLimitMiddleware(nil, gconfig.RateLimitConfig{ mw := RateLimitMiddleware(nil, gconfig.RateLimitConfig{
@ -305,7 +305,7 @@ func TestRateLimitMiddlewareDegradedModeAndHelpers(t *testing.T) {
} }
} }
// TestRateLimitMiddlewareRedisAllowedAndBlocked verifies expected behavior. // TestRateLimitMiddlewareRedisAllowedAndBlocked ensures rate limit middleware redis allowed and blocked behavior is handled correctly.
func TestRateLimitMiddlewareRedisAllowedAndBlocked(t *testing.T) { func TestRateLimitMiddlewareRedisAllowedAndBlocked(t *testing.T) {
mr, err := miniredis.Run() mr, err := miniredis.Run()
if err != nil { if err != nil {
@ -358,7 +358,7 @@ func TestRateLimitMiddlewareRedisAllowedAndBlocked(t *testing.T) {
} }
} }
// TestRateLimitMiddlewareRedisErrorDegrades verifies expected behavior. // TestRateLimitMiddlewareRedisErrorDegrades ensures rate limit middleware redis error degrades behavior is handled correctly.
func TestRateLimitMiddlewareRedisErrorDegrades(t *testing.T) { func TestRateLimitMiddlewareRedisErrorDegrades(t *testing.T) {
// Use a client pointing to an unreachable endpoint to force script.Run error. // Use a client pointing to an unreachable endpoint to force script.Run error.
client := redisv9.NewClient(&redisv9.Options{Addr: "127.0.0.1:1"}) client := redisv9.NewClient(&redisv9.Options{Addr: "127.0.0.1:1"})
@ -390,7 +390,7 @@ func TestRateLimitMiddlewareRedisErrorDegrades(t *testing.T) {
} }
} }
// TestIdentifyRequester verifies expected behavior. // TestIdentifyRequester ensures identify requester behavior is handled correctly.
func TestIdentifyRequester(t *testing.T) { func TestIdentifyRequester(t *testing.T) {
app := fiber.New() app := fiber.New()
app.Get("/id", func(c fiber.Ctx) error { app.Get("/id", func(c fiber.Ctx) error {

@ -1,6 +1,6 @@
package tests package tests
// integration_http_test.go contains tests for backend behavior. // integration_http_test.go contains backend tests for package behavior, error paths, and regressions.
import ( import (
"io" "io"
@ -21,7 +21,7 @@ import (
"knowfoolery/backend/shared/infra/auth/zitadel" "knowfoolery/backend/shared/infra/auth/zitadel"
) )
// TestGateway_PublicRoute_ProxiesAndRewritesPath verifies expected behavior. // TestGateway_PublicRoute_ProxiesAndRewritesPath ensures gateway public route proxies and rewrites path behavior is handled correctly.
func TestGateway_PublicRoute_ProxiesAndRewritesPath(t *testing.T) { func TestGateway_PublicRoute_ProxiesAndRewritesPath(t *testing.T) {
t.Parallel() t.Parallel()
@ -49,7 +49,7 @@ func TestGateway_PublicRoute_ProxiesAndRewritesPath(t *testing.T) {
require.Equal(t, "degraded", res.Header.Get("X-RateLimit-Policy")) require.Equal(t, "degraded", res.Header.Get("X-RateLimit-Policy"))
} }
// TestGateway_ProtectedRoute_RequiresAuth verifies expected behavior. // TestGateway_ProtectedRoute_RequiresAuth ensures gateway protected route requires auth behavior is handled correctly.
func TestGateway_ProtectedRoute_RequiresAuth(t *testing.T) { func TestGateway_ProtectedRoute_RequiresAuth(t *testing.T) {
t.Parallel() t.Parallel()
@ -67,7 +67,7 @@ func TestGateway_ProtectedRoute_RequiresAuth(t *testing.T) {
require.Equal(t, http.StatusUnauthorized, res.StatusCode) require.Equal(t, http.StatusUnauthorized, res.StatusCode)
} }
// TestGateway_ProtectedRoute_ForwardsUserHeaders verifies expected behavior. // TestGateway_ProtectedRoute_ForwardsUserHeaders ensures gateway protected route forwards user headers behavior is handled correctly.
func TestGateway_ProtectedRoute_ForwardsUserHeaders(t *testing.T) { func TestGateway_ProtectedRoute_ForwardsUserHeaders(t *testing.T) {
t.Parallel() t.Parallel()
@ -95,7 +95,7 @@ func TestGateway_ProtectedRoute_ForwardsUserHeaders(t *testing.T) {
require.Equal(t, "true", received["x-user-mfa"]) require.Equal(t, "true", received["x-user-mfa"])
} }
// TestGateway_PreflightCors verifies expected behavior. // TestGateway_PreflightCors ensures gateway preflight cors behavior is handled correctly.
func TestGateway_PreflightCors(t *testing.T) { func TestGateway_PreflightCors(t *testing.T) {
t.Parallel() t.Parallel()
@ -116,7 +116,7 @@ func TestGateway_PreflightCors(t *testing.T) {
require.Equal(t, "http://localhost:5173", res.Header.Get("Access-Control-Allow-Origin")) require.Equal(t, "http://localhost:5173", res.Header.Get("Access-Control-Allow-Origin"))
} }
// buildTestApp is a test helper. // buildTestApp wires the test application with mocked dependencies.
func buildTestApp(t *testing.T, upstreamURL string, client *http.Client) *fiber.App { func buildTestApp(t *testing.T, upstreamURL string, client *http.Client) *fiber.App {
t.Helper() t.Helper()
@ -186,12 +186,12 @@ func buildTestApp(t *testing.T, upstreamURL string, client *http.Client) *fiber.
type roundTripperFunc func(*http.Request) (*http.Response, error) type roundTripperFunc func(*http.Request) (*http.Response, error)
// RoundTrip is a test helper. // RoundTrip stubs upstream round trips for gateway proxy assertions.
func (fn roundTripperFunc) RoundTrip(req *http.Request) (*http.Response, error) { func (fn roundTripperFunc) RoundTrip(req *http.Request) (*http.Response, error) {
return fn(req) return fn(req)
} }
// jsonResponse is a test helper. // jsonResponse supports json response test setup and assertions.
func jsonResponse(status int, body string) *http.Response { func jsonResponse(status int, body string) *http.Response {
return &http.Response{ return &http.Response{
StatusCode: status, StatusCode: status,

@ -1,6 +1,6 @@
package leaderboard package leaderboard
// service_test.go contains tests for backend behavior. // service_test.go contains backend tests for package behavior, error paths, and regressions.
import ( import (
"context" "context"
@ -25,7 +25,7 @@ type fakeRepo struct {
globalErr error globalErr error
} }
// newFakeRepo is a test helper. // newFakeRepo creates in-memory test doubles and deterministic fixtures.
func newFakeRepo() *fakeRepo { func newFakeRepo() *fakeRepo {
return &fakeRepo{ return &fakeRepo{
entries: make([]*domain.LeaderboardEntry, 0), entries: make([]*domain.LeaderboardEntry, 0),
@ -33,10 +33,10 @@ func newFakeRepo() *fakeRepo {
} }
} }
// EnsureSchema is a test helper. // EnsureSchema initializes schema state required before repository operations.
func (r *fakeRepo) EnsureSchema(ctx context.Context) error { return nil } func (r *fakeRepo) EnsureSchema(ctx context.Context) error { return nil }
// IngestEntry is a test helper. // IngestEntry supports ingest entry test setup and assertions.
func (r *fakeRepo) IngestEntry( func (r *fakeRepo) IngestEntry(
ctx context.Context, ctx context.Context,
entry *domain.LeaderboardEntry, entry *domain.LeaderboardEntry,
@ -93,7 +93,7 @@ func (r *fakeRepo) IngestEntry(
return &cp, false, nil return &cp, false, nil
} }
// ListTop is a test helper. // ListTop returns filtered collections from the in-memory repository.
func (r *fakeRepo) ListTop( func (r *fakeRepo) ListTop(
ctx context.Context, ctx context.Context,
filter domain.TopFilter, filter domain.TopFilter,
@ -112,7 +112,7 @@ func (r *fakeRepo) ListTop(
return out, nil return out, nil
} }
// GetPlayerStats is a test helper. // GetPlayerStats retrieves data from the in-memory repository.
func (r *fakeRepo) GetPlayerStats(ctx context.Context, playerID string) (*domain.PlayerStats, error) { func (r *fakeRepo) GetPlayerStats(ctx context.Context, playerID string) (*domain.PlayerStats, error) {
if r.statsErr != nil { if r.statsErr != nil {
return nil, r.statsErr return nil, r.statsErr
@ -125,7 +125,7 @@ func (r *fakeRepo) GetPlayerStats(ctx context.Context, playerID string) (*domain
return &cp, nil return &cp, nil
} }
// GetPlayerRank is a test helper. // GetPlayerRank retrieves data from the in-memory repository.
func (r *fakeRepo) GetPlayerRank(ctx context.Context, playerID string) (int64, error) { func (r *fakeRepo) GetPlayerRank(ctx context.Context, playerID string) (int64, error) {
if r.rankErr != nil { if r.rankErr != nil {
return 0, r.rankErr return 0, r.rankErr
@ -136,7 +136,7 @@ func (r *fakeRepo) GetPlayerRank(ctx context.Context, playerID string) (int64, e
return 1, nil return 1, nil
} }
// ListPlayerHistory is a test helper. // ListPlayerHistory returns filtered collections from the in-memory repository.
func (r *fakeRepo) ListPlayerHistory( func (r *fakeRepo) ListPlayerHistory(
ctx context.Context, ctx context.Context,
playerID string, playerID string,
@ -154,7 +154,7 @@ func (r *fakeRepo) ListPlayerHistory(
return out, int64(len(out)), nil return out, int64(len(out)), nil
} }
// GetGlobalStats is a test helper. // GetGlobalStats retrieves data from the in-memory repository.
func (r *fakeRepo) GetGlobalStats(ctx context.Context, filter domain.TopFilter) (*domain.GlobalStats, error) { func (r *fakeRepo) GetGlobalStats(ctx context.Context, filter domain.TopFilter) (*domain.GlobalStats, error) {
if r.globalErr != nil { if r.globalErr != nil {
return nil, r.globalErr return nil, r.globalErr
@ -168,18 +168,18 @@ type fakeState struct {
deleteErr error deleteErr error
} }
// newFakeState is a test helper. // newFakeState creates in-memory test doubles and deterministic fixtures.
func newFakeState() *fakeState { func newFakeState() *fakeState {
return &fakeState{data: map[string]string{}} return &fakeState{data: map[string]string{}}
} }
// Get is a test helper. // Get retrieves data from the in-memory repository.
func (s *fakeState) Get(ctx context.Context, key string) (string, bool) { func (s *fakeState) Get(ctx context.Context, key string) (string, bool) {
v, ok := s.data[key] v, ok := s.data[key]
return v, ok return v, ok
} }
// Set is a test helper. // Set stores ephemeral state in the fake cache or state store.
func (s *fakeState) Set(ctx context.Context, key, value string, ttl time.Duration) error { func (s *fakeState) Set(ctx context.Context, key, value string, ttl time.Duration) error {
if s.setErr != nil { if s.setErr != nil {
return s.setErr return s.setErr
@ -188,7 +188,7 @@ func (s *fakeState) Set(ctx context.Context, key, value string, ttl time.Duratio
return nil return nil
} }
// Delete is a test helper. // Delete removes ephemeral state from the fake cache or state store.
func (s *fakeState) Delete(ctx context.Context, keys ...string) error { func (s *fakeState) Delete(ctx context.Context, keys ...string) error {
if s.deleteErr != nil { if s.deleteErr != nil {
return s.deleteErr return s.deleteErr
@ -199,7 +199,7 @@ func (s *fakeState) Delete(ctx context.Context, keys ...string) error {
return nil return nil
} }
// TestUpdateScoreIdempotent verifies expected behavior. // TestUpdateScoreIdempotent ensures update score idempotent behavior is handled correctly.
func TestUpdateScoreIdempotent(t *testing.T) { func TestUpdateScoreIdempotent(t *testing.T) {
repo := newFakeRepo() repo := newFakeRepo()
state := newFakeState() state := newFakeState()
@ -230,7 +230,7 @@ func TestUpdateScoreIdempotent(t *testing.T) {
} }
} }
// TestUpdateScoreValidatesInput verifies expected behavior. // TestUpdateScoreValidatesInput ensures update score validates input behavior is handled correctly.
func TestUpdateScoreValidatesInput(t *testing.T) { func TestUpdateScoreValidatesInput(t *testing.T) {
svc := NewService(newFakeRepo(), newFakeState(), Config{}) svc := NewService(newFakeRepo(), newFakeState(), Config{})
_, err := svc.UpdateScore(context.Background(), UpdateScoreInput{ _, err := svc.UpdateScore(context.Background(), UpdateScoreInput{
@ -249,7 +249,7 @@ func TestUpdateScoreValidatesInput(t *testing.T) {
} }
} }
// TestGetPlayerRanking verifies expected behavior. // TestGetPlayerRanking ensures get player ranking behavior is handled correctly.
func TestGetPlayerRanking(t *testing.T) { func TestGetPlayerRanking(t *testing.T) {
repo := newFakeRepo() repo := newFakeRepo()
state := newFakeState() state := newFakeState()
@ -284,7 +284,7 @@ func TestGetPlayerRanking(t *testing.T) {
} }
} }
// TestUpdateScoreValidationAndErrorPaths verifies expected behavior. // TestUpdateScoreValidationAndErrorPaths ensures update score validation and error paths behavior is handled correctly.
func TestUpdateScoreValidationAndErrorPaths(t *testing.T) { func TestUpdateScoreValidationAndErrorPaths(t *testing.T) {
svc := NewService(newFakeRepo(), newFakeState(), Config{}) svc := NewService(newFakeRepo(), newFakeState(), Config{})
cases := []UpdateScoreInput{ cases := []UpdateScoreInput{
@ -312,7 +312,7 @@ func TestUpdateScoreValidationAndErrorPaths(t *testing.T) {
} }
} }
// TestTopAndStatsCachePaths verifies expected behavior. // TestTopAndStatsCachePaths ensures top and stats cache paths behavior is handled correctly.
func TestTopAndStatsCachePaths(t *testing.T) { func TestTopAndStatsCachePaths(t *testing.T) {
repo := newFakeRepo() repo := newFakeRepo()
state := newFakeState() state := newFakeState()
@ -368,7 +368,7 @@ func TestTopAndStatsCachePaths(t *testing.T) {
} }
} }
// TestRankingValidationAndErrorPaths verifies expected behavior. // TestRankingValidationAndErrorPaths ensures ranking validation and error paths behavior is handled correctly.
func TestRankingValidationAndErrorPaths(t *testing.T) { func TestRankingValidationAndErrorPaths(t *testing.T) {
repo := newFakeRepo() repo := newFakeRepo()
state := newFakeState() state := newFakeState()
@ -423,7 +423,7 @@ func TestRankingValidationAndErrorPaths(t *testing.T) {
} }
} }
// TestGlobalStatsAndTopErrors verifies expected behavior. // TestGlobalStatsAndTopErrors ensures global stats and top errors behavior is handled correctly.
func TestGlobalStatsAndTopErrors(t *testing.T) { func TestGlobalStatsAndTopErrors(t *testing.T) {
repo := newFakeRepo() repo := newFakeRepo()
repo.topErr = errors.New("top boom") repo.topErr = errors.New("top boom")

@ -1,6 +1,6 @@
package http package http
// handler_unit_test.go contains tests for backend behavior. // handler_unit_test.go contains backend tests for package behavior, error paths, and regressions.
import ( import (
"bytes" "bytes"
@ -13,7 +13,7 @@ import (
"knowfoolery/backend/shared/infra/utils/validation" "knowfoolery/backend/shared/infra/utils/validation"
) )
// TestUpdateAuthAndValidationBranches verifies expected behavior. // TestUpdateAuthAndValidationBranches ensures update auth and validation branches behavior is handled correctly.
func TestUpdateAuthAndValidationBranches(t *testing.T) { func TestUpdateAuthAndValidationBranches(t *testing.T) {
h := NewHandler(nil, validation.NewValidator(), nil, nil, true, 20, 100) h := NewHandler(nil, validation.NewValidator(), nil, nil, true, 20, 100)
app := fiber.New() app := fiber.New()
@ -89,7 +89,7 @@ func TestUpdateAuthAndValidationBranches(t *testing.T) {
} }
} }
// TestGetPlayerRankingForbiddenBranch verifies expected behavior. // TestGetPlayerRankingForbiddenBranch ensures get player ranking forbidden branch behavior is handled correctly.
func TestGetPlayerRankingForbiddenBranch(t *testing.T) { func TestGetPlayerRankingForbiddenBranch(t *testing.T) {
h := NewHandler(nil, validation.NewValidator(), nil, nil, false, 20, 100) h := NewHandler(nil, validation.NewValidator(), nil, nil, false, 20, 100)
app := fiber.New() app := fiber.New()
@ -111,7 +111,7 @@ func TestGetPlayerRankingForbiddenBranch(t *testing.T) {
} }
} }
// TestHelperFunctions verifies expected behavior. // TestHelperFunctions ensures helper functions behavior is handled correctly.
func TestHelperFunctions(t *testing.T) { func TestHelperFunctions(t *testing.T) {
if got := atoiWithDefault("", 3); got != 3 { if got := atoiWithDefault("", 3); got != 3 {
t.Fatalf("expected default for empty input") t.Fatalf("expected default for empty input")

@ -1,6 +1,6 @@
package tests package tests
// integration_http_test.go contains tests for backend behavior. // integration_http_test.go contains backend tests for package behavior, error paths, and regressions.
import ( import (
"bytes" "bytes"
@ -29,7 +29,7 @@ type inMemoryRepo struct {
stats map[string]*domain.PlayerStats stats map[string]*domain.PlayerStats
} }
// newInMemoryRepo is a test helper. // newInMemoryRepo creates in-memory test doubles and deterministic fixtures.
func newInMemoryRepo() *inMemoryRepo { func newInMemoryRepo() *inMemoryRepo {
return &inMemoryRepo{ return &inMemoryRepo{
entries: make([]*domain.LeaderboardEntry, 0), entries: make([]*domain.LeaderboardEntry, 0),
@ -37,10 +37,10 @@ func newInMemoryRepo() *inMemoryRepo {
} }
} }
// EnsureSchema is a test helper. // EnsureSchema initializes schema state required before repository operations.
func (r *inMemoryRepo) EnsureSchema(ctx context.Context) error { return nil } func (r *inMemoryRepo) EnsureSchema(ctx context.Context) error { return nil }
// IngestEntry is a test helper. // IngestEntry supports ingest entry test setup and assertions.
func (r *inMemoryRepo) IngestEntry( func (r *inMemoryRepo) IngestEntry(
ctx context.Context, ctx context.Context,
entry *domain.LeaderboardEntry, entry *domain.LeaderboardEntry,
@ -75,7 +75,7 @@ func (r *inMemoryRepo) IngestEntry(
return &cp, false, nil return &cp, false, nil
} }
// ListTop is a test helper. // ListTop returns filtered collections from the in-memory repository.
func (r *inMemoryRepo) ListTop( func (r *inMemoryRepo) ListTop(
ctx context.Context, ctx context.Context,
filter domain.TopFilter, filter domain.TopFilter,
@ -91,7 +91,7 @@ func (r *inMemoryRepo) ListTop(
return out, nil return out, nil
} }
// GetPlayerStats is a test helper. // GetPlayerStats retrieves data from the in-memory repository.
func (r *inMemoryRepo) GetPlayerStats(ctx context.Context, playerID string) (*domain.PlayerStats, error) { func (r *inMemoryRepo) GetPlayerStats(ctx context.Context, playerID string) (*domain.PlayerStats, error) {
stats := r.stats[playerID] stats := r.stats[playerID]
if stats == nil { if stats == nil {
@ -101,7 +101,7 @@ func (r *inMemoryRepo) GetPlayerStats(ctx context.Context, playerID string) (*do
return &cp, nil return &cp, nil
} }
// GetPlayerRank is a test helper. // GetPlayerRank retrieves data from the in-memory repository.
func (r *inMemoryRepo) GetPlayerRank(ctx context.Context, playerID string) (int64, error) { func (r *inMemoryRepo) GetPlayerRank(ctx context.Context, playerID string) (int64, error) {
if _, ok := r.stats[playerID]; !ok { if _, ok := r.stats[playerID]; !ok {
return 0, domain.ErrPlayerNotFound return 0, domain.ErrPlayerNotFound
@ -109,7 +109,7 @@ func (r *inMemoryRepo) GetPlayerRank(ctx context.Context, playerID string) (int6
return 1, nil return 1, nil
} }
// ListPlayerHistory is a test helper. // ListPlayerHistory returns filtered collections from the in-memory repository.
func (r *inMemoryRepo) ListPlayerHistory( func (r *inMemoryRepo) ListPlayerHistory(
ctx context.Context, ctx context.Context,
playerID string, playerID string,
@ -124,7 +124,7 @@ func (r *inMemoryRepo) ListPlayerHistory(
return out, int64(len(out)), nil return out, int64(len(out)), nil
} }
// GetGlobalStats is a test helper. // GetGlobalStats retrieves data from the in-memory repository.
func (r *inMemoryRepo) GetGlobalStats(ctx context.Context, filter domain.TopFilter) (*domain.GlobalStats, error) { func (r *inMemoryRepo) GetGlobalStats(ctx context.Context, filter domain.TopFilter) (*domain.GlobalStats, error) {
return &domain.GlobalStats{ return &domain.GlobalStats{
TotalGames: int64(len(r.entries)), TotalGames: int64(len(r.entries)),
@ -135,18 +135,18 @@ func (r *inMemoryRepo) GetGlobalStats(ctx context.Context, filter domain.TopFilt
type fakeState struct{} type fakeState struct{}
// Get is a test helper. // Get retrieves data from the in-memory repository.
func (s *fakeState) Get(ctx context.Context, key string) (string, bool) { return "", false } func (s *fakeState) Get(ctx context.Context, key string) (string, bool) { return "", false }
// Set is a test helper. // Set stores ephemeral state in the fake cache or state store.
func (s *fakeState) Set(ctx context.Context, key, value string, ttl time.Duration) error { func (s *fakeState) Set(ctx context.Context, key, value string, ttl time.Duration) error {
return nil return nil
} }
// Delete is a test helper. // Delete removes ephemeral state from the fake cache or state store.
func (s *fakeState) Delete(ctx context.Context, keys ...string) error { return nil } func (s *fakeState) Delete(ctx context.Context, keys ...string) error { return nil }
// setupApp is a test helper. // setupApp wires the test application with mocked dependencies.
func setupApp(t *testing.T) *fiber.App { func setupApp(t *testing.T) *fiber.App {
t.Helper() t.Helper()
repo := newInMemoryRepo() repo := newInMemoryRepo()
@ -178,7 +178,7 @@ func setupApp(t *testing.T) *fiber.App {
return app return app
} }
// TestUpdateAndTop10 verifies expected behavior. // TestUpdateAndTop10 ensures update and top10 behavior is handled correctly.
func TestUpdateAndTop10(t *testing.T) { func TestUpdateAndTop10(t *testing.T) {
app := setupApp(t) app := setupApp(t)
@ -211,7 +211,7 @@ func TestUpdateAndTop10(t *testing.T) {
} }
} }
// TestPlayerAuthAndStats verifies expected behavior. // TestPlayerAuthAndStats ensures player auth and stats behavior is handled correctly.
func TestPlayerAuthAndStats(t *testing.T) { func TestPlayerAuthAndStats(t *testing.T) {
app := setupApp(t) app := setupApp(t)
@ -239,7 +239,7 @@ func TestPlayerAuthAndStats(t *testing.T) {
} }
} }
// TestMetricsEndpoint verifies expected behavior. // TestMetricsEndpoint ensures metrics endpoint behavior is handled correctly.
func TestMetricsEndpoint(t *testing.T) { func TestMetricsEndpoint(t *testing.T) {
app := setupApp(t) app := setupApp(t)
req := httptest.NewRequest(http.MethodGet, "/metrics", nil) req := httptest.NewRequest(http.MethodGet, "/metrics", nil)
@ -250,7 +250,7 @@ func TestMetricsEndpoint(t *testing.T) {
} }
} }
// assertStatus is a test helper. // assertStatus asserts HTTP status codes and response payload invariants.
func assertStatus(t *testing.T, resp *http.Response, want int, msg string) { func assertStatus(t *testing.T, resp *http.Response, want int, msg string) {
t.Helper() t.Helper()
if resp.StatusCode != want { if resp.StatusCode != want {

@ -26,7 +26,7 @@ type fakeRepo struct {
bulkCount int bulkCount int
} }
// GetByID returns a fake question by ID. // GetByID retrieves data from the in-memory repository.
func (f *fakeRepo) GetByID(ctx context.Context, id string) (*domain.Question, error) { func (f *fakeRepo) GetByID(ctx context.Context, id string) (*domain.Question, error) {
for _, q := range f.items { for _, q := range f.items {
if q.ID == id { if q.ID == id {
@ -36,7 +36,7 @@ func (f *fakeRepo) GetByID(ctx context.Context, id string) (*domain.Question, er
return nil, domain.ErrQuestionNotFound return nil, domain.ErrQuestionNotFound
} }
// Create appends a fake question and assigns a deterministic ID. // Create persists a new entity in the in-memory repository.
func (f *fakeRepo) Create(ctx context.Context, q *domain.Question) (*domain.Question, error) { func (f *fakeRepo) Create(ctx context.Context, q *domain.Question) (*domain.Question, error) {
if f.createErr != nil { if f.createErr != nil {
return nil, f.createErr return nil, f.createErr
@ -46,7 +46,7 @@ func (f *fakeRepo) Create(ctx context.Context, q *domain.Question) (*domain.Ques
return q, nil return q, nil
} }
// Update returns the provided fake question as updated. // Update updates an existing entity in the in-memory repository.
func (f *fakeRepo) Update(ctx context.Context, id string, q *domain.Question) (*domain.Question, error) { func (f *fakeRepo) Update(ctx context.Context, id string, q *domain.Question) (*domain.Question, error) {
if f.updateErr != nil { if f.updateErr != nil {
return nil, f.updateErr return nil, f.updateErr
@ -54,12 +54,12 @@ func (f *fakeRepo) Update(ctx context.Context, id string, q *domain.Question) (*
return q, nil return q, nil
} }
// SoftDelete is a no-op fake delete implementation. // SoftDelete supports soft delete test setup and assertions.
func (f *fakeRepo) SoftDelete(ctx context.Context, id string) error { func (f *fakeRepo) SoftDelete(ctx context.Context, id string) error {
return f.deleteErr return f.deleteErr
} }
// ListThemes returns a fixed fake theme set. // ListThemes returns filtered collections from the in-memory repository.
func (f *fakeRepo) ListThemes(ctx context.Context) ([]string, error) { func (f *fakeRepo) ListThemes(ctx context.Context) ([]string, error) {
if f.listErr != nil { if f.listErr != nil {
return nil, f.listErr return nil, f.listErr
@ -67,7 +67,7 @@ func (f *fakeRepo) ListThemes(ctx context.Context) ([]string, error) {
return []string{"Science"}, nil return []string{"Science"}, nil
} }
// CountRandomCandidates returns fake candidate count from in-memory state. // CountRandomCandidates returns aggregate counts from the in-memory repository.
func (f *fakeRepo) CountRandomCandidates(ctx context.Context, filter domain.RandomFilter) (int, error) { func (f *fakeRepo) CountRandomCandidates(ctx context.Context, filter domain.RandomFilter) (int, error) {
if f.countErr != nil { if f.countErr != nil {
return 0, f.countErr return 0, f.countErr
@ -78,7 +78,7 @@ func (f *fakeRepo) CountRandomCandidates(ctx context.Context, filter domain.Rand
return len(f.items), nil return len(f.items), nil
} }
// RandomByOffset returns a fake candidate by offset. // RandomByOffset supports random by offset test setup and assertions.
func (f *fakeRepo) RandomByOffset( func (f *fakeRepo) RandomByOffset(
ctx context.Context, ctx context.Context,
filter domain.RandomFilter, filter domain.RandomFilter,
@ -96,7 +96,7 @@ func (f *fakeRepo) RandomByOffset(
return f.items[offset], nil return f.items[offset], nil
} }
// BulkCreate appends all incoming questions and reports all as created. // BulkCreate supports bulk create test setup and assertions.
func (f *fakeRepo) BulkCreate( func (f *fakeRepo) BulkCreate(
ctx context.Context, ctx context.Context,
questions []*domain.Question, questions []*domain.Question,
@ -117,13 +117,13 @@ type fakeCache struct {
invalidated bool invalidated bool
} }
// Get returns a cached fake question when present. // Get retrieves data from the in-memory repository.
func (f *fakeCache) Get(ctx context.Context, key string) (*domain.Question, bool) { func (f *fakeCache) Get(ctx context.Context, key string) (*domain.Question, bool) {
q, ok := f.store[key] q, ok := f.store[key]
return q, ok return q, ok
} }
// Set stores a fake question in cache. // Set stores ephemeral state in the fake cache or state store.
func (f *fakeCache) Set(ctx context.Context, key string, q *domain.Question, ttl time.Duration) { func (f *fakeCache) Set(ctx context.Context, key string, q *domain.Question, ttl time.Duration) {
if f.store == nil { if f.store == nil {
f.store = map[string]*domain.Question{} f.store = map[string]*domain.Question{}
@ -131,10 +131,10 @@ func (f *fakeCache) Set(ctx context.Context, key string, q *domain.Question, ttl
f.store[key] = q f.store[key] = q
} }
// Invalidate marks cache invalidation in tests. // Invalidate removes ephemeral state from the fake cache or state store.
func (f *fakeCache) Invalidate(ctx context.Context) { f.invalidated = true } func (f *fakeCache) Invalidate(ctx context.Context) { f.invalidated = true }
// TestGetRandomQuestion_NoQuestions returns ErrNoQuestionsAvailable when no active candidates exist. // TestGetRandomQuestion_NoQuestions ensures get random question no questions behavior is handled correctly.
func TestGetRandomQuestion_NoQuestions(t *testing.T) { func TestGetRandomQuestion_NoQuestions(t *testing.T) {
svc := NewService(&fakeRepo{}, &fakeCache{}, time.Minute, 200) svc := NewService(&fakeRepo{}, &fakeCache{}, time.Minute, 200)
_, err := svc.GetRandomQuestion(context.Background(), RandomQuestionRequest{}) _, err := svc.GetRandomQuestion(context.Background(), RandomQuestionRequest{})
@ -143,7 +143,7 @@ func TestGetRandomQuestion_NoQuestions(t *testing.T) {
} }
} }
// TestGetRandomQuestion_WithCache returns a random question successfully with cache enabled. // TestGetRandomQuestion_WithCache ensures get random question with cache behavior is handled correctly.
func TestGetRandomQuestion_WithCache(t *testing.T) { func TestGetRandomQuestion_WithCache(t *testing.T) {
repo := &fakeRepo{items: []*domain.Question{ repo := &fakeRepo{items: []*domain.Question{
{ {
@ -175,7 +175,7 @@ func TestGetRandomQuestion_WithCache(t *testing.T) {
} }
} }
// TestValidateAnswerByQuestionID returns a matched result and expected threshold for equivalent answers. // TestValidateAnswerByQuestionID ensures validate answer by question id behavior is handled correctly.
func TestValidateAnswerByQuestionID(t *testing.T) { func TestValidateAnswerByQuestionID(t *testing.T) {
repo := &fakeRepo{items: []*domain.Question{ repo := &fakeRepo{items: []*domain.Question{
{ {
@ -201,7 +201,7 @@ func TestValidateAnswerByQuestionID(t *testing.T) {
} }
} }
// TestValidateAnswerByQuestionID_ValidationError returns validation error for empty provided answers. // TestValidateAnswerByQuestionID_ValidationError ensures validate answer by question id validation error behavior is handled correctly.
func TestValidateAnswerByQuestionID_ValidationError(t *testing.T) { func TestValidateAnswerByQuestionID_ValidationError(t *testing.T) {
repo := &fakeRepo{items: []*domain.Question{ repo := &fakeRepo{items: []*domain.Question{
{ {
@ -219,7 +219,7 @@ func TestValidateAnswerByQuestionID_ValidationError(t *testing.T) {
} }
} }
// TestGetRandomQuestion_InvalidDifficulty verifies expected behavior. // TestGetRandomQuestion_InvalidDifficulty ensures get random question invalid difficulty behavior is handled correctly.
func TestGetRandomQuestion_InvalidDifficulty(t *testing.T) { func TestGetRandomQuestion_InvalidDifficulty(t *testing.T) {
svc := NewService(&fakeRepo{}, &fakeCache{}, time.Minute, 200) svc := NewService(&fakeRepo{}, &fakeCache{}, time.Minute, 200)
_, err := svc.GetRandomQuestion(context.Background(), RandomQuestionRequest{ _, err := svc.GetRandomQuestion(context.Background(), RandomQuestionRequest{
@ -230,7 +230,7 @@ func TestGetRandomQuestion_InvalidDifficulty(t *testing.T) {
} }
} }
// TestGetRandomQuestion_RepoErrors verifies expected behavior. // TestGetRandomQuestion_RepoErrors ensures get random question repo errors behavior is handled correctly.
func TestGetRandomQuestion_RepoErrors(t *testing.T) { func TestGetRandomQuestion_RepoErrors(t *testing.T) {
repo := &fakeRepo{countErr: errors.New("count boom")} repo := &fakeRepo{countErr: errors.New("count boom")}
svc := NewService(repo, &fakeCache{}, time.Minute, 200) svc := NewService(repo, &fakeCache{}, time.Minute, 200)
@ -251,7 +251,7 @@ func TestGetRandomQuestion_RepoErrors(t *testing.T) {
} }
} }
// TestQuestionCRUDAndThemes verifies expected behavior. // TestQuestionCRUDAndThemes ensures question crud and themes behavior is handled correctly.
func TestQuestionCRUDAndThemes(t *testing.T) { func TestQuestionCRUDAndThemes(t *testing.T) {
repo := &fakeRepo{} repo := &fakeRepo{}
cache := &fakeCache{} cache := &fakeCache{}
@ -307,7 +307,7 @@ func TestQuestionCRUDAndThemes(t *testing.T) {
} }
} }
// TestQuestionCRUDValidationAndErrors verifies expected behavior. // TestQuestionCRUDValidationAndErrors ensures question crud validation and errors behavior is handled correctly.
func TestQuestionCRUDValidationAndErrors(t *testing.T) { func TestQuestionCRUDValidationAndErrors(t *testing.T) {
repo := &fakeRepo{createErr: errors.New("create boom")} repo := &fakeRepo{createErr: errors.New("create boom")}
svc := NewService(repo, &fakeCache{}, time.Minute, 200) svc := NewService(repo, &fakeCache{}, time.Minute, 200)
@ -357,7 +357,7 @@ func TestQuestionCRUDValidationAndErrors(t *testing.T) {
} }
} }
// TestBulkImportScenarios verifies expected behavior. // TestBulkImportScenarios ensures bulk import behavior is handled correctly.
func TestBulkImportScenarios(t *testing.T) { func TestBulkImportScenarios(t *testing.T) {
cache := &fakeCache{} cache := &fakeCache{}
repo := &fakeRepo{bulkCount: 1, bulkErrors: []domain.BulkError{{Index: 0, Reason: "row"}}} repo := &fakeRepo{bulkCount: 1, bulkErrors: []domain.BulkError{{Index: 0, Reason: "row"}}}

@ -4,7 +4,7 @@ package question
import "testing" import "testing"
// TestIsAnswerMatch validates match decisions for exact, near, and below-threshold answers. // TestIsAnswerMatch ensures is answer match behavior is handled correctly.
func TestIsAnswerMatch(t *testing.T) { func TestIsAnswerMatch(t *testing.T) {
tests := []struct { tests := []struct {
name string name string

@ -29,12 +29,12 @@ type inMemoryRepo struct {
items map[string]*domain.Question items map[string]*domain.Question
} }
// newInMemoryRepo is a test helper. // newInMemoryRepo creates in-memory test doubles and deterministic fixtures.
func newInMemoryRepo() *inMemoryRepo { func newInMemoryRepo() *inMemoryRepo {
return &inMemoryRepo{items: map[string]*domain.Question{}} return &inMemoryRepo{items: map[string]*domain.Question{}}
} }
// GetByID is a test helper. // GetByID retrieves data from the in-memory repository.
func (r *inMemoryRepo) GetByID(ctx context.Context, id string) (*domain.Question, error) { func (r *inMemoryRepo) GetByID(ctx context.Context, id string) (*domain.Question, error) {
if q, ok := r.items[id]; ok { if q, ok := r.items[id]; ok {
return q, nil return q, nil
@ -42,7 +42,7 @@ func (r *inMemoryRepo) GetByID(ctx context.Context, id string) (*domain.Question
return nil, domain.ErrQuestionNotFound return nil, domain.ErrQuestionNotFound
} }
// Create is a test helper. // Create persists a new entity in the in-memory repository.
func (r *inMemoryRepo) Create(ctx context.Context, q *domain.Question) (*domain.Question, error) { func (r *inMemoryRepo) Create(ctx context.Context, q *domain.Question) (*domain.Question, error) {
q.ID = "q-created" q.ID = "q-created"
q.IsActive = true q.IsActive = true
@ -50,7 +50,7 @@ func (r *inMemoryRepo) Create(ctx context.Context, q *domain.Question) (*domain.
return q, nil return q, nil
} }
// Update is a test helper. // Update updates an existing entity in the in-memory repository.
func (r *inMemoryRepo) Update(ctx context.Context, id string, q *domain.Question) (*domain.Question, error) { func (r *inMemoryRepo) Update(ctx context.Context, id string, q *domain.Question) (*domain.Question, error) {
if _, ok := r.items[id]; !ok { if _, ok := r.items[id]; !ok {
return nil, domain.ErrQuestionNotFound return nil, domain.ErrQuestionNotFound
@ -60,7 +60,7 @@ func (r *inMemoryRepo) Update(ctx context.Context, id string, q *domain.Question
return q, nil return q, nil
} }
// SoftDelete is a test helper. // SoftDelete supports soft delete test setup and assertions.
func (r *inMemoryRepo) SoftDelete(ctx context.Context, id string) error { func (r *inMemoryRepo) SoftDelete(ctx context.Context, id string) error {
if q, ok := r.items[id]; ok { if q, ok := r.items[id]; ok {
q.IsActive = false q.IsActive = false
@ -69,12 +69,12 @@ func (r *inMemoryRepo) SoftDelete(ctx context.Context, id string) error {
return domain.ErrQuestionNotFound return domain.ErrQuestionNotFound
} }
// ListThemes is a test helper. // ListThemes returns filtered collections from the in-memory repository.
func (r *inMemoryRepo) ListThemes(ctx context.Context) ([]string, error) { func (r *inMemoryRepo) ListThemes(ctx context.Context) ([]string, error) {
return []string{"Science"}, nil return []string{"Science"}, nil
} }
// CountRandomCandidates is a test helper. // CountRandomCandidates returns aggregate counts from the in-memory repository.
func (r *inMemoryRepo) CountRandomCandidates(ctx context.Context, filter domain.RandomFilter) (int, error) { func (r *inMemoryRepo) CountRandomCandidates(ctx context.Context, filter domain.RandomFilter) (int, error) {
count := 0 count := 0
for _, q := range r.items { for _, q := range r.items {
@ -86,7 +86,7 @@ func (r *inMemoryRepo) CountRandomCandidates(ctx context.Context, filter domain.
return count, nil return count, nil
} }
// RandomByOffset is a test helper. // RandomByOffset supports random by offset test setup and assertions.
func (r *inMemoryRepo) RandomByOffset(ctx context.Context, func (r *inMemoryRepo) RandomByOffset(ctx context.Context,
filter domain.RandomFilter, offset int) (*domain.Question, error) { filter domain.RandomFilter, offset int) (*domain.Question, error) {
for _, q := range r.items { for _, q := range r.items {
@ -97,7 +97,7 @@ func (r *inMemoryRepo) RandomByOffset(ctx context.Context,
return nil, domain.ErrNoQuestionsAvailable return nil, domain.ErrNoQuestionsAvailable
} }
// BulkCreate is a test helper. // BulkCreate supports bulk create test setup and assertions.
func (r *inMemoryRepo) BulkCreate(ctx context.Context, questions []*domain.Question) (int, []domain.BulkError, error) { func (r *inMemoryRepo) BulkCreate(ctx context.Context, questions []*domain.Question) (int, []domain.BulkError, error) {
for i, q := range questions { for i, q := range questions {
id := "bulk-" + strconv.Itoa(i) id := "bulk-" + strconv.Itoa(i)
@ -111,18 +111,18 @@ func (r *inMemoryRepo) BulkCreate(ctx context.Context, questions []*domain.Quest
// noOpCache disables caching behavior in HTTP integration tests. // noOpCache disables caching behavior in HTTP integration tests.
type noOpCache struct{} type noOpCache struct{}
// Get is a test helper. // Get retrieves data from the in-memory repository.
func (c *noOpCache) Get(ctx context.Context, key string) (*domain.Question, bool) { func (c *noOpCache) Get(ctx context.Context, key string) (*domain.Question, bool) {
return nil, false return nil, false
} }
// Set is a test helper. // Set stores ephemeral state in the fake cache or state store.
func (c *noOpCache) Set(ctx context.Context, key string, q *domain.Question, ttl time.Duration) {} func (c *noOpCache) Set(ctx context.Context, key string, q *domain.Question, ttl time.Duration) {}
// Invalidate is a test helper. // Invalidate removes ephemeral state from the fake cache or state store.
func (c *noOpCache) Invalidate(ctx context.Context) {} func (c *noOpCache) Invalidate(ctx context.Context) {}
// setupApp wires a test Fiber app with in-memory dependencies and admin middleware. // setupApp wires the test application with mocked dependencies.
func setupApp(t *testing.T) *fiber.App { func setupApp(t *testing.T) *fiber.App {
t.Helper() t.Helper()
repo := newInMemoryRepo() repo := newInMemoryRepo()
@ -154,7 +154,7 @@ func setupApp(t *testing.T) *fiber.App {
return app return app
} }
// TestHTTPRandomAndGet validates public random and get-by-id endpoints. // TestHTTPRandomAndGet ensures http random and get behavior is handled correctly.
func TestHTTPRandomAndGet(t *testing.T) { func TestHTTPRandomAndGet(t *testing.T) {
app := setupApp(t) app := setupApp(t)
@ -174,7 +174,7 @@ func TestHTTPRandomAndGet(t *testing.T) {
defer resp.Body.Close() defer resp.Body.Close()
} }
// TestHTTPAdminAuthAndMetrics validates admin auth guard and metrics endpoint availability. // TestHTTPAdminAuthAndMetrics ensures http admin auth and metrics behavior is handled correctly.
func TestHTTPAdminAuthAndMetrics(t *testing.T) { func TestHTTPAdminAuthAndMetrics(t *testing.T) {
app := setupApp(t) app := setupApp(t)
@ -211,7 +211,7 @@ func TestHTTPAdminAuthAndMetrics(t *testing.T) {
defer resp.Body.Close() defer resp.Body.Close()
} }
// TestHTTPValidateAnswer covers matched/unmatched, missing question, and invalid answer responses. // TestHTTPValidateAnswer ensures http validate answer behavior is handled correctly.
func TestHTTPValidateAnswer(t *testing.T) { func TestHTTPValidateAnswer(t *testing.T) {
app := setupApp(t) app := setupApp(t)
@ -256,7 +256,7 @@ func TestHTTPValidateAnswer(t *testing.T) {
defer resp.Body.Close() defer resp.Body.Close()
} }
// TestHTTPAdminCRUDThemesAndBulk validates admin-only CRUD, themes, and bulk import flows. // TestHTTPAdminCRUDThemesAndBulk ensures http admin crud themes and bulk behavior is handled correctly.
func TestHTTPAdminCRUDThemesAndBulk(t *testing.T) { func TestHTTPAdminCRUDThemesAndBulk(t *testing.T) {
app := setupApp(t) app := setupApp(t)
@ -320,7 +320,7 @@ func TestHTTPAdminCRUDThemesAndBulk(t *testing.T) {
defer resp.Body.Close() defer resp.Body.Close()
} }
// TestHTTPRandomQuestionInputValidation validates malformed and invalid random question inputs. // TestHTTPRandomQuestionInputValidation ensures http random question input validation behavior is handled correctly.
func TestHTTPRandomQuestionInputValidation(t *testing.T) { func TestHTTPRandomQuestionInputValidation(t *testing.T) {
app := setupApp(t) app := setupApp(t)

@ -13,7 +13,7 @@ import (
sharedcontainers "knowfoolery/backend/shared/testutil/containers" sharedcontainers "knowfoolery/backend/shared/testutil/containers"
) )
// TestRepositoryLifecycle validates core repository lifecycle operations against Postgres. // TestRepositoryLifecycle ensures repository lifecycle behavior is handled correctly.
func TestRepositoryLifecycle(t *testing.T) { func TestRepositoryLifecycle(t *testing.T) {
pg := sharedcontainers.StartPostgres(t) pg := sharedcontainers.StartPostgres(t)
cfg := pg.Config() cfg := pg.Config()

@ -1,6 +1,6 @@
package user package user
// service_test.go contains tests for backend behavior. // service_test.go contains backend tests for package behavior, error paths, and regressions.
import ( import (
"context" "context"
@ -32,7 +32,7 @@ type fakeRepo struct {
writeAuditErr error writeAuditErr error
} }
// newFakeRepo creates a fake repository with empty stores. // newFakeRepo creates in-memory test doubles and deterministic fixtures.
func newFakeRepo() *fakeRepo { func newFakeRepo() *fakeRepo {
return &fakeRepo{ return &fakeRepo{
usersByID: map[string]*domain.User{}, usersByID: map[string]*domain.User{},
@ -41,10 +41,10 @@ func newFakeRepo() *fakeRepo {
} }
} }
// EnsureSchema is a no-op for the in-memory fake repository. // EnsureSchema initializes schema state required before repository operations.
func (r *fakeRepo) EnsureSchema(ctx context.Context) error { return nil } func (r *fakeRepo) EnsureSchema(ctx context.Context) error { return nil }
// Create stores a user and assigns deterministic timestamps and ID for tests. // Create persists a new entity in the in-memory repository.
func (r *fakeRepo) Create(ctx context.Context, user *domain.User) (*domain.User, error) { func (r *fakeRepo) Create(ctx context.Context, user *domain.User) (*domain.User, error) {
if r.createErr != nil { if r.createErr != nil {
return nil, r.createErr return nil, r.createErr
@ -59,7 +59,7 @@ func (r *fakeRepo) Create(ctx context.Context, user *domain.User) (*domain.User,
return user, nil return user, nil
} }
// GetByID returns a non-deleted user by ID. // GetByID retrieves data from the in-memory repository.
func (r *fakeRepo) GetByID(ctx context.Context, id string) (*domain.User, error) { func (r *fakeRepo) GetByID(ctx context.Context, id string) (*domain.User, error) {
if r.getByIDErr != nil { if r.getByIDErr != nil {
return nil, r.getByIDErr return nil, r.getByIDErr
@ -70,7 +70,7 @@ func (r *fakeRepo) GetByID(ctx context.Context, id string) (*domain.User, error)
return nil, sharederrors.Wrap(sharederrors.CodeUserNotFound, "user not found", nil) return nil, sharederrors.Wrap(sharederrors.CodeUserNotFound, "user not found", nil)
} }
// GetByEmail returns a non-deleted user by email. // GetByEmail retrieves data from the in-memory repository.
func (r *fakeRepo) GetByEmail(ctx context.Context, email string) (*domain.User, error) { func (r *fakeRepo) GetByEmail(ctx context.Context, email string) (*domain.User, error) {
if r.getByEmailErr != nil { if r.getByEmailErr != nil {
return nil, r.getByEmailErr return nil, r.getByEmailErr
@ -81,7 +81,7 @@ func (r *fakeRepo) GetByEmail(ctx context.Context, email string) (*domain.User,
return nil, sharederrors.Wrap(sharederrors.CodeUserNotFound, "user not found", nil) return nil, sharederrors.Wrap(sharederrors.CodeUserNotFound, "user not found", nil)
} }
// GetByZitadelUserID returns a non-deleted user by Zitadel subject. // GetByZitadelUserID retrieves data from the in-memory repository.
func (r *fakeRepo) GetByZitadelUserID(ctx context.Context, zid string) (*domain.User, error) { func (r *fakeRepo) GetByZitadelUserID(ctx context.Context, zid string) (*domain.User, error) {
if r.getByZidErr != nil { if r.getByZidErr != nil {
return nil, r.getByZidErr return nil, r.getByZidErr
@ -92,7 +92,7 @@ func (r *fakeRepo) GetByZitadelUserID(ctx context.Context, zid string) (*domain.
return nil, sharederrors.Wrap(sharederrors.CodeUserNotFound, "user not found", nil) return nil, sharederrors.Wrap(sharederrors.CodeUserNotFound, "user not found", nil)
} }
// UpdateProfile updates mutable user profile fields in place. // UpdateProfile updates an existing entity in the in-memory repository.
func (r *fakeRepo) UpdateProfile( func (r *fakeRepo) UpdateProfile(
ctx context.Context, ctx context.Context,
id, displayName string, id, displayName string,
@ -113,7 +113,7 @@ func (r *fakeRepo) UpdateProfile(
return u, nil return u, nil
} }
// MarkEmailVerified marks a user as verified in the fake store. // MarkEmailVerified supports mark email verified test setup and assertions.
func (r *fakeRepo) MarkEmailVerified(ctx context.Context, id string) (*domain.User, error) { func (r *fakeRepo) MarkEmailVerified(ctx context.Context, id string) (*domain.User, error) {
if r.verifyErr != nil { if r.verifyErr != nil {
return nil, r.verifyErr return nil, r.verifyErr
@ -127,7 +127,7 @@ func (r *fakeRepo) MarkEmailVerified(ctx context.Context, id string) (*domain.Us
return u, nil return u, nil
} }
// SoftDelete marks a user as deleted and records an audit entry. // SoftDelete supports soft delete test setup and assertions.
func (r *fakeRepo) SoftDelete(ctx context.Context, id string, actorUserID string) error { func (r *fakeRepo) SoftDelete(ctx context.Context, id string, actorUserID string) error {
if r.deleteErr != nil { if r.deleteErr != nil {
return r.deleteErr return r.deleteErr
@ -149,7 +149,7 @@ func (r *fakeRepo) SoftDelete(ctx context.Context, id string, actorUserID string
return nil return nil
} }
// List returns users while honoring include-deleted filtering. // List returns filtered collections from the in-memory repository.
func (r *fakeRepo) List( func (r *fakeRepo) List(
ctx context.Context, ctx context.Context,
pagination sharedtypes.Pagination, pagination sharedtypes.Pagination,
@ -168,7 +168,7 @@ func (r *fakeRepo) List(
return items, int64(len(items)), nil return items, int64(len(items)), nil
} }
// AuditLogsByUserID returns audit entries associated with a target user. // AuditLogsByUserID supports audit logs by user id test setup and assertions.
func (r *fakeRepo) AuditLogsByUserID(ctx context.Context, id string) ([]domain.AuditLogEntry, error) { func (r *fakeRepo) AuditLogsByUserID(ctx context.Context, id string) ([]domain.AuditLogEntry, error) {
if r.logsErr != nil { if r.logsErr != nil {
return nil, r.logsErr return nil, r.logsErr
@ -182,7 +182,7 @@ func (r *fakeRepo) AuditLogsByUserID(ctx context.Context, id string) ([]domain.A
return out, nil return out, nil
} }
// WriteAuditLog appends an audit entry to the fake repository. // WriteAuditLog supports write audit log test setup and assertions.
func (r *fakeRepo) WriteAuditLog(ctx context.Context, entry domain.AuditLogEntry) error { func (r *fakeRepo) WriteAuditLog(ctx context.Context, entry domain.AuditLogEntry) error {
if r.writeAuditErr != nil { if r.writeAuditErr != nil {
return r.writeAuditErr return r.writeAuditErr
@ -191,7 +191,7 @@ func (r *fakeRepo) WriteAuditLog(ctx context.Context, entry domain.AuditLogEntry
return nil return nil
} }
// TestRegisterIdempotent verifies repeated registration returns the same existing user. // TestRegisterIdempotent ensures register idempotent behavior is handled correctly.
func TestRegisterIdempotent(t *testing.T) { func TestRegisterIdempotent(t *testing.T) {
repo := newFakeRepo() repo := newFakeRepo()
svc := NewService(repo) svc := NewService(repo)
@ -223,7 +223,7 @@ func TestRegisterIdempotent(t *testing.T) {
} }
} }
// TestDeleteAndExport verifies a deleted user can no longer be fetched. // TestDeleteAndExport ensures delete and export behavior is handled correctly.
func TestDeleteAndExport(t *testing.T) { func TestDeleteAndExport(t *testing.T) {
repo := newFakeRepo() repo := newFakeRepo()
svc := NewService(repo) svc := NewService(repo)
@ -249,7 +249,7 @@ func TestDeleteAndExport(t *testing.T) {
} }
} }
// TestRegisterValidationAndRepoErrors verifies expected behavior. // TestRegisterValidationAndRepoErrors ensures register validation and repo errors behavior is handled correctly.
func TestRegisterValidationAndRepoErrors(t *testing.T) { func TestRegisterValidationAndRepoErrors(t *testing.T) {
svc := NewService(newFakeRepo()) svc := NewService(newFakeRepo())
_, err := svc.Register(context.Background(), RegisterInput{}) _, err := svc.Register(context.Background(), RegisterInput{})
@ -308,7 +308,7 @@ func TestRegisterValidationAndRepoErrors(t *testing.T) {
} }
} }
// TestProfileAndEmailFlows verifies expected behavior. // TestProfileAndEmailFlows ensures profile and email flows behavior is handled correctly.
func TestProfileAndEmailFlows(t *testing.T) { func TestProfileAndEmailFlows(t *testing.T) {
repo := newFakeRepo() repo := newFakeRepo()
svc := NewService(repo) svc := NewService(repo)
@ -349,7 +349,7 @@ func TestProfileAndEmailFlows(t *testing.T) {
} }
} }
// TestProfileValidationAndRepoErrors verifies expected behavior. // TestProfileValidationAndRepoErrors ensures profile validation and repo errors behavior is handled correctly.
func TestProfileValidationAndRepoErrors(t *testing.T) { func TestProfileValidationAndRepoErrors(t *testing.T) {
repo := newFakeRepo() repo := newFakeRepo()
svc := NewService(repo) svc := NewService(repo)
@ -400,7 +400,7 @@ func TestProfileValidationAndRepoErrors(t *testing.T) {
} }
} }
// TestAdminListAndExport verifies expected behavior. // TestAdminListAndExport ensures admin list and export behavior is handled correctly.
func TestAdminListAndExport(t *testing.T) { func TestAdminListAndExport(t *testing.T) {
repo := newFakeRepo() repo := newFakeRepo()
svc := NewService(repo) svc := NewService(repo)
@ -438,7 +438,7 @@ func TestAdminListAndExport(t *testing.T) {
} }
} }
// TestDeleteListExportErrors verifies expected behavior. // TestDeleteListExportErrors ensures delete list export errors behavior is handled correctly.
func TestDeleteListExportErrors(t *testing.T) { func TestDeleteListExportErrors(t *testing.T) {
repo := newFakeRepo() repo := newFakeRepo()
svc := NewService(repo) svc := NewService(repo)

@ -1,6 +1,6 @@
package tests package tests
// integration_http_test.go contains tests for backend behavior. // integration_http_test.go contains backend tests for package behavior, error paths, and regressions.
import ( import (
"bytes" "bytes"
@ -32,15 +32,15 @@ type inMemoryRepo struct {
audit []domain.AuditLogEntry audit []domain.AuditLogEntry
} }
// newInMemoryRepo creates an empty in-memory repository. // newInMemoryRepo creates in-memory test doubles and deterministic fixtures.
func newInMemoryRepo() *inMemoryRepo { func newInMemoryRepo() *inMemoryRepo {
return &inMemoryRepo{items: map[string]*domain.User{}, audit: make([]domain.AuditLogEntry, 0)} return &inMemoryRepo{items: map[string]*domain.User{}, audit: make([]domain.AuditLogEntry, 0)}
} }
// EnsureSchema is a no-op for the in-memory repository. // EnsureSchema initializes schema state required before repository operations.
func (r *inMemoryRepo) EnsureSchema(ctx context.Context) error { return nil } func (r *inMemoryRepo) EnsureSchema(ctx context.Context) error { return nil }
// Create inserts a user in memory and fills missing identity/timestamps. // Create persists a new entity in the in-memory repository.
func (r *inMemoryRepo) Create(ctx context.Context, user *domain.User) (*domain.User, error) { func (r *inMemoryRepo) Create(ctx context.Context, user *domain.User) (*domain.User, error) {
if user.ID == "" { if user.ID == "" {
user.ID = "user-" + strconv.Itoa(len(r.items)+1) user.ID = "user-" + strconv.Itoa(len(r.items)+1)
@ -54,7 +54,7 @@ func (r *inMemoryRepo) Create(ctx context.Context, user *domain.User) (*domain.U
return user, nil return user, nil
} }
// GetByID returns a non-deleted user by ID. // GetByID retrieves data from the in-memory repository.
func (r *inMemoryRepo) GetByID(ctx context.Context, id string) (*domain.User, error) { func (r *inMemoryRepo) GetByID(ctx context.Context, id string) (*domain.User, error) {
if u, ok := r.items[id]; ok && !u.IsDeleted() { if u, ok := r.items[id]; ok && !u.IsDeleted() {
return u, nil return u, nil
@ -62,7 +62,7 @@ func (r *inMemoryRepo) GetByID(ctx context.Context, id string) (*domain.User, er
return nil, sharederrors.Wrap(sharederrors.CodeUserNotFound, "user not found", nil) return nil, sharederrors.Wrap(sharederrors.CodeUserNotFound, "user not found", nil)
} }
// GetByEmail returns a non-deleted user matching the provided email. // GetByEmail retrieves data from the in-memory repository.
func (r *inMemoryRepo) GetByEmail(ctx context.Context, email string) (*domain.User, error) { func (r *inMemoryRepo) GetByEmail(ctx context.Context, email string) (*domain.User, error) {
for _, u := range r.items { for _, u := range r.items {
if u.Email == email && !u.IsDeleted() { if u.Email == email && !u.IsDeleted() {
@ -72,7 +72,7 @@ func (r *inMemoryRepo) GetByEmail(ctx context.Context, email string) (*domain.Us
return nil, sharederrors.Wrap(sharederrors.CodeUserNotFound, "user not found", nil) return nil, sharederrors.Wrap(sharederrors.CodeUserNotFound, "user not found", nil)
} }
// GetByZitadelUserID returns a non-deleted user by identity subject. // GetByZitadelUserID retrieves data from the in-memory repository.
func (r *inMemoryRepo) GetByZitadelUserID(ctx context.Context, zid string) (*domain.User, error) { func (r *inMemoryRepo) GetByZitadelUserID(ctx context.Context, zid string) (*domain.User, error) {
for _, u := range r.items { for _, u := range r.items {
if u.ZitadelUserID == zid && !u.IsDeleted() { if u.ZitadelUserID == zid && !u.IsDeleted() {
@ -82,7 +82,7 @@ func (r *inMemoryRepo) GetByZitadelUserID(ctx context.Context, zid string) (*dom
return nil, sharederrors.Wrap(sharederrors.CodeUserNotFound, "user not found", nil) return nil, sharederrors.Wrap(sharederrors.CodeUserNotFound, "user not found", nil)
} }
// UpdateProfile updates mutable profile fields for the selected user. // UpdateProfile updates an existing entity in the in-memory repository.
func (r *inMemoryRepo) UpdateProfile( func (r *inMemoryRepo) UpdateProfile(
ctx context.Context, ctx context.Context,
id, displayName string, id, displayName string,
@ -100,7 +100,7 @@ func (r *inMemoryRepo) UpdateProfile(
return u, nil return u, nil
} }
// MarkEmailVerified sets email verification on a stored user. // MarkEmailVerified supports mark email verified test setup and assertions.
func (r *inMemoryRepo) MarkEmailVerified(ctx context.Context, id string) (*domain.User, error) { func (r *inMemoryRepo) MarkEmailVerified(ctx context.Context, id string) (*domain.User, error) {
u, err := r.GetByID(ctx, id) u, err := r.GetByID(ctx, id)
if err != nil { if err != nil {
@ -111,7 +111,7 @@ func (r *inMemoryRepo) MarkEmailVerified(ctx context.Context, id string) (*domai
return u, nil return u, nil
} }
// SoftDelete marks a user as deleted and records a delete audit log. // SoftDelete supports soft delete test setup and assertions.
func (r *inMemoryRepo) SoftDelete(ctx context.Context, id string, actorUserID string) error { func (r *inMemoryRepo) SoftDelete(ctx context.Context, id string, actorUserID string) error {
u, err := r.GetByID(ctx, id) u, err := r.GetByID(ctx, id)
if err != nil { if err != nil {
@ -131,7 +131,7 @@ func (r *inMemoryRepo) SoftDelete(ctx context.Context, id string, actorUserID st
return nil return nil
} }
// List returns users and applies the include-deleted filter. // List returns filtered collections from the in-memory repository.
func (r *inMemoryRepo) List( func (r *inMemoryRepo) List(
ctx context.Context, ctx context.Context,
pagination sharedtypes.Pagination, pagination sharedtypes.Pagination,
@ -147,7 +147,7 @@ func (r *inMemoryRepo) List(
return out, int64(len(out)), nil return out, int64(len(out)), nil
} }
// AuditLogsByUserID returns audit events for a target user. // AuditLogsByUserID supports audit logs by user id test setup and assertions.
func (r *inMemoryRepo) AuditLogsByUserID(ctx context.Context, id string) ([]domain.AuditLogEntry, error) { func (r *inMemoryRepo) AuditLogsByUserID(ctx context.Context, id string) ([]domain.AuditLogEntry, error) {
out := make([]domain.AuditLogEntry, 0) out := make([]domain.AuditLogEntry, 0)
for _, a := range r.audit { for _, a := range r.audit {
@ -158,14 +158,14 @@ func (r *inMemoryRepo) AuditLogsByUserID(ctx context.Context, id string) ([]doma
return out, nil return out, nil
} }
// WriteAuditLog appends an audit event with a synthetic timestamp. // WriteAuditLog supports write audit log test setup and assertions.
func (r *inMemoryRepo) WriteAuditLog(ctx context.Context, entry domain.AuditLogEntry) error { func (r *inMemoryRepo) WriteAuditLog(ctx context.Context, entry domain.AuditLogEntry) error {
entry.CreatedAt = time.Now().UTC() entry.CreatedAt = time.Now().UTC()
r.audit = append(r.audit, entry) r.audit = append(r.audit, entry)
return nil return nil
} }
// setupApp wires a Fiber test app with auth middleware and in-memory dependencies. // setupApp wires the test application with mocked dependencies.
func setupApp(t *testing.T) (*fiber.App, *inMemoryRepo) { func setupApp(t *testing.T) (*fiber.App, *inMemoryRepo) {
t.Helper() t.Helper()
@ -221,7 +221,7 @@ func setupApp(t *testing.T) (*fiber.App, *inMemoryRepo) {
return app, repo return app, repo
} }
// TestRegisterAndCRUD verifies register, read, update, and delete HTTP flows. // TestRegisterAndCRUD ensures register and crud behavior is handled correctly.
func TestRegisterAndCRUD(t *testing.T) { func TestRegisterAndCRUD(t *testing.T) {
app, _ := setupApp(t) app, _ := setupApp(t)
@ -260,7 +260,7 @@ func TestRegisterAndCRUD(t *testing.T) {
defer resp.Body.Close() defer resp.Body.Close()
} }
// TestAuthGuardsAndAdminRoutes verifies auth guards and admin-only route access. // TestAuthGuardsAndAdminRoutes ensures auth guards and admin routes behavior is handled correctly.
func TestAuthGuardsAndAdminRoutes(t *testing.T) { func TestAuthGuardsAndAdminRoutes(t *testing.T) {
app, _ := setupApp(t) app, _ := setupApp(t)
@ -294,7 +294,7 @@ func TestAuthGuardsAndAdminRoutes(t *testing.T) {
defer resp.Body.Close() defer resp.Body.Close()
} }
// TestMetricsEndpoint verifies the Prometheus metrics endpoint is exposed. // TestMetricsEndpoint ensures metrics endpoint behavior is handled correctly.
func TestMetricsEndpoint(t *testing.T) { func TestMetricsEndpoint(t *testing.T) {
app, _ := setupApp(t) app, _ := setupApp(t)
@ -304,7 +304,7 @@ func TestMetricsEndpoint(t *testing.T) {
defer resp.Body.Close() defer resp.Body.Close()
} }
// TestVerifyEmailAndValidationPaths covers verify-email success/unauthorized and admin query validation errors. // TestVerifyEmailAndValidationPaths ensures verify email and validation paths behavior is handled correctly.
func TestVerifyEmailAndValidationPaths(t *testing.T) { func TestVerifyEmailAndValidationPaths(t *testing.T) {
app, _ := setupApp(t) app, _ := setupApp(t)
@ -332,7 +332,7 @@ func TestVerifyEmailAndValidationPaths(t *testing.T) {
defer resp.Body.Close() defer resp.Body.Close()
} }
// TestRegisterAndUpdateInvalidPayloads covers invalid request-body and validation paths. // TestRegisterAndUpdateInvalidPayloads ensures register and update invalid payloads behavior is handled correctly.
func TestRegisterAndUpdateInvalidPayloads(t *testing.T) { func TestRegisterAndUpdateInvalidPayloads(t *testing.T) {
app, _ := setupApp(t) app, _ := setupApp(t)

@ -9,7 +9,7 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
// TestDomainError_ErrorFormatting verifies error strings with and without wrapped errors. // TestDomainError_ErrorFormatting ensures domain error error formatting behavior is handled correctly.
func TestDomainError_ErrorFormatting(t *testing.T) { func TestDomainError_ErrorFormatting(t *testing.T) {
base := New(CodeNotFound, "missing") base := New(CodeNotFound, "missing")
require.Equal(t, "[NOT_FOUND] missing", base.Error()) require.Equal(t, "[NOT_FOUND] missing", base.Error())
@ -18,21 +18,21 @@ func TestDomainError_ErrorFormatting(t *testing.T) {
require.Equal(t, "[INVALID_INPUT] bad: details", wrapped.Error()) require.Equal(t, "[INVALID_INPUT] bad: details", wrapped.Error())
} }
// TestDomainError_Is ensures errors.Is matches on error code. // TestDomainError_Is ensures domain error is behavior is handled correctly.
func TestDomainError_Is(t *testing.T) { func TestDomainError_Is(t *testing.T) {
err := Wrap(CodeForbidden, "nope", nil) err := Wrap(CodeForbidden, "nope", nil)
target := New(CodeForbidden, "other") target := New(CodeForbidden, "other")
require.True(t, errors.Is(err, target)) require.True(t, errors.Is(err, target))
} }
// TestDomainError_Is_DifferentCode ensures errors.Is returns false for different codes. // TestDomainError_Is_DifferentCode ensures domain error is different code behavior is handled correctly.
func TestDomainError_Is_DifferentCode(t *testing.T) { func TestDomainError_Is_DifferentCode(t *testing.T) {
err := Wrap(CodeForbidden, "nope", nil) err := Wrap(CodeForbidden, "nope", nil)
target := New(CodeUnauthorized, "other") target := New(CodeUnauthorized, "other")
require.False(t, errors.Is(err, target)) require.False(t, errors.Is(err, target))
} }
// TestDomainError_NewAndWrapFields verifies fields are set for New and Wrap. // TestDomainError_NewAndWrapFields ensures domain error new and wrap fields behavior is handled correctly.
func TestDomainError_NewAndWrapFields(t *testing.T) { func TestDomainError_NewAndWrapFields(t *testing.T) {
created := New(CodeConflict, "conflict") created := New(CodeConflict, "conflict")
require.Equal(t, CodeConflict, created.Code) require.Equal(t, CodeConflict, created.Code)

@ -10,7 +10,7 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
// TestNewBaseEvent verifies event fields and timestamp range. // TestNewBaseEvent ensures new base event behavior is handled correctly.
func TestNewBaseEvent(t *testing.T) { func TestNewBaseEvent(t *testing.T) {
before := time.Now() before := time.Now()
base := NewBaseEvent(GameSessionStarted, "agg-1", "session") base := NewBaseEvent(GameSessionStarted, "agg-1", "session")
@ -23,12 +23,12 @@ func TestNewBaseEvent(t *testing.T) {
require.True(t, base.OccurredAt().Before(after) || base.OccurredAt().Equal(after)) require.True(t, base.OccurredAt().Before(after) || base.OccurredAt().Equal(after))
} }
// TestEventType_String ensures event type string returns the underlying value. // TestEventType_String ensures event type string behavior is handled correctly.
func TestEventType_String(t *testing.T) { func TestEventType_String(t *testing.T) {
require.Equal(t, "game_session.started", GameSessionStarted.String()) require.Equal(t, "game_session.started", GameSessionStarted.String())
} }
// TestEventType_NonEmpty ensures all event type constants are non-empty strings. // TestEventType_NonEmpty ensures event type non empty behavior is handled correctly.
func TestEventType_NonEmpty(t *testing.T) { func TestEventType_NonEmpty(t *testing.T) {
types := []EventType{ types := []EventType{
GameSessionStarted, GameSessionStarted,
@ -54,12 +54,12 @@ func TestEventType_NonEmpty(t *testing.T) {
type dummyEventBus struct{} type dummyEventBus struct{}
// Publish is a test helper. // Publish records event bus interactions for publish-subscribe assertions.
func (d *dummyEventBus) Publish(_ context.Context, _ Event) error { func (d *dummyEventBus) Publish(_ context.Context, _ Event) error {
return nil return nil
} }
// Subscribe is a test helper. // Subscribe records event bus interactions for publish-subscribe assertions.
func (d *dummyEventBus) Subscribe(_ EventHandler) error { func (d *dummyEventBus) Subscribe(_ EventHandler) error {
return nil return nil
} }

@ -8,14 +8,14 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
// TestID_NewIDIsValid ensures a new ID is non-empty and valid UUID. // TestID_NewIDIsValid ensures id new id is valid behavior is handled correctly.
func TestID_NewIDIsValid(t *testing.T) { func TestID_NewIDIsValid(t *testing.T) {
id := NewID() id := NewID()
require.False(t, id.IsEmpty()) require.False(t, id.IsEmpty())
require.True(t, id.IsValid()) require.True(t, id.IsValid())
} }
// TestID_IsValid verifies validity for empty, invalid, and generated IDs. // TestID_IsValid ensures id is valid behavior is handled correctly.
func TestID_IsValid(t *testing.T) { func TestID_IsValid(t *testing.T) {
require.False(t, ID("").IsValid()) require.False(t, ID("").IsValid())
require.False(t, IDFromString("not-a-uuid").IsValid()) require.False(t, IDFromString("not-a-uuid").IsValid())

@ -8,7 +8,7 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
// TestPagination_LimitOffsetNormalize verifies defaulting, clamping, and offset calculation. // TestPagination_LimitOffsetNormalize ensures pagination limit offset normalize behavior is handled correctly.
func TestPagination_LimitOffsetNormalize(t *testing.T) { func TestPagination_LimitOffsetNormalize(t *testing.T) {
p := Pagination{Page: 0, PageSize: 0} p := Pagination{Page: 0, PageSize: 0}
require.Equal(t, 0, p.Offset()) require.Equal(t, 0, p.Offset())
@ -22,7 +22,7 @@ func TestPagination_LimitOffsetNormalize(t *testing.T) {
require.Equal(t, MaxPageSize, p.PageSize) require.Equal(t, MaxPageSize, p.PageSize)
} }
// TestPaginatedResult verifies total pages and page navigation helpers. // TestPaginatedResult ensures paginated result behavior is handled correctly.
func TestPaginatedResult(t *testing.T) { func TestPaginatedResult(t *testing.T) {
pagination := Pagination{Page: 1, PageSize: 2} pagination := Pagination{Page: 1, PageSize: 2}
result := NewPaginatedResult([]string{"a", "b"}, 3, pagination) result := NewPaginatedResult([]string{"a", "b"}, 3, pagination)

@ -11,14 +11,14 @@ import (
errs "knowfoolery/backend/shared/domain/errors" errs "knowfoolery/backend/shared/domain/errors"
) )
// TestNewPlayerName_NormalizesAndTrims verifies whitespace trimming and space normalization. // TestNewPlayerName_NormalizesAndTrims ensures new player name normalizes and trims behavior is handled correctly.
func TestNewPlayerName_NormalizesAndTrims(t *testing.T) { func TestNewPlayerName_NormalizesAndTrims(t *testing.T) {
name, err := NewPlayerName(" Alice Bob ") name, err := NewPlayerName(" Alice Bob ")
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, "Alice Bob", name.String()) require.Equal(t, "Alice Bob", name.String())
} }
// TestNewPlayerName_LengthValidation ensures too-short and too-long names are rejected. // TestNewPlayerName_LengthValidation ensures new player name length validation behavior is handled correctly.
func TestNewPlayerName_LengthValidation(t *testing.T) { func TestNewPlayerName_LengthValidation(t *testing.T) {
_, err := NewPlayerName("A") _, err := NewPlayerName("A")
require.Error(t, err) require.Error(t, err)
@ -28,7 +28,7 @@ func TestNewPlayerName_LengthValidation(t *testing.T) {
require.Error(t, err) require.Error(t, err)
} }
// TestNewPlayerName_InvalidCharacters verifies disallowed characters yield an invalid player name error. // TestNewPlayerName_InvalidCharacters ensures new player name invalid characters behavior is handled correctly.
func TestNewPlayerName_InvalidCharacters(t *testing.T) { func TestNewPlayerName_InvalidCharacters(t *testing.T) {
_, err := NewPlayerName("Alice!") _, err := NewPlayerName("Alice!")
require.Error(t, err) require.Error(t, err)
@ -38,7 +38,7 @@ func TestNewPlayerName_InvalidCharacters(t *testing.T) {
require.Equal(t, errs.CodeInvalidPlayerName, domainErr.Code) require.Equal(t, errs.CodeInvalidPlayerName, domainErr.Code)
} }
// TestPlayerName_EqualsCaseInsensitive confirms equality ignores case differences. // TestPlayerName_EqualsCaseInsensitive ensures player name equals case insensitive behavior is handled correctly.
func TestPlayerName_EqualsCaseInsensitive(t *testing.T) { func TestPlayerName_EqualsCaseInsensitive(t *testing.T) {
name1, err := NewPlayerName("Alice") name1, err := NewPlayerName("Alice")
require.NoError(t, err) require.NoError(t, err)
@ -48,7 +48,7 @@ func TestPlayerName_EqualsCaseInsensitive(t *testing.T) {
require.True(t, name1.Equals(name2)) require.True(t, name1.Equals(name2))
} }
// TestPlayerName_IsEmpty confirms the zero value reports empty. // TestPlayerName_IsEmpty ensures player name is empty behavior is handled correctly.
func TestPlayerName_IsEmpty(t *testing.T) { func TestPlayerName_IsEmpty(t *testing.T) {
var name PlayerName var name PlayerName
require.True(t, name.IsEmpty()) require.True(t, name.IsEmpty())

@ -8,20 +8,20 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
// TestNewScore_ClampsNegativeToZero ensures negative inputs are clamped to zero. // TestNewScore_ClampsNegativeToZero ensures new score clamps negative to zero behavior is handled correctly.
func TestNewScore_ClampsNegativeToZero(t *testing.T) { func TestNewScore_ClampsNegativeToZero(t *testing.T) {
score := NewScore(-5) score := NewScore(-5)
require.Equal(t, 0, score.Value()) require.Equal(t, 0, score.Value())
} }
// TestScore_AddClampsBelowZero ensures adding negative points does not drop below zero. // TestScore_AddClampsBelowZero ensures score add clamps below zero behavior is handled correctly.
func TestScore_AddClampsBelowZero(t *testing.T) { func TestScore_AddClampsBelowZero(t *testing.T) {
score := NewScore(2) score := NewScore(2)
updated := score.Add(-10) updated := score.Add(-10)
require.Equal(t, 0, updated.Value()) require.Equal(t, 0, updated.Value())
} }
// TestCalculateQuestionScore verifies scoring for correct/incorrect and hint usage. // TestCalculateQuestionScore ensures calculate question score behavior is handled correctly.
func TestCalculateQuestionScore(t *testing.T) { func TestCalculateQuestionScore(t *testing.T) {
cases := []struct { cases := []struct {
name string name string
@ -41,7 +41,7 @@ func TestCalculateQuestionScore(t *testing.T) {
} }
} }
// TestAttempts verifies retry eligibility and remaining attempts calculation. // TestAttempts ensures attempts behavior is handled correctly.
func TestAttempts(t *testing.T) { func TestAttempts(t *testing.T) {
require.True(t, CanRetry(MaxAttempts-1)) require.True(t, CanRetry(MaxAttempts-1))
require.False(t, CanRetry(MaxAttempts)) require.False(t, CanRetry(MaxAttempts))

@ -8,7 +8,7 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
// TestRolePermissions verifies role permission checks for individual and aggregate queries. // TestRolePermissions ensures role permissions behavior is handled correctly.
func TestRolePermissions(t *testing.T) { func TestRolePermissions(t *testing.T) {
require.True(t, HasPermission(RolePlayer, PermissionPlayGame)) require.True(t, HasPermission(RolePlayer, PermissionPlayGame))
require.False(t, HasPermission(RolePlayer, PermissionManageSystem)) require.False(t, HasPermission(RolePlayer, PermissionManageSystem))
@ -20,13 +20,13 @@ func TestRolePermissions(t *testing.T) {
require.False(t, HasAllPermissions(RolePlayer, PermissionViewDashboard, PermissionPlayGame)) require.False(t, HasAllPermissions(RolePlayer, PermissionViewDashboard, PermissionPlayGame))
} }
// TestUserHasPermission verifies role strings grant expected permissions. // TestUserHasPermission ensures user has permission behavior is handled correctly.
func TestUserHasPermission(t *testing.T) { func TestUserHasPermission(t *testing.T) {
require.True(t, UserHasPermission([]string{"player"}, PermissionPlayGame)) require.True(t, UserHasPermission([]string{"player"}, PermissionPlayGame))
require.False(t, UserHasPermission([]string{"player"}, PermissionManageSystem)) require.False(t, UserHasPermission([]string{"player"}, PermissionManageSystem))
} }
// TestGetPermissionsReturnsCopy ensures returned permission slices are not shared. // TestGetPermissionsReturnsCopy ensures get permissions returns copy behavior is handled correctly.
func TestGetPermissionsReturnsCopy(t *testing.T) { func TestGetPermissionsReturnsCopy(t *testing.T) {
perms := GetPermissions(RolePlayer) perms := GetPermissions(RolePlayer)
require.NotEmpty(t, perms) require.NotEmpty(t, perms)
@ -36,7 +36,7 @@ func TestGetPermissionsReturnsCopy(t *testing.T) {
require.Equal(t, PermissionPlayGame, fresh[0]) require.Equal(t, PermissionPlayGame, fresh[0])
} }
// TestIsValidRole verifies known roles are accepted and unknown roles rejected. // TestIsValidRole ensures is valid role behavior is handled correctly.
func TestIsValidRole(t *testing.T) { func TestIsValidRole(t *testing.T) {
require.True(t, IsValidRole("player")) require.True(t, IsValidRole("player"))
require.False(t, IsValidRole("ghost")) require.False(t, IsValidRole("ghost"))

@ -33,7 +33,7 @@ type jwks struct {
Keys []jwksKey `json:"keys"` Keys []jwksKey `json:"keys"`
} }
// generateJWKS is a test helper. // generateJWKS supports generate jwks test setup and assertions.
func generateJWKS(t *testing.T) (*rsa.PrivateKey, jwks, string) { func generateJWKS(t *testing.T) (*rsa.PrivateKey, jwks, string) {
t.Helper() t.Helper()
key, err := rsa.GenerateKey(rand.Reader, 2048) key, err := rsa.GenerateKey(rand.Reader, 2048)
@ -57,7 +57,7 @@ func generateJWKS(t *testing.T) (*rsa.PrivateKey, jwks, string) {
}, kid }, kid
} }
// signToken is a test helper. // signToken supports sign token test setup and assertions.
func signToken(t *testing.T, key *rsa.PrivateKey, kid string, claims jwt.MapClaims) string { func signToken(t *testing.T, key *rsa.PrivateKey, kid string, claims jwt.MapClaims) string {
t.Helper() t.Helper()
token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims) token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
@ -67,7 +67,7 @@ func signToken(t *testing.T, key *rsa.PrivateKey, kid string, claims jwt.MapClai
return signed return signed
} }
// newOIDCServer is a test helper. // newOIDCServer creates in-memory test doubles and deterministic fixtures.
func newOIDCServer(t *testing.T, jwksDoc jwks) *httptest.Server { func newOIDCServer(t *testing.T, jwksDoc jwks) *httptest.Server {
t.Helper() t.Helper()
var baseURL string var baseURL string
@ -110,7 +110,7 @@ func newOIDCServer(t *testing.T, jwksDoc jwks) *httptest.Server {
return server return server
} }
// TestGetUserInfo_Success verifies user info retrieval on a 200 response. // TestGetUserInfo_Success ensures get user info success behavior is handled correctly.
func TestGetUserInfo_Success(t *testing.T) { func TestGetUserInfo_Success(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
require.Equal(t, "Bearer token", r.Header.Get("Authorization")) require.Equal(t, "Bearer token", r.Header.Get("Authorization"))
@ -131,7 +131,7 @@ func TestGetUserInfo_Success(t *testing.T) {
require.Equal(t, "user-1", info.ID) require.Equal(t, "user-1", info.ID)
} }
// TestGetUserInfo_NonOK verifies non-200 responses return an error. // TestGetUserInfo_NonOK ensures get user info non ok behavior is handled correctly.
func TestGetUserInfo_NonOK(t *testing.T) { func TestGetUserInfo_NonOK(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusInternalServerError) w.WriteHeader(http.StatusInternalServerError)
@ -143,7 +143,7 @@ func TestGetUserInfo_NonOK(t *testing.T) {
require.Error(t, err) require.Error(t, err)
} }
// TestValidateToken_Success verifies JWT validation using JWKS and discovery. // TestValidateToken_Success ensures validate token success behavior is handled correctly.
func TestValidateToken_Success(t *testing.T) { func TestValidateToken_Success(t *testing.T) {
key, jwksDoc, kid := generateJWKS(t) key, jwksDoc, kid := generateJWKS(t)
server := newOIDCServer(t, jwksDoc) server := newOIDCServer(t, jwksDoc)
@ -175,7 +175,7 @@ func TestValidateToken_Success(t *testing.T) {
require.Contains(t, parsed.Roles, "admin") require.Contains(t, parsed.Roles, "admin")
} }
// TestValidateToken_InvalidAudience verifies audience validation. // TestValidateToken_InvalidAudience ensures validate token invalid audience behavior is handled correctly.
func TestValidateToken_InvalidAudience(t *testing.T) { func TestValidateToken_InvalidAudience(t *testing.T) {
key, jwksDoc, kid := generateJWKS(t) key, jwksDoc, kid := generateJWKS(t)
server := newOIDCServer(t, jwksDoc) server := newOIDCServer(t, jwksDoc)
@ -196,7 +196,7 @@ func TestValidateToken_InvalidAudience(t *testing.T) {
require.Error(t, err) require.Error(t, err)
} }
// TestValidateToken_MissingRequiredClaim verifies required claim enforcement. // TestValidateToken_MissingRequiredClaim ensures validate token missing required claim behavior is handled correctly.
func TestValidateToken_MissingRequiredClaim(t *testing.T) { func TestValidateToken_MissingRequiredClaim(t *testing.T) {
key, jwksDoc, kid := generateJWKS(t) key, jwksDoc, kid := generateJWKS(t)
server := newOIDCServer(t, jwksDoc) server := newOIDCServer(t, jwksDoc)
@ -218,7 +218,7 @@ func TestValidateToken_MissingRequiredClaim(t *testing.T) {
require.Error(t, err) require.Error(t, err)
} }
// TestRefreshToken_Success verifies token refresh against discovery endpoint. // TestRefreshToken_Success ensures refresh token success behavior is handled correctly.
func TestRefreshToken_Success(t *testing.T) { func TestRefreshToken_Success(t *testing.T) {
key, jwksDoc, _ := generateJWKS(t) key, jwksDoc, _ := generateJWKS(t)
_ = key _ = key
@ -237,7 +237,7 @@ func TestRefreshToken_Success(t *testing.T) {
require.Equal(t, "new-access", resp.AccessToken) require.Equal(t, "new-access", resp.AccessToken)
} }
// TestRevokeToken_Success verifies token revocation against discovery endpoint. // TestRevokeToken_Success ensures revoke token success behavior is handled correctly.
func TestRevokeToken_Success(t *testing.T) { func TestRevokeToken_Success(t *testing.T) {
key, jwksDoc, _ := generateJWKS(t) key, jwksDoc, _ := generateJWKS(t)
_ = key _ = key
@ -255,7 +255,7 @@ func TestRevokeToken_Success(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
} }
// TestRefreshToken_UsesForm ensures refresh uses form-encoded requests. // TestRefreshToken_UsesForm ensures refresh token uses form behavior is handled correctly.
func TestRefreshToken_UsesForm(t *testing.T) { func TestRefreshToken_UsesForm(t *testing.T) {
key, jwksDoc, _ := generateJWKS(t) key, jwksDoc, _ := generateJWKS(t)
_ = key _ = key
@ -298,7 +298,7 @@ func TestRefreshToken_UsesForm(t *testing.T) {
require.Contains(t, captured, "client_id=client") require.Contains(t, captured, "client_id=client")
} }
// serverURL is a test helper. // serverURL returns the test OIDC server endpoint URL.
func serverURL(r *http.Request) string { func serverURL(r *http.Request) string {
return "http://" + r.Host return "http://" + r.Host
} }

@ -19,13 +19,13 @@ type fakeValidator struct {
called int called int
} }
// ValidateToken is a test helper. // ValidateToken simulates authentication token validation outcomes.
func (f *fakeValidator) ValidateToken(ctx context.Context, token string, _ ValidationOptions) (*AuthClaims, error) { func (f *fakeValidator) ValidateToken(ctx context.Context, token string, _ ValidationOptions) (*AuthClaims, error) {
f.called++ f.called++
return f.claims, f.err return f.claims, f.err
} }
// TestJWTMiddleware_Success verifies valid tokens populate context and allow requests. // TestJWTMiddleware_Success ensures jwt middleware success behavior is handled correctly.
func TestJWTMiddleware_Success(t *testing.T) { func TestJWTMiddleware_Success(t *testing.T) {
validator := &fakeValidator{claims: &AuthClaims{ validator := &fakeValidator{claims: &AuthClaims{
Subject: "user-1", Subject: "user-1",
@ -57,7 +57,7 @@ func TestJWTMiddleware_Success(t *testing.T) {
require.Equal(t, "user-1", body["user_id"]) require.Equal(t, "user-1", body["user_id"])
} }
// TestJWTMiddleware_MissingHeader verifies missing Authorization header returns 401. // TestJWTMiddleware_MissingHeader ensures jwt middleware missing header behavior is handled correctly.
func TestJWTMiddleware_MissingHeader(t *testing.T) { func TestJWTMiddleware_MissingHeader(t *testing.T) {
validator := &fakeValidator{} validator := &fakeValidator{}
app := fiber.New() app := fiber.New()
@ -71,7 +71,7 @@ func TestJWTMiddleware_MissingHeader(t *testing.T) {
require.Equal(t, http.StatusUnauthorized, resp.StatusCode) require.Equal(t, http.StatusUnauthorized, resp.StatusCode)
} }
// TestJWTMiddleware_InvalidHeaderFormat verifies malformed Authorization header returns 401. // TestJWTMiddleware_InvalidHeaderFormat ensures jwt middleware invalid header format behavior is handled correctly.
func TestJWTMiddleware_InvalidHeaderFormat(t *testing.T) { func TestJWTMiddleware_InvalidHeaderFormat(t *testing.T) {
validator := &fakeValidator{} validator := &fakeValidator{}
app := fiber.New() app := fiber.New()
@ -86,7 +86,7 @@ func TestJWTMiddleware_InvalidHeaderFormat(t *testing.T) {
require.Equal(t, http.StatusUnauthorized, resp.StatusCode) require.Equal(t, http.StatusUnauthorized, resp.StatusCode)
} }
// TestJWTMiddleware_AdminRoleRequired ensures admin paths reject non-admin roles. // TestJWTMiddleware_AdminRoleRequired ensures jwt middleware admin role required behavior is handled correctly.
func TestJWTMiddleware_AdminRoleRequired(t *testing.T) { func TestJWTMiddleware_AdminRoleRequired(t *testing.T) {
validator := &fakeValidator{claims: &AuthClaims{ validator := &fakeValidator{claims: &AuthClaims{
Subject: "user-1", Subject: "user-1",
@ -106,7 +106,7 @@ func TestJWTMiddleware_AdminRoleRequired(t *testing.T) {
require.Equal(t, http.StatusForbidden, resp.StatusCode) require.Equal(t, http.StatusForbidden, resp.StatusCode)
} }
// TestJWTMiddleware_MFARequiredForAdmin ensures admin paths require MFA verification. // TestJWTMiddleware_MFARequiredForAdmin ensures jwt middleware mfa required for admin behavior is handled correctly.
func TestJWTMiddleware_MFARequiredForAdmin(t *testing.T) { func TestJWTMiddleware_MFARequiredForAdmin(t *testing.T) {
validator := &fakeValidator{claims: &AuthClaims{ validator := &fakeValidator{claims: &AuthClaims{
Subject: "user-1", Subject: "user-1",
@ -126,7 +126,7 @@ func TestJWTMiddleware_MFARequiredForAdmin(t *testing.T) {
require.Equal(t, http.StatusForbidden, resp.StatusCode) require.Equal(t, http.StatusForbidden, resp.StatusCode)
} }
// TestJWTMiddleware_SkipPath verifies skip paths bypass token validation. // TestJWTMiddleware_SkipPath ensures jwt middleware skip path behavior is handled correctly.
func TestJWTMiddleware_SkipPath(t *testing.T) { func TestJWTMiddleware_SkipPath(t *testing.T) {
validator := &fakeValidator{err: fiber.ErrUnauthorized} validator := &fakeValidator{err: fiber.ErrUnauthorized}
app := fiber.New() app := fiber.New()

@ -10,21 +10,21 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
// TestDefaultConfig verifies default configuration values for the client. // TestDefaultConfig ensures default config behavior is handled correctly.
func TestDefaultConfig(t *testing.T) { func TestDefaultConfig(t *testing.T) {
cfg := DefaultConfig() cfg := DefaultConfig()
require.Equal(t, "localhost", cfg.Host) require.Equal(t, "localhost", cfg.Host)
require.Equal(t, 5432, cfg.Port) require.Equal(t, 5432, cfg.Port)
} }
// TestConfigDSNAndURL verifies DSN and URL formatting include expected parts. // TestConfigDSNAndURL ensures config dsn and url behavior is handled correctly.
func TestConfigDSNAndURL(t *testing.T) { func TestConfigDSNAndURL(t *testing.T) {
cfg := DefaultConfig() cfg := DefaultConfig()
require.Contains(t, cfg.DSN(), "host=localhost") require.Contains(t, cfg.DSN(), "host=localhost")
require.Contains(t, cfg.URL(), "postgresql://") require.Contains(t, cfg.URL(), "postgresql://")
} }
// TestConfigFromEnvDefaults verifies defaults are used when env vars are absent. // TestConfigFromEnvDefaults ensures config from env defaults behavior is handled correctly.
func TestConfigFromEnvDefaults(t *testing.T) { func TestConfigFromEnvDefaults(t *testing.T) {
t.Setenv("POSTGRES_HOST", "") t.Setenv("POSTGRES_HOST", "")
t.Setenv("POSTGRES_PORT", "") t.Setenv("POSTGRES_PORT", "")
@ -44,7 +44,7 @@ func TestConfigFromEnvDefaults(t *testing.T) {
require.Equal(t, def.SSLMode, cfg.SSLMode) require.Equal(t, def.SSLMode, cfg.SSLMode)
} }
// TestConfigFromEnvOverrides verifies valid env vars override defaults. // TestConfigFromEnvOverrides ensures config from env overrides behavior is handled correctly.
func TestConfigFromEnvOverrides(t *testing.T) { func TestConfigFromEnvOverrides(t *testing.T) {
t.Setenv("POSTGRES_HOST", "db") t.Setenv("POSTGRES_HOST", "db")
t.Setenv("POSTGRES_PORT", "15432") t.Setenv("POSTGRES_PORT", "15432")
@ -70,7 +70,7 @@ func TestConfigFromEnvOverrides(t *testing.T) {
require.Equal(t, 2*time.Minute, cfg.ConnMaxIdleTime) require.Equal(t, 2*time.Minute, cfg.ConnMaxIdleTime)
} }
// TestConfigFromEnvInvalidFallsBack verifies invalid numeric/duration values fall back. // TestConfigFromEnvInvalidFallsBack ensures config from env invalid falls back behavior is handled correctly.
func TestConfigFromEnvInvalidFallsBack(t *testing.T) { func TestConfigFromEnvInvalidFallsBack(t *testing.T) {
def := DefaultConfig() def := DefaultConfig()
t.Setenv("POSTGRES_PORT", "not-a-number") t.Setenv("POSTGRES_PORT", "not-a-number")
@ -83,7 +83,7 @@ func TestConfigFromEnvInvalidFallsBack(t *testing.T) {
require.Equal(t, def.ConnMaxLifetime, cfg.ConnMaxLifetime) require.Equal(t, def.ConnMaxLifetime, cfg.ConnMaxLifetime)
} }
// TestHealthCheckInvalidConfig verifies config validation runs before DB connection. // TestHealthCheckInvalidConfig ensures health check invalid config behavior is handled correctly.
func TestHealthCheckInvalidConfig(t *testing.T) { func TestHealthCheckInvalidConfig(t *testing.T) {
cfg := DefaultConfig() cfg := DefaultConfig()
cfg.Host = "" cfg.Host = ""
@ -93,7 +93,7 @@ func TestHealthCheckInvalidConfig(t *testing.T) {
require.Contains(t, err.Error(), "host is required") require.Contains(t, err.Error(), "host is required")
} }
// TestHealthCheckConnectionFailure verifies connection issues are surfaced. // TestHealthCheckConnectionFailure ensures health check connection failure behavior is handled correctly.
func TestHealthCheckConnectionFailure(t *testing.T) { func TestHealthCheckConnectionFailure(t *testing.T) {
cfg := DefaultConfig() cfg := DefaultConfig()
cfg.Host = "127.0.0.1" cfg.Host = "127.0.0.1"

@ -15,20 +15,20 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
// TestDefaultConfig verifies default configuration values for the client. // TestDefaultConfig ensures default config behavior is handled correctly.
func TestDefaultConfig(t *testing.T) { func TestDefaultConfig(t *testing.T) {
cfg := DefaultConfig() cfg := DefaultConfig()
require.Equal(t, "localhost", cfg.Host) require.Equal(t, "localhost", cfg.Host)
require.Equal(t, 6379, cfg.Port) require.Equal(t, 6379, cfg.Port)
} }
// TestAddr verifies Redis address formatting from config. // TestAddr ensures addr behavior is handled correctly.
func TestAddr(t *testing.T) { func TestAddr(t *testing.T) {
cfg := DefaultConfig() cfg := DefaultConfig()
require.Equal(t, "localhost:6379", cfg.Addr()) require.Equal(t, "localhost:6379", cfg.Addr())
} }
// newClientForMiniRedis is a test helper. // newClientForMiniRedis creates in-memory test doubles and deterministic fixtures.
func newClientForMiniRedis(t *testing.T) (*Client, *miniredis.Miniredis) { func newClientForMiniRedis(t *testing.T) (*Client, *miniredis.Miniredis) {
t.Helper() t.Helper()
@ -50,13 +50,13 @@ func newClientForMiniRedis(t *testing.T) (*Client, *miniredis.Miniredis) {
return client, mr return client, mr
} }
// TestHealthCheck verifies health checks use the real ping. // TestHealthCheck ensures health check behavior is handled correctly.
func TestHealthCheck(t *testing.T) { func TestHealthCheck(t *testing.T) {
client, _ := newClientForMiniRedis(t) client, _ := newClientForMiniRedis(t)
require.NoError(t, client.HealthCheck(context.Background())) require.NoError(t, client.HealthCheck(context.Background()))
} }
// TestSetGet verifies values can be stored and retrieved. // TestSetGet ensures set get behavior is handled correctly.
func TestSetGet(t *testing.T) { func TestSetGet(t *testing.T) {
client, _ := newClientForMiniRedis(t) client, _ := newClientForMiniRedis(t)
ctx := context.Background() ctx := context.Background()
@ -68,7 +68,7 @@ func TestSetGet(t *testing.T) {
require.Equal(t, "v", got) require.Equal(t, "v", got)
} }
// TestDeleteExists verifies deletion and existence checks. // TestDeleteExists ensures delete exists behavior is handled correctly.
func TestDeleteExists(t *testing.T) { func TestDeleteExists(t *testing.T) {
client, _ := newClientForMiniRedis(t) client, _ := newClientForMiniRedis(t)
ctx := context.Background() ctx := context.Background()
@ -84,7 +84,7 @@ func TestDeleteExists(t *testing.T) {
require.False(t, exists) require.False(t, exists)
} }
// TestIncr verifies atomic increments. // TestIncr ensures incr behavior is handled correctly.
func TestIncr(t *testing.T) { func TestIncr(t *testing.T) {
client, _ := newClientForMiniRedis(t) client, _ := newClientForMiniRedis(t)
ctx := context.Background() ctx := context.Background()
@ -98,7 +98,7 @@ func TestIncr(t *testing.T) {
require.Equal(t, int64(2), n) require.Equal(t, int64(2), n)
} }
// TestExpire verifies key expiration behavior. // TestExpire ensures expire behavior is handled correctly.
func TestExpire(t *testing.T) { func TestExpire(t *testing.T) {
client, mr := newClientForMiniRedis(t) client, mr := newClientForMiniRedis(t)
ctx := context.Background() ctx := context.Background()
@ -113,7 +113,7 @@ func TestExpire(t *testing.T) {
require.True(t, errors.Is(err, redisv9.Nil)) require.True(t, errors.Is(err, redisv9.Nil))
} }
// TestGetNotFound verifies missing keys return redis.Nil. // TestGetNotFound ensures get not found behavior is handled correctly.
func TestGetNotFound(t *testing.T) { func TestGetNotFound(t *testing.T) {
client, _ := newClientForMiniRedis(t) client, _ := newClientForMiniRedis(t)
_, err := client.Get(context.Background(), "missing") _, err := client.Get(context.Background(), "missing")
@ -121,7 +121,7 @@ func TestGetNotFound(t *testing.T) {
require.True(t, errors.Is(err, redisv9.Nil)) require.True(t, errors.Is(err, redisv9.Nil))
} }
// TestConfigFromEnvDefaults verifies default env behavior. // TestConfigFromEnvDefaults ensures config from env defaults behavior is handled correctly.
func TestConfigFromEnvDefaults(t *testing.T) { func TestConfigFromEnvDefaults(t *testing.T) {
t.Setenv("REDIS_HOST", "") t.Setenv("REDIS_HOST", "")
t.Setenv("REDIS_PORT", "") t.Setenv("REDIS_PORT", "")
@ -131,7 +131,7 @@ func TestConfigFromEnvDefaults(t *testing.T) {
require.Equal(t, def.Port, cfg.Port) require.Equal(t, def.Port, cfg.Port)
} }
// TestConfigFromEnvOverrides verifies valid env overrides. // TestConfigFromEnvOverrides ensures config from env overrides behavior is handled correctly.
func TestConfigFromEnvOverrides(t *testing.T) { func TestConfigFromEnvOverrides(t *testing.T) {
t.Setenv("REDIS_HOST", "cache") t.Setenv("REDIS_HOST", "cache")
t.Setenv("REDIS_PORT", "6380") t.Setenv("REDIS_PORT", "6380")
@ -155,7 +155,7 @@ func TestConfigFromEnvOverrides(t *testing.T) {
require.Equal(t, 5*time.Second, cfg.WriteTimeout) require.Equal(t, 5*time.Second, cfg.WriteTimeout)
} }
// TestNewClientInvalidConfig verifies early validation errors. // TestNewClientInvalidConfig ensures new client invalid config behavior is handled correctly.
func TestNewClientInvalidConfig(t *testing.T) { func TestNewClientInvalidConfig(t *testing.T) {
cfg := DefaultConfig() cfg := DefaultConfig()
cfg.Port = 0 cfg.Port = 0

@ -9,7 +9,7 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
// TestNewLogger_NoPanic verifies logger creation handles invalid levels and nil output. // TestNewLogger_NoPanic ensures new logger no panic behavior is handled correctly.
func TestNewLogger_NoPanic(t *testing.T) { func TestNewLogger_NoPanic(t *testing.T) {
require.NotPanics(t, func() { require.NotPanics(t, func() {
_ = NewLogger(Config{Level: "invalid", Environment: "development", Output: io.Discard}) _ = NewLogger(Config{Level: "invalid", Environment: "development", Output: io.Discard})
@ -20,7 +20,7 @@ func TestNewLogger_NoPanic(t *testing.T) {
}) })
} }
// TestLogger_WithFields verifies field-enriched loggers are created. // TestLogger_WithFields ensures logger with fields behavior is handled correctly.
func TestLogger_WithFields(t *testing.T) { func TestLogger_WithFields(t *testing.T) {
logger := NewLogger(DefaultConfig()) logger := NewLogger(DefaultConfig())
withField := logger.WithField("key", "value") withField := logger.WithField("key", "value")
@ -31,7 +31,7 @@ func TestLogger_WithFields(t *testing.T) {
require.NotNil(t, logger.Zerolog()) require.NotNil(t, logger.Zerolog())
} }
// TestDefaultConfig_Stable verifies defaults remain unchanged. // TestDefaultConfig_Stable ensures default config stable behavior is handled correctly.
func TestDefaultConfig_Stable(t *testing.T) { func TestDefaultConfig_Stable(t *testing.T) {
cfg := DefaultConfig() cfg := DefaultConfig()
require.Equal(t, "info", cfg.Level) require.Equal(t, "info", cfg.Level)

@ -1,6 +1,6 @@
package metrics package metrics
// http_test.go contains tests for backend behavior. // http_test.go contains backend tests for package behavior, error paths, and regressions.
import ( import (
"net/http" "net/http"
@ -10,7 +10,7 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
// TestHandler verifies Handler returns a usable HTTP handler. // TestHandler ensures handler behavior is handled correctly.
func TestHandler(t *testing.T) { func TestHandler(t *testing.T) {
h := Handler() h := Handler()
require.NotNil(t, h) require.NotNil(t, h)

@ -9,7 +9,7 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
// TestNewMetrics_WithCustomRegistry verifies metrics register on a provided registry. // TestNewMetrics_WithCustomRegistry ensures new metrics with custom registry behavior is handled correctly.
func TestNewMetrics_WithCustomRegistry(t *testing.T) { func TestNewMetrics_WithCustomRegistry(t *testing.T) {
registry := prometheus.NewRegistry() registry := prometheus.NewRegistry()
m := NewMetrics(Config{ServiceName: "svc", Enabled: true, Registry: registry}) m := NewMetrics(Config{ServiceName: "svc", Enabled: true, Registry: registry})
@ -20,7 +20,7 @@ func TestNewMetrics_WithCustomRegistry(t *testing.T) {
require.NotNil(t, m.ScoreDistribution) require.NotNil(t, m.ScoreDistribution)
} }
// TestConfigFromEnvDefaults verifies default env behavior. // TestConfigFromEnvDefaults ensures config from env defaults behavior is handled correctly.
func TestConfigFromEnvDefaults(t *testing.T) { func TestConfigFromEnvDefaults(t *testing.T) {
t.Setenv("METRICS_ENABLED", "") t.Setenv("METRICS_ENABLED", "")
t.Setenv("METRICS_SERVICE_NAME", "") t.Setenv("METRICS_SERVICE_NAME", "")
@ -31,7 +31,7 @@ func TestConfigFromEnvDefaults(t *testing.T) {
require.Equal(t, def, cfg) require.Equal(t, def, cfg)
} }
// TestConfigFromEnvOverrides verifies valid env overrides. // TestConfigFromEnvOverrides ensures config from env overrides behavior is handled correctly.
func TestConfigFromEnvOverrides(t *testing.T) { func TestConfigFromEnvOverrides(t *testing.T) {
t.Setenv("METRICS_ENABLED", "false") t.Setenv("METRICS_ENABLED", "false")
t.Setenv("METRICS_SERVICE_NAME", "admin-service") t.Setenv("METRICS_SERVICE_NAME", "admin-service")

@ -10,7 +10,7 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
// TestNewTracer_Disabled verifies disabled mode initializes and safely starts spans. // TestNewTracer_Disabled ensures new tracer disabled behavior is handled correctly.
func TestNewTracer_Disabled(t *testing.T) { func TestNewTracer_Disabled(t *testing.T) {
cfg := DefaultConfig() cfg := DefaultConfig()
cfg.Enabled = false cfg.Enabled = false
@ -29,7 +29,7 @@ func TestNewTracer_Disabled(t *testing.T) {
}) })
} }
// TestNewTracer_Enabled verifies enabled mode initializes tracer components. // TestNewTracer_Enabled ensures new tracer enabled behavior is handled correctly.
func TestNewTracer_Enabled(t *testing.T) { func TestNewTracer_Enabled(t *testing.T) {
cfg := DefaultConfig() cfg := DefaultConfig()
cfg.Enabled = true cfg.Enabled = true
@ -40,7 +40,7 @@ func TestNewTracer_Enabled(t *testing.T) {
require.NoError(t, tracer.Shutdown(context.Background())) require.NoError(t, tracer.Shutdown(context.Background()))
} }
// TestShutdown_Disabled verifies shutdown is a no-op when tracing is disabled. // TestShutdown_Disabled ensures shutdown disabled behavior is handled correctly.
func TestShutdown_Disabled(t *testing.T) { func TestShutdown_Disabled(t *testing.T) {
tracer, err := NewTracer(DefaultConfig()) tracer, err := NewTracer(DefaultConfig())
require.NoError(t, err) require.NoError(t, err)
@ -49,7 +49,7 @@ func TestShutdown_Disabled(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
} }
// TestTraceServiceOperation verifies tracing wraps a service operation and returns errors. // TestTraceServiceOperation ensures trace service operation behavior is handled correctly.
func TestTraceServiceOperation(t *testing.T) { func TestTraceServiceOperation(t *testing.T) {
tracer, err := NewTracer(DefaultConfig()) tracer, err := NewTracer(DefaultConfig())
require.NoError(t, err) require.NoError(t, err)
@ -61,7 +61,7 @@ func TestTraceServiceOperation(t *testing.T) {
require.Equal(t, expected, err) require.Equal(t, expected, err)
} }
// TestTraceDatabaseOperation verifies tracing wraps a database operation and calls the function. // TestTraceDatabaseOperation ensures trace database operation behavior is handled correctly.
func TestTraceDatabaseOperation(t *testing.T) { func TestTraceDatabaseOperation(t *testing.T) {
tracer, err := NewTracer(DefaultConfig()) tracer, err := NewTracer(DefaultConfig())
require.NoError(t, err) require.NoError(t, err)
@ -75,7 +75,7 @@ func TestTraceDatabaseOperation(t *testing.T) {
require.True(t, called) require.True(t, called)
} }
// TestConfigFromEnvDefaults verifies defaults are used when env vars are absent or empty. // TestConfigFromEnvDefaults ensures config from env defaults behavior is handled correctly.
func TestConfigFromEnvDefaults(t *testing.T) { func TestConfigFromEnvDefaults(t *testing.T) {
t.Setenv("TRACING_ENABLED", "") t.Setenv("TRACING_ENABLED", "")
t.Setenv("TRACING_SERVICE_NAME", "") t.Setenv("TRACING_SERVICE_NAME", "")
@ -91,7 +91,7 @@ func TestConfigFromEnvDefaults(t *testing.T) {
require.Equal(t, def, cfg) require.Equal(t, def, cfg)
} }
// TestConfigFromEnvOverrides verifies valid env vars override defaults. // TestConfigFromEnvOverrides ensures config from env overrides behavior is handled correctly.
func TestConfigFromEnvOverrides(t *testing.T) { func TestConfigFromEnvOverrides(t *testing.T) {
t.Setenv("TRACING_ENABLED", "true") t.Setenv("TRACING_ENABLED", "true")
t.Setenv("TRACING_SERVICE_NAME", "gateway") t.Setenv("TRACING_SERVICE_NAME", "gateway")
@ -111,7 +111,7 @@ func TestConfigFromEnvOverrides(t *testing.T) {
require.Equal(t, 0.25, cfg.SampleRate) require.Equal(t, 0.25, cfg.SampleRate)
} }
// TestConfigFromEnvInvalidSampleRate verifies invalid sample rate falls back to default. // TestConfigFromEnvInvalidSampleRate ensures config from env invalid sample rate behavior is handled correctly.
func TestConfigFromEnvInvalidSampleRate(t *testing.T) { func TestConfigFromEnvInvalidSampleRate(t *testing.T) {
t.Setenv("TRACING_SAMPLE_RATE", "not-a-float") t.Setenv("TRACING_SAMPLE_RATE", "not-a-float")
@ -119,7 +119,7 @@ func TestConfigFromEnvInvalidSampleRate(t *testing.T) {
require.Equal(t, DefaultConfig().SampleRate, cfg.SampleRate) require.Equal(t, DefaultConfig().SampleRate, cfg.SampleRate)
} }
// TestConfigFromEnvLegacyEndpointFallback verifies legacy env var is used when OTLP endpoint is unset. // TestConfigFromEnvLegacyEndpointFallback ensures config from env legacy endpoint fallback behavior is handled correctly.
func TestConfigFromEnvLegacyEndpointFallback(t *testing.T) { func TestConfigFromEnvLegacyEndpointFallback(t *testing.T) {
t.Setenv("TRACING_OTLP_ENDPOINT", "") t.Setenv("TRACING_OTLP_ENDPOINT", "")
t.Setenv("TRACING_JAEGER_ENDPOINT", "http://legacy:14268/api/traces") t.Setenv("TRACING_JAEGER_ENDPOINT", "http://legacy:14268/api/traces")
@ -129,7 +129,7 @@ func TestConfigFromEnvLegacyEndpointFallback(t *testing.T) {
require.Equal(t, "http://legacy:14268/api/traces", cfg.JaegerEndpoint) require.Equal(t, "http://legacy:14268/api/traces", cfg.JaegerEndpoint)
} }
// TestConfigFromEnvEndpointPrecedence verifies OTLP endpoint has precedence over legacy Jaeger endpoint. // TestConfigFromEnvEndpointPrecedence ensures config from env endpoint precedence behavior is handled correctly.
func TestConfigFromEnvEndpointPrecedence(t *testing.T) { func TestConfigFromEnvEndpointPrecedence(t *testing.T) {
t.Setenv("TRACING_OTLP_ENDPOINT", "https://collector:4318/v1/traces") t.Setenv("TRACING_OTLP_ENDPOINT", "https://collector:4318/v1/traces")
t.Setenv("TRACING_JAEGER_ENDPOINT", "http://legacy:14268/api/traces") t.Setenv("TRACING_JAEGER_ENDPOINT", "http://legacy:14268/api/traces")

@ -11,7 +11,7 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
// TestSanitize_Options verifies trimming, space collapsing, escaping, max length, and allowed patterns. // TestSanitize_Options ensures sanitize options behavior is handled correctly.
func TestSanitize_Options(t *testing.T) { func TestSanitize_Options(t *testing.T) {
opts := SanitizeOptions{ opts := SanitizeOptions{
TrimWhitespace: true, TrimWhitespace: true,
@ -31,29 +31,29 @@ func TestSanitize_Options(t *testing.T) {
require.Equal(t, "", Sanitize("Hello123", opts)) require.Equal(t, "", Sanitize("Hello123", opts))
} }
// TestSanitizePlayerName verifies player name normalization and invalid input rejection. // TestSanitizePlayerName ensures sanitize player name behavior is handled correctly.
func TestSanitizePlayerName(t *testing.T) { func TestSanitizePlayerName(t *testing.T) {
require.Equal(t, "Alice Bob", SanitizePlayerName(" Alice Bob ")) require.Equal(t, "Alice Bob", SanitizePlayerName(" Alice Bob "))
require.Equal(t, "", SanitizePlayerName("Alice <")) require.Equal(t, "", SanitizePlayerName("Alice <"))
} }
// TestSanitizeAnswer verifies lowercasing and whitespace normalization. // TestSanitizeAnswer ensures sanitize answer behavior is handled correctly.
func TestSanitizeAnswer(t *testing.T) { func TestSanitizeAnswer(t *testing.T) {
require.Equal(t, "hello", SanitizeAnswer(" HeLLo ")) require.Equal(t, "hello", SanitizeAnswer(" HeLLo "))
} }
// TestSanitizeQuestionText verifies script tags are removed from question text. // TestSanitizeQuestionText ensures sanitize question text behavior is handled correctly.
func TestSanitizeQuestionText(t *testing.T) { func TestSanitizeQuestionText(t *testing.T) {
result := SanitizeQuestionText("<script>alert(1)</script> Question") result := SanitizeQuestionText("<script>alert(1)</script> Question")
require.NotContains(t, result, "<script") require.NotContains(t, result, "<script")
} }
// TestSanitizeTheme verifies theme normalization and title casing. // TestSanitizeTheme ensures sanitize theme behavior is handled correctly.
func TestSanitizeTheme(t *testing.T) { func TestSanitizeTheme(t *testing.T) {
require.Equal(t, "Science Fiction", SanitizeTheme(" science fiction ")) require.Equal(t, "Science Fiction", SanitizeTheme(" science fiction "))
} }
// TestSanitizeLengthBoundaries verifies exact max length is preserved and over-limit input is truncated. // TestSanitizeLengthBoundaries ensures sanitize length boundaries behavior is handled correctly.
func TestSanitizeLengthBoundaries(t *testing.T) { func TestSanitizeLengthBoundaries(t *testing.T) {
playerExact := strings.Repeat("a", PlayerNameMaxLength) playerExact := strings.Repeat("a", PlayerNameMaxLength)
playerOver := strings.Repeat("a", PlayerNameMaxLength+1) playerOver := strings.Repeat("a", PlayerNameMaxLength+1)
@ -76,12 +76,12 @@ func TestSanitizeLengthBoundaries(t *testing.T) {
require.Equal(t, ThemeMaxLength, len(SanitizeTheme(themeOver))) require.Equal(t, ThemeMaxLength, len(SanitizeTheme(themeOver)))
} }
// TestRemoveHTMLTags verifies all HTML tags are stripped from input. // TestRemoveHTMLTags ensures remove html tags behavior is handled correctly.
func TestRemoveHTMLTags(t *testing.T) { func TestRemoveHTMLTags(t *testing.T) {
require.Equal(t, "Hi", RemoveHTMLTags("<b>Hi</b>")) require.Equal(t, "Hi", RemoveHTMLTags("<b>Hi</b>"))
} }
// TestContainsDangerousPatterns detects known dangerous substrings. // TestContainsDangerousPatterns ensures contains dangerous patterns behavior is handled correctly.
func TestContainsDangerousPatterns(t *testing.T) { func TestContainsDangerousPatterns(t *testing.T) {
cases := []struct { cases := []struct {
name string name string
@ -108,13 +108,13 @@ func TestContainsDangerousPatterns(t *testing.T) {
} }
} }
// TestIsValidEmail verifies basic email pattern validation. // TestIsValidEmail ensures is valid email behavior is handled correctly.
func TestIsValidEmail(t *testing.T) { func TestIsValidEmail(t *testing.T) {
require.True(t, IsValidEmail("a@b.com")) require.True(t, IsValidEmail("a@b.com"))
require.False(t, IsValidEmail("bad@")) require.False(t, IsValidEmail("bad@"))
} }
// TestSanitizeRuneSafeClamping verifies rune-based truncation remains valid UTF-8 and deterministic. // TestSanitizeRuneSafeClamping ensures sanitize rune safe clamping behavior is handled correctly.
func TestSanitizeRuneSafeClamping(t *testing.T) { func TestSanitizeRuneSafeClamping(t *testing.T) {
input := strings.Repeat("é", 5) input := strings.Repeat("é", 5)
opts := SanitizeOptions{ opts := SanitizeOptions{

@ -1,6 +1,6 @@
package envutil package envutil
// env_test.go contains tests for backend behavior. // env_test.go contains backend tests for package behavior, error paths, and regressions.
import ( import (
"testing" "testing"
@ -9,37 +9,37 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
// TestString verifies expected behavior. // TestString ensures string behavior is handled correctly.
func TestString(t *testing.T) { func TestString(t *testing.T) {
t.Setenv("APP_VALUE", "configured") t.Setenv("APP_VALUE", "configured")
require.Equal(t, "configured", String("APP_VALUE", "fallback")) require.Equal(t, "configured", String("APP_VALUE", "fallback"))
} }
// TestStringFallback verifies expected behavior. // TestStringFallback ensures string fallback behavior is handled correctly.
func TestStringFallback(t *testing.T) { func TestStringFallback(t *testing.T) {
t.Setenv("APP_VALUE", "") t.Setenv("APP_VALUE", "")
require.Equal(t, "fallback", String("APP_VALUE", "fallback")) require.Equal(t, "fallback", String("APP_VALUE", "fallback"))
} }
// TestInt verifies expected behavior. // TestInt ensures int behavior is handled correctly.
func TestInt(t *testing.T) { func TestInt(t *testing.T) {
t.Setenv("APP_PORT", "8080") t.Setenv("APP_PORT", "8080")
require.Equal(t, 8080, Int("APP_PORT", 3000)) require.Equal(t, 8080, Int("APP_PORT", 3000))
} }
// TestIntFallbackInvalid verifies expected behavior. // TestIntFallbackInvalid ensures int fallback invalid behavior is handled correctly.
func TestIntFallbackInvalid(t *testing.T) { func TestIntFallbackInvalid(t *testing.T) {
t.Setenv("APP_PORT", "oops") t.Setenv("APP_PORT", "oops")
require.Equal(t, 3000, Int("APP_PORT", 3000)) require.Equal(t, 3000, Int("APP_PORT", 3000))
} }
// TestDuration verifies expected behavior. // TestDuration ensures duration behavior is handled correctly.
func TestDuration(t *testing.T) { func TestDuration(t *testing.T) {
t.Setenv("APP_TIMEOUT", "7s") t.Setenv("APP_TIMEOUT", "7s")
require.Equal(t, 7*time.Second, Duration("APP_TIMEOUT", time.Second)) require.Equal(t, 7*time.Second, Duration("APP_TIMEOUT", time.Second))
} }
// TestDurationFallbackInvalid verifies expected behavior. // TestDurationFallbackInvalid ensures duration fallback invalid behavior is handled correctly.
func TestDurationFallbackInvalid(t *testing.T) { func TestDurationFallbackInvalid(t *testing.T) {
t.Setenv("APP_TIMEOUT", "oops") t.Setenv("APP_TIMEOUT", "oops")
require.Equal(t, 2*time.Second, Duration("APP_TIMEOUT", 2*time.Second)) require.Equal(t, 2*time.Second, Duration("APP_TIMEOUT", 2*time.Second))

@ -16,7 +16,7 @@ import (
errorspkg "knowfoolery/backend/shared/domain/errors" errorspkg "knowfoolery/backend/shared/domain/errors"
) )
// TestMapError verifies domain error codes map to HTTP status codes and response codes. // TestMapError ensures map error behavior is handled correctly.
func TestMapError(t *testing.T) { func TestMapError(t *testing.T) {
cases := []struct { cases := []struct {
name string name string
@ -47,7 +47,7 @@ func TestMapError(t *testing.T) {
} }
} }
// TestMapError_AllCodes ensures all known error codes map to expected HTTP status codes. // TestMapError_AllCodes ensures map error all codes behavior is handled correctly.
func TestMapError_AllCodes(t *testing.T) { func TestMapError_AllCodes(t *testing.T) {
cases := []struct { cases := []struct {
code errorspkg.ErrorCode code errorspkg.ErrorCode
@ -92,14 +92,14 @@ func TestMapError_AllCodes(t *testing.T) {
} }
} }
// TestMapError_UnknownCode ensures unknown codes fall back to 500. // TestMapError_UnknownCode ensures map error unknown code behavior is handled correctly.
func TestMapError_UnknownCode(t *testing.T) { func TestMapError_UnknownCode(t *testing.T) {
status, resp := MapError(errorspkg.New(errorspkg.ErrorCode("UNKNOWN"), "oops")) status, resp := MapError(errorspkg.New(errorspkg.ErrorCode("UNKNOWN"), "oops"))
require.Equal(t, http.StatusInternalServerError, status) require.Equal(t, http.StatusInternalServerError, status)
require.Equal(t, "UNKNOWN", resp.Code) require.Equal(t, "UNKNOWN", resp.Code)
} }
// TestSendError verifies SendError writes the correct status and payload. // TestSendError ensures send error behavior is handled correctly.
func TestSendError(t *testing.T) { func TestSendError(t *testing.T) {
app := fiber.New() app := fiber.New()
app.Get("/", func(c fiber.Ctx) error { app.Get("/", func(c fiber.Ctx) error {
@ -118,7 +118,7 @@ func TestSendError(t *testing.T) {
require.Equal(t, "NOT_FOUND", body.Code) require.Equal(t, "NOT_FOUND", body.Code)
} }
// TestMapError_WrappedDomainError verifies wrapped domain errors are recognized. // TestMapError_WrappedDomainError ensures map error wrapped domain error behavior is handled correctly.
func TestMapError_WrappedDomainError(t *testing.T) { func TestMapError_WrappedDomainError(t *testing.T) {
wrapped := fmt.Errorf("outer: %w", errorspkg.New(errorspkg.CodeValidationFailed, "bad input")) wrapped := fmt.Errorf("outer: %w", errorspkg.New(errorspkg.CodeValidationFailed, "bad input"))
status, resp := MapError(wrapped) status, resp := MapError(wrapped)
@ -127,7 +127,7 @@ func TestMapError_WrappedDomainError(t *testing.T) {
require.Equal(t, "bad input", resp.Message) require.Equal(t, "bad input", resp.Message)
} }
// TestTooManyRequestsRetryAfterHeader verifies Retry-After is set as a decimal string. // TestTooManyRequestsRetryAfterHeader ensures too many requests retry after header behavior is handled correctly.
func TestTooManyRequestsRetryAfterHeader(t *testing.T) { func TestTooManyRequestsRetryAfterHeader(t *testing.T) {
app := fiber.New() app := fiber.New()
app.Get("/", func(c fiber.Ctx) error { app.Get("/", func(c fiber.Ctx) error {

@ -14,7 +14,7 @@ import (
"knowfoolery/backend/shared/domain/types" "knowfoolery/backend/shared/domain/types"
) )
// TestPaginationFromQuery verifies defaulting and bounds from query parameters. // TestPaginationFromQuery ensures pagination from query behavior is handled correctly.
func TestPaginationFromQuery(t *testing.T) { func TestPaginationFromQuery(t *testing.T) {
app := fiber.New() app := fiber.New()
app.Get("/", func(c fiber.Ctx) error { app.Get("/", func(c fiber.Ctx) error {
@ -33,7 +33,7 @@ func TestPaginationFromQuery(t *testing.T) {
require.Equal(t, types.MaxPageSize, p.PageSize) require.Equal(t, types.MaxPageSize, p.PageSize)
} }
// TestSortingFromQuery verifies allowed field enforcement and direction normalization. // TestSortingFromQuery ensures sorting from query behavior is handled correctly.
func TestSortingFromQuery(t *testing.T) { func TestSortingFromQuery(t *testing.T) {
app := fiber.New() app := fiber.New()
app.Get("/", func(c fiber.Ctx) error { app.Get("/", func(c fiber.Ctx) error {
@ -52,7 +52,7 @@ func TestSortingFromQuery(t *testing.T) {
require.Equal(t, "asc", s.Direction) require.Equal(t, "asc", s.Direction)
} }
// TestFiltersFromQueryAndCustom verifies base filters and custom filter extraction. // TestFiltersFromQueryAndCustom ensures filters from query and custom behavior is handled correctly.
func TestFiltersFromQueryAndCustom(t *testing.T) { func TestFiltersFromQueryAndCustom(t *testing.T) {
app := fiber.New() app := fiber.New()
app.Get("/", func(c fiber.Ctx) error { app.Get("/", func(c fiber.Ctx) error {

@ -12,14 +12,14 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
// TestNewPaginatedResponse verifies total page calculation in paginated responses. // TestNewPaginatedResponse ensures new paginated response behavior is handled correctly.
func TestNewPaginatedResponse(t *testing.T) { func TestNewPaginatedResponse(t *testing.T) {
resp := NewPaginatedResponse([]string{"a"}, 1, 2, 3) resp := NewPaginatedResponse([]string{"a"}, 1, 2, 3)
require.NotNil(t, resp.Meta) require.NotNil(t, resp.Meta)
require.Equal(t, 2, resp.Meta.TotalPages) require.Equal(t, 2, resp.Meta.TotalPages)
} }
// TestHealthStatus verifies health status flips to unhealthy when any check fails. // TestHealthStatus ensures health status behavior is handled correctly.
func TestHealthStatus(t *testing.T) { func TestHealthStatus(t *testing.T) {
app := fiber.New() app := fiber.New()
app.Get("/health", func(c fiber.Ctx) error { app.Get("/health", func(c fiber.Ctx) error {
@ -39,7 +39,7 @@ func TestHealthStatus(t *testing.T) {
require.Equal(t, "unhealthy", body.Status) require.Equal(t, "unhealthy", body.Status)
} }
// TestResponseHelpers verifies status codes for OK, Created, NoContent, Paginated, and Message. // TestResponseHelpers ensures response helpers behavior is handled correctly.
func TestResponseHelpers(t *testing.T) { func TestResponseHelpers(t *testing.T) {
app := fiber.New() app := fiber.New()
app.Get("/ok", func(c fiber.Ctx) error { app.Get("/ok", func(c fiber.Ctx) error {

@ -1,6 +1,6 @@
package serviceboot package serviceboot
// fiber_test.go contains tests for backend behavior. // fiber_test.go contains backend tests for package behavior, error paths, and regressions.
import ( import (
"encoding/json" "encoding/json"
@ -11,7 +11,7 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
// TestRegisterHealth verifies expected behavior. // TestRegisterHealth ensures register health behavior is handled correctly.
func TestRegisterHealth(t *testing.T) { func TestRegisterHealth(t *testing.T) {
app := NewFiberApp(Config{AppName: "test-service"}) app := NewFiberApp(Config{AppName: "test-service"})
RegisterHealth(app, "svc") RegisterHealth(app, "svc")
@ -27,19 +27,19 @@ func TestRegisterHealth(t *testing.T) {
require.Equal(t, "svc", body["service"]) require.Equal(t, "svc", body["service"])
} }
// TestListenAddressFromEnv verifies expected behavior. // TestListenAddressFromEnv ensures listen address from env behavior is handled correctly.
func TestListenAddressFromEnv(t *testing.T) { func TestListenAddressFromEnv(t *testing.T) {
t.Setenv("SERVICE_PORT", "9090") t.Setenv("SERVICE_PORT", "9090")
require.Equal(t, ":9090", ListenAddress("SERVICE_PORT", 8080)) require.Equal(t, ":9090", ListenAddress("SERVICE_PORT", 8080))
} }
// TestListenAddressFallback verifies expected behavior. // TestListenAddressFallback ensures listen address fallback behavior is handled correctly.
func TestListenAddressFallback(t *testing.T) { func TestListenAddressFallback(t *testing.T) {
t.Setenv("SERVICE_PORT", "bad") t.Setenv("SERVICE_PORT", "bad")
require.Equal(t, ":8080", ListenAddress("SERVICE_PORT", 8080)) require.Equal(t, ":8080", ListenAddress("SERVICE_PORT", 8080))
} }
// TestListenAddressOutOfRangeFallback verifies expected behavior. // TestListenAddressOutOfRangeFallback ensures listen address out of range fallback behavior is handled correctly.
func TestListenAddressOutOfRangeFallback(t *testing.T) { func TestListenAddressOutOfRangeFallback(t *testing.T) {
t.Setenv("SERVICE_PORT", "70000") t.Setenv("SERVICE_PORT", "70000")
require.Equal(t, ":8080", ListenAddress("SERVICE_PORT", 8080)) require.Equal(t, ":8080", ListenAddress("SERVICE_PORT", 8080))

@ -19,7 +19,7 @@ type playerStruct struct {
PlayerName string `json:"player_name" validate:"required,player_name"` PlayerName string `json:"player_name" validate:"required,player_name"`
} }
// TestValidator_CustomTags verifies custom validation tags reject invalid inputs. // TestValidator_CustomTags ensures validator custom tags behavior is handled correctly.
func TestValidator_CustomTags(t *testing.T) { func TestValidator_CustomTags(t *testing.T) {
v := NewValidator() v := NewValidator()
@ -29,7 +29,7 @@ func TestValidator_CustomTags(t *testing.T) {
require.Error(t, v.ValidateVar("A", "player_name")) require.Error(t, v.ValidateVar("A", "player_name"))
} }
// TestValidator_ValidateReturnsDomainError ensures struct validation returns a domain error with messages. // TestValidator_ValidateReturnsDomainError ensures validator validate returns domain error behavior is handled correctly.
func TestValidator_ValidateReturnsDomainError(t *testing.T) { func TestValidator_ValidateReturnsDomainError(t *testing.T) {
v := NewValidator() v := NewValidator()
err := v.Validate(sampleStruct{}) err := v.Validate(sampleStruct{})
@ -41,7 +41,7 @@ func TestValidator_ValidateReturnsDomainError(t *testing.T) {
require.True(t, strings.Contains(domainErr.Message, "name is required")) require.True(t, strings.Contains(domainErr.Message, "name is required"))
} }
// TestValidator_ValidateVarReturnsDomainError ensures field validation returns a domain error. // TestValidator_ValidateVarReturnsDomainError ensures validator validate var returns domain error behavior is handled correctly.
func TestValidator_ValidateVarReturnsDomainError(t *testing.T) { func TestValidator_ValidateVarReturnsDomainError(t *testing.T) {
v := NewValidator() v := NewValidator()
err := v.ValidateVar("bad", "email") err := v.ValidateVar("bad", "email")
@ -53,7 +53,7 @@ func TestValidator_ValidateVarReturnsDomainError(t *testing.T) {
require.Contains(t, domainErr.Message, "must be a valid email") require.Contains(t, domainErr.Message, "must be a valid email")
} }
// TestValidator_JSONTagNameMapping ensures JSON tag names appear in validation error text. // TestValidator_JSONTagNameMapping ensures validator json tag name mapping behavior is handled correctly.
func TestValidator_JSONTagNameMapping(t *testing.T) { func TestValidator_JSONTagNameMapping(t *testing.T) {
v := NewValidator() v := NewValidator()
err := v.Validate(playerStruct{PlayerName: "A"}) err := v.Validate(playerStruct{PlayerName: "A"})

Loading…
Cancel
Save