diff --git a/test/cadet/assessments/assessments_test.exs b/test/cadet/assessments/assessments_test.exs index 3575768ff..7894ed5de 100644 --- a/test/cadet/assessments/assessments_test.exs +++ b/test/cadet/assessments/assessments_test.exs @@ -1700,6 +1700,534 @@ defmodule Cadet.AssessmentsTest do end end + # Tests assume each config has only 1 assessment + describe "get submission function" do + setup do + seed = Cadet.Test.Seeds.assessments() + + total_submissions = + Integer.to_string( + Enum.reduce(seed[:assessments], 0, fn {_, %{submissions: submissions}}, acc -> + length(submissions) + acc + end) + ) + + Map.put(seed, :total_submissions, total_submissions) + end + + test "filter by assessment title", %{ + course_regs: %{avenger1_cr: avenger}, + assessments: assessments, + total_submissions: total_submissions + } do + assessment = assessments["mission"][:assessment] + title = assessment.title + + {_, res} = + Assessments.submissions_by_grader_for_index(avenger, %{ + "title" => title, + "pageSize" => total_submissions + }) + + assessments_from_res = res[:data][:assessments] + + Enum.each(assessments_from_res, fn a -> + assert a.title == title + end) + end + + test "filter by submission status :attempting", %{ + course_regs: %{avenger1_cr: avenger}, + assessments: assessments, + total_submissions: total_submissions + } do + expected_length = + Enum.reduce(assessments, 0, fn {_, %{submissions: submissions}}, acc -> + Enum.count(submissions, fn s -> s.status == :attempting end) + acc + end) + + {_, res} = + Assessments.submissions_by_grader_for_index(avenger, %{ + "status" => "attempting", + "pageSize" => total_submissions + }) + + submissions_from_res = res[:data][:submissions] + + assert length(submissions_from_res) == expected_length + + Enum.each(submissions_from_res, fn s -> + assert s.status == :attempting + end) + end + + test "filter by submission status :attempted", %{ + course_regs: %{avenger1_cr: avenger}, + assessments: assessments, + total_submissions: total_submissions + } do + expected_length = + Enum.reduce(assessments, 0, fn {_, %{submissions: submissions}}, acc -> + Enum.count(submissions, fn s -> s.status == :attempted end) + acc + end) + + {_, res} = + Assessments.submissions_by_grader_for_index(avenger, %{ + "status" => "attempted", + "pageSize" => total_submissions + }) + + submissions_from_res = res[:data][:submissions] + + assert length(submissions_from_res) == expected_length + + Enum.each(submissions_from_res, fn s -> + assert s.status == :attempted + end) + end + + test "filter by submission status :submitted", %{ + course_regs: %{avenger1_cr: avenger}, + assessments: assessments, + total_submissions: total_submissions + } do + expected_length = + Enum.reduce(assessments, 0, fn {_, %{submissions: submissions}}, acc -> + Enum.count(submissions, fn s -> s.status == :submitted end) + acc + end) + + {_, res} = + Assessments.submissions_by_grader_for_index(avenger, %{ + "status" => "submitted", + "pageSize" => total_submissions + }) + + submissions_from_res = res[:data][:submissions] + + assert length(submissions_from_res) == expected_length + + Enum.each(submissions_from_res, fn s -> + assert s.status == :submitted + end) + end + + test "filter by submission not fully graded", %{ + course_regs: %{avenger1_cr: avenger, students: students}, + assessments: assessments, + total_submissions: total_submissions + } do + # All but one is fully graded + expected_length = length(Map.keys(assessments)) * (length(students) - 1) + + {_, res} = + Assessments.submissions_by_grader_for_index(avenger, %{ + "notFullyGraded" => "true", + "pageSize" => total_submissions + }) + + submissions_from_res = res[:data][:submissions] + + assert length(submissions_from_res) == expected_length + + Enum.each(submissions_from_res, fn s -> + assert s.question_count > s.graded_count + end) + end + + test "filter by group avenger", %{ + course_regs: %{avenger1_cr: avenger, group: group, students: students}, + assessments: assessments, + total_submissions: total_submissions + } do + # All but one is in the same group + expected_length = length(Map.keys(assessments)) * (length(students) - 1) + + {_, res} = + Assessments.submissions_by_grader_for_index(avenger, %{ + "group" => "true", + "pageSize" => total_submissions + }) + + submissions_from_res = res[:data][:submissions] + + assert length(submissions_from_res) == expected_length + + Enum.each(submissions_from_res, fn s -> + student = Enum.find(students, fn student -> student.id == s.student_id end) + assert student.group.id == group.id + end) + end + + test "filter by group avenger2", %{ + course_regs: %{avenger2_cr: avenger2, group2: group2, students: students}, + assessments: assessments, + total_submissions: total_submissions + } do + # One in the same group + expected_length = length(Map.keys(assessments)) + + {_, res} = + Assessments.submissions_by_grader_for_index(avenger2, %{ + "group" => "true", + "pageSize" => total_submissions + }) + + submissions_from_res = res[:data][:submissions] + + assert length(submissions_from_res) == expected_length + + Enum.each(submissions_from_res, fn s -> + student = Enum.find(students, fn student -> student.id == s.student_id end) + assert student.group.id == group2.id + end) + end + + # Chose avenger2 to ensure that the group name is not the same as the avenger's group + test "filter by group name group", %{ + course_regs: %{avenger2_cr: avenger2, group: group, students: students}, + assessments: assessments, + total_submissions: total_submissions + } do + # All but one is in group + expected_length = length(Map.keys(assessments)) * (length(students) - 1) + group_name = group.name + + {_, res} = + Assessments.submissions_by_grader_for_index(avenger2, %{ + "groupName" => group_name, + "pageSize" => total_submissions + }) + + submissions_from_res = res[:data][:submissions] + + assert length(submissions_from_res) == expected_length + + Enum.each(submissions_from_res, fn s -> + student = Enum.find(students, fn student -> student.id == s.student_id end) + assert student.group.id == group.id + end) + end + + # Chose avenger to ensure that the group name is not the same as the avenger's group + test "filter by group name group2", %{ + course_regs: %{avenger1_cr: avenger, group2: group2, students: students}, + assessments: assessments, + total_submissions: total_submissions + } do + # One in the group + expected_length = length(Map.keys(assessments)) + group_name = group2.name + + {_, res} = + Assessments.submissions_by_grader_for_index(avenger, %{ + "groupName" => group_name, + "pageSize" => total_submissions + }) + + submissions_from_res = res[:data][:submissions] + + assert length(submissions_from_res) == expected_length + + Enum.each(submissions_from_res, fn s -> + student = Enum.find(students, fn student -> student.id == s.student_id end) + assert student.group.id == group2.id + end) + end + + test "filter by student name", %{ + course_regs: %{avenger1_cr: avenger, students: students}, + assessments: assessments, + total_submissions: total_submissions + } do + expected_length = length(Map.keys(assessments)) + student = Enum.at(students, 0) + student_name = student.user.name + + {_, res} = + Assessments.submissions_by_grader_for_index(avenger, %{ + "name" => student_name, + "pageSize" => total_submissions + }) + + submissions_from_res = res[:data][:submissions] + + assert length(submissions_from_res) == expected_length + + Enum.each(submissions_from_res, fn s -> + assert s.student_id == student.id + end) + end + + test "filter by student name 2", %{ + course_regs: %{avenger1_cr: avenger, students: students}, + assessments: assessments, + total_submissions: total_submissions + } do + expected_length = length(Map.keys(assessments)) + student = Enum.at(students, 1) + student_name = student.user.name + + {_, res} = + Assessments.submissions_by_grader_for_index(avenger, %{ + "name" => student_name, + "pageSize" => total_submissions + }) + + submissions_from_res = res[:data][:submissions] + + assert length(submissions_from_res) == expected_length + + Enum.each(submissions_from_res, fn s -> + assert s.student_id == student.id + end) + end + + test "filter by student name 3", %{ + course_regs: %{avenger1_cr: avenger, students: students}, + assessments: assessments, + total_submissions: total_submissions + } do + expected_length = length(Map.keys(assessments)) + student = Enum.at(students, 2) + student_name = student.user.name + + {_, res} = + Assessments.submissions_by_grader_for_index(avenger, %{ + "name" => student_name, + "pageSize" => total_submissions + }) + + submissions_from_res = res[:data][:submissions] + + assert length(submissions_from_res) == expected_length + + Enum.each(submissions_from_res, fn s -> + assert s.student_id == student.id + end) + end + + test "filter by student username 1", %{ + course_regs: %{avenger1_cr: avenger, students: students}, + assessments: assessments, + total_submissions: total_submissions + } do + expected_length = length(Map.keys(assessments)) + student = Enum.at(students, 0) + student_username = student.user.username + + {_, res} = + Assessments.submissions_by_grader_for_index(avenger, %{ + "username" => student_username, + "pageSize" => total_submissions + }) + + submissions_from_res = res[:data][:submissions] + + assert length(submissions_from_res) == expected_length + + Enum.each(submissions_from_res, fn s -> + assert s.student_id == student.id + end) + end + + test "filter by student username 2", %{ + course_regs: %{avenger1_cr: avenger, students: students}, + assessments: assessments, + total_submissions: total_submissions + } do + expected_length = length(Map.keys(assessments)) + student = Enum.at(students, 1) + student_username = student.user.username + + {_, res} = + Assessments.submissions_by_grader_for_index(avenger, %{ + "username" => student_username, + "pageSize" => total_submissions + }) + + submissions_from_res = res[:data][:submissions] + + assert length(submissions_from_res) == expected_length + + Enum.each(submissions_from_res, fn s -> + assert s.student_id == student.id + end) + end + + test "filter by student username 3", %{ + course_regs: %{avenger1_cr: avenger, students: students}, + assessments: assessments, + total_submissions: total_submissions + } do + expected_length = length(Map.keys(assessments)) + student = Enum.at(students, 2) + student_username = student.user.username + + {_, res} = + Assessments.submissions_by_grader_for_index(avenger, %{ + "username" => student_username, + "pageSize" => total_submissions + }) + + submissions_from_res = res[:data][:submissions] + + assert length(submissions_from_res) == expected_length + + Enum.each(submissions_from_res, fn s -> + assert s.student_id == student.id + end) + end + + test "filter by assessment config 1", %{ + course_regs: %{avenger1_cr: avenger, students: students}, + assessments: assessments, + assessment_configs: assessment_configs, + total_submissions: total_submissions + } do + expected_length = 1 * length(students) + assessment_config = Enum.at(assessment_configs, 0) + assessment_type = assessment_config.type + + {_, res} = + Assessments.submissions_by_grader_for_index(avenger, %{ + "type" => assessment_type, + "pageSize" => total_submissions + }) + + assessments_from_res = res[:data][:assessments] + submissions_from_res = res[:data][:submissions] + assessment = Enum.at(assessments_from_res, 0) + assessment_id = assessment.id + + assert length(assessments_from_res) == 1 + assert length(submissions_from_res) == expected_length + + Enum.each(submissions_from_res, fn s -> + assert s.assessment_id == assessment_id + end) + end + + test "filter by assessment config 2", %{ + course_regs: %{avenger1_cr: avenger, students: students}, + assessments: assessments, + assessment_configs: assessment_configs, + total_submissions: total_submissions + } do + expected_length = 1 * length(students) + + assessment_config = Enum.at(assessment_configs, 1) + assessment_type = assessment_config.type + + {_, res} = + Assessments.submissions_by_grader_for_index(avenger, %{ + "type" => assessment_type, + "pageSize" => total_submissions + }) + + assessments_from_res = res[:data][:assessments] + submissions_from_res = res[:data][:submissions] + assessment = Enum.at(assessments_from_res, 0) + assessment_id = assessment.id + + assert length(assessments_from_res) == 1 + assert length(submissions_from_res) == expected_length + + Enum.each(submissions_from_res, fn s -> + assert s.assessment_id == assessment_id + end) + end + + test "filter by assessment config 3", %{ + course_regs: %{avenger1_cr: avenger, students: students}, + assessments: assessments, + assessment_configs: assessment_configs, + total_submissions: total_submissions + } do + expected_length = 1 * length(students) + + assessment_config = Enum.at(assessment_configs, 2) + assessment_type = assessment_config.type + + {_, res} = + Assessments.submissions_by_grader_for_index(avenger, %{ + "type" => assessment_type, + "pageSize" => total_submissions + }) + + assessments_from_res = res[:data][:assessments] + submissions_from_res = res[:data][:submissions] + assessment = Enum.at(assessments_from_res, 0) + assessment_id = assessment.id + + assert length(assessments_from_res) == 1 + assert length(submissions_from_res) == expected_length + + Enum.each(submissions_from_res, fn s -> + assert s.assessment_id == assessment_id + end) + end + + test "filter by assessment config manually graded", %{ + course_regs: %{avenger1_cr: avenger, students: students}, + assessments: assessments, + assessment_configs: assessment_configs, + total_submissions: total_submissions + } do + expected_length = + Enum.reduce(assessment_configs, 0, fn config, acc -> + if config.is_manually_graded, do: acc + 1, else: acc + end) * length(students) + + {_, res} = + Assessments.submissions_by_grader_for_index(avenger, %{ + "isManuallyGraded" => "true", + "pageSize" => total_submissions + }) + + submissions_from_res = res[:data][:submissions] + assessments_from_res = res[:data][:assessments] + assessment_configs_from_res = Enum.map(assessments_from_res, fn a -> a.config end) + + assert length(submissions_from_res) == expected_length + Enum.each(assessment_configs_from_res, fn config -> assert config.is_manually_graded end) + + # We know all assessments_from_res have correct config from previous check + Enum.each(submissions_from_res, fn s -> + assert Enum.find(assessments_from_res, fn a -> a.id == s.assessment_id end) != nil + end) + end + + test "filter by assessment config not manually graded", %{ + course_regs: %{avenger1_cr: avenger, students: students}, + assessments: assessments, + assessment_configs: assessment_configs, + total_submissions: total_submissions + } do + expected_length = + Enum.reduce(assessment_configs, 0, fn config, acc -> + if config.is_manually_graded, do: acc, else: acc + 1 + end) * length(students) + + {_, res} = + Assessments.submissions_by_grader_for_index(avenger, %{ + "isManuallyGraded" => "false", + "pageSize" => total_submissions + }) + + submissions_from_res = res[:data][:submissions] + assessments_from_res = res[:data][:assessments] + assessment_configs_from_res = Enum.map(assessments_from_res, fn a -> a.config end) + + assert length(submissions_from_res) == expected_length + Enum.each(assessment_configs_from_res, fn config -> assert !config.is_manually_graded end) + + # We know all assessments_from_res have correct config from previous check + Enum.each(submissions_from_res, fn s -> + assert Enum.find(assessments_from_res, fn a -> a.id == s.assessment_id end) != nil + end) + end + end + defp get_answer_relative_scores(answers) do answers |> Enum.map(fn ans -> ans.relative_score end) end diff --git a/test/support/seeds.ex b/test/support/seeds.ex index e88ec7f21..ce9f55205 100644 --- a/test/support/seeds.ex +++ b/test/support/seeds.ex @@ -48,16 +48,30 @@ defmodule Cadet.Test.Seeds do email: "avenger1@gmail.com" }) + avenger2 = + insert(:user, %{ + name: "avenger2", + latest_viewed_course: course1, + email: "avenger2@gmail.com" + }) + admin1 = insert(:user, %{name: "admin", latest_viewed_course: course1}) studenta1admin2 = insert(:user, %{name: "student a", latest_viewed_course: course1}) studentb1 = insert(:user, %{latest_viewed_course: course1}) studentc1 = insert(:user, %{latest_viewed_course: course1}) + student_attempted = insert(:user, %{latest_viewed_course: course1}) + student_submitted = insert(:user, %{latest_viewed_course: course1}) + student_graded = insert(:user, %{latest_viewed_course: course1}) + student_different_group = insert(:user, %{latest_viewed_course: course1}) + # CourseRegistration and Group avenger1_cr = insert(:course_registration, %{user: avenger1, course: course1, role: :staff}) + avenger2_cr = insert(:course_registration, %{user: avenger2, course: course1, role: :staff}) admin1_cr = insert(:course_registration, %{user: admin1, course: course1, role: :admin}) group = insert(:group, %{leader: avenger1_cr}) + group2 = insert(:group, %{leader: avenger2_cr}) student1a_cr = insert(:course_registration, %{ @@ -83,7 +97,58 @@ defmodule Cadet.Test.Seeds do group: group }) - students = [student1a_cr, student1b_cr, student1c_cr] + student_attempted_cr = + insert(:course_registration, %{ + user: student_attempted, + course: course1, + role: :student, + group: group + }) + + student_submitted_cr = + insert(:course_registration, %{ + user: student_submitted, + course: course1, + role: :student, + group: group + }) + + student_graded_cr = + insert(:course_registration, %{ + user: student_graded, + course: course1, + role: :student, + group: group + }) + + student_different_group_cr = + insert(:course_registration, %{ + user: student_different_group, + course: course1, + role: :student, + group: group2 + }) + + students = [ + student1a_cr, + student1b_cr, + student1c_cr, + student_attempted_cr, + student_submitted_cr, + student_graded_cr, + student_different_group_cr + ] + + # {student_cr, submission_status, is_graded, avenger} + students_with_assessment_info = [ + {student1a_cr, :attempting, false, avenger1_cr}, + {student1b_cr, :attempting, false, avenger1_cr}, + {student1c_cr, :attempting, false, avenger1_cr}, + {student_attempted_cr, :attempted, false, avenger1_cr}, + {student_submitted_cr, :submitted, false, avenger1_cr}, + {student_graded_cr, :submitted, true, avenger1_cr}, + {student_different_group_cr, :attempting, false, avenger2_cr} + ] _admin2cr = insert(:course_registration, %{user: studenta1admin2, course: course2, role: :admin}) @@ -113,7 +178,11 @@ defmodule Cadet.Test.Seeds do |> Enum.reduce( %{}, fn config, acc -> - Map.put(acc, config.type, insert_assessments(config, students, course1)) + Map.put( + acc, + config.type, + insert_assessments(config, students_with_assessment_info, course1) + ) end ) @@ -124,7 +193,9 @@ defmodule Cadet.Test.Seeds do }, course_regs: %{ avenger1_cr: avenger1_cr, + avenger2_cr: avenger2_cr, group: group, + group2: group2, students: students, admin1_cr: admin1_cr }, @@ -177,44 +248,56 @@ defmodule Cadet.Test.Seeds do }) end) - submissions = + submissions_with_grader = students - |> Enum.take(2) - |> Enum.map(&insert(:submission, %{assessment: assessment, student: &1})) + |> Enum.map(fn {student, submission_status, is_graded, avenger} -> + grader = if is_graded, do: avenger, else: nil + + {grader, + insert(:submission, %{ + assessment: assessment, + student: student, + status: submission_status + })} + end) + submissions = Enum.map(submissions_with_grader, fn {_, submission} -> submission end) # Programming Answers programming_answers = - Enum.map(submissions, fn submission -> + Enum.map(submissions_with_grader, fn {grader, submission} -> Enum.map(programming_questions, fn question -> insert(:answer, %{ xp: 800, question: question, submission: submission, - answer: build(:programming_answer) + answer: build(:programming_answer), + grader: grader }) end) end) mcq_answers = - Enum.map(submissions, fn submission -> + Enum.map(submissions_with_grader, fn {grader, submission} -> Enum.map(mcq_questions, fn question -> insert(:answer, %{ xp: 500, question: question, submission: submission, - answer: build(:mcq_answer) + answer: build(:mcq_answer), + grader: grader }) end) end) voting_answers = - Enum.map(submissions, fn submission -> + Enum.map(submissions_with_grader, fn {grader, submission} -> Enum.map(voting_questions, fn question -> insert(:answer, %{ xp: 100, question: question, submission: submission, - answer: build(:voting_answer) + answer: build(:voting_answer), + grader: grader }) end) end)