Skip to content

Commit a4b6df6

Browse files
seer-by-sentry[bot]RichDom2185
authored andcommitted
test: add unit tests for PR#1238
1 parent 8736877 commit a4b6df6

File tree

3 files changed

+500
-0
lines changed

3 files changed

+500
-0
lines changed

test/cadet/assessments/assessments_test.exs

Lines changed: 249 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3368,6 +3368,255 @@ defmodule Cadet.AssessmentsTest do
33683368
end
33693369
end
33703370

3371+
describe "fetch_contest_voting_assesment_id function" do
3372+
test "correctly fetches voting assessment id when contest exists" do
3373+
course = insert(:course)
3374+
config = insert(:assessment_config)
3375+
3376+
contest_assessment = insert(:assessment, %{course: course, config: config})
3377+
voting_assessment = insert(:assessment, %{course: course, config: config})
3378+
3379+
contest_question = insert(:programming_question, assessment: contest_assessment)
3380+
voting_question = insert(:voting_question, %{
3381+
assessment: voting_assessment,
3382+
question: build(:voting_question_content, contest_number: contest_assessment.number)
3383+
})
3384+
3385+
result = Assessments.fetch_contest_voting_assesment_id(voting_assessment.id)
3386+
3387+
assert result == contest_assessment.id
3388+
end
3389+
3390+
test "returns nil when assessment does not exist" do
3391+
non_existent_id = 999_999
3392+
result = Assessments.fetch_contest_voting_assesment_id(non_existent_id)
3393+
assert is_nil(result)
3394+
end
3395+
3396+
test "returns nil when no contest number matches" do
3397+
course = insert(:course)
3398+
config = insert(:assessment_config)
3399+
3400+
voting_assessment = insert(:assessment, %{course: course, config: config})
3401+
3402+
insert(:voting_question, %{
3403+
assessment: voting_assessment,
3404+
question: build(:voting_question_content, contest_number: "non_existent_number")
3405+
})
3406+
3407+
result = Assessments.fetch_contest_voting_assesment_id(voting_assessment.id)
3408+
3409+
assert is_nil(result)
3410+
end
3411+
end
3412+
3413+
describe "fetch_all_contests function" do
3414+
test "fetches all contests for a course" do
3415+
course = insert(:course)
3416+
config = insert(:assessment_config, %{type: "Contests"})
3417+
3418+
contest_assessment_1 = insert(:assessment, %{
3419+
course: course,
3420+
config: config,
3421+
is_published: true,
3422+
title: "Contest 1"
3423+
})
3424+
contest_assessment_2 = insert(:assessment, %{
3425+
course: course,
3426+
config: config,
3427+
is_published: false,
3428+
title: "Contest 2"
3429+
})
3430+
3431+
voting_assessment = insert(:assessment, %{course: course})
3432+
3433+
insert(:voting_question, %{
3434+
assessment: voting_assessment,
3435+
question: build(:voting_question_content, contest_number: contest_assessment_1.number)
3436+
})
3437+
insert(:voting_question, %{
3438+
assessment: voting_assessment,
3439+
question: build(:voting_question_content, contest_number: contest_assessment_2.number)
3440+
})
3441+
3442+
result = Assessments.fetch_all_contests(course.id)
3443+
3444+
assert length(result) == 2
3445+
assert Enum.find(result, fn c -> c.contest_id == contest_assessment_1.id end).published == true
3446+
assert Enum.find(result, fn c -> c.contest_id == contest_assessment_2.id end).published == false
3447+
end
3448+
3449+
test "returns empty list when no voting questions exist" do
3450+
course = insert(:course)
3451+
result = Assessments.fetch_all_contests(course.id)
3452+
assert result == []
3453+
end
3454+
3455+
test "excludes contests that are not of type Contests" do
3456+
course = insert(:course)
3457+
non_contest_config = insert(:assessment_config, %{type: "Mission"})
3458+
3459+
non_contest_assessment = insert(:assessment, %{
3460+
course: course,
3461+
config: non_contest_config,
3462+
is_published: true
3463+
})
3464+
3465+
voting_assessment = insert(:assessment, %{course: course})
3466+
3467+
insert(:voting_question, %{
3468+
assessment: voting_assessment,
3469+
question: build(:voting_question_content, contest_number: non_contest_assessment.number)
3470+
})
3471+
3472+
result = Assessments.fetch_all_contests(course.id)
3473+
3474+
assert result == []
3475+
end
3476+
end
3477+
3478+
describe "fetch_top_relative_score_answers ranking" do
3479+
test "correctly ranks answers with RANK() OVER function" do
3480+
course = insert(:course)
3481+
config = insert(:assessment_config)
3482+
contest_assessment = insert(:assessment, %{course: course, config: config})
3483+
contest_question = insert(:programming_question, assessment: contest_assessment)
3484+
voting_assessment = insert(:assessment, %{course: course, config: config})
3485+
voting_question = insert(:voting_question, assessment: voting_assessment)
3486+
3487+
# generate 5 students with answers having different relative scores
3488+
student_list = insert_list(5, :course_registration, %{course: course, role: :student})
3489+
3490+
submission_list =
3491+
Enum.map(
3492+
student_list,
3493+
fn student ->
3494+
insert(
3495+
:submission,
3496+
student: student,
3497+
assessment: contest_assessment,
3498+
status: "submitted"
3499+
)
3500+
end
3501+
)
3502+
3503+
ans_list =
3504+
Enum.map(
3505+
Enum.with_index(submission_list),
3506+
fn {submission, index} ->
3507+
insert(
3508+
:answer,
3509+
answer: build(:programming_answer),
3510+
submission: submission,
3511+
question: contest_question,
3512+
relative_score: 10 - index
3513+
)
3514+
end
3515+
)
3516+
3517+
top_2 = Assessments.fetch_top_relative_score_answers(contest_question.id, 2)
3518+
3519+
assert length(top_2) == 2
3520+
assert Enum.all?(top_2, fn ans -> ans.rank <= 2 end)
3521+
assert Enum.map(top_2, fn ans -> ans.relative_score end) == [10.0, 9.0]
3522+
end
3523+
3524+
test "handles tied scores correctly with RANK() OVER" do
3525+
course = insert(:course)
3526+
config = insert(:assessment_config)
3527+
contest_assessment = insert(:assessment, %{course: course, config: config})
3528+
contest_question = insert(:programming_question, assessment: contest_assessment)
3529+
voting_assessment = insert(:assessment, %{course: course, config: config})
3530+
voting_question = insert(:voting_question, assessment: voting_assessment)
3531+
3532+
student_list = insert_list(3, :course_registration, %{course: course, role: :student})
3533+
3534+
submission_list =
3535+
Enum.map(
3536+
student_list,
3537+
fn student ->
3538+
insert(
3539+
:submission,
3540+
student: student,
3541+
assessment: contest_assessment,
3542+
status: "submitted"
3543+
)
3544+
end
3545+
)
3546+
3547+
# Two answers with same relative_score (tied for first)
3548+
insert(:answer, %{
3549+
answer: build(:programming_answer),
3550+
submission: Enum.at(submission_list, 0),
3551+
question: contest_question,
3552+
relative_score: 10.0
3553+
})
3554+
insert(:answer, %{
3555+
answer: build(:programming_answer),
3556+
submission: Enum.at(submission_list, 1),
3557+
question: contest_question,
3558+
relative_score: 10.0
3559+
})
3560+
insert(:answer, %{
3561+
answer: build(:programming_answer),
3562+
submission: Enum.at(submission_list, 2),
3563+
question: contest_question,
3564+
relative_score: 9.0
3565+
})
3566+
3567+
top_2 = Assessments.fetch_top_relative_score_answers(contest_question.id, 2)
3568+
3569+
assert length(top_2) == 2
3570+
assert Enum.count(top_2, fn ans -> ans.rank == 1 end) == 2
3571+
end
3572+
end
3573+
3574+
describe "fetch_top_popular_score_answers ranking" do
3575+
test "correctly ranks answers with RANK() OVER function" do
3576+
course = insert(:course)
3577+
config = insert(:assessment_config)
3578+
contest_assessment = insert(:assessment, %{course: course, config: config})
3579+
contest_question = insert(:programming_question, assessment: contest_assessment)
3580+
voting_assessment = insert(:assessment, %{course: course, config: config})
3581+
voting_question = insert(:voting_question, assessment: voting_assessment)
3582+
3583+
student_list = insert_list(5, :course_registration, %{course: course, role: :student})
3584+
3585+
submission_list =
3586+
Enum.map(
3587+
student_list,
3588+
fn student ->
3589+
insert(
3590+
:submission,
3591+
student: student,
3592+
assessment: contest_assessment,
3593+
status: "submitted"
3594+
)
3595+
end
3596+
)
3597+
3598+
ans_list =
3599+
Enum.map(
3600+
Enum.with_index(submission_list),
3601+
fn {submission, index} ->
3602+
insert(
3603+
:answer,
3604+
answer: build(:programming_answer),
3605+
submission: submission,
3606+
question: contest_question,
3607+
popular_score: 20 - index
3608+
)
3609+
end
3610+
)
3611+
3612+
top_3 = Assessments.fetch_top_popular_score_answers(contest_question.id, 3)
3613+
3614+
assert length(top_3) == 3
3615+
assert Enum.all?(top_3, fn ans -> ans.rank <= 3 end)
3616+
assert Enum.map(top_3, fn ans -> ans.popular_score end) == [20.0, 19.0, 18.0]
3617+
end
3618+
end
3619+
33713620
defp get_answer_relative_scores(answers) do
33723621
answers |> Enum.map(fn ans -> ans.relative_score end)
33733622
end

