Skip to content

Commit 3973e40

Browse files
test: add unit tests for PR#1238
1 parent a9a2288 commit 3973e40

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
@@ -3366,6 +3366,255 @@ defmodule Cadet.AssessmentsTest do
33663366
end
33673367
end
33683368

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