diff --git a/apps/backend/apps/client/src/contest/contest.service.spec.ts b/apps/backend/apps/client/src/contest/contest.service.spec.ts index 11d41267a6..8c0b1f6939 100644 --- a/apps/backend/apps/client/src/contest/contest.service.spec.ts +++ b/apps/backend/apps/client/src/contest/contest.service.spec.ts @@ -79,7 +79,54 @@ const contests = [ ...ongoingContests, ...finishedContests, ...upcomingContests -] satisfies Partial[] +] satisfies Partial[] + +const sortedContestRecordsWithUserDetail = [ + { + user: { + id: 13, + username: 'user10' + }, + score: 36, + totalPenalty: 720 + }, + { + user: { + id: 12, + username: 'user09' + }, + score: 33, + totalPenalty: 660 + }, + { + user: { + id: 11, + username: 'user08' + }, + score: 30, + totalPenalty: 600 + } +] + +const mockPrismaService = { + contest: { + findUnique: stub(), + findUniqueOrThrow: stub(), + findFirst: stub(), + findFirstOrThrow: stub(), + findMany: stub() + }, + contestRecord: { + findFirst: stub(), + findMany: stub(), + create: stub() + }, + userGroup: { + findFirst: stub(), + findMany: stub() + }, + getPaginator: PrismaService.prototype.getPaginator +} describe('ContestService', () => { let service: ContestService @@ -296,7 +343,18 @@ describe('ContestService', () => { }) it('should return contest', async () => { - expect(await service.getContest(contestId, groupId, user01Id)).to.be.ok + mockPrismaService.contest.findUniqueOrThrow.resolves(contestDetail) + mockPrismaService.contestRecord.findMany.resolves( + sortedContestRecordsWithUserDetail + ) + + expect(await service.getContest(groupId, contestId)).to.deep.equal({ + ...contestDetail, + standings: sortedContestRecordsWithUserDetail.map((record, index) => ({ + ...record, + standing: index + 1 + })) + }) }) }) diff --git a/apps/backend/apps/client/src/submission/mock/submissionResult.mock.ts b/apps/backend/apps/client/src/submission/mock/submissionResult.mock.ts index 9c4251f510..39d5c9ccbc 100644 --- a/apps/backend/apps/client/src/submission/mock/submissionResult.mock.ts +++ b/apps/backend/apps/client/src/submission/mock/submissionResult.mock.ts @@ -9,7 +9,7 @@ export const submissionResults: SubmissionResult[] = [ result: ResultStatus.Accepted, submissionId: 1, updateTime: new Date('2023-01-01'), - cpuTime: BigInt(12345), + cpuTime: Number(12345), memoryUsage: 12345 }, { @@ -19,7 +19,7 @@ export const submissionResults: SubmissionResult[] = [ result: ResultStatus.Accepted, submissionId: 2, updateTime: new Date('2023-01-01'), - cpuTime: BigInt(12345), + cpuTime: Number(12345), memoryUsage: 12345 }, { @@ -29,7 +29,7 @@ export const submissionResults: SubmissionResult[] = [ result: ResultStatus.WrongAnswer, submissionId: 1, updateTime: new Date('2023-01-01'), - cpuTime: BigInt(12345), + cpuTime: Number(12345), memoryUsage: 12345 }, { @@ -39,7 +39,7 @@ export const submissionResults: SubmissionResult[] = [ result: ResultStatus.CompileError, submissionId: 1, updateTime: new Date('2023-01-01'), - cpuTime: BigInt(12345), + cpuTime: Number(12345), memoryUsage: 12345 } ] diff --git a/apps/backend/prisma/migrations/20240401074346_add_latest_ac_time_and_chang_cpu_time_to_int/migration.sql b/apps/backend/prisma/migrations/20240401074346_add_latest_ac_time_and_chang_cpu_time_to_int/migration.sql new file mode 100644 index 0000000000..69eff7c711 --- /dev/null +++ b/apps/backend/prisma/migrations/20240401074346_add_latest_ac_time_and_chang_cpu_time_to_int/migration.sql @@ -0,0 +1,11 @@ +/* + Warnings: + + - You are about to alter the column `cpu_time` on the `submission_result` table. The data in that column could be lost. The data in that column will be cast from `BigInt` to `Integer`. + +*/ +-- AlterTable +ALTER TABLE "contest_record" ADD COLUMN "latest_accepted_time" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP; + +-- AlterTable +ALTER TABLE "submission_result" ALTER COLUMN "cpu_time" SET DATA TYPE INTEGER; diff --git a/apps/backend/prisma/schema.prisma b/apps/backend/prisma/schema.prisma index b96f9b6093..d6cd48e478 100644 --- a/apps/backend/prisma/schema.prisma +++ b/apps/backend/prisma/schema.prisma @@ -318,6 +318,9 @@ model ContestRecord { userId Int? @map("user_id") acceptedProblemNum Int @default(0) @map("accepted_problem_num") score Int @default(0) + // FIXME: latestAcceptedTime(최근 정답 제출 시간)의 default 값을 now()로 설정했습니다. + // 구현 로직에 따라 추후 수정이 필요할 수 있습니다. + latestAcceptedTime DateTime @default(now()) @map("latest_accepted_time") totalPenalty Int @default(0) @map("total_penalty") createTime DateTime @default(now()) @map("create_time") updateTime DateTime @updatedAt @map("update_time") @@ -395,7 +398,7 @@ model SubmissionResult { problemTestcase ProblemTestcase @relation(fields: [problemTestcaseId], references: [id], onDelete: Cascade) problemTestcaseId Int @map("problem_test_case_id") result ResultStatus - cpuTime BigInt @map("cpu_time") + cpuTime Int @map("cpu_time") memoryUsage Int @map("memory_usage") createTime DateTime @default(now()) @map("create_time") updateTime DateTime @updatedAt @map("update_time") diff --git a/apps/backend/prisma/seed.ts b/apps/backend/prisma/seed.ts index cf26ca601a..3ce42c4145 100644 --- a/apps/backend/prisma/seed.ts +++ b/apps/backend/prisma/seed.ts @@ -1244,6 +1244,7 @@ const createContests = async () => { data: { order: problem.id - 1, contestId: ongoingContests[0].id, + score: problem.id + 3, problemId: problem.id } }) diff --git a/collection/client/Contest/Get contest by ID/Succeed.bru b/collection/client/Contest/Get contest by ID/Succeed.bru index ac7eb909e2..867e0ffbcc 100644 --- a/collection/client/Contest/Get contest by ID/Succeed.bru +++ b/collection/client/Contest/Get contest by ID/Succeed.bru @@ -28,7 +28,7 @@ assert { } script:pre-request { - await require("./login").loginUser2nd(req); + await require("./login").loginUser(req); } docs { @@ -37,6 +37,9 @@ docs { 하나의 대회 정보와 Contest 참여자 정보, 로그인한 유저가 해당 Contest에 참여가능한지 여부에 대한 정보를 가져옵니다. (로그인 하지 않은 유저는 Open Space의 Contest 정보만 볼 수 있고, `isRegistered`는 항상 `false`) ### Path + 하나의 대회 정보와 Contest 참여자 정보를 가져옵니다. + ## Path + | 이름 | 타입 | 설명 | |-----|-----|-----| |id|Integer|Contest(대회) ID| diff --git a/collection/client/Submission/Create Submission/ 409: You already got AC for this problem.bru b/collection/client/Submission/Create Submission/ 409: You already got AC for this problem.bru new file mode 100644 index 0000000000..e50d49268e --- /dev/null +++ b/collection/client/Submission/Create Submission/ 409: You already got AC for this problem.bru @@ -0,0 +1,42 @@ +meta { + name: 409: You already got AC for this problem + type: http + seq: 1 +} + +post { + url: {{baseUrl}}/submission?problemId=1&groupId=1&contestId=1 + body: json + auth: none +} + +query { + problemId: 1 + groupId: 1 + contestId: 1 + ~workbookId: 1 +} + +body:json { + { + "code": [ + { + "id": 1, + "text": "#include \nint main() { int a, b; scanf(\"%d%d\", &a, &b); printf(\"%d\\n\", a + b);}", + "locked": false + } + ], + "language": "C" + } +} + +assert { + res.status: eq 409 + res.body.message: eq You already got AC for this problem + res.body.error: eq Conflict + res.body.statusCode: eq 409 +} + +script:pre-request { + await require("./login").loginUser(req); +} diff --git a/collection/client/Submission/Create Submission/422: Required Query Missing.bru b/collection/client/Submission/Create Submission/422: Required Query Missing.bru new file mode 100644 index 0000000000..634b5949b7 --- /dev/null +++ b/collection/client/Submission/Create Submission/422: Required Query Missing.bru @@ -0,0 +1,42 @@ +meta { + name: 422: Required Query Missing + type: http + seq: 1 +} + +post { + url: {{baseUrl}}/submission + body: json + auth: none +} + +query { + ~problemId: + ~groupId: 1 + ~workbookId: 1 + ~contestId: 1 +} + +body:json { + { + "code": [ + { + "id": 1, + "text": "#include \nint main() { int a, b; scanf(\"%d%d\", &a, &b); printf(\"%d\\n\", a + b);}", + "locked": false + } + ], + "language": "C" + } +} + +assert { + res.status: eq 422 + res.body.message: eq Required query is missing: problemId + res.body.error: eq Unprocessable Entity + res.body.statusCode: eq 422 +} + +script:pre-request { + await require("./login").loginUser(req); +} diff --git a/collection/client/Submission/Create Submission/Succeed.bru b/collection/client/Submission/Create Submission/Succeed.bru index ef8c516a8a..b788fab019 100644 --- a/collection/client/Submission/Create Submission/Succeed.bru +++ b/collection/client/Submission/Create Submission/Succeed.bru @@ -5,15 +5,15 @@ meta { } post { - url: {{baseUrl}}/submission?problemId=1 + url: {{baseUrl}}/submission?problemId=1&groupId=1&contestId=1 body: json auth: none } query { problemId: 1 - ~groupId: 1 - ~contestId: 1 + groupId: 1 + contestId: 1 ~workbookId: 1 } @@ -30,24 +30,47 @@ body:json { } } +assert { + res.status: eq 201 + res.body.id: isNumber + res.body.userId: isNumber + res.body.problemId: isNumber + res.body.language: isString + res.body.result: isString + res.body.createTime: isString + res.body.updateTime: isString + res.body.code[0].id: isNumber + res.body.code[0].text: isString + res.body.code[0].locked: isBoolean +} + script:pre-request { await require("./login").loginUser(req); } docs { - # Create Submission - + ## Create Submission + 코드를 제출하여 채점 요청을 보냅니다. - - ## Query - + + ### Query + > 필수 query는 * 표시하였습니다. - + | 이름 | 타입 | 설명 | |-----|-----|-----| |problemId *|Integer|문제 ID| |groupId|Integer|문제가 속한 Group ID (default: 1)| |contestId|Integer|문제가 속한 대회 ID| |workbookId|Integer|문제가 속한 문제집 ID| - + + ### Error Case + + #### 422: Required query is missing: problemId + + query parameter인 problemId가 없습니다. + + #### 409: You already got AC for this problem + + 이미 같은 문제로 AC를 받은 경우, 재제출을 할 수가 없습니다. } diff --git a/collection/client/Submission/Get Submission by ID/Succeed.bru b/collection/client/Submission/Get Submission by ID/Succeed.bru index c40fb44f87..1628c80096 100644 --- a/collection/client/Submission/Get Submission by ID/Succeed.bru +++ b/collection/client/Submission/Get Submission by ID/Succeed.bru @@ -5,15 +5,27 @@ meta { } get { - url: {{baseUrl}}/submission/1?problemId=1 + url: {{baseUrl}}/submission/8?problemId=1&groupId=1&contestId=1 body: none auth: none } query { problemId: 1 - ~groupId: 1 - ~contestId: 1 + groupId: 1 + contestId: 1 +} + +assert { + res.status: eq 200 + res.body[0].id: isNumber + res.body[0].submissionId: isNumber + res.body[0].problemTestcaseId: isNumber + res.body[0].result: isString + res.body[0].cpuTime: isNumber + res.body[0].memoryUsage: isNumber + res.body[0].createTime: isString + res.body[0].updateTime: isString } script:pre-request { @@ -21,18 +33,33 @@ script:pre-request { } docs { - # Get Submission by ID - + ## Get Submission by ID + 문제의 제출 내역 정보를 가져옵니다. - - ## Query - + + ### Path + + | 이름 | 타입 | 설명 | + |-----|-----|-----| + |:id|Integer|Submission ID| + + ### Query + > 필수 query는 * 표시하였습니다. - + | 이름 | 타입 | 설명 | |-----|-----|-----| |problemId *|Integer|문제 ID| |contestId|Integer|문제가 속한 대회 ID| |groupId|Integer|문제가 속한 Group ID (default: 1)| - + + ### Error Case + + #### 404: Nonexistent Submission + + 존재하지 않는 `submissionId`를 요청합니다. + + #### 404: Nonexistent Problem + + 존재하지 않는 `problemId`를 요청합니다. } diff --git a/collection/client/Submission/Get Submissions/Succeed.bru b/collection/client/Submission/Get Submissions/Succeed.bru index 339d5ed69e..3332048611 100644 --- a/collection/client/Submission/Get Submissions/Succeed.bru +++ b/collection/client/Submission/Get Submissions/Succeed.bru @@ -5,32 +5,50 @@ meta { } get { - url: {{baseUrl}}/submission?problemId=1 + url: {{baseUrl}}/submission?problemId=1&contestId=1&groupId=1&take=10 body: none auth: none } query { problemId: 1 - ~groupId: 1 - ~take: 10 - ~cursor: 5 - ~contestId: 1 + contestId: 1 + groupId: 1 + take: 10 +} + +assert { + res.body[0].id: isNumber + res.body[0].user.username: isString + res.body[0].createTime: isString + res.body[0].language: isString + res.body[0].result: isString + res.status: eq 200 +} + +script:pre-request { + await require("./login").loginUser(req); } docs { - # Get Submissions - + ## Get Submissions + 문제의 제출 내역을 가져옵니다. - - ## Query - + + ### Query + > 필수 query는 * 표시하였습니다. - + | 이름 | 타입 | 설명 | |-----|-----|-----| |problemId *|Integer|문제 ID| |groupId|Integer|문제가 속한 Group ID (default: 1)| |take|Integer|가져올 제출 내역 개수 (default: 10)| - |cursor|Integer|cursor 값 다음의 ID를 가진 제출 내역들을 반환| + |cursor|Integer|cursor 값 다음의 ID를 가진 제출 내역들을 반환 (cursor 값이 없을 시, 가장 작은 ID부터 반환)| + + ### Error Case + + #### 404: Nonexistent Problem + + 존재하지 않는 `problemId`에 대해 제출 내역 요청을 합니다. } diff --git a/collection/client/Submission/Get Submissions/[404] Nonexistent Problem.bru b/collection/client/Submission/Get Submissions/[404] Nonexistent Problem.bru index 1f2a3ecf5d..785645d9bd 100644 --- a/collection/client/Submission/Get Submissions/[404] Nonexistent Problem.bru +++ b/collection/client/Submission/Get Submissions/[404] Nonexistent Problem.bru @@ -13,3 +13,10 @@ get { query { problemId: 99999 } + +assert { + res.status: eq 404 + res.body.message: eq No Problem found + res.body.error: eq Not Found + res.body.statusCode: eq 404 +}