CSC/ECE 517 Spring 2022 - E2202- Testing for badges controller, publishing controller
About Expertiza
Expertiza is the software benefits for both instructors and students by supporting various types of submissions and providing reusable objects for peer review. It is an open-source project based on Ruby on Rails framework. It allows the instructors not only to create and customize new or existing assignments but also to create a list of topics the students can sign up for. Students can form teams to work on various projects and assignments. Expertiza also lets students peer-review other students' submissions, enabling them to work together to improve others' learning experiences.
Description about project
This page is a description of Expertiza OSS project E2202 which is adding test cases for badges_controller.rb and publishing_controller.rb. The badges controller allows users to create badges by entering the badge name, description and an image that serves as the badge icon. The publishing controller enables the students to grant/revoke publishing rights to past assignments. It does this by either allowing the students to grant and deny rights individually or all of them together. It makes use of the private key to verify the digital signature of the user granting the rights.
Files Involved
badges_controller.rb
badges_controller_spec.rb
publishing_controller.rb
publishing_controller_spec.rb
Running Tests
rspec spec/controllers/publishing_controller_spec.rb spec/controllers/badges_controller_spec.rb
- If you're having any difficulty in running the tests, please drop an email to sbose2@ncsu.edu, apatil25@ncsu.edu or kkhulla@ncsu.edu.
Test Plan
After setting up the development environment, we ran the original test cases for the badges_controller. However the test cases were very limited and only partially covered one of the methods. We wrote additional test cases to cover all the 5 methods badges controller and expanded the existing test case. For the publishing_controller no tests were available so we had to write every test from scratch. We have written tests to cover all the 6 methods in the publishing_controller. In total we wrote 27 test cases for both the controllers. We used object mocking for creating the unit test cases.
Badges Controller Methods
The methods in the controller are:
- action_allowed?
- new
- redirect_to_assignment
- create
- badge_params
Publishing Controller Methods
The methods in the controller are:
- action_allowed?
- view
- set_publish_permission
- update_publish_permissions
- grant
- grant_with_private_key
Test Frame for Badges Controller
1. action_allowed? - When user tries to access the badges this method is called to check if the current user is authorized to view this page. All the users except the user with student privileges has access to perform actions in badges.
Code snippet:
describe '#action_allowed?' do it 'allows super_admin to perform certain action' do stub_current_user(super_admin, super_admin.role.name, super_admin.role) expect(controller.send(:action_allowed?)).to be_truthy end it 'allows instructor to perform certain action' do stub_current_user(instructor1, instructor1.role.name, instructor1.role) expect(controller.send(:action_allowed?)).to be_truthy end it 'refuses student from performing certain action' do stub_current_user(student1, student1.role.name, student1.role) expect(controller.send(:action_allowed?)).to be_falsey end it 'allows teaching assisstant to perform certain action' do stub_current_user(ta, ta.role.name, ta.role) expect(controller.send(:action_allowed?)).to be_truthy end it 'allows admin to perform certain action' do stub_current_user(admin, admin.role.name, admin.role) expect(controller.send(:action_allowed?)).to be_truthy end end
2. New - When user tries to create a new badge it redirects to the new create badge page and allow the user to enter details for the badge creation.
Code snippet:
describe '#new' do context 'when user wants to create a new badge' do it 'should call the new#badge page url' do get :new expect(get: 'badges/new').to route_to('badges#new') end it 'should render the create new form and allow the user to enter details' do allow(Badge).to receive(:new).and_return(badge) params = {} session = { user: instructor1 } get :new, params, session expect(response).to render_template('new') end end end
3. redirect_to_assignment - When the user has successfully created a badge it redirects to the assignment page.
Code Snippet:
describe 'redirect_to_assignment' do context 'after user successfully creates a badge' do it 'calls the redirect_to_assignment url' do session[:return_to] ||= 'http://test.host/assignments/844/edit' get :redirect_to_assignment expect(get: 'badges/redirect_to_assignment').to route_to('badges#redirect_to_assignment') end it 'redirects to the assignment page' do stub_current_user(ta, ta.role.name, ta.role) session[:return_to] ||= 'http://test.host/assignments/844/edit' get :redirect_to_assignment response.should redirect_to("http://test.host/assignments/844/edit") end end end
4. create - When the user tries to create a badge by entering all the required details, it saves the badge. If there are any missing required details for badge creation it throws an error for missing fields.
Code Snippet:
describe '#create' do context 'when user enters all the required badge details correctly' do it 'should save the badge successfully' do @file = fixture_file_upload('app/assets/images/badges/test.png', 'image/png') allow(@file).to receive(:original_filename).and_return("test.png") session = { user: instructor1 } params = { badge:{ name: 'test', description: 'test badge', image_name: 'test.png', image_file: @file } } session[:return_to] ||= 'http://test.host/assignments/844/edit' allow(Badge).to receive(:get_id_from_name).with('test').and_return(badge) allow(Badge).to receive(:get_image_name_from_name).with('test').and_return(badge) post :create, params, session, "file" => @file expect(response).to redirect_to 'http://test.host/assignments/844/edit' end end context 'when user forgets to enter few of the required badge details' do it 'should throw an error for missing image file' do @file = nil session = { user: instructor1 } params = { badge:{ name: 'test', description: 'test badge', image_name: 'test.png', image_file: @file } } session[:return_to] ||= 'http://test.host/assignments/844/edit' allow(Badge).to receive(:get_id_from_name).with('test').and_return(badge) allow(Badge).to receive(:get_image_name_from_name).with('test').and_return(badge) post :create, params, session, "file" => @file expect(response).to render_template('new') end it 'should throw an error for missing badge name' do @file = fixture_file_upload('app/assets/images/badges/test.png', 'image/png') allow(@file).to receive(:original_filename).and_return("test.png") session = { user: instructor1 } params = { badge:{ name: '', description: 'test badge', image_name: 'test.png', image_file: @file } } session[:return_to] ||= 'http://test.host/assignments/844/edit' allow(Badge).to receive(:get_id_from_name).with('test').and_return(badge) allow(Badge).to receive(:get_image_name_from_name).with('test').and_return(badge) post :create, params, session, "file" => @file expect(response).to render_template('new') end it 'should throw an error for missing badge description' do @file = fixture_file_upload('app/assets/images/badges/test.png', 'image/png') allow(@file).to receive(:original_filename).and_return("test.png") session = { user: instructor1 } params = { badge:{ name: 'test', description: '', image_name: 'test.png', image_file: @file } } session[:return_to] ||= 'http://test.host/assignments/844/edit' allow(Badge).to receive(:get_id_from_name).with('test').and_return(badge) allow(Badge).to receive(:get_image_name_from_name).with('test').and_return(badge) post :create, params, session, "file" => @file expect(response).to render_template('new') end end end
Test Frame for Publishing Controller
1. action_allowed? - When user tries to grant or view publishing rights this method is called to check if the current user is authorized to perform the action. All users with student privileges have access to perform actions for publishing.
Code Snippet:
describe '#action_allowed?' do it 'allows super_admin to perform certain action' do stub_current_user(super_admin, super_admin.role.name, super_admin.role) expect(controller.send(:action_allowed?)).to be_truthy end it 'allows instructor to perform certain action' do stub_current_user(instructor1, instructor1.role.name, instructor1.role) expect(controller.send(:action_allowed?)).to be_truthy end it 'allows student to perform certain action' do stub_current_user(student1, student1.role.name, student1.role) expect(controller.send(:action_allowed?)).to be_truthy end it 'allows teaching assisstant to peform certain action' do stub_current_user(ta, ta.role.name, ta.role) expect(controller.send(:action_allowed?)).to be_truthy end it 'allows admin to perform certain action' do stub_current_user(admin, admin.role.name, admin.role) expect(controller.send(:action_allowed?)).to be_truthy end end
2. view - When user visits the publishing rights page, it displays all past assignment participants.
Code Snippet:
describe 'view' do context 'user visits the publishing rights page' do it 'displays all the assignment participants' do stub_current_user(student1, student1.role.name, student1.role) params = { id: 21 } get :view, params expect(assigns(:user)).to eq(student1) end end end
3. set_publish_permission - When the user matches with the participant and the user clicks on the grant button next to the assignment, it redirects to the grant page. When the user matches with the participant and the assignment is already granted permission, it redirects to the view page.
Code Snippet:
describe 'set_publish_permission' do context 'user matches with participant and user clicks on the grant button next to the assignment' do it 'redirects to the grant page' do allow(AssignmentParticipant).to receive(:find).with('1').and_return(assignment_participant1) stub_current_user(student1, student1.role.name, student1.role) params ={id: 1, allow: 1} post :set_publish_permission, params expect(response).to redirect_to(action: :grant) end end context 'user matches with participant and the assignment is already granted permission' do it 'redirects to the view page' do stub_current_user(student1, student1.role.name, student1.role) allow(AssignmentParticipant).to receive(:find).with('1').and_return(assignment_participant1) allow(assignment_participant1).to receive(:update_attribute).and_return(true) params ={id: 1, allow: '0'} post :set_publish_permission, params expect(response).to redirect_to(action: :view) end end end
4. update_publish_permissions - When a user clicks on the grant publishing rights to all past assignments button it redirects to the grant page. When a user clicks on the deny publishing rights to all past assignments button it redirects to the view page.
Code Snippet:
describe 'update_publish_permissions' do context 'user clicks on the grant publishing rights to all past assignments button' do it 'redirects to grant page' do allow(AssignmentParticipant).to receive(:find).with('3').and_return(assignment_participant2) stub_current_user(student1, student1.role.name, student1.role) params ={id: 3, allow: 1} post :update_publish_permissions, params expect(response).to redirect_to(action: :grant) end end context 'user clicks on the deny publishing rights to all past assignments button' do it 'redirects to view page' do allow(AssignmentParticipant).to receive(:where).with(user_id: 21).and_return([assignment_participant1]) stub_current_user(student1, student1.role.name, student1.role) params ={id: 3, allow: 0} [assignment_participant1].each do |participant| allow(participant).to receive(:update_attribute).and_return(true) allow(participant).to receive(:save).and_return(true) end post :update_publish_permissions, params expect(response).to redirect_to(action: :view) end end end
5. grant - When a user clicks on the grant publishing option, it displays the page where the user can supply their private key and grant publishing rights.
Code Snippet:
describe 'grant' do context 'user clicks on grant option' do it 'displays the page where the user can supply their private key and grant publishing rights' do allow(AssignmentParticipant).to receive(:find).with('3').and_return(assignment_participant2) stub_current_user(student1, student1.role.name, student1.role) params ={id: 3} get :grant, params expect(assigns(:user)).to eq(student1) end end end
6. grant_with_private_key - When a user visits the grant page without id and enters incorrect RSA private key, it throws StandardError, displays notice and redirects to grant page. When a user visits the grant page with id and enters correct RSA private key, it verifies key to be successful for all past assignments and redirects to view page. When a user visits the grant page with id and enters incorrect RSA private key, it throws StandardError, displays notice and redirects to grant.
Code Snippet:
describe 'grant_with_private_key' do context 'user visits the grant page without id and enters incorrect RSA private key' do it 'displays notice and redirects to grant' do allow(AssignmentParticipant).to receive(:where).with(user_id: 21).and_return([assignment_participant1]) stub_current_user(student1, student1.role.name, student1.role) params = {} private_key = double(:private_key) [assignment_participant1].each do |participant| allow(participant).to receive(:verify_digital_signature).with(private_key).and_return(true) allow(participant).to receive(:assign_copyright).with(private_key).and_raise('The private key you inputted was invalid.', StandardError) end post :grant_with_private_key, params expect(flash[:notice]).to eq('The private key you inputted was invalid.') expect(response).to redirect_to(action: :grant) end end context 'user visits the grant page with id and enters correct RSA private key' do it 'verifies to be successful for all past assignments and redirect to view' do allow(AssignmentParticipant).to receive(:find).with('2').and_return(assignment_participant1) stub_current_user(student1, student1.role.name, student1.role) private_key = OpenSSL::PKey::RSA.new 2048 params = {id: 2, private_key: private_key} [assignment_participant1].each do |participant| allow(participant).to receive(:verify_digital_signature).with(any_args).and_return(true) allow(participant).to receive(:assign_copyright).with(any_args).and_return(true) end post :grant_with_private_key, params expect(response).to redirect_to(action: :view) end end context 'user visits the grant page with id and enters incorrect RSA private key' do it 'displays notice and redirects to grant' do allow(AssignmentParticipant).to receive(:find).with('2').and_return(assignment_participant1) stub_current_user(student1, student1.role.name, student1.role) private_key = OpenSSL::PKey::RSA.new 2048 params = {id: 2, private_key: private_key} [assignment_participant1].each do |participant| allow(participant).to receive(:verify_digital_signature).with(any_args).and_return(true) allow(participant).to receive(:assign_copyright).with(any_args).and_raise('The private key you inputted was invalid.', StandardError) end post :grant_with_private_key, params expect(flash[:notice]).to eq('The private key you inputted was invalid.') expect(response).to redirect_to(action: :grant,params:{id:2}) end end end
Results
The total coverage of the tests is 100%. We covered all the edges cases to ensure maximum coverage.
The screenshot for test coverage can be found here
Related Links
A video of all the tests running can be found here
The pull request can be accessed here
The forked git repository for this project can be found here
Conclusion
We followed a behavior-driven approach to write unit tests for badges_controller and publishing_controller. We considered all edge cases to achieve 100% coverage of both the controllers.