Improved code comments

master
oabrivard 1 month ago
parent 6cadc15448
commit 2fde7f1ff7

@ -15,6 +15,7 @@ type Service struct {
retention time.Duration
}
// NewService builds an admin service with a retention period in days.
func NewService(auditRepo audit.Repository, retentionDays int) *Service {
if retentionDays <= 0 {
retentionDays = 90

@ -17,6 +17,7 @@ type AuditRepository struct {
db *sharedpostgres.Client
}
// NewAuditRepository creates a PostgreSQL-backed audit repository.
func NewAuditRepository(db *sharedpostgres.Client) *AuditRepository {
return &AuditRepository{db: db}
}

@ -20,6 +20,7 @@ type Handler struct {
metrics *metrics.Metrics
}
// NewHandler creates HTTP handlers for admin endpoints.
func NewHandler(svc *appadmin.Service, logger *logging.Logger, metrics *metrics.Metrics) *Handler {
return &Handler{svc: svc, logger: logger, metrics: metrics}
}

@ -1,5 +1,7 @@
package tests
// integration_http_test.go contains tests for backend behavior.
import (
"context"
"encoding/json"
@ -28,12 +30,15 @@ type inMemoryAuditRepo struct {
appendErr error
}
// newInMemoryAuditRepo is a test helper.
func newInMemoryAuditRepo() *inMemoryAuditRepo {
return &inMemoryAuditRepo{entries: make([]audit.Entry, 0)}
}
// EnsureSchema is a test helper.
func (r *inMemoryAuditRepo) EnsureSchema(ctx context.Context) error { return nil }
// Append is a test helper.
func (r *inMemoryAuditRepo) Append(ctx context.Context, e audit.Entry) error {
if r.appendErr != nil {
return r.appendErr
@ -42,6 +47,7 @@ func (r *inMemoryAuditRepo) Append(ctx context.Context, e audit.Entry) error {
return nil
}
// List is a test helper.
func (r *inMemoryAuditRepo) List(ctx context.Context, limit, offset int) ([]audit.Entry, error) {
if r.listErr != nil {
return nil, r.listErr
@ -58,6 +64,7 @@ func (r *inMemoryAuditRepo) List(ctx context.Context, limit, offset int) ([]audi
return out, nil
}
// Count is a test helper.
func (r *inMemoryAuditRepo) Count(ctx context.Context) (int64, error) {
if r.countErr != nil {
return 0, r.countErr
@ -65,10 +72,12 @@ func (r *inMemoryAuditRepo) Count(ctx context.Context) (int64, error) {
return int64(len(r.entries)), nil
}
// PruneBefore is a test helper.
func (r *inMemoryAuditRepo) PruneBefore(ctx context.Context, before time.Time) (int64, error) {
return 0, nil
}
// setupApp is a test helper.
func setupApp(t *testing.T, repo *inMemoryAuditRepo) *fiber.App {
t.Helper()
@ -105,6 +114,7 @@ func setupApp(t *testing.T, repo *inMemoryAuditRepo) *fiber.App {
return app
}
// TestAdminAuthRoute verifies expected behavior.
func TestAdminAuthRoute(t *testing.T) {
repo := newInMemoryAuditRepo()
app := setupApp(t, repo)
@ -123,6 +133,7 @@ func TestAdminAuthRoute(t *testing.T) {
}
}
// TestDashboardRouteSuccessAndErrors verifies expected behavior.
func TestDashboardRouteSuccessAndErrors(t *testing.T) {
t.Run("forbidden for non-admin", func(t *testing.T) {
repo := newInMemoryAuditRepo()
@ -170,6 +181,7 @@ func TestDashboardRouteSuccessAndErrors(t *testing.T) {
})
}
// TestAuditRouteSuccessAndErrors verifies expected behavior.
func TestAuditRouteSuccessAndErrors(t *testing.T) {
t.Run("forbidden for non-admin", func(t *testing.T) {
repo := newInMemoryAuditRepo()
@ -221,6 +233,7 @@ func TestAuditRouteSuccessAndErrors(t *testing.T) {
})
}
// TestRegisterRoutesDoesNotPanic verifies expected behavior.
func TestRegisterRoutesDoesNotPanic(t *testing.T) {
app := fiber.New()
repo := newInMemoryAuditRepo()

@ -1,5 +1,7 @@
package session
// service_test.go contains tests for backend behavior.
import (
"context"
"errors"
@ -17,6 +19,7 @@ type fakeRepo struct {
events []*domain.SessionEvent
}
// newFakeRepo is a test helper.
func newFakeRepo() *fakeRepo {
return &fakeRepo{
sessions: map[string]*domain.GameSession{},
@ -25,8 +28,10 @@ func newFakeRepo() *fakeRepo {
}
}
// EnsureSchema is a test helper.
func (r *fakeRepo) EnsureSchema(ctx context.Context) error { return nil }
// CreateSession is a test helper.
func (r *fakeRepo) CreateSession(ctx context.Context, session *domain.GameSession) (*domain.GameSession, error) {
session.ID = "sess-1"
now := time.Now().UTC()
@ -37,6 +42,7 @@ func (r *fakeRepo) CreateSession(ctx context.Context, session *domain.GameSessio
return &cp, nil
}
// GetSessionByID is a test helper.
func (r *fakeRepo) GetSessionByID(ctx context.Context, id string) (*domain.GameSession, error) {
s, ok := r.sessions[id]
if !ok {
@ -46,6 +52,7 @@ func (r *fakeRepo) GetSessionByID(ctx context.Context, id string) (*domain.GameS
return &cp, nil
}
// GetActiveSessionByPlayerID is a test helper.
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 {
@ -56,6 +63,7 @@ func (r *fakeRepo) GetActiveSessionByPlayerID(ctx context.Context, playerID stri
return nil, domain.ErrSessionNotFound
}
// UpdateSession is a test helper.
func (r *fakeRepo) UpdateSession(ctx context.Context, session *domain.GameSession) (*domain.GameSession, error) {
if _, ok := r.sessions[session.ID]; !ok {
return nil, domain.ErrSessionNotFound
@ -66,18 +74,21 @@ func (r *fakeRepo) UpdateSession(ctx context.Context, session *domain.GameSessio
return &cp, nil
}
// CreateAttempt is a test helper.
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.
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.
func (r *fakeRepo) ListQuestionIDsForSession(ctx context.Context, sessionID string) ([]string, error) {
seen := map[string]bool{}
ids := make([]string, 0)
@ -100,6 +111,7 @@ type fakeQuestionBank struct {
answerOK bool
}
// GetRandomQuestion is a test helper.
func (f *fakeQuestionBank) GetRandomQuestion(
ctx context.Context,
exclusions []string,
@ -119,6 +131,7 @@ func (f *fakeQuestionBank) GetRandomQuestion(
return nil, sharederrors.New(sharederrors.CodeNoQuestionsAvailable, "no questions available")
}
// GetQuestionByID is a test helper.
func (f *fakeQuestionBank) GetQuestionByID(ctx context.Context, id string) (*SessionQuestion, error) {
for _, q := range f.questions {
if q.ID == id {
@ -129,6 +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.
func (f *fakeQuestionBank) ValidateAnswer(
ctx context.Context,
questionID, answer string,
@ -141,6 +155,7 @@ type fakeUserClient struct {
profile UserProfile
}
// GetUserProfile is a test helper.
func (f *fakeUserClient) GetUserProfile(ctx context.Context, userID, bearerToken string) (*UserProfile, error) {
p := f.profile
if p.ID == "" {
@ -156,6 +171,7 @@ type fakeStateStore struct {
locks map[string]bool
}
// newFakeStateStore is a test helper.
func newFakeStateStore() *fakeStateStore {
return &fakeStateStore{
active: map[string]string{},
@ -164,30 +180,43 @@ func newFakeStateStore() *fakeStateStore {
}
}
// GetActiveSession is a test helper.
func (s *fakeStateStore) GetActiveSession(ctx context.Context, playerID string) (string, bool) {
id, ok := s.active[playerID]
return id, ok
}
// SetActiveSession is a test helper.
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.
func (s *fakeStateStore) ClearActiveSession(ctx context.Context, playerID string) error {
delete(s.active, playerID)
return nil
}
// SetTimer is a test helper.
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.
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.
func (s *fakeStateStore) ClearTimer(ctx context.Context, sessionID string) error {
delete(s.timers, sessionID)
return nil
}
// AcquireLock is a test helper.
func (s *fakeStateStore) AcquireLock(ctx context.Context, sessionID string, ttl time.Duration) bool {
if s.locks[sessionID] {
return false
@ -195,6 +224,8 @@ func (s *fakeStateStore) AcquireLock(ctx context.Context, sessionID string, ttl
s.locks[sessionID] = true
return true
}
// ReleaseLock is a test helper.
func (s *fakeStateStore) ReleaseLock(ctx context.Context, sessionID string) {
delete(s.locks, sessionID)
}

@ -1,5 +1,7 @@
package http
// handler_unit_test.go contains tests for backend behavior.
import (
"bytes"
"net/http"
@ -11,6 +13,7 @@ import (
"knowfoolery/backend/shared/infra/utils/validation"
)
// TestUnauthorizedBranches verifies expected behavior.
func TestUnauthorizedBranches(t *testing.T) {
h := NewHandler(nil, validation.NewValidator(), nil, nil)
app := fiber.New()
@ -45,6 +48,7 @@ func TestUnauthorizedBranches(t *testing.T) {
}
}
// TestStartAndEndValidationBranches verifies expected behavior.
func TestStartAndEndValidationBranches(t *testing.T) {
h := NewHandler(nil, validation.NewValidator(), nil, nil)
app := fiber.New()
@ -103,6 +107,7 @@ func TestStartAndEndValidationBranches(t *testing.T) {
}
// TestBearerTokenAndClaimsHelpers verifies expected behavior.
func TestBearerTokenAndClaimsHelpers(t *testing.T) {
app := fiber.New()
app.Use(func(c fiber.Ctx) error {

@ -29,6 +29,7 @@ type inMemoryRepo struct {
attempts []*domain.SessionAttempt
}
// newInMemoryRepo is a test helper.
func newInMemoryRepo() *inMemoryRepo {
return &inMemoryRepo{
sessions: map[string]*domain.GameSession{},
@ -36,8 +37,10 @@ func newInMemoryRepo() *inMemoryRepo {
}
}
// EnsureSchema is a test helper.
func (r *inMemoryRepo) EnsureSchema(ctx context.Context) error { return nil }
// CreateSession is a test helper.
func (r *inMemoryRepo) CreateSession(ctx context.Context, session *domain.GameSession) (*domain.GameSession, error) {
session.ID = "sess-1"
now := time.Now().UTC()
@ -48,6 +51,7 @@ func (r *inMemoryRepo) CreateSession(ctx context.Context, session *domain.GameSe
return &cp, nil
}
// GetSessionByID is a test helper.
func (r *inMemoryRepo) GetSessionByID(ctx context.Context, id string) (*domain.GameSession, error) {
s, ok := r.sessions[id]
if !ok {
@ -57,6 +61,7 @@ func (r *inMemoryRepo) GetSessionByID(ctx context.Context, id string) (*domain.G
return &cp, nil
}
// GetActiveSessionByPlayerID is a test helper.
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 {
@ -67,6 +72,7 @@ func (r *inMemoryRepo) GetActiveSessionByPlayerID(ctx context.Context, playerID
return nil, domain.ErrSessionNotFound
}
// UpdateSession is a test helper.
func (r *inMemoryRepo) UpdateSession(ctx context.Context, session *domain.GameSession) (*domain.GameSession, error) {
cp := *session
cp.UpdatedAt = time.Now().UTC()
@ -74,14 +80,17 @@ func (r *inMemoryRepo) UpdateSession(ctx context.Context, session *domain.GameSe
return &cp, nil
}
// CreateAttempt is a test helper.
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.
func (r *inMemoryRepo) CreateEvent(ctx context.Context, event *domain.SessionEvent) error { return nil }
// ListQuestionIDsForSession is a test helper.
func (r *inMemoryRepo) ListQuestionIDsForSession(ctx context.Context, sessionID string) ([]string, error) {
seen := map[string]bool{}
ids := make([]string, 0)
@ -103,6 +112,7 @@ type fakeQuestionBank struct {
questions []appsession.SessionQuestion
}
// GetRandomQuestion is a test helper.
func (f *fakeQuestionBank) GetRandomQuestion(
ctx context.Context,
exclusions []string,
@ -122,6 +132,7 @@ func (f *fakeQuestionBank) GetRandomQuestion(
return nil, domain.ErrSessionNotFound
}
// GetQuestionByID is a test helper.
func (f *fakeQuestionBank) GetQuestionByID(ctx context.Context, id string) (*appsession.SessionQuestion, error) {
for _, q := range f.questions {
if q.ID == id {
@ -132,6 +143,7 @@ func (f *fakeQuestionBank) GetQuestionByID(ctx context.Context, id string) (*app
return nil, domain.ErrSessionNotFound
}
// ValidateAnswer is a test helper.
func (f *fakeQuestionBank) ValidateAnswer(
ctx context.Context,
questionID, answer string,
@ -145,6 +157,7 @@ func (f *fakeQuestionBank) ValidateAnswer(
// fakeUserClient returns a verified user profile for tests.
type fakeUserClient struct{}
// GetUserProfile is a test helper.
func (f *fakeUserClient) GetUserProfile(
ctx context.Context,
userID, bearerToken string,
@ -162,6 +175,7 @@ type fakeStateStore struct {
locks map[string]bool
}
// newFakeStateStore is a test helper.
func newFakeStateStore() *fakeStateStore {
return &fakeStateStore{
active: map[string]string{},
@ -169,25 +183,38 @@ func newFakeStateStore() *fakeStateStore {
}
}
// GetActiveSession is a test helper.
func (s *fakeStateStore) GetActiveSession(ctx context.Context, playerID string) (string, bool) {
id, ok := s.active[playerID]
return id, ok
}
// SetActiveSession is a test helper.
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.
func (s *fakeStateStore) ClearActiveSession(ctx context.Context, playerID string) error {
delete(s.active, playerID)
return nil
}
// SetTimer is a test helper.
func (s *fakeStateStore) SetTimer(ctx context.Context, sessionID string, expiresAt time.Time, ttl time.Duration) error {
return nil
}
// GetTimer is a test helper.
func (s *fakeStateStore) GetTimer(ctx context.Context, sessionID string) (time.Time, bool) {
return time.Time{}, false
}
// ClearTimer is a test helper.
func (s *fakeStateStore) ClearTimer(ctx context.Context, sessionID string) error { return nil }
// AcquireLock is a test helper.
func (s *fakeStateStore) AcquireLock(ctx context.Context, sessionID string, ttl time.Duration) bool {
if s.locks[sessionID] {
return false
@ -195,6 +222,8 @@ func (s *fakeStateStore) AcquireLock(ctx context.Context, sessionID string, ttl
s.locks[sessionID] = true
return true
}
// ReleaseLock is a test helper.
func (s *fakeStateStore) ReleaseLock(ctx context.Context, sessionID string) {
delete(s.locks, sessionID)
}
@ -343,6 +372,7 @@ func TestMetricsEndpoint(t *testing.T) {
assertStatus(t, resp, http.StatusOK, "metrics failed")
}
// mustJSONRequest is a test helper.
func mustJSONRequest(
t *testing.T,
app *fiber.App,
@ -369,6 +399,7 @@ func mustJSONRequest(
return sharedhttpx.MustTest(t, app, req)
}
// assertStatus is a test helper.
func assertStatus(t *testing.T, resp *http.Response, want int, msg string) {
t.Helper()
if resp.StatusCode != want {
@ -376,6 +407,7 @@ func assertStatus(t *testing.T, resp *http.Response, want int, msg string) {
}
}
// decodeDataMap is a test helper.
func decodeDataMap(t *testing.T, resp *http.Response) map[string]any {
t.Helper()
@ -389,6 +421,7 @@ func decodeDataMap(t *testing.T, resp *http.Response) map[string]any {
return payload.Data
}
// asMap is a test helper.
func asMap(t *testing.T, v any) map[string]any {
t.Helper()
m, ok := v.(map[string]any)
@ -398,6 +431,7 @@ func asMap(t *testing.T, v any) map[string]any {
return m
}
// asString is a test helper.
func asString(t *testing.T, v any) string {
t.Helper()
s, ok := v.(string)
@ -407,6 +441,7 @@ func asString(t *testing.T, v any) string {
return s
}
// decodeAny is a test helper.
func decodeAny(t *testing.T, resp *http.Response) map[string]any {
t.Helper()

@ -1,5 +1,7 @@
package middleware
// middleware_test.go contains tests for backend behavior.
import (
"net/http"
"net/http/httptest"
@ -16,6 +18,7 @@ import (
"knowfoolery/backend/shared/infra/observability/logging"
)
// TestCORSMiddleware verifies expected behavior.
func TestCORSMiddleware(t *testing.T) {
app := fiber.New()
app.Use(CORS(gconfig.CORSConfig{
@ -66,6 +69,7 @@ func TestCORSMiddleware(t *testing.T) {
}
}
// TestCORSAllowAll verifies expected behavior.
func TestCORSAllowAll(t *testing.T) {
app := fiber.New()
app.Use(CORS(gconfig.CORSConfig{AllowedOrigins: []string{"*"}}))
@ -83,6 +87,7 @@ func TestCORSAllowAll(t *testing.T) {
}
}
// TestSecurityHeadersMiddleware verifies expected behavior.
func TestSecurityHeadersMiddleware(t *testing.T) {
app := fiber.New()
app.Use(SecurityHeaders(gconfig.SecurityHeadersConfig{
@ -118,6 +123,7 @@ func TestSecurityHeadersMiddleware(t *testing.T) {
}
}
// TestSecurityHeadersNoHSTSOnHTTP verifies expected behavior.
func TestSecurityHeadersNoHSTSOnHTTP(t *testing.T) {
app := fiber.New()
app.Use(SecurityHeaders(gconfig.SecurityHeadersConfig{
@ -137,6 +143,7 @@ func TestSecurityHeadersNoHSTSOnHTTP(t *testing.T) {
}
}
// TestRequestContextMiddlewareAndRequestID verifies expected behavior.
func TestRequestContextMiddlewareAndRequestID(t *testing.T) {
app := fiber.New()
app.Use(RequestContext(nil))
@ -172,6 +179,7 @@ func TestRequestContextMiddlewareAndRequestID(t *testing.T) {
}
}
// TestRequestContextWithLoggerAndInvalidRequestIDLocal verifies expected behavior.
func TestRequestContextWithLoggerAndInvalidRequestIDLocal(t *testing.T) {
logger := logging.NewLogger(logging.DefaultConfig())
app := fiber.New()
@ -195,6 +203,7 @@ func TestRequestContextWithLoggerAndInvalidRequestIDLocal(t *testing.T) {
}
}
// TestRateLimitMiddlewareDegradedModeAndHelpers verifies expected behavior.
func TestRateLimitMiddlewareDegradedModeAndHelpers(t *testing.T) {
app := fiber.New()
mw := RateLimitMiddleware(nil, gconfig.RateLimitConfig{
@ -296,6 +305,7 @@ func TestRateLimitMiddlewareDegradedModeAndHelpers(t *testing.T) {
}
}
// TestRateLimitMiddlewareRedisAllowedAndBlocked verifies expected behavior.
func TestRateLimitMiddlewareRedisAllowedAndBlocked(t *testing.T) {
mr, err := miniredis.Run()
if err != nil {
@ -348,6 +358,7 @@ func TestRateLimitMiddlewareRedisAllowedAndBlocked(t *testing.T) {
}
}
// TestRateLimitMiddlewareRedisErrorDegrades verifies expected behavior.
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"})
@ -379,6 +390,7 @@ func TestRateLimitMiddlewareRedisErrorDegrades(t *testing.T) {
}
}
// TestIdentifyRequester verifies expected behavior.
func TestIdentifyRequester(t *testing.T) {
app := fiber.New()
app.Get("/id", func(c fiber.Ctx) error {

@ -1,5 +1,7 @@
package tests
// integration_http_test.go contains tests for backend behavior.
import (
"io"
"net/http"
@ -19,6 +21,7 @@ import (
"knowfoolery/backend/shared/infra/auth/zitadel"
)
// TestGateway_PublicRoute_ProxiesAndRewritesPath verifies expected behavior.
func TestGateway_PublicRoute_ProxiesAndRewritesPath(t *testing.T) {
t.Parallel()
@ -46,6 +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.
func TestGateway_ProtectedRoute_RequiresAuth(t *testing.T) {
t.Parallel()
@ -63,6 +67,7 @@ func TestGateway_ProtectedRoute_RequiresAuth(t *testing.T) {
require.Equal(t, http.StatusUnauthorized, res.StatusCode)
}
// TestGateway_ProtectedRoute_ForwardsUserHeaders verifies expected behavior.
func TestGateway_ProtectedRoute_ForwardsUserHeaders(t *testing.T) {
t.Parallel()
@ -90,6 +95,7 @@ func TestGateway_ProtectedRoute_ForwardsUserHeaders(t *testing.T) {
require.Equal(t, "true", received["x-user-mfa"])
}
// TestGateway_PreflightCors verifies expected behavior.
func TestGateway_PreflightCors(t *testing.T) {
t.Parallel()
@ -110,6 +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.
func buildTestApp(t *testing.T, upstreamURL string, client *http.Client) *fiber.App {
t.Helper()
@ -179,10 +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.
func (fn roundTripperFunc) RoundTrip(req *http.Request) (*http.Response, error) {
return fn(req)
}
// jsonResponse is a test helper.
func jsonResponse(status int, body string) *http.Response {
return &http.Response{
StatusCode: status,

@ -1,5 +1,7 @@
package leaderboard
// service_test.go contains tests for backend behavior.
import (
"context"
"encoding/json"
@ -23,6 +25,7 @@ type fakeRepo struct {
globalErr error
}
// newFakeRepo is a test helper.
func newFakeRepo() *fakeRepo {
return &fakeRepo{
entries: make([]*domain.LeaderboardEntry, 0),
@ -30,8 +33,10 @@ func newFakeRepo() *fakeRepo {
}
}
// EnsureSchema is a test helper.
func (r *fakeRepo) EnsureSchema(ctx context.Context) error { return nil }
// IngestEntry is a test helper.
func (r *fakeRepo) IngestEntry(
ctx context.Context,
entry *domain.LeaderboardEntry,
@ -88,6 +93,7 @@ func (r *fakeRepo) IngestEntry(
return &cp, false, nil
}
// ListTop is a test helper.
func (r *fakeRepo) ListTop(
ctx context.Context,
filter domain.TopFilter,
@ -106,6 +112,7 @@ func (r *fakeRepo) ListTop(
return out, nil
}
// GetPlayerStats is a test helper.
func (r *fakeRepo) GetPlayerStats(ctx context.Context, playerID string) (*domain.PlayerStats, error) {
if r.statsErr != nil {
return nil, r.statsErr
@ -118,6 +125,7 @@ func (r *fakeRepo) GetPlayerStats(ctx context.Context, playerID string) (*domain
return &cp, nil
}
// GetPlayerRank is a test helper.
func (r *fakeRepo) GetPlayerRank(ctx context.Context, playerID string) (int64, error) {
if r.rankErr != nil {
return 0, r.rankErr
@ -128,6 +136,7 @@ func (r *fakeRepo) GetPlayerRank(ctx context.Context, playerID string) (int64, e
return 1, nil
}
// ListPlayerHistory is a test helper.
func (r *fakeRepo) ListPlayerHistory(
ctx context.Context,
playerID string,
@ -145,6 +154,7 @@ func (r *fakeRepo) ListPlayerHistory(
return out, int64(len(out)), nil
}
// GetGlobalStats is a test helper.
func (r *fakeRepo) GetGlobalStats(ctx context.Context, filter domain.TopFilter) (*domain.GlobalStats, error) {
if r.globalErr != nil {
return nil, r.globalErr
@ -158,13 +168,18 @@ type fakeState struct {
deleteErr error
}
// newFakeState is a test helper.
func newFakeState() *fakeState {
return &fakeState{data: map[string]string{}}
}
// Get is a test helper.
func (s *fakeState) Get(ctx context.Context, key string) (string, bool) {
v, ok := s.data[key]
return v, ok
}
// Set is a test helper.
func (s *fakeState) Set(ctx context.Context, key, value string, ttl time.Duration) error {
if s.setErr != nil {
return s.setErr
@ -172,6 +187,8 @@ func (s *fakeState) Set(ctx context.Context, key, value string, ttl time.Duratio
s.data[key] = value
return nil
}
// Delete is a test helper.
func (s *fakeState) Delete(ctx context.Context, keys ...string) error {
if s.deleteErr != nil {
return s.deleteErr
@ -182,6 +199,7 @@ func (s *fakeState) Delete(ctx context.Context, keys ...string) error {
return nil
}
// TestUpdateScoreIdempotent verifies expected behavior.
func TestUpdateScoreIdempotent(t *testing.T) {
repo := newFakeRepo()
state := newFakeState()
@ -212,6 +230,7 @@ func TestUpdateScoreIdempotent(t *testing.T) {
}
}
// TestUpdateScoreValidatesInput verifies expected behavior.
func TestUpdateScoreValidatesInput(t *testing.T) {
svc := NewService(newFakeRepo(), newFakeState(), Config{})
_, err := svc.UpdateScore(context.Background(), UpdateScoreInput{
@ -230,6 +249,7 @@ func TestUpdateScoreValidatesInput(t *testing.T) {
}
}
// TestGetPlayerRanking verifies expected behavior.
func TestGetPlayerRanking(t *testing.T) {
repo := newFakeRepo()
state := newFakeState()
@ -264,6 +284,7 @@ func TestGetPlayerRanking(t *testing.T) {
}
}
// TestUpdateScoreValidationAndErrorPaths verifies expected behavior.
func TestUpdateScoreValidationAndErrorPaths(t *testing.T) {
svc := NewService(newFakeRepo(), newFakeState(), Config{})
cases := []UpdateScoreInput{
@ -291,6 +312,7 @@ func TestUpdateScoreValidationAndErrorPaths(t *testing.T) {
}
}
// TestTopAndStatsCachePaths verifies expected behavior.
func TestTopAndStatsCachePaths(t *testing.T) {
repo := newFakeRepo()
state := newFakeState()
@ -346,6 +368,7 @@ func TestTopAndStatsCachePaths(t *testing.T) {
}
}
// TestRankingValidationAndErrorPaths verifies expected behavior.
func TestRankingValidationAndErrorPaths(t *testing.T) {
repo := newFakeRepo()
state := newFakeState()
@ -400,6 +423,7 @@ func TestRankingValidationAndErrorPaths(t *testing.T) {
}
}
// TestGlobalStatsAndTopErrors verifies expected behavior.
func TestGlobalStatsAndTopErrors(t *testing.T) {
repo := newFakeRepo()
repo.topErr = errors.New("top boom")

@ -1,5 +1,7 @@
package http
// handler_unit_test.go contains tests for backend behavior.
import (
"bytes"
"net/http"
@ -11,6 +13,7 @@ import (
"knowfoolery/backend/shared/infra/utils/validation"
)
// TestUpdateAuthAndValidationBranches verifies expected behavior.
func TestUpdateAuthAndValidationBranches(t *testing.T) {
h := NewHandler(nil, validation.NewValidator(), nil, nil, true, 20, 100)
app := fiber.New()
@ -86,6 +89,7 @@ func TestUpdateAuthAndValidationBranches(t *testing.T) {
}
}
// TestGetPlayerRankingForbiddenBranch verifies expected behavior.
func TestGetPlayerRankingForbiddenBranch(t *testing.T) {
h := NewHandler(nil, validation.NewValidator(), nil, nil, false, 20, 100)
app := fiber.New()
@ -107,6 +111,7 @@ func TestGetPlayerRankingForbiddenBranch(t *testing.T) {
}
}
// TestHelperFunctions verifies expected behavior.
func TestHelperFunctions(t *testing.T) {
if got := atoiWithDefault("", 3); got != 3 {
t.Fatalf("expected default for empty input")

@ -1,5 +1,7 @@
package tests
// integration_http_test.go contains tests for backend behavior.
import (
"bytes"
"context"
@ -27,6 +29,7 @@ type inMemoryRepo struct {
stats map[string]*domain.PlayerStats
}
// newInMemoryRepo is a test helper.
func newInMemoryRepo() *inMemoryRepo {
return &inMemoryRepo{
entries: make([]*domain.LeaderboardEntry, 0),
@ -34,7 +37,10 @@ func newInMemoryRepo() *inMemoryRepo {
}
}
// EnsureSchema is a test helper.
func (r *inMemoryRepo) EnsureSchema(ctx context.Context) error { return nil }
// IngestEntry is a test helper.
func (r *inMemoryRepo) IngestEntry(
ctx context.Context,
entry *domain.LeaderboardEntry,
@ -68,6 +74,8 @@ func (r *inMemoryRepo) IngestEntry(
}
return &cp, false, nil
}
// ListTop is a test helper.
func (r *inMemoryRepo) ListTop(
ctx context.Context,
filter domain.TopFilter,
@ -82,6 +90,8 @@ func (r *inMemoryRepo) ListTop(
}
return out, nil
}
// GetPlayerStats is a test helper.
func (r *inMemoryRepo) GetPlayerStats(ctx context.Context, playerID string) (*domain.PlayerStats, error) {
stats := r.stats[playerID]
if stats == nil {
@ -90,12 +100,16 @@ func (r *inMemoryRepo) GetPlayerStats(ctx context.Context, playerID string) (*do
cp := *stats
return &cp, nil
}
// GetPlayerRank is a test helper.
func (r *inMemoryRepo) GetPlayerRank(ctx context.Context, playerID string) (int64, error) {
if _, ok := r.stats[playerID]; !ok {
return 0, domain.ErrPlayerNotFound
}
return 1, nil
}
// ListPlayerHistory is a test helper.
func (r *inMemoryRepo) ListPlayerHistory(
ctx context.Context,
playerID string,
@ -109,6 +123,8 @@ func (r *inMemoryRepo) ListPlayerHistory(
}
return out, int64(len(out)), nil
}
// GetGlobalStats is a test helper.
func (r *inMemoryRepo) GetGlobalStats(ctx context.Context, filter domain.TopFilter) (*domain.GlobalStats, error) {
return &domain.GlobalStats{
TotalGames: int64(len(r.entries)),
@ -119,12 +135,18 @@ func (r *inMemoryRepo) GetGlobalStats(ctx context.Context, filter domain.TopFilt
type fakeState struct{}
// Get is a test helper.
func (s *fakeState) Get(ctx context.Context, key string) (string, bool) { return "", false }
// Set is a test helper.
func (s *fakeState) Set(ctx context.Context, key, value string, ttl time.Duration) error {
return nil
}
// Delete is a test helper.
func (s *fakeState) Delete(ctx context.Context, keys ...string) error { return nil }
// setupApp is a test helper.
func setupApp(t *testing.T) *fiber.App {
t.Helper()
repo := newInMemoryRepo()
@ -156,6 +178,7 @@ func setupApp(t *testing.T) *fiber.App {
return app
}
// TestUpdateAndTop10 verifies expected behavior.
func TestUpdateAndTop10(t *testing.T) {
app := setupApp(t)
@ -188,6 +211,7 @@ func TestUpdateAndTop10(t *testing.T) {
}
}
// TestPlayerAuthAndStats verifies expected behavior.
func TestPlayerAuthAndStats(t *testing.T) {
app := setupApp(t)
@ -215,6 +239,7 @@ func TestPlayerAuthAndStats(t *testing.T) {
}
}
// TestMetricsEndpoint verifies expected behavior.
func TestMetricsEndpoint(t *testing.T) {
app := setupApp(t)
req := httptest.NewRequest(http.MethodGet, "/metrics", nil)
@ -225,6 +250,7 @@ func TestMetricsEndpoint(t *testing.T) {
}
}
// assertStatus is a test helper.
func assertStatus(t *testing.T, resp *http.Response, want int, msg string) {
t.Helper()
if resp.StatusCode != want {

@ -219,6 +219,7 @@ func TestValidateAnswerByQuestionID_ValidationError(t *testing.T) {
}
}
// TestGetRandomQuestion_InvalidDifficulty verifies expected behavior.
func TestGetRandomQuestion_InvalidDifficulty(t *testing.T) {
svc := NewService(&fakeRepo{}, &fakeCache{}, time.Minute, 200)
_, err := svc.GetRandomQuestion(context.Background(), RandomQuestionRequest{
@ -229,6 +230,7 @@ func TestGetRandomQuestion_InvalidDifficulty(t *testing.T) {
}
}
// TestGetRandomQuestion_RepoErrors verifies expected behavior.
func TestGetRandomQuestion_RepoErrors(t *testing.T) {
repo := &fakeRepo{countErr: errors.New("count boom")}
svc := NewService(repo, &fakeCache{}, time.Minute, 200)
@ -249,6 +251,7 @@ func TestGetRandomQuestion_RepoErrors(t *testing.T) {
}
}
// TestQuestionCRUDAndThemes verifies expected behavior.
func TestQuestionCRUDAndThemes(t *testing.T) {
repo := &fakeRepo{}
cache := &fakeCache{}
@ -304,6 +307,7 @@ func TestQuestionCRUDAndThemes(t *testing.T) {
}
}
// TestQuestionCRUDValidationAndErrors verifies expected behavior.
func TestQuestionCRUDValidationAndErrors(t *testing.T) {
repo := &fakeRepo{createErr: errors.New("create boom")}
svc := NewService(repo, &fakeCache{}, time.Minute, 200)
@ -353,6 +357,7 @@ func TestQuestionCRUDValidationAndErrors(t *testing.T) {
}
}
// TestBulkImportScenarios verifies expected behavior.
func TestBulkImportScenarios(t *testing.T) {
cache := &fakeCache{}
repo := &fakeRepo{bulkCount: 1, bulkErrors: []domain.BulkError{{Index: 0, Reason: "row"}}}

@ -29,22 +29,28 @@ type inMemoryRepo struct {
items map[string]*domain.Question
}
// newInMemoryRepo is a test helper.
func newInMemoryRepo() *inMemoryRepo {
return &inMemoryRepo{items: map[string]*domain.Question{}}
}
// GetByID is a test helper.
func (r *inMemoryRepo) GetByID(ctx context.Context, id string) (*domain.Question, error) {
if q, ok := r.items[id]; ok {
return q, nil
}
return nil, domain.ErrQuestionNotFound
}
// Create is a test helper.
func (r *inMemoryRepo) Create(ctx context.Context, q *domain.Question) (*domain.Question, error) {
q.ID = "q-created"
q.IsActive = true
r.items[q.ID] = q
return q, nil
}
// Update is a test helper.
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
@ -53,6 +59,8 @@ func (r *inMemoryRepo) Update(ctx context.Context, id string, q *domain.Question
r.items[id] = q
return q, nil
}
// SoftDelete is a test helper.
func (r *inMemoryRepo) SoftDelete(ctx context.Context, id string) error {
if q, ok := r.items[id]; ok {
q.IsActive = false
@ -60,9 +68,13 @@ func (r *inMemoryRepo) SoftDelete(ctx context.Context, id string) error {
}
return domain.ErrQuestionNotFound
}
// ListThemes is a test helper.
func (r *inMemoryRepo) ListThemes(ctx context.Context) ([]string, error) {
return []string{"Science"}, nil
}
// CountRandomCandidates is a test helper.
func (r *inMemoryRepo) CountRandomCandidates(ctx context.Context, filter domain.RandomFilter) (int, error) {
count := 0
for _, q := range r.items {
@ -73,6 +85,8 @@ func (r *inMemoryRepo) CountRandomCandidates(ctx context.Context, filter domain.
}
return count, nil
}
// RandomByOffset is a test helper.
func (r *inMemoryRepo) RandomByOffset(ctx context.Context,
filter domain.RandomFilter, offset int) (*domain.Question, error) {
for _, q := range r.items {
@ -82,6 +96,8 @@ func (r *inMemoryRepo) RandomByOffset(ctx context.Context,
}
return nil, domain.ErrNoQuestionsAvailable
}
// BulkCreate is a test helper.
func (r *inMemoryRepo) BulkCreate(ctx context.Context, questions []*domain.Question) (int, []domain.BulkError, error) {
for i, q := range questions {
id := "bulk-" + strconv.Itoa(i)
@ -95,12 +111,15 @@ 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.
func (c *noOpCache) Get(ctx context.Context, key string) (*domain.Question, bool) {
return nil, false
}
// Set is a test helper.
func (c *noOpCache) Set(ctx context.Context, key string, q *domain.Question, ttl time.Duration) {}
// Invalidate is a test helper.
func (c *noOpCache) Invalidate(ctx context.Context) {}
// setupApp wires a test Fiber app with in-memory dependencies and admin middleware.

@ -1,5 +1,7 @@
package user
// service_test.go contains tests for backend behavior.
import (
"context"
"errors"
@ -247,6 +249,7 @@ func TestDeleteAndExport(t *testing.T) {
}
}
// TestRegisterValidationAndRepoErrors verifies expected behavior.
func TestRegisterValidationAndRepoErrors(t *testing.T) {
svc := NewService(newFakeRepo())
_, err := svc.Register(context.Background(), RegisterInput{})
@ -305,6 +308,7 @@ func TestRegisterValidationAndRepoErrors(t *testing.T) {
}
}
// TestProfileAndEmailFlows verifies expected behavior.
func TestProfileAndEmailFlows(t *testing.T) {
repo := newFakeRepo()
svc := NewService(repo)
@ -345,6 +349,7 @@ func TestProfileAndEmailFlows(t *testing.T) {
}
}
// TestProfileValidationAndRepoErrors verifies expected behavior.
func TestProfileValidationAndRepoErrors(t *testing.T) {
repo := newFakeRepo()
svc := NewService(repo)
@ -395,6 +400,7 @@ func TestProfileValidationAndRepoErrors(t *testing.T) {
}
}
// TestAdminListAndExport verifies expected behavior.
func TestAdminListAndExport(t *testing.T) {
repo := newFakeRepo()
svc := NewService(repo)
@ -432,6 +438,7 @@ func TestAdminListAndExport(t *testing.T) {
}
}
// TestDeleteListExportErrors verifies expected behavior.
func TestDeleteListExportErrors(t *testing.T) {
repo := newFakeRepo()
svc := NewService(repo)

@ -1,5 +1,7 @@
package tests
// integration_http_test.go contains tests for backend behavior.
import (
"bytes"
"context"

@ -54,10 +54,12 @@ func TestEventType_NonEmpty(t *testing.T) {
type dummyEventBus struct{}
// Publish is a test helper.
func (d *dummyEventBus) Publish(_ context.Context, _ Event) error {
return nil
}
// Subscribe is a test helper.
func (d *dummyEventBus) Subscribe(_ EventHandler) error {
return nil
}

@ -33,6 +33,7 @@ type jwks struct {
Keys []jwksKey `json:"keys"`
}
// generateJWKS is a test helper.
func generateJWKS(t *testing.T) (*rsa.PrivateKey, jwks, string) {
t.Helper()
key, err := rsa.GenerateKey(rand.Reader, 2048)
@ -56,6 +57,7 @@ func generateJWKS(t *testing.T) (*rsa.PrivateKey, jwks, string) {
}, kid
}
// signToken is a test helper.
func signToken(t *testing.T, key *rsa.PrivateKey, kid string, claims jwt.MapClaims) string {
t.Helper()
token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
@ -65,6 +67,7 @@ func signToken(t *testing.T, key *rsa.PrivateKey, kid string, claims jwt.MapClai
return signed
}
// newOIDCServer is a test helper.
func newOIDCServer(t *testing.T, jwksDoc jwks) *httptest.Server {
t.Helper()
var baseURL string
@ -295,6 +298,7 @@ func TestRefreshToken_UsesForm(t *testing.T) {
require.Contains(t, captured, "client_id=client")
}
// serverURL is a test helper.
func serverURL(r *http.Request) string {
return "http://" + r.Host
}

@ -19,6 +19,7 @@ type fakeValidator struct {
called int
}
// ValidateToken is a test helper.
func (f *fakeValidator) ValidateToken(ctx context.Context, token string, _ ValidationOptions) (*AuthClaims, error) {
f.called++
return f.claims, f.err

@ -28,6 +28,7 @@ func TestAddr(t *testing.T) {
require.Equal(t, "localhost:6379", cfg.Addr())
}
// newClientForMiniRedis is a test helper.
func newClientForMiniRedis(t *testing.T) (*Client, *miniredis.Miniredis) {
t.Helper()

@ -1,5 +1,7 @@
package metrics
// http_test.go contains tests for backend behavior.
import (
"net/http"
"net/http/httptest"

@ -1,5 +1,7 @@
package envutil
// env_test.go contains tests for backend behavior.
import (
"testing"
"time"
@ -7,31 +9,37 @@ import (
"github.com/stretchr/testify/require"
)
// TestString verifies expected behavior.
func TestString(t *testing.T) {
t.Setenv("APP_VALUE", "configured")
require.Equal(t, "configured", String("APP_VALUE", "fallback"))
}
// TestStringFallback verifies expected behavior.
func TestStringFallback(t *testing.T) {
t.Setenv("APP_VALUE", "")
require.Equal(t, "fallback", String("APP_VALUE", "fallback"))
}
// TestInt verifies expected behavior.
func TestInt(t *testing.T) {
t.Setenv("APP_PORT", "8080")
require.Equal(t, 8080, Int("APP_PORT", 3000))
}
// TestIntFallbackInvalid verifies expected behavior.
func TestIntFallbackInvalid(t *testing.T) {
t.Setenv("APP_PORT", "oops")
require.Equal(t, 3000, Int("APP_PORT", 3000))
}
// TestDuration verifies expected behavior.
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.
func TestDurationFallbackInvalid(t *testing.T) {
t.Setenv("APP_TIMEOUT", "oops")
require.Equal(t, 2*time.Second, Duration("APP_TIMEOUT", 2*time.Second))

@ -1,5 +1,7 @@
package serviceboot
// fiber_test.go contains tests for backend behavior.
import (
"encoding/json"
"net/http"
@ -9,6 +11,7 @@ import (
"github.com/stretchr/testify/require"
)
// TestRegisterHealth verifies expected behavior.
func TestRegisterHealth(t *testing.T) {
app := NewFiberApp(Config{AppName: "test-service"})
RegisterHealth(app, "svc")
@ -24,16 +27,19 @@ func TestRegisterHealth(t *testing.T) {
require.Equal(t, "svc", body["service"])
}
// TestListenAddressFromEnv verifies expected behavior.
func TestListenAddressFromEnv(t *testing.T) {
t.Setenv("SERVICE_PORT", "9090")
require.Equal(t, ":9090", ListenAddress("SERVICE_PORT", 8080))
}
// TestListenAddressFallback verifies expected behavior.
func TestListenAddressFallback(t *testing.T) {
t.Setenv("SERVICE_PORT", "bad")
require.Equal(t, ":8080", ListenAddress("SERVICE_PORT", 8080))
}
// TestListenAddressOutOfRangeFallback verifies expected behavior.
func TestListenAddressOutOfRangeFallback(t *testing.T) {
t.Setenv("SERVICE_PORT", "70000")
require.Equal(t, ":8080", ListenAddress("SERVICE_PORT", 8080))

Loading…
Cancel
Save