CSC/ECE 517 Spring 2022 - E2208: Testing for submission records controller, profile controller

From Expertiza_Wiki
Jump to navigation Jump to search

About Expertiza

Expertiza is an open source Ruby on Rails framework which can be used by instructors and students. It can be used for submissions and for peer reviews by students. Students can form teams and work with other people on the assignments. It can be used to create assignments and topics by the instructor. Students can form teams to work on various projects and assignments.

Project Description

This Expertiza OSS project E2208 is adding unit tests for submission_records_controller.rb, profile_controller.rb

Files Involved

submission_records_controller.rb, submission_records_controller_spec.rb, profile_controller.rb, profile_controller_spec.rb

Running Tests

Run the redis server and then use the commands below to run test cases for each of the controllers:

 rspec ./spec/controllers/submission_records_controller_spec.rb 
 rspec ./spec/controllers/profile_controller_spec.rb 

Test Plan

We started by setting up the Expertiza development environment and running the limited test cases already available for profile_controller. The test only covered one of the methods (action_allowed method) in the controller. We had to write tests to cover the remaining methods. For the submission_records_controller, we had to write every test from scratch as there was no test available for the controller. We wrote test cases to cover all the required methods under submission_records_controller and profile_controller. We created unit test cases with the help of factory objects and stub objects.

Submission Records Controller Methods

The code of the controller can be found here. The methods are:

  • action_allowed?
  • index

Profile Controller Methods

The code of the controller can be found here. The methods are:

  • action_allowed?
  • edit
  • update
  • user_params (But this is a private method. And private methods are not tested directly. They are considered an implementation detail.)

Using RSpec, the test cases cover various scenarios and tests each scenario in depth. Eg, for access control using the action_allowed? method, we check the access for all roles for a user comprehensively to ensure that only people with permissions can view the page.

Test Frame for submission records controller

1. index - This action fetches all the submission records of a particular team id coming in the request.

Code snippet:

describe '#index' do
    it 'call index method' do
      params = { team_id: 27158 }
      allow(AssignmentTeam).to receive(:find).with(any_args).and_return(team)
      allow(Assignment).to receive(:find).with(any_args).and_return(assignment)
      allow(SubmissionRecord).to receive(:where).with(any_args).and_return([submission_record])

      result = get :index, params
      expect(result.status).to eq 302

      controller.send(:index)
      expect(controller.instance_variable_get(:@submission_records)).to eq [submission_record]
    end
end

2. action_allowed? - This action check is invoked before any of the actions in the submission records controller. It checks whether to allow/ refuse the action for the current user. Action is allowed for the current user if the user is admin/ instructor who instructs the current assignment/ TA of the course which current assignment belongs to. The test case is also to make sure other roles can not access the feature.

Code snippet:

describe '#action_allowed?' do
    before(:each) do
      controller.params = { team_id: '27158' }
      allow(AssignmentTeam).to receive(:find).with('27158').and_return(team)
      allow(Assignment).to receive(:find).with(team.parent_id).and_return(assignment)
    end

    context 'when superadmin is logged in' do
      it 'allows certain action' do
        stub_current_user(super_admin, super_admin.role.name, super_admin.role)
        expect(controller.send(:action_allowed?)).to be_truthy
      end
    end

    context 'when current user is instructor who instructs the current assignment' do
      it 'allows certain action' do
        stub_current_user(instructor1, instructor1.role.name, instructor1.role)
        expect(controller.send(:action_allowed?)).to be_truthy
      end
    end

    context 'when current user is instructor but NOT the instructor who instructs the current assignment' do
      it 'refuses certain action' do
        stub_current_user(instructor2, instructor2.role.name, instructor2.role)
        expect(controller.send(:action_allowed?)).to be_falsey
      end
    end

    context 'when current user is TA of the course which current assignment belongs to' do
      it 'allows certain action' do
        stub_current_user(ta, ta.role.name, ta.role)
        allow(TaMapping).to receive(:exists?).with(ta_id: 8, course_id: 1).and_return(true)
        expect(controller.send(:action_allowed?)).to be true
      end
    end

    context 'when current user is a TA but NOT the TA of course which current assignment belongs to' do
      it 'refuses certain action' do
        stub_current_user(ta, ta.role.name, ta.role)
        allow(TaMapping).to receive(:exists?).with(ta_id: 8, course_id: 1).and_return(false )
        expect(controller.send(:action_allowed?)).to be false
      end
    end

    context 'when current user is a student' do
      it 'refuses certain action' do
        stub_current_user(student, student.role.name, student.role)
        expect(controller.send(:action_allowed?)).to be false
      end
    end
end

Test Frame for profile controller

1. action_allowed? - This action check is invoked before any of the actions in the profile controller. It checks whether to allow/refuse the action for the current user. Action is allowed for the current user if the user is logged in. The test case is also to make sure that a user who is not logged in can not access the feature.

Code snippet:

