diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-02-15 22:13:50 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-02-15 22:16:22 +0900 |
| commit | 5ed369a6c70707543fd5ec9a13c79851fdfc5d6c (patch) | |
| tree | e5678d6d88fab3ac0ae8c05b85236f3e7d5eddfd /backend/db | |
| parent | 87e9f5ed48af3a8dca5f6373ae900336f285eef5 (diff) | |
| download | phperkaigi-2026-albatross-5ed369a6c70707543fd5ec9a13c79851fdfc5d6c.tar.gz phperkaigi-2026-albatross-5ed369a6c70707543fd5ec9a13c79851fdfc5d6c.tar.zst phperkaigi-2026-albatross-5ed369a6c70707543fd5ec9a13c79851fdfc5d6c.zip | |
refactor(backend): introduce DI interfaces for testability
Replace concrete *db.Queries and *pgxpool.Pool dependencies with
db.Querier and db.TxManager interfaces across all handlers, game hub,
and auth. This enables unit testing with mocks.
- Enable sqlc emit_interface to generate Querier interface
- Add TxManager abstraction to encapsulate transactions
- Convert auth package-level functions to Authenticator struct
- Add TaskQueueInterface/TaskWorkerInterface for game.Hub
- Add initial unit tests for game logic and API handlers
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat (limited to 'backend/db')
| -rw-r--r-- | backend/db/querier.go | 63 | ||||
| -rw-r--r-- | backend/db/txmanager.go | 40 |
2 files changed, 103 insertions, 0 deletions
diff --git a/backend/db/querier.go b/backend/db/querier.go new file mode 100644 index 0000000..89d4b55 --- /dev/null +++ b/backend/db/querier.go @@ -0,0 +1,63 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.30.0 + +package db + +import ( + "context" +) + +type Querier interface { + AddMainPlayer(ctx context.Context, arg AddMainPlayerParams) error + AggregateTestcaseResults(ctx context.Context, submissionID int32) (string, error) + CreateGame(ctx context.Context, arg CreateGameParams) (int32, error) + CreateProblem(ctx context.Context, arg CreateProblemParams) (int32, error) + CreateSession(ctx context.Context, arg CreateSessionParams) error + CreateSubmission(ctx context.Context, arg CreateSubmissionParams) (int32, error) + CreateTestcase(ctx context.Context, arg CreateTestcaseParams) (int32, error) + CreateTestcaseResult(ctx context.Context, arg CreateTestcaseResultParams) error + CreateUser(ctx context.Context, username string) (int32, error) + CreateUserAuth(ctx context.Context, arg CreateUserAuthParams) error + DeleteExpiredSessions(ctx context.Context) error + DeleteSession(ctx context.Context, sessionID string) error + DeleteTestcase(ctx context.Context, testcaseID int32) error + GetGameByID(ctx context.Context, gameID int32) (GetGameByIDRow, error) + GetLatestState(ctx context.Context, arg GetLatestStateParams) (GetLatestStateRow, error) + GetLatestStatesOfMainPlayers(ctx context.Context, gameID int32) ([]GetLatestStatesOfMainPlayersRow, error) + GetProblemByID(ctx context.Context, problemID int32) (Problem, error) + GetQualifyingRanking(ctx context.Context, arg GetQualifyingRankingParams) ([]GetQualifyingRankingRow, error) + GetRanking(ctx context.Context, gameID int32) ([]GetRankingRow, error) + GetSubmissionByID(ctx context.Context, submissionID int32) (Submission, error) + GetSubmissionsByGameID(ctx context.Context, gameID int32) ([]Submission, error) + GetTestcaseByID(ctx context.Context, testcaseID int32) (Testcase, error) + GetTestcaseResultsBySubmissionID(ctx context.Context, submissionID int32) ([]TestcaseResult, error) + GetUserAuthByUsername(ctx context.Context, username string) (GetUserAuthByUsernameRow, error) + GetUserByID(ctx context.Context, userID int32) (User, error) + GetUserBySession(ctx context.Context, sessionID string) (User, error) + GetUserIDByUsername(ctx context.Context, username string) (int32, error) + ListAllGames(ctx context.Context) ([]Game, error) + ListGameStateIDs(ctx context.Context) ([]ListGameStateIDsRow, error) + ListMainPlayers(ctx context.Context, dollar_1 []int32) ([]ListMainPlayersRow, error) + ListProblems(ctx context.Context) ([]Problem, error) + ListPublicGames(ctx context.Context) ([]ListPublicGamesRow, error) + ListSubmissionIDs(ctx context.Context) ([]int32, error) + ListTestcases(ctx context.Context) ([]Testcase, error) + ListTestcasesByGameID(ctx context.Context, gameID int32) ([]Testcase, error) + ListTestcasesByProblemID(ctx context.Context, problemID int32) ([]Testcase, error) + ListUsers(ctx context.Context) ([]User, error) + RemoveAllMainPlayers(ctx context.Context, gameID int32) error + SyncGameStateBestScoreSubmission(ctx context.Context, arg SyncGameStateBestScoreSubmissionParams) error + UpdateCode(ctx context.Context, arg UpdateCodeParams) error + UpdateCodeAndStatus(ctx context.Context, arg UpdateCodeAndStatusParams) error + UpdateGame(ctx context.Context, arg UpdateGameParams) error + UpdateGameStartedAt(ctx context.Context, arg UpdateGameStartedAtParams) error + UpdateGameStateStatus(ctx context.Context, arg UpdateGameStateStatusParams) error + UpdateProblem(ctx context.Context, arg UpdateProblemParams) error + UpdateSubmissionStatus(ctx context.Context, arg UpdateSubmissionStatusParams) error + UpdateTestcase(ctx context.Context, arg UpdateTestcaseParams) error + UpdateUser(ctx context.Context, arg UpdateUserParams) error + UpdateUserIconPath(ctx context.Context, arg UpdateUserIconPathParams) error +} + +var _ Querier = (*Queries)(nil) diff --git a/backend/db/txmanager.go b/backend/db/txmanager.go new file mode 100644 index 0000000..9495288 --- /dev/null +++ b/backend/db/txmanager.go @@ -0,0 +1,40 @@ +package db + +import ( + "context" + "log/slog" + + "github.com/jackc/pgx/v5" + "github.com/jackc/pgx/v5/pgxpool" +) + +type TxManager interface { + RunInTx(ctx context.Context, fn func(q Querier) error) error +} + +type PgxTxManager struct { + pool *pgxpool.Pool + queries *Queries +} + +func NewTxManager(pool *pgxpool.Pool, queries *Queries) *PgxTxManager { + return &PgxTxManager{pool: pool, queries: queries} +} + +func (m *PgxTxManager) RunInTx(ctx context.Context, fn func(q Querier) error) error { + tx, err := m.pool.Begin(ctx) + if err != nil { + return err + } + defer func() { + if err := tx.Rollback(ctx); err != nil && err != pgx.ErrTxClosed { + slog.Error("failed to rollback transaction", "error", err) + } + }() + + qtx := m.queries.WithTx(tx) + if err := fn(qtx); err != nil { + return err + } + return tx.Commit(ctx) +} |