test/cadet_web/admin_controllers/admin_assessments_controller_test.exs

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,84 @@ defmodule CadetWeb.AdminAssessmentsControllerTest do
159159
end
160160
end
161161

162+
describe "POST /assessments/:assessmentid/calculateContestScore" do
163+
@tag authenticate: :admin
164+
test "successfully calculates contest score", %{conn: conn} do
165+
test_cr = conn.assigns.test_cr
166+
course = test_cr.course
167+
config = insert(:assessment_config, %{course: course})
168+
169+
contest_assessment = insert(:assessment, %{course: course, config: config})
170+
contest_students = insert_list(5, :course_registration, %{course: course, role: :student})
171+
contest_question = insert(:programming_question, %{assessment: contest_assessment})
172+
173+
contest_submissions =
174+
contest_students
175+
|> Enum.map(&insert(:submission, %{assessment: contest_assessment, student: &1}))
176+
177+
_contest_answers =
178+
contest_submissions
179+
|> Enum.map(
180+
&insert(:answer, %{
181+
question: contest_question,
182+
submission: &1,
183+
answer: build(:programming_answer)
184+
})
185+
)
186+
187+
voting_assessment = insert(:assessment, %{course: course, config: config})
188+
189+
voting_question = insert(:voting_question, %{
190+
assessment: voting_assessment,
191+
question: build(:voting_question_content, contest_number: contest_assessment.number)
192+
})
193+
194+
conn
195+
|> post(
196+
"/v2/courses/#{course.id}/admin/assessments/#{voting_assessment.id}/calculateContestScore"
197+
)
198+
|> response(200)
199+
end
200+
end
201+
202+
describe "POST /assessments/:assessmentid/dispatchContestXp" do
203+
@tag authenticate: :admin
204+
test "successfully dispatches xp to contest winners", %{conn: conn} do
205+
test_cr = conn.assigns.test_cr
206+
course = test_cr.course
207+
config = insert(:assessment_config, %{course: course})
208+
209+
contest_assessment = insert(:assessment, %{course: course, config: config})
210+
contest_students = insert_list(5, :course_registration, %{course: course, role: :student})
211+
contest_question = insert(:programming_question, %{assessment: contest_assessment})
212+
213+
contest_submissions =
214+
contest_students
215+
|> Enum.map(&insert(:submission, %{assessment: contest_assessment, student: &1}))
216+
217+
_contest_answers =
218+
contest_submissions
219+
|> Enum.map(
220+
&insert(:answer, %{
221+
question: contest_question,
222+
submission: &1,
223+
popular_score: 10.0,
224+
relative_score: 10.0,
225+
answer: build(:programming_answer)
226+
})
227+
)
228+
229+
voting_assessment = insert(:assessment, %{course: course, config: config})
230+
voting_question = insert(:voting_question, %{assessment: voting_assessment})
231+
232+
conn
233+
|> post(
234+
"/v2/courses/#{course.id}/admin/assessments/#{voting_assessment.id}/dispatchContestXp"
235+
)
236+
|> response(200)
237+
end
238+
end
239+
162240
describe "POST /, unauthenticated" do
163241
test "unauthorized", %{
164242
conn: conn,

0 commit comments

Comments
 (0)