Improved test comments

master
oabrivard 1 month ago
parent 2fde7f1ff7
commit 05d950218c

@ -1,6 +1,6 @@
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 (
"context"
@ -30,15 +30,15 @@ type inMemoryAuditRepo struct {
appendErr error
}
// newInMemoryAuditRepo is a test helper.
// newInMemoryAuditRepo creates in-memory test doubles and deterministic fixtures.
func newInMemoryAuditRepo() *inMemoryAuditRepo {
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 }
// 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 {
if r.appendErr != nil {
return r.appendErr
@ -47,7 +47,7 @@ func (r *inMemoryAuditRepo) Append(ctx context.Context, e audit.Entry) error {
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) {
if r.listErr != nil {
return nil, r.listErr
@ -64,7 +64,7 @@ func (r *inMemoryAuditRepo) List(ctx context.Context, limit, offset int) ([]audi
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) {
if r.countErr != nil {
return 0, r.countErr
@ -72,12 +72,12 @@ func (r *inMemoryAuditRepo) Count(ctx context.Context) (int64, error) {
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) {
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 {
t.Helper()
@ -114,7 +114,7 @@ func setupApp(t *testing.T, repo *inMemoryAuditRepo) *fiber.App {
return app
}
// TestAdminAuthRoute verifies expected behavior.
// TestAdminAuthRoute ensures admin auth route behavior is handled correctly.
func TestAdminAuthRoute(t *testing.T) {
repo := newInMemoryAuditRepo()
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) {
t.Run("forbidden for non-admin", func(t *testing.T) {
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) {
t.Run("forbidden for non-admin", func(t *testing.T) {
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) {
app := fiber.New()
repo := newInMemoryAuditRepo()

@ -1,6 +1,6 @@
package session
// service_test.go contains tests for backend behavior.
// service_test.go contains backend tests for package behavior, error paths, and regressions.
import (
"context"
@ -19,7 +19,7 @@ type fakeRepo struct {
events []*domain.SessionEvent
}
// newFakeRepo is a test helper.
// newFakeRepo creates in-memory test doubles and deterministic fixtures.
func newFakeRepo() *fakeRepo {
return &fakeRepo{
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 }
// 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) {
session.ID = "sess-1"
now := time.Now().UTC()
@ -42,7 +42,7 @@ func (r *fakeRepo) CreateSession(ctx context.Context, session *domain.GameSessio
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) {
s, ok := r.sessions[id]
if !ok {
@ -52,7 +52,7 @@ func (r *fakeRepo) GetSessionByID(ctx context.Context, id string) (*domain.GameS
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) {
for _, s := range r.sessions {
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
}
// 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) {
if _, ok := r.sessions[session.ID]; !ok {
return nil, domain.ErrSessionNotFound
@ -74,21 +74,21 @@ func (r *fakeRepo) UpdateSession(ctx context.Context, session *domain.GameSessio
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 {
cp := *attempt
r.attempts = append(r.attempts, &cp)
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 {
cp := *event
r.events = append(r.events, &cp)
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) {
seen := map[string]bool{}
ids := make([]string, 0)
@ -111,7 +111,7 @@ type fakeQuestionBank struct {
answerOK bool
}
// GetRandomQuestion is a test helper.
// GetRandomQuestion retrieves data from the in-memory repository.
func (f *fakeQuestionBank) GetRandomQuestion(
ctx context.Context,
exclusions []string,
@ -131,7 +131,7 @@ func (f *fakeQuestionBank) GetRandomQuestion(
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) {
for _, q := range f.questions {
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")
}
// ValidateAnswer is a test helper.
// ValidateAnswer supports validate answer test setup and assertions.
func (f *fakeQuestionBank) ValidateAnswer(
ctx context.Context,
questionID, answer string,
@ -155,7 +155,7 @@ type fakeUserClient struct {
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) {
p := f.profile
if p.ID == "" {
@ -171,7 +171,7 @@ type fakeStateStore struct {
locks map[string]bool
}
// newFakeStateStore is a test helper.
// newFakeStateStore creates in-memory test doubles and deterministic fixtures.
func newFakeStateStore() *fakeStateStore {
return &fakeStateStore{
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) {
id, ok := s.active[playerID]
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 {
s.active[playerID] = sessionID
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 {
delete(s.active, playerID)
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 {
s.timers[sessionID] = expiresAt
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) {
t, ok := s.timers[sessionID]
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 {
delete(s.timers, sessionID)
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 {
if s.locks[sessionID] {
return false
@ -225,12 +225,12 @@ func (s *fakeStateStore) AcquireLock(ctx context.Context, sessionID string, ttl
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) {
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) {
svc := NewService(
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) {
repo := newFakeRepo()
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) {
svc := NewService(
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) {
repo := newFakeRepo()
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) {
svc := NewService(
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) {
svc := NewService(
newFakeRepo(),

@ -1,6 +1,6 @@
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 (
"bytes"
@ -13,7 +13,7 @@ import (
"knowfoolery/backend/shared/infra/utils/validation"
)
// TestUnauthorizedBranches verifies expected behavior.
// TestUnauthorizedBranches ensures unauthorized branches behavior is handled correctly.
func TestUnauthorizedBranches(t *testing.T) {
h := NewHandler(nil, validation.NewValidator(), nil, nil)
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) {
h := NewHandler(nil, validation.NewValidator(), nil, nil)
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) {
app := fiber.New()
app.Use(func(c fiber.Ctx) error {

@ -29,7 +29,7 @@ type inMemoryRepo struct {
attempts []*domain.SessionAttempt
}
// newInMemoryRepo is a test helper.
// newInMemoryRepo creates in-memory test doubles and deterministic fixtures.
func newInMemoryRepo() *inMemoryRepo {
return &inMemoryRepo{
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 }
// 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) {
session.ID = "sess-1"
now := time.Now().UTC()
@ -51,7 +51,7 @@ func (r *inMemoryRepo) CreateSession(ctx context.Context, session *domain.GameSe
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) {
s, ok := r.sessions[id]
if !ok {
@ -61,7 +61,7 @@ func (r *inMemoryRepo) GetSessionByID(ctx context.Context, id string) (*domain.G
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) {
for _, s := range r.sessions {
if s.PlayerID == playerID && s.Status == domain.StatusActive {
@ -72,7 +72,7 @@ func (r *inMemoryRepo) GetActiveSessionByPlayerID(ctx context.Context, playerID
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) {
cp := *session
cp.UpdatedAt = time.Now().UTC()
@ -80,17 +80,17 @@ func (r *inMemoryRepo) UpdateSession(ctx context.Context, session *domain.GameSe
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 {
cp := *attempt
r.attempts = append(r.attempts, &cp)
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 }
// 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) {
seen := map[string]bool{}
ids := make([]string, 0)
@ -112,7 +112,7 @@ type fakeQuestionBank struct {
questions []appsession.SessionQuestion
}
// GetRandomQuestion is a test helper.
// GetRandomQuestion retrieves data from the in-memory repository.
func (f *fakeQuestionBank) GetRandomQuestion(
ctx context.Context,
exclusions []string,
@ -132,7 +132,7 @@ func (f *fakeQuestionBank) GetRandomQuestion(
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) {
for _, q := range f.questions {
if q.ID == id {
@ -143,7 +143,7 @@ func (f *fakeQuestionBank) GetQuestionByID(ctx context.Context, id string) (*app
return nil, domain.ErrSessionNotFound
}
// ValidateAnswer is a test helper.
// ValidateAnswer supports validate answer test setup and assertions.
func (f *fakeQuestionBank) ValidateAnswer(
ctx context.Context,
questionID, answer string,
@ -157,7 +157,7 @@ func (f *fakeQuestionBank) ValidateAnswer(
// fakeUserClient returns a verified user profile for tests.
type fakeUserClient struct{}
// GetUserProfile is a test helper.
// GetUserProfile retrieves data from the in-memory repository.
func (f *fakeUserClient) GetUserProfile(
ctx context.Context,
userID, bearerToken string,
@ -175,7 +175,7 @@ type fakeStateStore struct {
locks map[string]bool
}
// newFakeStateStore is a test helper.
// newFakeStateStore creates in-memory test doubles and deterministic fixtures.
func newFakeStateStore() *fakeStateStore {
return &fakeStateStore{
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) {
id, ok := s.active[playerID]
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 {
s.active[playerID] = sessionID
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 {
delete(s.active, playerID)
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 {
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) {
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 }
// 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 {
if s.locks[sessionID] {
return false
@ -223,12 +223,12 @@ func (s *fakeStateStore) AcquireLock(ctx context.Context, sessionID string, ttl
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) {
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 {
t.Helper()
@ -280,7 +280,7 @@ func setupApp(t *testing.T) *fiber.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) {
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) {
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) {
app := setupApp(t)
req := httptest.NewRequest(http.MethodGet, "/metrics", nil)
@ -372,7 +372,7 @@ func TestMetricsEndpoint(t *testing.T) {
assertStatus(t, resp, http.StatusOK, "metrics failed")
}
// mustJSONRequest is a test helper.
// mustJSONRequest builds request fixtures and fails fast on malformed setup.
func mustJSONRequest(
t *testing.T,
app *fiber.App,
@ -399,7 +399,7 @@ func mustJSONRequest(
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) {
t.Helper()
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 {
t.Helper()
@ -421,7 +421,7 @@ func decodeDataMap(t *testing.T, resp *http.Response) map[string]any {
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 {
t.Helper()
m, ok := v.(map[string]any)
@ -431,7 +431,7 @@ func asMap(t *testing.T, v any) map[string]any {
return m
}
// asString is a test helper.
// asString decodes response data into assertion-friendly structures.
func asString(t *testing.T, v any) string {
t.Helper()
s, ok := v.(string)
@ -441,7 +441,7 @@ func asString(t *testing.T, v any) string {
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 {
t.Helper()

@ -1,6 +1,6 @@
package middleware
// middleware_test.go contains tests for backend behavior.
// middleware_test.go contains backend tests for package behavior, error paths, and regressions.
import (
"net/http"
@ -18,7 +18,7 @@ import (
"knowfoolery/backend/shared/infra/observability/logging"
)
// TestCORSMiddleware verifies expected behavior.
// TestCORSMiddleware ensures cors middleware behavior is handled correctly.
func TestCORSMiddleware(t *testing.T) {
app := fiber.New()
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) {
app := fiber.New()
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) {
app := fiber.New()
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) {
app := fiber.New()
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) {
app := fiber.New()
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) {
logger := logging.NewLogger(logging.DefaultConfig())
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) {
app := fiber.New()
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) {
mr, err := miniredis.Run()
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) {
// Use a client pointing to an unreachable endpoint to force script.Run error.
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) {
app := fiber.New()
app.Get("/id", func(c fiber.Ctx) error {

@ -1,6 +1,6 @@
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 (
"io"
@ -21,7 +21,7 @@ import (
"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) {
t.Parallel()
@ -49,7 +49,7 @@ func TestGateway_PublicRoute_ProxiesAndRewritesPath(t *testing.T) {
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) {
t.Parallel()
@ -67,7 +67,7 @@ func TestGateway_ProtectedRoute_RequiresAuth(t *testing.T) {
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) {
t.Parallel()
@ -95,7 +95,7 @@ func TestGateway_ProtectedRoute_ForwardsUserHeaders(t *testing.T) {
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) {
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"))
}
// 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 {
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)
// RoundTrip is a test helper.
// RoundTrip stubs upstream round trips for gateway proxy assertions.
func (fn roundTripperFunc) RoundTrip(req *http.Request) (*http.Response, error) {
return fn(req)
}
// jsonResponse is a test helper.
// jsonResponse supports json response test setup and assertions.
func jsonResponse(status int, body string) *http.Response {
return &http.Response{
StatusCode: status,

@ -1,6 +1,6 @@
package leaderboard
// service_test.go contains tests for backend behavior.
// service_test.go contains backend tests for package behavior, error paths, and regressions.
import (
"context"
@ -25,7 +25,7 @@ type fakeRepo struct {
globalErr error
}
// newFakeRepo is a test helper.
// newFakeRepo creates in-memory test doubles and deterministic fixtures.
func newFakeRepo() *fakeRepo {
return &fakeRepo{
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 }
// IngestEntry is a test helper.
// IngestEntry supports ingest entry test setup and assertions.
func (r *fakeRepo) IngestEntry(
ctx context.Context,
entry *domain.LeaderboardEntry,
@ -93,7 +93,7 @@ func (r *fakeRepo) IngestEntry(
return &cp, false, nil
}
// ListTop is a test helper.
// ListTop returns filtered collections from the in-memory repository.
func (r *fakeRepo) ListTop(
ctx context.Context,
filter domain.TopFilter,
@ -112,7 +112,7 @@ func (r *fakeRepo) ListTop(
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) {
if r.statsErr != nil {
return nil, r.statsErr
@ -125,7 +125,7 @@ func (r *fakeRepo) GetPlayerStats(ctx context.Context, playerID string) (*domain
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) {
if r.rankErr != nil {
return 0, r.rankErr
@ -136,7 +136,7 @@ func (r *fakeRepo) GetPlayerRank(ctx context.Context, playerID string) (int64, e
return 1, nil
}
// ListPlayerHistory is a test helper.
// ListPlayerHistory returns filtered collections from the in-memory repository.
func (r *fakeRepo) ListPlayerHistory(
ctx context.Context,
playerID string,
@ -154,7 +154,7 @@ func (r *fakeRepo) ListPlayerHistory(
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) {
if r.globalErr != nil {
return nil, r.globalErr
@ -168,18 +168,18 @@ type fakeState struct {
deleteErr error
}
// newFakeState is a test helper.
// newFakeState creates in-memory test doubles and deterministic fixtures.
func newFakeState() *fakeState {
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) {
v, ok := s.data[key]
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 {
if s.setErr != nil {
return s.setErr
@ -188,7 +188,7 @@ func (s *fakeState) Set(ctx context.Context, key, value string, ttl time.Duratio
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 {
if s.deleteErr != nil {
return s.deleteErr
@ -199,7 +199,7 @@ func (s *fakeState) Delete(ctx context.Context, keys ...string) error {
return nil
}
// TestUpdateScoreIdempotent verifies expected behavior.
// TestUpdateScoreIdempotent ensures update score idempotent behavior is handled correctly.
func TestUpdateScoreIdempotent(t *testing.T) {
repo := newFakeRepo()
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) {
svc := NewService(newFakeRepo(), newFakeState(), Config{})
_, 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) {
repo := newFakeRepo()
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) {
svc := NewService(newFakeRepo(), newFakeState(), Config{})
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) {
repo := newFakeRepo()
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) {
repo := newFakeRepo()
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) {
repo := newFakeRepo()
repo.topErr = errors.New("top boom")

@ -1,6 +1,6 @@
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 (
"bytes"
@ -13,7 +13,7 @@ import (
"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) {
h := NewHandler(nil, validation.NewValidator(), nil, nil, true, 20, 100)
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) {
h := NewHandler(nil, validation.NewValidator(), nil, nil, false, 20, 100)
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) {
if got := atoiWithDefault("", 3); got != 3 {
t.Fatalf("expected default for empty input")

@ -1,6 +1,6 @@
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 (
"bytes"
@ -29,7 +29,7 @@ type inMemoryRepo struct {
stats map[string]*domain.PlayerStats
}
// newInMemoryRepo is a test helper.
// newInMemoryRepo creates in-memory test doubles and deterministic fixtures.
func newInMemoryRepo() *inMemoryRepo {
return &inMemoryRepo{
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 }
// IngestEntry is a test helper.
// IngestEntry supports ingest entry test setup and assertions.
func (r *inMemoryRepo) IngestEntry(
ctx context.Context,
entry *domain.LeaderboardEntry,
@ -75,7 +75,7 @@ func (r *inMemoryRepo) IngestEntry(
return &cp, false, nil
}
// ListTop is a test helper.
// ListTop returns filtered collections from the in-memory repository.
func (r *inMemoryRepo) ListTop(
ctx context.Context,
filter domain.TopFilter,
@ -91,7 +91,7 @@ func (r *inMemoryRepo) ListTop(
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) {
stats := r.stats[playerID]
if stats == nil {
@ -101,7 +101,7 @@ func (r *inMemoryRepo) GetPlayerStats(ctx context.Context, playerID string) (*do
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) {
if _, ok := r.stats[playerID]; !ok {
return 0, domain.ErrPlayerNotFound
@ -109,7 +109,7 @@ func (r *inMemoryRepo) GetPlayerRank(ctx context.Context, playerID string) (int6
return 1, nil
}
// ListPlayerHistory is a test helper.
// ListPlayerHistory returns filtered collections from the in-memory repository.
func (r *inMemoryRepo) ListPlayerHistory(
ctx context.Context,
playerID string,
@ -124,7 +124,7 @@ func (r *inMemoryRepo) ListPlayerHistory(
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) {
return &domain.GlobalStats{
TotalGames: int64(len(r.entries)),
@ -135,18 +135,18 @@ func (r *inMemoryRepo) GetGlobalStats(ctx context.Context, filter domain.TopFilt
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 }
// 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 {
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 }
// setupApp is a test helper.
// setupApp wires the test application with mocked dependencies.
func setupApp(t *testing.T) *fiber.App {
t.Helper()
repo := newInMemoryRepo()
@ -178,7 +178,7 @@ func setupApp(t *testing.T) *fiber.App {
return app
}
// TestUpdateAndTop10 verifies expected behavior.
// TestUpdateAndTop10 ensures update and top10 behavior is handled correctly.
func TestUpdateAndTop10(t *testing.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) {
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) {
app := setupApp(t)
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) {
t.Helper()
if resp.StatusCode != want {

@ -26,7 +26,7 @@ type fakeRepo struct {
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) {
for _, q := range f.items {
if q.ID == id {
@ -36,7 +36,7 @@ func (f *fakeRepo) GetByID(ctx context.Context, id string) (*domain.Question, er
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) {
if f.createErr != nil {
return nil, f.createErr
@ -46,7 +46,7 @@ func (f *fakeRepo) Create(ctx context.Context, q *domain.Question) (*domain.Ques
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) {
if f.updateErr != nil {
return nil, f.updateErr
@ -54,12 +54,12 @@ func (f *fakeRepo) Update(ctx context.Context, id string, q *domain.Question) (*
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 {
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) {
if f.listErr != nil {
return nil, f.listErr
@ -67,7 +67,7 @@ func (f *fakeRepo) ListThemes(ctx context.Context) ([]string, error) {
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) {
if f.countErr != nil {
return 0, f.countErr
@ -78,7 +78,7 @@ func (f *fakeRepo) CountRandomCandidates(ctx context.Context, filter domain.Rand
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(
ctx context.Context,
filter domain.RandomFilter,
@ -96,7 +96,7 @@ func (f *fakeRepo) RandomByOffset(
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(
ctx context.Context,
questions []*domain.Question,
@ -117,13 +117,13 @@ type fakeCache struct {
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) {
q, ok := f.store[key]
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) {
if f.store == nil {
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
}
// 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 }
// 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) {
svc := NewService(&fakeRepo{}, &fakeCache{}, time.Minute, 200)
_, 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) {
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) {
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) {
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) {
svc := NewService(&fakeRepo{}, &fakeCache{}, time.Minute, 200)
_, 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) {
repo := &fakeRepo{countErr: errors.New("count boom")}
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) {
repo := &fakeRepo{}
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) {
repo := &fakeRepo{createErr: errors.New("create boom")}
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) {
cache := &fakeCache{}
repo := &fakeRepo{bulkCount: 1, bulkErrors: []domain.BulkError{{Index: 0, Reason: "row"}}}

@ -4,7 +4,7 @@ package question
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) {
tests := []struct {
name string

@ -29,12 +29,12 @@ type inMemoryRepo struct {
items map[string]*domain.Question
}
// newInMemoryRepo is a test helper.
// newInMemoryRepo creates in-memory test doubles and deterministic fixtures.
func newInMemoryRepo() *inMemoryRepo {
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) {
if q, ok := r.items[id]; ok {
return q, nil
@ -42,7 +42,7 @@ func (r *inMemoryRepo) GetByID(ctx context.Context, id string) (*domain.Question
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) {
q.ID = "q-created"
q.IsActive = true
@ -50,7 +50,7 @@ func (r *inMemoryRepo) Create(ctx context.Context, q *domain.Question) (*domain.
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) {
if _, ok := r.items[id]; !ok {
return nil, domain.ErrQuestionNotFound
@ -60,7 +60,7 @@ func (r *inMemoryRepo) Update(ctx context.Context, id string, q *domain.Question
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 {
if q, ok := r.items[id]; ok {
q.IsActive = false
@ -69,12 +69,12 @@ func (r *inMemoryRepo) SoftDelete(ctx context.Context, id string) error {
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) {
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) {
count := 0
for _, q := range r.items {
@ -86,7 +86,7 @@ func (r *inMemoryRepo) CountRandomCandidates(ctx context.Context, filter domain.
return count, nil
}
// RandomByOffset is a test helper.
// RandomByOffset supports random by offset test setup and assertions.
func (r *inMemoryRepo) RandomByOffset(ctx context.Context,
filter domain.RandomFilter, offset int) (*domain.Question, error) {
for _, q := range r.items {
@ -97,7 +97,7 @@ func (r *inMemoryRepo) RandomByOffset(ctx context.Context,
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) {
for i, q := range questions {
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.
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) {
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) {}
// Invalidate is a test helper.
// Invalidate removes ephemeral state from the fake cache or state store.
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 {
t.Helper()
repo := newInMemoryRepo()
@ -154,7 +154,7 @@ func setupApp(t *testing.T) *fiber.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) {
app := setupApp(t)
@ -174,7 +174,7 @@ func TestHTTPRandomAndGet(t *testing.T) {
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) {
app := setupApp(t)
@ -211,7 +211,7 @@ func TestHTTPAdminAuthAndMetrics(t *testing.T) {
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) {
app := setupApp(t)
@ -256,7 +256,7 @@ func TestHTTPValidateAnswer(t *testing.T) {
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) {
app := setupApp(t)
@ -320,7 +320,7 @@ func TestHTTPAdminCRUDThemesAndBulk(t *testing.T) {
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) {
app := setupApp(t)

@ -13,7 +13,7 @@ import (
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) {
pg := sharedcontainers.StartPostgres(t)
cfg := pg.Config()

@ -1,6 +1,6 @@
package user
// service_test.go contains tests for backend behavior.
// service_test.go contains backend tests for package behavior, error paths, and regressions.
import (
"context"
@ -32,7 +32,7 @@ type fakeRepo struct {
writeAuditErr error
}
// newFakeRepo creates a fake repository with empty stores.
// newFakeRepo creates in-memory test doubles and deterministic fixtures.
func newFakeRepo() *fakeRepo {
return &fakeRepo{
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 }
// 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) {
if r.createErr != nil {
return nil, r.createErr
@ -59,7 +59,7 @@ func (r *fakeRepo) Create(ctx context.Context, user *domain.User) (*domain.User,
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) {
if r.getByIDErr != nil {
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)
}
// 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) {
if r.getByEmailErr != nil {
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)
}
// 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) {
if r.getByZidErr != nil {
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)
}
// UpdateProfile updates mutable user profile fields in place.
// UpdateProfile updates an existing entity in the in-memory repository.
func (r *fakeRepo) UpdateProfile(
ctx context.Context,
id, displayName string,
@ -113,7 +113,7 @@ func (r *fakeRepo) UpdateProfile(
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) {
if r.verifyErr != nil {
return nil, r.verifyErr
@ -127,7 +127,7 @@ func (r *fakeRepo) MarkEmailVerified(ctx context.Context, id string) (*domain.Us
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 {
if r.deleteErr != nil {
return r.deleteErr
@ -149,7 +149,7 @@ func (r *fakeRepo) SoftDelete(ctx context.Context, id string, actorUserID string
return nil
}
// List returns users while honoring include-deleted filtering.
// List returns filtered collections from the in-memory repository.
func (r *fakeRepo) List(
ctx context.Context,
pagination sharedtypes.Pagination,
@ -168,7 +168,7 @@ func (r *fakeRepo) List(
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) {
if r.logsErr != nil {
return nil, r.logsErr
@ -182,7 +182,7 @@ func (r *fakeRepo) AuditLogsByUserID(ctx context.Context, id string) ([]domain.A
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 {
if r.writeAuditErr != nil {
return r.writeAuditErr
@ -191,7 +191,7 @@ func (r *fakeRepo) WriteAuditLog(ctx context.Context, entry domain.AuditLogEntry
return nil
}
// TestRegisterIdempotent verifies repeated registration returns the same existing user.
// TestRegisterIdempotent ensures register idempotent behavior is handled correctly.
func TestRegisterIdempotent(t *testing.T) {
repo := newFakeRepo()
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) {
repo := newFakeRepo()
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) {
svc := NewService(newFakeRepo())
_, 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) {
repo := newFakeRepo()
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) {
repo := newFakeRepo()
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) {
repo := newFakeRepo()
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) {
repo := newFakeRepo()
svc := NewService(repo)

@ -1,6 +1,6 @@
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 (
"bytes"
@ -32,15 +32,15 @@ type inMemoryRepo struct {
audit []domain.AuditLogEntry
}
// newInMemoryRepo creates an empty in-memory repository.
// newInMemoryRepo creates in-memory test doubles and deterministic fixtures.
func newInMemoryRepo() *inMemoryRepo {
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 }
// 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) {
if user.ID == "" {
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
}
// 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) {
if u, ok := r.items[id]; ok && !u.IsDeleted() {
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)
}
// 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) {
for _, u := range r.items {
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)
}
// 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) {
for _, u := range r.items {
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)
}
// UpdateProfile updates mutable profile fields for the selected user.
// UpdateProfile updates an existing entity in the in-memory repository.
func (r *inMemoryRepo) UpdateProfile(
ctx context.Context,
id, displayName string,
@ -100,7 +100,7 @@ func (r *inMemoryRepo) UpdateProfile(
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) {
u, err := r.GetByID(ctx, id)
if err != nil {
@ -111,7 +111,7 @@ func (r *inMemoryRepo) MarkEmailVerified(ctx context.Context, id string) (*domai
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 {
u, err := r.GetByID(ctx, id)
if err != nil {
@ -131,7 +131,7 @@ func (r *inMemoryRepo) SoftDelete(ctx context.Context, id string, actorUserID st
return nil
}
// List returns users and applies the include-deleted filter.
// List returns filtered collections from the in-memory repository.
func (r *inMemoryRepo) List(
ctx context.Context,
pagination sharedtypes.Pagination,
@ -147,7 +147,7 @@ func (r *inMemoryRepo) List(
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) {
out := make([]domain.AuditLogEntry, 0)
for _, a := range r.audit {
@ -158,14 +158,14 @@ func (r *inMemoryRepo) AuditLogsByUserID(ctx context.Context, id string) ([]doma
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 {
entry.CreatedAt = time.Now().UTC()
r.audit = append(r.audit, entry)
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) {
t.Helper()
@ -221,7 +221,7 @@ func setupApp(t *testing.T) (*fiber.App, *inMemoryRepo) {
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) {
app, _ := setupApp(t)
@ -260,7 +260,7 @@ func TestRegisterAndCRUD(t *testing.T) {
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) {
app, _ := setupApp(t)
@ -294,7 +294,7 @@ func TestAuthGuardsAndAdminRoutes(t *testing.T) {
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) {
app, _ := setupApp(t)
@ -304,7 +304,7 @@ func TestMetricsEndpoint(t *testing.T) {
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) {
app, _ := setupApp(t)
@ -332,7 +332,7 @@ func TestVerifyEmailAndValidationPaths(t *testing.T) {
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) {
app, _ := setupApp(t)

@ -9,7 +9,7 @@ import (
"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) {
base := New(CodeNotFound, "missing")
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())
}
// 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) {
err := Wrap(CodeForbidden, "nope", nil)
target := New(CodeForbidden, "other")
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) {
err := Wrap(CodeForbidden, "nope", nil)
target := New(CodeUnauthorized, "other")
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) {
created := New(CodeConflict, "conflict")
require.Equal(t, CodeConflict, created.Code)

@ -10,7 +10,7 @@ import (
"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) {
before := time.Now()
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))
}
// 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) {
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) {
types := []EventType{
GameSessionStarted,
@ -54,12 +54,12 @@ func TestEventType_NonEmpty(t *testing.T) {
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 {
return nil
}
// Subscribe is a test helper.
// Subscribe records event bus interactions for publish-subscribe assertions.
func (d *dummyEventBus) Subscribe(_ EventHandler) error {
return nil
}

@ -8,14 +8,14 @@ import (
"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) {
id := NewID()
require.False(t, id.IsEmpty())
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) {
require.False(t, ID("").IsValid())
require.False(t, IDFromString("not-a-uuid").IsValid())

@ -8,7 +8,7 @@ import (
"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) {
p := Pagination{Page: 0, PageSize: 0}
require.Equal(t, 0, p.Offset())
@ -22,7 +22,7 @@ func TestPagination_LimitOffsetNormalize(t *testing.T) {
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) {
pagination := Pagination{Page: 1, PageSize: 2}
result := NewPaginatedResult([]string{"a", "b"}, 3, pagination)

@ -11,14 +11,14 @@ import (
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) {
name, err := NewPlayerName(" Alice Bob ")
require.NoError(t, err)
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) {
_, err := NewPlayerName("A")
require.Error(t, err)
@ -28,7 +28,7 @@ func TestNewPlayerName_LengthValidation(t *testing.T) {
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) {
_, err := NewPlayerName("Alice!")
require.Error(t, err)
@ -38,7 +38,7 @@ func TestNewPlayerName_InvalidCharacters(t *testing.T) {
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) {
name1, err := NewPlayerName("Alice")
require.NoError(t, err)
@ -48,7 +48,7 @@ func TestPlayerName_EqualsCaseInsensitive(t *testing.T) {
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) {
var name PlayerName
require.True(t, name.IsEmpty())

@ -8,20 +8,20 @@ import (
"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) {
score := NewScore(-5)
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) {
score := NewScore(2)
updated := score.Add(-10)
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) {
cases := []struct {
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) {
require.True(t, CanRetry(MaxAttempts-1))
require.False(t, CanRetry(MaxAttempts))

@ -8,7 +8,7 @@ import (
"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) {
require.True(t, HasPermission(RolePlayer, PermissionPlayGame))
require.False(t, HasPermission(RolePlayer, PermissionManageSystem))
@ -20,13 +20,13 @@ func TestRolePermissions(t *testing.T) {
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) {
require.True(t, UserHasPermission([]string{"player"}, PermissionPlayGame))
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) {
perms := GetPermissions(RolePlayer)
require.NotEmpty(t, perms)
@ -36,7 +36,7 @@ func TestGetPermissionsReturnsCopy(t *testing.T) {
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) {
require.True(t, IsValidRole("player"))
require.False(t, IsValidRole("ghost"))

@ -33,7 +33,7 @@ type jwks struct {
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) {
t.Helper()
key, err := rsa.GenerateKey(rand.Reader, 2048)
@ -57,7 +57,7 @@ func generateJWKS(t *testing.T) (*rsa.PrivateKey, jwks, string) {
}, 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 {
t.Helper()
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
}
// newOIDCServer is a test helper.
// newOIDCServer creates in-memory test doubles and deterministic fixtures.
func newOIDCServer(t *testing.T, jwksDoc jwks) *httptest.Server {
t.Helper()
var baseURL string
@ -110,7 +110,7 @@ func newOIDCServer(t *testing.T, jwksDoc jwks) *httptest.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) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
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)
}
// 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) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusInternalServerError)
@ -143,7 +143,7 @@ func TestGetUserInfo_NonOK(t *testing.T) {
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) {
key, jwksDoc, kid := generateJWKS(t)
server := newOIDCServer(t, jwksDoc)
@ -175,7 +175,7 @@ func TestValidateToken_Success(t *testing.T) {
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) {
key, jwksDoc, kid := generateJWKS(t)
server := newOIDCServer(t, jwksDoc)
@ -196,7 +196,7 @@ func TestValidateToken_InvalidAudience(t *testing.T) {
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) {
key, jwksDoc, kid := generateJWKS(t)
server := newOIDCServer(t, jwksDoc)
@ -218,7 +218,7 @@ func TestValidateToken_MissingRequiredClaim(t *testing.T) {
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) {
key, jwksDoc, _ := generateJWKS(t)
_ = key
@ -237,7 +237,7 @@ func TestRefreshToken_Success(t *testing.T) {
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) {
key, jwksDoc, _ := generateJWKS(t)
_ = key
@ -255,7 +255,7 @@ func TestRevokeToken_Success(t *testing.T) {
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) {
key, jwksDoc, _ := generateJWKS(t)
_ = key
@ -298,7 +298,7 @@ func TestRefreshToken_UsesForm(t *testing.T) {
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 {
return "http://" + r.Host
}

@ -19,13 +19,13 @@ type fakeValidator struct {
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) {
f.called++
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) {
validator := &fakeValidator{claims: &AuthClaims{
Subject: "user-1",
@ -57,7 +57,7 @@ func TestJWTMiddleware_Success(t *testing.T) {
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) {
validator := &fakeValidator{}
app := fiber.New()
@ -71,7 +71,7 @@ func TestJWTMiddleware_MissingHeader(t *testing.T) {
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) {
validator := &fakeValidator{}
app := fiber.New()
@ -86,7 +86,7 @@ func TestJWTMiddleware_InvalidHeaderFormat(t *testing.T) {
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) {
validator := &fakeValidator{claims: &AuthClaims{
Subject: "user-1",
@ -106,7 +106,7 @@ func TestJWTMiddleware_AdminRoleRequired(t *testing.T) {
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) {
validator := &fakeValidator{claims: &AuthClaims{
Subject: "user-1",
@ -126,7 +126,7 @@ func TestJWTMiddleware_MFARequiredForAdmin(t *testing.T) {
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) {
validator := &fakeValidator{err: fiber.ErrUnauthorized}
app := fiber.New()

@ -10,21 +10,21 @@ import (
"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) {
cfg := DefaultConfig()
require.Equal(t, "localhost", cfg.Host)
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) {
cfg := DefaultConfig()
require.Contains(t, cfg.DSN(), "host=localhost")
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) {
t.Setenv("POSTGRES_HOST", "")
t.Setenv("POSTGRES_PORT", "")
@ -44,7 +44,7 @@ func TestConfigFromEnvDefaults(t *testing.T) {
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) {
t.Setenv("POSTGRES_HOST", "db")
t.Setenv("POSTGRES_PORT", "15432")
@ -70,7 +70,7 @@ func TestConfigFromEnvOverrides(t *testing.T) {
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) {
def := DefaultConfig()
t.Setenv("POSTGRES_PORT", "not-a-number")
@ -83,7 +83,7 @@ func TestConfigFromEnvInvalidFallsBack(t *testing.T) {
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) {
cfg := DefaultConfig()
cfg.Host = ""
@ -93,7 +93,7 @@ func TestHealthCheckInvalidConfig(t *testing.T) {
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) {
cfg := DefaultConfig()
cfg.Host = "127.0.0.1"

@ -15,20 +15,20 @@ import (
"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) {
cfg := DefaultConfig()
require.Equal(t, "localhost", cfg.Host)
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) {
cfg := DefaultConfig()
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) {
t.Helper()
@ -50,13 +50,13 @@ func newClientForMiniRedis(t *testing.T) (*Client, *miniredis.Miniredis) {
return client, mr
}
// TestHealthCheck verifies health checks use the real ping.
// TestHealthCheck ensures health check behavior is handled correctly.
func TestHealthCheck(t *testing.T) {
client, _ := newClientForMiniRedis(t)
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) {
client, _ := newClientForMiniRedis(t)
ctx := context.Background()
@ -68,7 +68,7 @@ func TestSetGet(t *testing.T) {
require.Equal(t, "v", got)
}
// TestDeleteExists verifies deletion and existence checks.
// TestDeleteExists ensures delete exists behavior is handled correctly.
func TestDeleteExists(t *testing.T) {
client, _ := newClientForMiniRedis(t)
ctx := context.Background()
@ -84,7 +84,7 @@ func TestDeleteExists(t *testing.T) {
require.False(t, exists)
}
// TestIncr verifies atomic increments.
// TestIncr ensures incr behavior is handled correctly.
func TestIncr(t *testing.T) {
client, _ := newClientForMiniRedis(t)
ctx := context.Background()
@ -98,7 +98,7 @@ func TestIncr(t *testing.T) {
require.Equal(t, int64(2), n)
}
// TestExpire verifies key expiration behavior.
// TestExpire ensures expire behavior is handled correctly.
func TestExpire(t *testing.T) {
client, mr := newClientForMiniRedis(t)
ctx := context.Background()
@ -113,7 +113,7 @@ func TestExpire(t *testing.T) {
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) {
client, _ := newClientForMiniRedis(t)
_, err := client.Get(context.Background(), "missing")
@ -121,7 +121,7 @@ func TestGetNotFound(t *testing.T) {
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) {
t.Setenv("REDIS_HOST", "")
t.Setenv("REDIS_PORT", "")
@ -131,7 +131,7 @@ func TestConfigFromEnvDefaults(t *testing.T) {
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) {
t.Setenv("REDIS_HOST", "cache")
t.Setenv("REDIS_PORT", "6380")
@ -155,7 +155,7 @@ func TestConfigFromEnvOverrides(t *testing.T) {
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) {
cfg := DefaultConfig()
cfg.Port = 0

@ -9,7 +9,7 @@ import (
"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) {
require.NotPanics(t, func() {
_ = 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) {
logger := NewLogger(DefaultConfig())
withField := logger.WithField("key", "value")
@ -31,7 +31,7 @@ func TestLogger_WithFields(t *testing.T) {
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) {
cfg := DefaultConfig()
require.Equal(t, "info", cfg.Level)

@ -1,6 +1,6 @@
package metrics
// http_test.go contains tests for backend behavior.
// http_test.go contains backend tests for package behavior, error paths, and regressions.
import (
"net/http"
@ -10,7 +10,7 @@ import (
"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) {
h := Handler()
require.NotNil(t, h)

@ -9,7 +9,7 @@ import (
"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) {
registry := prometheus.NewRegistry()
m := NewMetrics(Config{ServiceName: "svc", Enabled: true, Registry: registry})
@ -20,7 +20,7 @@ func TestNewMetrics_WithCustomRegistry(t *testing.T) {
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) {
t.Setenv("METRICS_ENABLED", "")
t.Setenv("METRICS_SERVICE_NAME", "")
@ -31,7 +31,7 @@ func TestConfigFromEnvDefaults(t *testing.T) {
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) {
t.Setenv("METRICS_ENABLED", "false")
t.Setenv("METRICS_SERVICE_NAME", "admin-service")

@ -10,7 +10,7 @@ import (
"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) {
cfg := DefaultConfig()
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) {
cfg := DefaultConfig()
cfg.Enabled = true
@ -40,7 +40,7 @@ func TestNewTracer_Enabled(t *testing.T) {
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) {
tracer, err := NewTracer(DefaultConfig())
require.NoError(t, err)
@ -49,7 +49,7 @@ func TestShutdown_Disabled(t *testing.T) {
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) {
tracer, err := NewTracer(DefaultConfig())
require.NoError(t, err)
@ -61,7 +61,7 @@ func TestTraceServiceOperation(t *testing.T) {
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) {
tracer, err := NewTracer(DefaultConfig())
require.NoError(t, err)
@ -75,7 +75,7 @@ func TestTraceDatabaseOperation(t *testing.T) {
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) {
t.Setenv("TRACING_ENABLED", "")
t.Setenv("TRACING_SERVICE_NAME", "")
@ -91,7 +91,7 @@ func TestConfigFromEnvDefaults(t *testing.T) {
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) {
t.Setenv("TRACING_ENABLED", "true")
t.Setenv("TRACING_SERVICE_NAME", "gateway")
@ -111,7 +111,7 @@ func TestConfigFromEnvOverrides(t *testing.T) {
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) {
t.Setenv("TRACING_SAMPLE_RATE", "not-a-float")
@ -119,7 +119,7 @@ func TestConfigFromEnvInvalidSampleRate(t *testing.T) {
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) {
t.Setenv("TRACING_OTLP_ENDPOINT", "")
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)
}
// 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) {
t.Setenv("TRACING_OTLP_ENDPOINT", "https://collector:4318/v1/traces")
t.Setenv("TRACING_JAEGER_ENDPOINT", "http://legacy:14268/api/traces")

@ -11,7 +11,7 @@ import (
"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) {
opts := SanitizeOptions{
TrimWhitespace: true,
@ -31,29 +31,29 @@ func TestSanitize_Options(t *testing.T) {
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) {
require.Equal(t, "Alice Bob", SanitizePlayerName(" Alice Bob "))
require.Equal(t, "", SanitizePlayerName("Alice <"))
}
// TestSanitizeAnswer verifies lowercasing and whitespace normalization.
// TestSanitizeAnswer ensures sanitize answer behavior is handled correctly.
func TestSanitizeAnswer(t *testing.T) {
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) {
result := SanitizeQuestionText("<script>alert(1)</script> Question")
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) {
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) {
playerExact := strings.Repeat("a", PlayerNameMaxLength)
playerOver := strings.Repeat("a", PlayerNameMaxLength+1)
@ -76,12 +76,12 @@ func TestSanitizeLengthBoundaries(t *testing.T) {
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) {
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) {
cases := []struct {
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) {
require.True(t, IsValidEmail("a@b.com"))
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) {
input := strings.Repeat("é", 5)
opts := SanitizeOptions{

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

@ -16,7 +16,7 @@ import (
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) {
cases := []struct {
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) {
cases := []struct {
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) {
status, resp := MapError(errorspkg.New(errorspkg.ErrorCode("UNKNOWN"), "oops"))
require.Equal(t, http.StatusInternalServerError, status)
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) {
app := fiber.New()
app.Get("/", func(c fiber.Ctx) error {
@ -118,7 +118,7 @@ func TestSendError(t *testing.T) {
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) {
wrapped := fmt.Errorf("outer: %w", errorspkg.New(errorspkg.CodeValidationFailed, "bad input"))
status, resp := MapError(wrapped)
@ -127,7 +127,7 @@ func TestMapError_WrappedDomainError(t *testing.T) {
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) {
app := fiber.New()
app.Get("/", func(c fiber.Ctx) error {

@ -14,7 +14,7 @@ import (
"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) {
app := fiber.New()
app.Get("/", func(c fiber.Ctx) error {
@ -33,7 +33,7 @@ func TestPaginationFromQuery(t *testing.T) {
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) {
app := fiber.New()
app.Get("/", func(c fiber.Ctx) error {
@ -52,7 +52,7 @@ func TestSortingFromQuery(t *testing.T) {
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) {
app := fiber.New()
app.Get("/", func(c fiber.Ctx) error {

@ -12,14 +12,14 @@ import (
"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) {
resp := NewPaginatedResponse([]string{"a"}, 1, 2, 3)
require.NotNil(t, resp.Meta)
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) {
app := fiber.New()
app.Get("/health", func(c fiber.Ctx) error {
@ -39,7 +39,7 @@ func TestHealthStatus(t *testing.T) {
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) {
app := fiber.New()
app.Get("/ok", func(c fiber.Ctx) error {

@ -1,6 +1,6 @@
package serviceboot
// fiber_test.go contains tests for backend behavior.
// fiber_test.go contains backend tests for package behavior, error paths, and regressions.
import (
"encoding/json"
@ -11,7 +11,7 @@ import (
"github.com/stretchr/testify/require"
)
// TestRegisterHealth verifies expected behavior.
// TestRegisterHealth ensures register health behavior is handled correctly.
func TestRegisterHealth(t *testing.T) {
app := NewFiberApp(Config{AppName: "test-service"})
RegisterHealth(app, "svc")
@ -27,19 +27,19 @@ func TestRegisterHealth(t *testing.T) {
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) {
t.Setenv("SERVICE_PORT", "9090")
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) {
t.Setenv("SERVICE_PORT", "bad")
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) {
t.Setenv("SERVICE_PORT", "70000")
require.Equal(t, ":8080", ListenAddress("SERVICE_PORT", 8080))

@ -19,7 +19,7 @@ type playerStruct struct {
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) {
v := NewValidator()
@ -29,7 +29,7 @@ func TestValidator_CustomTags(t *testing.T) {
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) {
v := NewValidator()
err := v.Validate(sampleStruct{})
@ -41,7 +41,7 @@ func TestValidator_ValidateReturnsDomainError(t *testing.T) {
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) {
v := NewValidator()
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")
}
// 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) {
v := NewValidator()
err := v.Validate(playerStruct{PlayerName: "A"})

Loading…
Cancel
Save