describe '#action_allowed?' do
    context 'when someone is logged in' do
      it 'allows certain action' do
        stub_current_user(instructor1, instructor1.role.name, instructor1.role)
        expect(controller.send(:action_allowed?)).to be_truthy
      end
    end

    context 'when no one is logged in' do
      it 'refuses certain action' do
        expect(controller.send(:action_allowed?)).to be_falsey
      end
    end
end

2. edit - This action is called in the process of editing a profile. It renders the corresponding edit view.

Code snippet:

describe '#edit' do
    it 'renders edit page' do
      stub_current_user(instructor1, instructor1.role.name, instructor1.role)
      allow(AssignmentQuestionnaire).to receive(:where).with(any_args)
                                                       .and_return([assignment_questionnaire])

      get :edit
      expect(response).to render_template(:edit)
    end
end

3. update - This action is called in the process of updating a profile. A success or error message is flashed depending on the case that a profile is successfully updated or not. And finally there is a redirect to the edit page.

Code snippet:

describe '#update' do
    context 'when profile is saved successfully' do
      it 'shows a success flash message and redirects to profile#edit page' do
        stub_current_user(instructor1, instructor1.role.name, instructor1.role)
        allow(instructor1).to receive(:update_attributes).with(any_args).and_return(true)
        allow(instructor1).to receive(:save!).and_return(true)
        params = {
          id: 1,
          no_show_action: 'not_show_actions'
        }
        post :update, params
        expect(flash[:success]).to eq('Your profile was successfully updated.')
        expect(response).to redirect_to('/profile/1/edit')
      end
    end

    context 'when profile is saved successfully and assignment_questionnaire is not nil' do
      it 'shows a success flash message and redirects to profile#edit page' do
        stub_current_user(instructor1, instructor1.role.name, instructor1.role)
        allow(instructor1).to receive(:update_attributes).with(any_args).and_return(true)
        allow(instructor1).to receive(:save!).and_return(true)
        allow(AssignmentQuestionnaire).to receive(:where).with(any_args).and_return([assignment_questionnaire])

        params = {
          id: 1,
          no_show_action: 'not_show_actions',
          assignment_questionnaire: { 'assignment_id' => '1', 'questionnaire_id' => '666', 'dropdown' => 'true',
                                      'questionnaire_weight' => '0', 'notification_limit' => '15', 'used_in_round' => '1' }
        }
        post :update, params
        expect(flash[:success]).to eq('Your profile was successfully updated.')
        expect(response).to redirect_to('/profile/1/edit')
      end
    end

    context 'when profile is not saved successfully' do
      it 'displays an error flash message and redirects to profile#edit page' do
        stub_current_user(instructor1, instructor1.role.name, instructor1.role)
        allow(instructor1).to receive(:update_attributes).with(any_args).and_return(false)
        params = {
          id: 1
        }
        post :update, params
        expect(flash[:error]).to eq('An error occurred and your profile could not updated.')
        expect(response).to redirect_to('/profile/1/edit')
      end
    end

end

Issues Fixed

Fixed the action_allowed? method in Submission Records Controller

Old Code (with relaxed checks)

  def action_allowed?
    # currently we only have a index method which shows all the submission records given a team_id
    assignment_team = AssignmentTeam.find(params[:team_id])
    assignment = Assignment.find(assignment_team.parent_id)
    return true if current_user_has_admin_privileges?
    return true if current_user_instructs_assignment?(assignment)
    return true if current_user_has_ta_mapping_for_assignment?(assignment)

    false
  end

Here the current_user_instructs_assignment? method just checks if the instructor_id of the assignment is equal to the id of the current logged in user. It is not checking if the logged in user has instructor privileges. Similarly for TA.

New Code (with strong checks)

  def action_allowed?
    # currently we only have a index method which shows all the submission records given a team_id
    assignment_team = AssignmentTeam.find(params[:team_id])
    assignment = Assignment.find(assignment_team.parent_id)
    return true if current_user_has_admin_privileges?
    return true if current_user_has_instructor_privileges? && current_user_instructs_assignment?(assignment)
    return true if current_user_has_ta_privileges? && current_user_has_ta_mapping_for_assignment?(assignment)

    false
  end

Here we first check if the current logged in user has instructor privileges and only then invoke the current_user_instructs_assignment? method to check if the instructor_id of the assignment is equal to the id of the current logged in user. Similarly for TA.

Results

We covered 2 methods under submission records controller and 3 methods under profile controller, we covered all edge cases under these methods. The coverage for submission records controller is 100% and the coverage for profile controller is 95.83%.

The test coverage for profile controller is not 100% because we have not implemented test case for the user_params method since it is no longer being used in the controller. Also, it is a private method and private methods are not tested generally.

Related Links

A video of all tests running can be seen here

The main repository can be found here

The forked git repository for this project can be found here

The pull request can be found here

Team

  • Chirrag Nangia (cnangia)
  • Rahul Shukla (rshukla3)
  • Rachana Sri Bollineni (rbollin)