From 91d92ed36b035bc9b506f71836fdbb9d4b220388 Mon Sep 17 00:00:00 2001 From: Naragod Date: Thu, 4 Dec 2025 11:11:50 -0500 Subject: [PATCH 1/6] ISSUE-7743: Allow inactive students to join a group --- app/controllers/groups_controller.rb | 33 +++- app/models/grouping.rb | 4 +- spec/controllers/groups_controller_spec.rb | 166 +++++++++++++++++- .../files/groups/form_group_size_exceeded.csv | 1 + .../files/groups/form_multiple_oversized.csv | 5 + .../groups/form_with_inactive_students.csv | 1 + spec/models/grouping_spec.rb | 15 +- 7 files changed, 211 insertions(+), 14 deletions(-) create mode 100644 spec/fixtures/files/groups/form_group_size_exceeded.csv create mode 100644 spec/fixtures/files/groups/form_multiple_oversized.csv create mode 100644 spec/fixtures/files/groups/form_with_inactive_students.csv diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index 76b3e2ae35..30d156dbe3 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -306,9 +306,16 @@ def upload group_rows << row.compact_blank end + if result[:invalid_lines].empty? - @current_job = CreateGroupsJob.perform_later assignment, group_rows - session[:job_id] = @current_job.job_id + group_size_error = validate_group_sizes(group_rows, assignment) + + if group_size_error.nil? + @current_job = CreateGroupsJob.perform_later assignment, group_rows + session[:job_id] = @current_job.job_id + else + flash_message(:error, group_size_error) + end else flash_message(:error, result[:invalid_lines]) end @@ -699,9 +706,6 @@ def add_member(student, grouping, assignment) end @bad_user_names = [] - if student.hidden - raise I18n.t('groups.invite_member.errors.not_found', user_name: student.user_name) - end if student.has_accepted_grouping_for?(assignment.id) raise I18n.t('groups.invite_member.errors.already_grouped', user_name: student.user_name) end @@ -764,4 +768,23 @@ def format_ocr_suggestions(ocr_suggestions) def record @record ||= Grouping.find_by(id: request.path_parameters[:id]) if request.path_parameters[:id] end + + # Validates that all groups in group_rows do not exceed the assignment's maximum group size. + # Returns nil if all groups are valid, or an error message string if any groups are oversized. + def validate_group_sizes(group_rows, assignment) + oversized_groups = group_rows.select { |row| row.length - 1 > assignment.group_max } + return if oversized_groups.empty? + + max_display = 3 + error_messages = oversized_groups.take(max_display).map do |row| + "#{row[0]} (#{row.length - 1} students, max: #{assignment.group_max})" + end + + if oversized_groups.length > max_display + "#{oversized_groups.length} groups exceed the maximum group size. " \ + "First #{max_display} Groups -> #{error_messages.join(', ')}" + else + "The following groups exceed the maximum group size: #{error_messages.join(', ')}" + end + end end diff --git a/app/models/grouping.rb b/app/models/grouping.rb index 5040f41451..77e072a00c 100644 --- a/app/models/grouping.rb +++ b/app/models/grouping.rb @@ -288,7 +288,7 @@ def invite(members, all_errors = [] members.each do |m| m = m.strip - user = course.students.joins(:user).where(hidden: false).find_by('users.user_name': m) + user = course.students.joins(:user).find_by('users.user_name': m) begin if user.nil? raise I18n.t('groups.invite_member.errors.not_found', user_name: m) @@ -305,7 +305,7 @@ def invite(members, # Add a new member to base def add_member(role, set_membership_status = StudentMembership::STATUSES[:accepted]) - if role.has_accepted_grouping_for?(self.assessment_id) || role.hidden + if role.has_accepted_grouping_for?(self.assessment_id) nil else member = StudentMembership.new(role: role, membership_status: diff --git a/spec/controllers/groups_controller_spec.rb b/spec/controllers/groups_controller_spec.rb index 3f47e9eee5..2eec46d6fa 100644 --- a/spec/controllers/groups_controller_spec.rb +++ b/spec/controllers/groups_controller_spec.rb @@ -532,7 +532,8 @@ # Setup for Git Repository allow(Settings.repository).to receive(:type).and_return('git') - @assignment = create(:assignment) + # Create assignment with group_max of 3 to accommodate test files + @assignment = create(:assignment, assignment_properties_attributes: { group_max: 3 }) # Create students corresponding to the file_good @student_user_names = %w[c8shosta c5bennet] @@ -570,6 +571,169 @@ expect(flash[:error]).not_to be_blank expect(response).to redirect_to(action: 'index') end + + context 'when validating group sizes' do + context 'with assignment.group_max = 2' do + before do + @assignment.update!(assignment_properties_attributes: { group_max: 2 }) + # Create students for the tests + %w[student1 student2 student3 student4 student5].each do |name| + create(:student, user: create(:end_user, user_name: name)) + end + end + + it 'rejects groups that exceed the maximum group size' do + expect do + post_as instructor, :upload, params: { + course_id: course.id, + assignment_id: @assignment.id, + upload_file: fixture_file_upload('groups/form_group_size_exceeded.csv', 'text/csv') + } + end.not_to have_enqueued_job(CreateGroupsJob) + + expect(response).to have_http_status(:found) + expect(flash[:error].first).to include('exceed the maximum group size') + expect(flash[:error].first).to include('group1 (3 students, max: 2)') + expect(response).to redirect_to(action: 'index') + end + + it 'accepts groups that are within the maximum group size' do + # Students c8shosta and c5bennet are already created in the outer before block + expect do + post_as instructor, :upload, params: { + course_id: course.id, + assignment_id: @assignment.id, + upload_file: fixture_file_upload('groups/form_good.csv', 'text/csv') + } + end.to have_enqueued_job(CreateGroupsJob) + + expect(response).to have_http_status(:found) + expect(flash[:error]).to be_blank + expect(response).to redirect_to(action: 'index') + end + end + + context 'with multiple oversized groups' do + before do + @assignment.update!(assignment_properties_attributes: { group_max: 2 }) + # Create students for multiple groups + (1..14).each do |i| + create(:student, user: create(:end_user, user_name: "student#{i}")) + end + end + + it 'shows only the first 3 oversized groups when more than 3 exist' do + expect do + post_as instructor, :upload, params: { + course_id: course.id, + assignment_id: @assignment.id, + upload_file: fixture_file_upload('groups/form_multiple_oversized.csv', 'text/csv') + } + end.not_to have_enqueued_job(CreateGroupsJob) + + expect(response).to have_http_status(:found) + expect(flash[:error].first).to include('4 groups exceed the maximum group size') + expect(flash[:error].first).to include('First 3 Groups') + expect(flash[:error].first).to include('group1 (3 students, max: 2)') + expect(flash[:error].first).to include('group3 (3 students, max: 2)') + expect(flash[:error].first).to include('group4 (3 students, max: 2)') + expect(flash[:error].first).not_to include('group5') + expect(response).to redirect_to(action: 'index') + end + end + + context 'with assignment.group_max = 1' do + before do + @assignment.update!(assignment_properties_attributes: { group_max: 1 }) + create(:student, user: create(:end_user, user_name: 'solo_student')) + end + + it 'accepts single-member groups' do + csv_content = "group1,solo_student\n" + file = Tempfile.new(['test_upload', '.csv']) + file.write(csv_content) + file.rewind + + expect do + post_as instructor, :upload, params: { course_id: course.id, + assignment_id: @assignment.id, + upload_file: fixture_file_upload(file.path, 'text/csv') } + end.to have_enqueued_job(CreateGroupsJob) + + file.close + file.unlink + + expect(flash[:error]).to be_blank + end + + it 'rejects multi-member groups' do + %w[student1 student2].each do |name| + create(:student, user: create(:end_user, user_name: name)) + end + + csv_content = "group1,student1,student2\n" + file = Tempfile.new(['test_upload', '.csv']) + file.write(csv_content) + file.rewind + + expect do + post_as instructor, :upload, params: { course_id: course.id, + assignment_id: @assignment.id, + upload_file: fixture_file_upload(file.path, 'text/csv') } + end.not_to have_enqueued_job(CreateGroupsJob) + + file.close + file.unlink + + expect(flash[:error].first).to include('group1 (2 students, max: 1)') + end + end + end + + context 'when uploading groups with inactive students' do + before do + @assignment.update!(assignment_properties_attributes: { group_max: 2 }) + # Create an active student + @active_student = create(:student, user: create(:end_user, user_name: 'active_student')) + # Create an inactive student (hidden: true) + @inactive_student = create(:student, hidden: true, user: create(:end_user, user_name: 'inactive_student')) + end + + it 'allows importing groups with inactive students (ISSUE-7743)' do + expect do + post_as instructor, :upload, params: { + course_id: course.id, + assignment_id: @assignment.id, + upload_file: fixture_file_upload('groups/form_with_inactive_students.csv', 'text/csv') + } + end.to have_enqueued_job(CreateGroupsJob) + + expect(response).to have_http_status(:found) + expect(flash[:error]).to be_blank + expect(response).to redirect_to(action: 'index') + end + + it 'still validates group size with inactive students' do + # Create additional students to exceed group_max + create(:student, user: create(:end_user, user_name: 'student3')) + + csv_content = "group1,active_student,inactive_student,student3\n" + file = Tempfile.new(['test_upload', '.csv']) + file.write(csv_content) + file.rewind + + expect do + post_as instructor, :upload, params: { course_id: course.id, + assignment_id: @assignment.id, + upload_file: fixture_file_upload(file.path, 'text/csv') } + end.not_to have_enqueued_job(CreateGroupsJob) + + file.close + file.unlink + + expect(flash[:error].first).to include('group1 (3 students, max: 2)') + end + end end describe '#create_groups_when_students_work_alone' do diff --git a/spec/fixtures/files/groups/form_group_size_exceeded.csv b/spec/fixtures/files/groups/form_group_size_exceeded.csv new file mode 100644 index 0000000000..9c3dec65d4 --- /dev/null +++ b/spec/fixtures/files/groups/form_group_size_exceeded.csv @@ -0,0 +1 @@ +group1,student1,student2,student3 diff --git a/spec/fixtures/files/groups/form_multiple_oversized.csv b/spec/fixtures/files/groups/form_multiple_oversized.csv new file mode 100644 index 0000000000..22af17ea03 --- /dev/null +++ b/spec/fixtures/files/groups/form_multiple_oversized.csv @@ -0,0 +1,5 @@ +group1,student1,student2,student3 +group2,student4,student5 +group3,student6,student7,student8 +group4,student9,student10,student11 +group5,student12,student13,student14 diff --git a/spec/fixtures/files/groups/form_with_inactive_students.csv b/spec/fixtures/files/groups/form_with_inactive_students.csv new file mode 100644 index 0000000000..6d223a0760 --- /dev/null +++ b/spec/fixtures/files/groups/form_with_inactive_students.csv @@ -0,0 +1 @@ +group1,active_student,inactive_student diff --git a/spec/models/grouping_spec.rb b/spec/models/grouping_spec.rb index c186548234..21bf9e4e6c 100644 --- a/spec/models/grouping_spec.rb +++ b/spec/models/grouping_spec.rb @@ -45,14 +45,17 @@ context 'hidden students' do let(:hidden) { create(:student, hidden: true) } - it 'cannot be invited' do - grouping.invite(hidden.user.user_name) - expect(grouping.memberships.count).to eq(0) + it 'can be invited (ISSUE-7743)' do + grouping.invite(hidden.user.user_name, StudentMembership::STATUSES[:accepted], invoked_by_instructor: true) + expect(grouping.memberships.count).to eq(1) + expect(grouping.memberships.first.role).to eq(hidden) end - it 'cannot be added' do - grouping.add_member(hidden) - expect(grouping.memberships.count).to eq(0) + it 'can be added (ISSUE-7743)' do + result = grouping.add_member(hidden) + expect(result).not_to be_nil + expect(grouping.memberships.count).to eq(1) + expect(grouping.memberships.first.role).to eq(hidden) end end From 7d489d128513d190431e0432f7f7033acb7c1e2c Mon Sep 17 00:00:00 2001 From: Naragod Date: Thu, 4 Dec 2025 11:14:39 -0500 Subject: [PATCH 2/6] ISSUE-7743: Update Changelog --- Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog.md b/Changelog.md index aa3adc9881..7d8f2c6c65 100644 --- a/Changelog.md +++ b/Changelog.md @@ -30,6 +30,7 @@ - Enable zip downloads of test results (#7733) - Create rake task to remove orphaned end users (#7741) - Enable scanned assignments the ability to add inactive students (#7737) +- Allow inactive students to join groups (#7757) ### 🐛 Bug fixes - Fix name column search in graders table (#7693) From 5f44794b61740f3c36b521a182cc1368863e955d Mon Sep 17 00:00:00 2001 From: Naragod Date: Thu, 4 Dec 2025 11:42:05 -0500 Subject: [PATCH 3/6] ISSUE-7743: Clean up code --- spec/models/grouping_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/models/grouping_spec.rb b/spec/models/grouping_spec.rb index 21bf9e4e6c..34a94ee7fd 100644 --- a/spec/models/grouping_spec.rb +++ b/spec/models/grouping_spec.rb @@ -45,13 +45,13 @@ context 'hidden students' do let(:hidden) { create(:student, hidden: true) } - it 'can be invited (ISSUE-7743)' do + it 'can be invited' do grouping.invite(hidden.user.user_name, StudentMembership::STATUSES[:accepted], invoked_by_instructor: true) expect(grouping.memberships.count).to eq(1) expect(grouping.memberships.first.role).to eq(hidden) end - it 'can be added (ISSUE-7743)' do + it 'can be added' do result = grouping.add_member(hidden) expect(result).not_to be_nil expect(grouping.memberships.count).to eq(1) From c58be840b238a525597c7db6fdd755015bfda325 Mon Sep 17 00:00:00 2001 From: Naragod Date: Mon, 22 Dec 2025 16:27:30 -0500 Subject: [PATCH 4/6] ISSUE-7743: Allow instructors, NOT students to invite members to groups --- app/controllers/groups_controller.rb | 4 ++-- app/models/grouping.rb | 2 ++ config/locales/views/groups/en.yml | 1 + spec/models/grouping_spec.rb | 9 ++++++++- 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index 30d156dbe3..6e5ab1d0d1 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -496,8 +496,8 @@ def invite_member if errors.blank? to_invite.each do |i| i = i.strip - invited_user = current_course.students.joins(:user).where(hidden: false).find_by('users.user_name': i) - if invited_user.receives_invite_emails? + invited_user = current_course.students.joins(:user).find_by('users.user_name': i) + if invited_user&.receives_invite_emails? NotificationMailer.with(inviter: current_role, invited: invited_user, grouping: @grouping).grouping_invite_email.deliver_later diff --git a/app/models/grouping.rb b/app/models/grouping.rb index 77e072a00c..b907b23c53 100644 --- a/app/models/grouping.rb +++ b/app/models/grouping.rb @@ -329,6 +329,8 @@ def add_member(role, set_membership_status = StudentMembership::STATUSES[:accept def can_invite?(role) if self.inviter == role raise I18n.t('groups.invite_member.errors.inviting_self') + elsif role.hidden + raise I18n.t('groups.invite_member.errors.inactive_student', user_name: role.user_name) elsif !extension.nil? raise I18n.t('groups.invite_member.errors.extension_exists') elsif self.student_membership_number >= self.assignment.group_max diff --git a/config/locales/views/groups/en.yml b/config/locales/views/groups/en.yml index 1dfb51543a..f307e9eb0c 100644 --- a/config/locales/views/groups/en.yml +++ b/config/locales/views/groups/en.yml @@ -42,6 +42,7 @@ en: extension_exists: You cannot modify a group with an extension to the due date. Please contact your instructor if you wish to make changes. group_max_reached: 'Could not invite %{user_name}: group maximum has been reached.' inactive_grader: Cannot assign inactive grader(s) %{user_names}. + inactive_student: 'Could not invite %{user_name}: this student is inactive.' inviting_self: You cannot invite yourself to your own group. need_to_create_group: You must create a group before you can invite members. not_found: No student '%{user_name}' could be found. diff --git a/spec/models/grouping_spec.rb b/spec/models/grouping_spec.rb index 34a94ee7fd..feddf8f5ad 100644 --- a/spec/models/grouping_spec.rb +++ b/spec/models/grouping_spec.rb @@ -45,12 +45,19 @@ context 'hidden students' do let(:hidden) { create(:student, hidden: true) } - it 'can be invited' do + it 'can be invited by instructors' do grouping.invite(hidden.user.user_name, StudentMembership::STATUSES[:accepted], invoked_by_instructor: true) expect(grouping.memberships.count).to eq(1) expect(grouping.memberships.first.role).to eq(hidden) end + it 'cannot be invited by students' do + errors = grouping.invite(hidden.user.user_name) + expect(errors).not_to be_empty + expect(errors.first).to include('inactive') + expect(grouping.memberships.count).to eq(0) + end + it 'can be added' do result = grouping.add_member(hidden) expect(result).not_to be_nil From 0d95e1f7feaeefda64913468c352e7af30a1724d Mon Sep 17 00:00:00 2001 From: Naragod Date: Mon, 22 Dec 2025 16:37:28 -0500 Subject: [PATCH 5/6] ISSUE-7743: Remove group size validation --- app/controllers/groups_controller.rb | 29 +--- spec/controllers/groups_controller_spec.rb | 160 ++++----------------- 2 files changed, 31 insertions(+), 158 deletions(-) diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index 6e5ab1d0d1..78ce50cb87 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -308,14 +308,8 @@ def upload end if result[:invalid_lines].empty? - group_size_error = validate_group_sizes(group_rows, assignment) - - if group_size_error.nil? - @current_job = CreateGroupsJob.perform_later assignment, group_rows - session[:job_id] = @current_job.job_id - else - flash_message(:error, group_size_error) - end + @current_job = CreateGroupsJob.perform_later assignment, group_rows + session[:job_id] = @current_job.job_id else flash_message(:error, result[:invalid_lines]) end @@ -768,23 +762,4 @@ def format_ocr_suggestions(ocr_suggestions) def record @record ||= Grouping.find_by(id: request.path_parameters[:id]) if request.path_parameters[:id] end - - # Validates that all groups in group_rows do not exceed the assignment's maximum group size. - # Returns nil if all groups are valid, or an error message string if any groups are oversized. - def validate_group_sizes(group_rows, assignment) - oversized_groups = group_rows.select { |row| row.length - 1 > assignment.group_max } - return if oversized_groups.empty? - - max_display = 3 - error_messages = oversized_groups.take(max_display).map do |row| - "#{row[0]} (#{row.length - 1} students, max: #{assignment.group_max})" - end - - if oversized_groups.length > max_display - "#{oversized_groups.length} groups exceed the maximum group size. " \ - "First #{max_display} Groups -> #{error_messages.join(', ')}" - else - "The following groups exceed the maximum group size: #{error_messages.join(', ')}" - end - end end diff --git a/spec/controllers/groups_controller_spec.rb b/spec/controllers/groups_controller_spec.rb index 2eec46d6fa..ce27a90f89 100644 --- a/spec/controllers/groups_controller_spec.rb +++ b/spec/controllers/groups_controller_spec.rb @@ -572,122 +572,41 @@ expect(response).to redirect_to(action: 'index') end - context 'when validating group sizes' do - context 'with assignment.group_max = 2' do - before do - @assignment.update!(assignment_properties_attributes: { group_max: 2 }) - # Create students for the tests - %w[student1 student2 student3 student4 student5].each do |name| - create(:student, user: create(:end_user, user_name: name)) - end - end - - it 'rejects groups that exceed the maximum group size' do - expect do - post_as instructor, :upload, params: { - course_id: course.id, - assignment_id: @assignment.id, - upload_file: fixture_file_upload('groups/form_group_size_exceeded.csv', 'text/csv') - } - end.not_to have_enqueued_job(CreateGroupsJob) - - expect(response).to have_http_status(:found) - expect(flash[:error].first).to include('exceed the maximum group size') - expect(flash[:error].first).to include('group1 (3 students, max: 2)') - expect(response).to redirect_to(action: 'index') - end - - it 'accepts groups that are within the maximum group size' do - # Students c8shosta and c5bennet are already created in the outer before block - expect do - post_as instructor, :upload, params: { - course_id: course.id, - assignment_id: @assignment.id, - upload_file: fixture_file_upload('groups/form_good.csv', 'text/csv') - } - end.to have_enqueued_job(CreateGroupsJob) - - expect(response).to have_http_status(:found) - expect(flash[:error]).to be_blank - expect(response).to redirect_to(action: 'index') - end - end - - context 'with multiple oversized groups' do - before do - @assignment.update!(assignment_properties_attributes: { group_max: 2 }) - # Create students for multiple groups - (1..14).each do |i| - create(:student, user: create(:end_user, user_name: "student#{i}")) - end - end - - it 'shows only the first 3 oversized groups when more than 3 exist' do - expect do - post_as instructor, :upload, params: { - course_id: course.id, - assignment_id: @assignment.id, - upload_file: fixture_file_upload('groups/form_multiple_oversized.csv', 'text/csv') - } - end.not_to have_enqueued_job(CreateGroupsJob) - - expect(response).to have_http_status(:found) - expect(flash[:error].first).to include('4 groups exceed the maximum group size') - expect(flash[:error].first).to include('First 3 Groups') - expect(flash[:error].first).to include('group1 (3 students, max: 2)') - expect(flash[:error].first).to include('group3 (3 students, max: 2)') - expect(flash[:error].first).to include('group4 (3 students, max: 2)') - expect(flash[:error].first).not_to include('group5') - expect(response).to redirect_to(action: 'index') - end - end - - context 'with assignment.group_max = 1' do - before do - @assignment.update!(assignment_properties_attributes: { group_max: 1 }) - create(:student, user: create(:end_user, user_name: 'solo_student')) - end - - it 'accepts single-member groups' do - csv_content = "group1,solo_student\n" - file = Tempfile.new(['test_upload', '.csv']) - file.write(csv_content) - file.rewind - - expect do - post_as instructor, :upload, params: { course_id: course.id, - assignment_id: @assignment.id, - upload_file: fixture_file_upload(file.path, 'text/csv') } - end.to have_enqueued_job(CreateGroupsJob) - - file.close - file.unlink + it 'accepts groups within the maximum group size' do + @assignment.update!(assignment_properties_attributes: { group_max: 2 }) + # Students c8shosta and c5bennet are already created in the outer before block + expect do + post_as instructor, :upload, params: { + course_id: course.id, + assignment_id: @assignment.id, + upload_file: fixture_file_upload('groups/form_good.csv', 'text/csv') + } + end.to have_enqueued_job(CreateGroupsJob) - expect(flash[:error]).to be_blank - end + expect(response).to have_http_status(:found) + expect(flash[:error]).to be_blank + expect(response).to redirect_to(action: 'index') + end - it 'rejects multi-member groups' do - %w[student1 student2].each do |name| - create(:student, user: create(:end_user, user_name: name)) - end + it 'accepts single-member groups' do + @assignment.update!(assignment_properties_attributes: { group_max: 1 }) + create(:student, user: create(:end_user, user_name: 'solo_student')) - csv_content = "group1,student1,student2\n" - file = Tempfile.new(['test_upload', '.csv']) - file.write(csv_content) - file.rewind + csv_content = "group1,solo_student\n" + file = Tempfile.new(['test_upload', '.csv']) + file.write(csv_content) + file.rewind - expect do - post_as instructor, :upload, params: { course_id: course.id, - assignment_id: @assignment.id, - upload_file: fixture_file_upload(file.path, 'text/csv') } - end.not_to have_enqueued_job(CreateGroupsJob) + expect do + post_as instructor, :upload, params: { course_id: course.id, + assignment_id: @assignment.id, + upload_file: fixture_file_upload(file.path, 'text/csv') } + end.to have_enqueued_job(CreateGroupsJob) - file.close - file.unlink + file.close + file.unlink - expect(flash[:error].first).to include('group1 (2 students, max: 1)') - end - end + expect(flash[:error]).to be_blank end context 'when uploading groups with inactive students' do @@ -712,27 +631,6 @@ expect(flash[:error]).to be_blank expect(response).to redirect_to(action: 'index') end - - it 'still validates group size with inactive students' do - # Create additional students to exceed group_max - create(:student, user: create(:end_user, user_name: 'student3')) - - csv_content = "group1,active_student,inactive_student,student3\n" - file = Tempfile.new(['test_upload', '.csv']) - file.write(csv_content) - file.rewind - - expect do - post_as instructor, :upload, params: { course_id: course.id, - assignment_id: @assignment.id, - upload_file: fixture_file_upload(file.path, 'text/csv') } - end.not_to have_enqueued_job(CreateGroupsJob) - - file.close - file.unlink - - expect(flash[:error].first).to include('group1 (3 students, max: 2)') - end end end From 262bd35d7fb463432fa32fd1b267d034c6e4eade Mon Sep 17 00:00:00 2001 From: Naragod Date: Mon, 22 Dec 2025 19:50:48 -0500 Subject: [PATCH 6/6] ISSUE-7743: Update changelog --- Changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 7d8f2c6c65..5208ffe8cd 100644 --- a/Changelog.md +++ b/Changelog.md @@ -7,6 +7,7 @@ ### ✨ New features and improvements - Enable test results downloads through the API (#7754) - Provide suggestions for partial student matching scans (#7760) +- Allow inactive students to join groups (#7757) ### 🐛 Bug fixes @@ -30,7 +31,6 @@ - Enable zip downloads of test results (#7733) - Create rake task to remove orphaned end users (#7741) - Enable scanned assignments the ability to add inactive students (#7737) -- Allow inactive students to join groups (#7757) ### 🐛 Bug fixes - Fix name column search in graders table (#7693)