From fa788237eb5649e08b2a38ec21689b481b10c073 Mon Sep 17 00:00:00 2001 From: nsfisis Date: Fri, 20 Feb 2026 21:30:49 +0900 Subject: feat(admin): add rejudge functionality for submissions Allow administrators to re-execute test cases for a specific submission from the submission detail page. This is useful after testcase fixes or worker issues. Co-Authored-By: Claude Opus 4.6 --- backend/admin/handler_test.go | 205 +++++++++++++++++++++++++++++++++++------- 1 file changed, 172 insertions(+), 33 deletions(-) (limited to 'backend/admin/handler_test.go') diff --git a/backend/admin/handler_test.go b/backend/admin/handler_test.go index 0e13c60..3b7a2ba 100644 --- a/backend/admin/handler_test.go +++ b/backend/admin/handler_test.go @@ -22,38 +22,40 @@ import ( // mockQuerier implements db.Querier for admin handler testing. type mockQuerier struct { db.Querier - getUserByIDFunc func(ctx context.Context, userID int32) (db.User, error) - listUsersFunc func(ctx context.Context) ([]db.User, error) - updateUserFunc func(ctx context.Context, arg db.UpdateUserParams) error - listAllGamesFunc func(ctx context.Context) ([]db.Game, error) - getGameByIDFunc func(ctx context.Context, gameID int32) (db.GetGameByIDRow, error) - listProblemsFunc func(ctx context.Context) ([]db.Problem, error) - getProblemByIDFunc func(ctx context.Context, problemID int32) (db.Problem, error) - createGameFunc func(ctx context.Context, arg db.CreateGameParams) (int32, error) - createProblemFunc func(ctx context.Context, arg db.CreateProblemParams) (int32, error) - updateProblemFunc func(ctx context.Context, arg db.UpdateProblemParams) error - listTestcasesByProblemIDFunc func(ctx context.Context, problemID int32) ([]db.Testcase, error) - getTestcaseByIDFunc func(ctx context.Context, testcaseID int32) (db.Testcase, error) - createTestcaseFunc func(ctx context.Context, arg db.CreateTestcaseParams) (int32, error) - updateTestcaseFunc func(ctx context.Context, arg db.UpdateTestcaseParams) error - deleteTestcaseFunc func(ctx context.Context, testcaseID int32) error - listMainPlayersFunc func(ctx context.Context, gameIDs []int32) ([]db.ListMainPlayersRow, error) - listSubmissionIDsFunc func(ctx context.Context) ([]int32, error) - getSubmissionsByGameIDFunc func(ctx context.Context, gameID int32) ([]db.Submission, error) - getSubmissionByIDFunc func(ctx context.Context, submissionID int32) (db.Submission, error) - getTestcaseResultsBySubmIDFunc func(ctx context.Context, submissionID int32) ([]db.TestcaseResult, error) - listTestcasesByGameIDFunc func(ctx context.Context, gameID int32) ([]db.Testcase, error) - updateGameStartedAtFunc func(ctx context.Context, arg db.UpdateGameStartedAtParams) error - listTournamentsFunc func(ctx context.Context) ([]db.Tournament, error) - getTournamentByIDFunc func(ctx context.Context, tournamentID int32) (db.Tournament, error) - createTournamentFunc func(ctx context.Context, arg db.CreateTournamentParams) (int32, error) - updateTournamentFunc func(ctx context.Context, arg db.UpdateTournamentParams) error - listTournamentEntriesFunc func(ctx context.Context, tournamentID int32) ([]db.ListTournamentEntriesRow, error) - deleteTournamentEntriesFunc func(ctx context.Context, tournamentID int32) error - createTournamentEntryFunc func(ctx context.Context, arg db.CreateTournamentEntryParams) error - listTournamentMatchesFunc func(ctx context.Context, tournamentID int32) ([]db.TournamentMatch, error) - createTournamentMatchFunc func(ctx context.Context, arg db.CreateTournamentMatchParams) error - updateTournamentMatchGameFunc func(ctx context.Context, arg db.UpdateTournamentMatchGameParams) error + getUserByIDFunc func(ctx context.Context, userID int32) (db.User, error) + listUsersFunc func(ctx context.Context) ([]db.User, error) + updateUserFunc func(ctx context.Context, arg db.UpdateUserParams) error + listAllGamesFunc func(ctx context.Context) ([]db.Game, error) + getGameByIDFunc func(ctx context.Context, gameID int32) (db.GetGameByIDRow, error) + listProblemsFunc func(ctx context.Context) ([]db.Problem, error) + getProblemByIDFunc func(ctx context.Context, problemID int32) (db.Problem, error) + createGameFunc func(ctx context.Context, arg db.CreateGameParams) (int32, error) + createProblemFunc func(ctx context.Context, arg db.CreateProblemParams) (int32, error) + updateProblemFunc func(ctx context.Context, arg db.UpdateProblemParams) error + listTestcasesByProblemIDFunc func(ctx context.Context, problemID int32) ([]db.Testcase, error) + getTestcaseByIDFunc func(ctx context.Context, testcaseID int32) (db.Testcase, error) + createTestcaseFunc func(ctx context.Context, arg db.CreateTestcaseParams) (int32, error) + updateTestcaseFunc func(ctx context.Context, arg db.UpdateTestcaseParams) error + deleteTestcaseFunc func(ctx context.Context, testcaseID int32) error + deleteTestcaseResultsBySubmissionIDFunc func(ctx context.Context, submissionID int32) error + listMainPlayersFunc func(ctx context.Context, gameIDs []int32) ([]db.ListMainPlayersRow, error) + listSubmissionIDsFunc func(ctx context.Context) ([]int32, error) + getSubmissionsByGameIDFunc func(ctx context.Context, gameID int32) ([]db.Submission, error) + getSubmissionByIDFunc func(ctx context.Context, submissionID int32) (db.Submission, error) + getTestcaseResultsBySubmIDFunc func(ctx context.Context, submissionID int32) ([]db.TestcaseResult, error) + updateSubmissionStatusFunc func(ctx context.Context, arg db.UpdateSubmissionStatusParams) error + listTestcasesByGameIDFunc func(ctx context.Context, gameID int32) ([]db.Testcase, error) + updateGameStartedAtFunc func(ctx context.Context, arg db.UpdateGameStartedAtParams) error + listTournamentsFunc func(ctx context.Context) ([]db.Tournament, error) + getTournamentByIDFunc func(ctx context.Context, tournamentID int32) (db.Tournament, error) + createTournamentFunc func(ctx context.Context, arg db.CreateTournamentParams) (int32, error) + updateTournamentFunc func(ctx context.Context, arg db.UpdateTournamentParams) error + listTournamentEntriesFunc func(ctx context.Context, tournamentID int32) ([]db.ListTournamentEntriesRow, error) + deleteTournamentEntriesFunc func(ctx context.Context, tournamentID int32) error + createTournamentEntryFunc func(ctx context.Context, arg db.CreateTournamentEntryParams) error + listTournamentMatchesFunc func(ctx context.Context, tournamentID int32) ([]db.TournamentMatch, error) + createTournamentMatchFunc func(ctx context.Context, arg db.CreateTournamentMatchParams) error + updateTournamentMatchGameFunc func(ctx context.Context, arg db.UpdateTournamentMatchGameParams) error } func (m *mockQuerier) GetUserByID(ctx context.Context, userID int32) (db.User, error) { @@ -277,6 +279,20 @@ func (m *mockQuerier) CreateTournamentMatch(ctx context.Context, arg db.CreateTo return nil } +func (m *mockQuerier) DeleteTestcaseResultsBySubmissionID(ctx context.Context, submissionID int32) error { + if m.deleteTestcaseResultsBySubmissionIDFunc != nil { + return m.deleteTestcaseResultsBySubmissionIDFunc(ctx, submissionID) + } + return nil +} + +func (m *mockQuerier) UpdateSubmissionStatus(ctx context.Context, arg db.UpdateSubmissionStatusParams) error { + if m.updateSubmissionStatusFunc != nil { + return m.updateSubmissionStatusFunc(ctx, arg) + } + return nil +} + func (m *mockQuerier) UpdateTournamentMatchGame(ctx context.Context, arg db.UpdateTournamentMatchGameParams) error { if m.updateTournamentMatchGameFunc != nil { return m.updateTournamentMatchGameFunc(ctx, arg) @@ -284,6 +300,18 @@ func (m *mockQuerier) UpdateTournamentMatchGame(ctx context.Context, arg db.Upda return nil } +// mockGameHub implements GameHub for testing. +type mockGameHub struct { + enqueueTestTasksFunc func(ctx context.Context, submissionID, gameID, userID int, language, code string) error +} + +func (m *mockGameHub) EnqueueTestTasks(ctx context.Context, submissionID, gameID, userID int, language, code string) error { + if m.enqueueTestTasksFunc != nil { + return m.enqueueTestTasksFunc(ctx, submissionID, gameID, userID, language, code) + } + return nil +} + // mockTxManager implements db.TxManager for testing. type mockTxManager struct { runInTxFunc func(ctx context.Context, fn func(q db.Querier) error) error @@ -312,6 +340,7 @@ func newTestHandler(q *mockQuerier) *Handler { return &Handler{ q: q, txm: &mockTxManager{}, + hub: &mockGameHub{}, conf: &config.Config{BasePath: "/test/"}, } } @@ -1230,7 +1259,8 @@ func TestPostTournamentNew_Success(t *testing.T) { var createdParams db.CreateTournamentParams var matchCount int h := &Handler{ - q: &mockQuerier{}, + q: &mockQuerier{}, + hub: &mockGameHub{}, txm: &mockTxManager{ runInTxFunc: func(_ context.Context, fn func(q db.Querier) error) error { return fn(&mockQuerier{ @@ -1360,3 +1390,112 @@ func TestPostTournamentEdit_NotFound(t *testing.T) { t.Errorf("status = %d, want %d", httpErr.Code, http.StatusNotFound) } } + +// --- Rejudge tests --- + +func TestPostSubmissionRejudge_Success(t *testing.T) { + var deletedSubmissionID int32 + var updatedStatus db.UpdateSubmissionStatusParams + var enqueuedSubmissionID, enqueuedGameID, enqueuedUserID int + var enqueuedLanguage, enqueuedCode string + + q := &mockQuerier{ + getSubmissionByIDFunc: func(_ context.Context, submissionID int32) (db.Submission, error) { + return db.Submission{ + SubmissionID: submissionID, + GameID: 1, + UserID: 10, + Code: "