Files
codeocean/spec/controllers/programming_groups_controller_spec.rb
kiragrammel 79ce069f68 Add CRUD operations for Programming Groups
* Correct sorting in table
* Modify page when nested in exercises
* Fix links between pages
* Link from statistics page to programming_groups/index
* Link from submission page to programming_groups/<id>
* Allow filtering for exercise ID on ProgrammingGroup#index
* Add search fields for internal and external user id on pg/index
2023-10-23 18:17:42 +02:00

346 lines
13 KiB
Ruby

# frozen_string_literal: true
require 'rails_helper'
RSpec.describe ProgrammingGroupsController do
render_views
let(:user) { create(:admin) }
let(:other_user) { create(:external_user) }
let(:exercise) { create(:math) }
let(:exercise_id) { exercise.id }
let(:programming_group) { create(:programming_group, exercise:, users: [user, other_user]) }
before { allow(controller).to receive(:current_user).and_return(user) }
describe 'POST #create' do
let(:user_to_params) { ->(users) { users.map(&:id_with_type).join(', ') } }
let(:perform_request) { proc { post :create, params: {exercise_id:, programming_group: pg_params} } }
context 'with a valid programming group' do
let(:pg_params) { {programming_partner_ids: user_to_params.call([other_user])} }
context 'when the request is performed' do
before { perform_request.call }
expect_assigns(exercise: :exercise, programming_group: ProgrammingGroup)
expect_redirect(ProgrammingGroup.last)
end
it 'creates the programming group' do
expect { perform_request.call }.to change(ProgrammingGroup, :count).by(1).and change(ProgrammingGroupMembership, :count).by(2)
end
it 'does not create a new event' do
expect { perform_request.call }.not_to change(Event, :count)
end
it 'updates the status of the initiating waiting user' do
pp_waiting_user = PairProgrammingWaitingUser.create(user:, exercise:, status: :waiting)
expect { perform_request.call && pp_waiting_user.reload }.to change(pp_waiting_user, :status).from('waiting').to('created_pg')
end
it 'updates the status of other waiting users' do
pp_waiting_user = PairProgrammingWaitingUser.create(user: other_user, exercise:, status: :waiting)
expect { perform_request.call && pp_waiting_user.reload }.to change(pp_waiting_user, :status).from('waiting').to('invited_to_pg')
end
it 'stores the programming group ID in the session' do
allow(controller.session).to receive(:[]=).and_call_original
perform_request.call
expect(controller.session).to have_received(:[]=).with(:pg_id, ProgrammingGroup.last.id)
end
end
context 'with an invalid programming group' do
let(:pg_params) { {} }
before { post :create, params: {exercise_id:, programming_group: pg_params} }
expect_assigns(exercise: :exercise, programming_group: ProgrammingGroup)
expect_http_status(:ok)
expect_template(:new)
it 'does not create a new programming group' do
expect { perform_request.call }.not_to change(ProgrammingGroup, :count)
end
it 'creates a new event' do
expect { perform_request.call }.to change(Event, :count).by(1)
end
it 'does not update the status of the initiating waiting user' do
pp_waiting_user = PairProgrammingWaitingUser.create(user:, exercise:, status: :waiting)
expect { perform_request.call && pp_waiting_user.reload }.not_to change(pp_waiting_user, :status)
end
it 'does not update the status of other waiting users' do
pp_waiting_user = PairProgrammingWaitingUser.create(user: other_user, exercise:, status: :waiting)
expect { perform_request.call && pp_waiting_user.reload }.not_to change(pp_waiting_user, :status)
end
end
context 'with a duplicated membership' do
let(:pg_params) { {programming_partner_ids: user_to_params.call(programming_group.users)} }
before { programming_group.save! }
context 'when the request is performed' do
before { perform_request.call }
expect_assigns(exercise: :exercise, programming_group: ProgrammingGroup)
expect_http_status(:ok)
expect_template(:new)
end
it 'does not create a new programming group' do
expect { perform_request.call }.not_to change(ProgrammingGroup, :count)
end
it 'creates a new event' do
expect { perform_request.call }.to change(Event, :count).by(1)
end
it 'does not update the status of the initiating waiting user' do
pp_waiting_user = PairProgrammingWaitingUser.create(user:, exercise:, status: :waiting)
expect { perform_request.call && pp_waiting_user.reload }.not_to change(pp_waiting_user, :status)
end
it 'does not update the status of other waiting users' do
pp_waiting_user = PairProgrammingWaitingUser.create(user: other_user, exercise:, status: :waiting)
expect { perform_request.call && pp_waiting_user.reload }.not_to change(pp_waiting_user, :status)
end
end
context 'with a user providing their own ID' do
let(:pg_params) { {programming_partner_ids: user_to_params.call([user, other_user])} }
context 'when the request is performed' do
before { perform_request.call }
expect_assigns(exercise: :exercise, programming_group: ProgrammingGroup)
expect_redirect(ProgrammingGroup.last)
end
it 'creates a new programming group' do
expect { perform_request.call }.to change(ProgrammingGroup, :count).by(1).and change(ProgrammingGroupMembership, :count).by(2)
end
it 'does not create a new event' do
expect { perform_request.call }.not_to change(Event, :count)
end
it 'updates the status of the initiating waiting user' do
pp_waiting_user = PairProgrammingWaitingUser.create(user:, exercise:, status: :waiting)
expect { perform_request.call && pp_waiting_user.reload }.to change(pp_waiting_user, :status).from('waiting').to('created_pg')
end
it 'updates the status of other waiting users' do
pp_waiting_user = PairProgrammingWaitingUser.create(user: other_user, exercise:, status: :waiting)
expect { perform_request.call && pp_waiting_user.reload }.to change(pp_waiting_user, :status).from('waiting').to('invited_to_pg')
end
it 'stores the programming group ID in the session' do
allow(controller.session).to receive(:[]=).and_call_original
perform_request.call
expect(controller.session).to have_received(:[]=).with(:pg_id, ProgrammingGroup.last.id)
end
end
context 'with invalid programming partner IDs' do
let(:pg_params) { {programming_partner_ids: 'test1234'} }
before { post :create, params: {exercise_id:, programming_group: pg_params} }
expect_assigns(exercise: :exercise, programming_group: ProgrammingGroup)
expect_http_status(:ok)
expect_template(:new)
it 'does not create a new programming group' do
expect { perform_request.call }.not_to change(ProgrammingGroup, :count)
end
it 'creates a new event' do
expect { perform_request.call }.to change(Event, :count).by(1)
end
end
context 'with too many users' do
let(:third_user) { create(:external_user) }
let(:pg_params) { {programming_partner_ids: user_to_params.call([other_user, third_user])} }
before { post :create, params: {exercise_id:, programming_group: pg_params} }
expect_assigns(exercise: :exercise, programming_group: ProgrammingGroup)
expect_http_status(:ok)
expect_template(:new)
it 'does not create a new programming group' do
expect { perform_request.call }.not_to change(ProgrammingGroup, :count)
end
it 'creates a new event' do
expect { perform_request.call }.to change(Event, :count).by(1)
end
end
end
describe 'DELETE #destroy' do
before { delete :destroy, params: {id: programming_group.id} }
expect_assigns(programming_group: ProgrammingGroup)
it 'destroys the programming group' do
programming_group = create(:programming_group)
expect { delete :destroy, params: {id: programming_group.id} }.to change(ProgrammingGroup, :count).by(-1)
end
it 'removes the programming group ID from the session' do
# Setup: Construct a programming group and set it as the current group in the session.
programming_group = create(:programming_group, users: [user, other_user])
allow(controller.session).to receive(:[]).and_call_original
allow(controller.session).to receive(:[]).with(:pg_id).and_return programming_group.id
# Test: Destroy the programming group and verify that it is no longer retained in the session.
allow(controller.session).to receive(:delete).and_call_original
delete :destroy, params: {id: programming_group.id}
expect(controller.session).to have_received(:delete).with(:pg_id)
end
expect_redirect(:programming_groups)
end
describe 'GET #edit' do
before { get :edit, params: {id: programming_group.id} }
expect_assigns(programming_group: ProgrammingGroup)
expect_http_status(:ok)
expect_template(:edit)
end
describe 'GET #index' do
before do
create_pair(:programming_group)
get :index
end
expect_assigns(programming_groups: ProgrammingGroup.all)
expect_http_status(:ok)
expect_template(:index)
end
describe 'GET #new' do
let(:perform_request) { proc { get :new, params: {exercise_id:} } }
context 'when the request is performed' do
before { perform_request.call }
expect_assigns(programming_group: ProgrammingGroup)
expect_http_status(:ok)
expect_template(:new)
end
it 'creates a new event' do
expect { perform_request.call }.to change(Event, :count).by(1)
end
context 'with an existing programming group' do
let(:programming_group) { create(:programming_group, exercise:, users: [user, other_user]) }
before { programming_group.save! }
context 'when the request is performed' do
before { perform_request.call }
expect_redirect { implement_exercise_path(exercise) }
it 'stores the programming group ID in the session' do
allow(controller.session).to receive(:[]=).and_call_original
perform_request.call
expect(controller.session).to have_received(:[]=).with(:pg_id, programming_group.id)
end
end
context 'when the user has already started the exercise' do
before { create(:submission, exercise:, user:) }
context 'when the request is performed' do
before { perform_request.call }
expect_redirect { implement_exercise_path(exercise) }
it 'does not store the programming group ID in the session' do
allow(controller.session).to receive(:[]=).and_call_original
perform_request.call
expect(controller.session).not_to have_received(:[]=).with(:pg_id)
end
end
end
end
end
describe 'GET #show' do
before { get :show, params: {id: programming_group.id} }
expect_assigns(programming_group: :programming_group)
expect_http_status(:ok)
expect_template(:show)
end
describe 'PUT #update' do
let(:perform_request) { proc { put :update, params: {programming_group: pg_params, id: programming_group.id} } }
before do
# In order to test a successful update, we need to remove a user from the programming group.
# Since otherwise the group size is fixed to exactly two members, we temporarily allow a larger group size.
allow_any_instance_of(ProgrammingGroup).to receive(:max_group_size).and_return(true)
# The programming group needs to be saved, otherwise we cannot attempt to update it.
programming_group.save!
end
context 'with a valid programming group' do
let(:programming_group) { create(:programming_group, exercise:, users: create_list(:external_user, 3)) }
let(:pg_params) { {programming_group_membership_ids: programming_group.programming_group_memberships.map(&:id)[0..1]} }
context 'when the request is performed' do
before { perform_request.call }
expect_assigns(programming_group: ProgrammingGroup)
expect_redirect(:programming_group)
end
it 'does not update the programming group' do
expect { perform_request.call }.not_to change(ProgrammingGroup, :count)
end
it 'removes the desired programming group membership' do
expect { perform_request.call }.to change(ProgrammingGroupMembership, :count).by(-1)
end
it 'does not update any existing programming group membership' do
expect { perform_request.call && programming_group.programming_group_memberships.first.reload }.not_to change(programming_group.programming_group_memberships.first, :updated_at)
end
end
context 'with an invalid programming group' do
let(:pg_params) { {programming_group_membership_ids: []} }
context 'when the request is performed' do
before { perform_request.call }
expect_assigns(programming_group: ProgrammingGroup)
expect_http_status(:ok)
expect_template(:edit)
end
it 'does not update the programming group' do
expect { perform_request.call }.not_to change(ProgrammingGroup, :count)
end
it 'does not update the programming group memberships' do
expect { perform_request.call }.not_to change(ProgrammingGroupMembership, :count)
end
end
end
end