diff --git a/app/policies/group_policy.rb b/app/policies/group_policy.rb index 7fe18b88..234d161d 100644 --- a/app/policies/group_policy.rb +++ b/app/policies/group_policy.rb @@ -51,7 +51,7 @@ def leave? end def invite? - user.is_admin? or user.owns(record) or member? && [Group::MEMBERSHIP[:open], Group::MEMBERSHIP[:invitation]].include?(record.membership) + user.is_admin? || user.owns(record) || (member? && [Group::MEMBERSHIP[:open], Group::MEMBERSHIP[:invitation]].include?(record.membership)) end def reject? diff --git a/spec/controllers/accounts/passwords_controller_spec.rb b/spec/controllers/accounts/passwords_controller_spec.rb index d220ff54..353e7fdc 100644 --- a/spec/controllers/accounts/passwords_controller_spec.rb +++ b/spec/controllers/accounts/passwords_controller_spec.rb @@ -1,47 +1,38 @@ require "spec_helper" describe Accounts::PasswordsController do - before(:each) do - @user = users(:user) - end + let(:user) { FactoryBot.create(:user) } - it "can get password reset form" do - get :new - expect(response).to be_success - end + describe "forgot password" do + it "can get password reset form" do + get :new + expect(response).to be_success + end - it "can send password reset email" do - post :create, user: {email: @user.email} + it "can send password reset email" do + post :create, user: {email: user.email} - expect(mail = ActionMailer::Base.deliveries.last).to_not be_nil + expect(mail = ActionMailer::Base.deliveries.last).to_not be_nil - host = ActionMailer::Base.default_url_options[:host] - expect(mail).to have_link("reset_password_token") + host = ActionMailer::Base.default_url_options[:host] + expect(mail).to have_link("reset_password_token") + end end - context "using password reset token" do - before(:all) do - @resetuser = FactoryBot.create(:user) - end - after(:all) do - @resetuser.destroy - end - before(:each) do - User.send_reset_password_instructions email: @resetuser.email - @resetuser.reload - expect(mail = ActionMailer::Base.deliveries.last).to_not be_nil - expect(mail.body.encoded =~ %r{}).to_not be_nil - @reset_token = $1 + describe "using password reset token" do + before do + @token = user.send_reset_password_instructions end it "can edit password" do - get :edit, reset_password_token: @reset_token + get :edit, reset_password_token: @token expect(response).to be_success end it "can update password" do - post :update, user: {reset_password_token: @reset_token, password: "newpassword", password_confirmation: "newpassword"} - expect(@resetuser.reload.valid_password?("newpassword")).to be true + post :update, user: {reset_password_token: @token, password: "newpassword", password_confirmation: "newpassword"} + + expect(user.reload.valid_password?("newpassword")).to be true end end end diff --git a/spec/controllers/contests_controller_spec.rb b/spec/controllers/contests_controller_spec.rb index 3037607a..7d79db1b 100644 --- a/spec/controllers/contests_controller_spec.rb +++ b/spec/controllers/contests_controller_spec.rb @@ -1,19 +1,14 @@ require "spec_helper" describe ContestsController do - before(:all) do - @problem_set = FactoryBot.create(:problem_set) - @contest = FactoryBot.create(:contest, problem_set: @problem_set) - end - after(:all) do - @contest.destroy - @problem_set.destroy - end + let(:problem_set) { FactoryBot.create(:problem_set) } + let(:contest) { FactoryBot.create(:contest, problem_set: problem_set) } context "as admin" do - before(:each) do - sign_in users(:admin) + before do + sign_in FactoryBot.create(:admin) end + can_index :contests can_index :contests, params: {filter: "my"} @@ -22,9 +17,10 @@ end context "as an organiser" do - before(:each) do - sign_in users(:organiser) + before do + sign_in FactoryBot.create(:organiser) end + can_index :contests, params: {filter: "my"} end end diff --git a/spec/controllers/evaluators_controller_spec.rb b/spec/controllers/evaluators_controller_spec.rb index 2b3376ea..232714d8 100644 --- a/spec/controllers/evaluators_controller_spec.rb +++ b/spec/controllers/evaluators_controller_spec.rb @@ -1,25 +1,26 @@ require "spec_helper" describe EvaluatorsController do - before(:all) do - @evaluator = FactoryBot.create(:evaluator) - end - after(:all) do - @evaluator.destroy + let(:evaluator) { FactoryBot.create(:evaluator) } + + before do + sign_in user end context "as admin" do - before(:each) do - sign_in users(:admin) - end + let(:user) { FactoryBot.create(:admin) } + can_index :evaluators can_create :evaluator, attributes: {name: "A unique name", description: "Unique description", source: "special sauce"} can_manage :evaluator, attributes: {name: "A unique name", description: "Unique description", source: "special sauce"} end context "as a normal user" do - before(:each) do - sign_in users(:user) + let(:user) { FactoryBot.create(:user) } + + it "can't be accessed" do + get :index + expect(response).to have_http_status(:forbidden) end end end diff --git a/spec/controllers/file_attachments_controller_spec.rb b/spec/controllers/file_attachments_controller_spec.rb index 80038384..e4aa9aa0 100644 --- a/spec/controllers/file_attachments_controller_spec.rb +++ b/spec/controllers/file_attachments_controller_spec.rb @@ -3,7 +3,7 @@ RSpec.describe FileAttachmentsController do describe "GET /file_attachments" do before do - sign_in users(:superadmin) + sign_in FactoryBot.create(:superadmin) end can_index :file_attachments diff --git a/spec/controllers/groups_controller_spec.rb b/spec/controllers/groups_controller_spec.rb index f69d927d..ccdbbb2d 100644 --- a/spec/controllers/groups_controller_spec.rb +++ b/spec/controllers/groups_controller_spec.rb @@ -1,33 +1,29 @@ require "spec_helper" describe GroupsController do - before(:all) do - @group = FactoryBot.create(:group) - end - after(:all) do - @group.destroy + let(:group) { FactoryBot.create(:group) } + + before do + sign_in user end context "as admin" do - before(:each) do - sign_in users(:admin) - end + let(:user) { FactoryBot.create(:admin) } + can_index :groups can_create :group, attributes: {name: "A unique name"} can_manage :group, attributes: {name: "A unique name"} end context "as a normal user" do - before(:each) do - sign_in users(:user) - end + let(:user) { FactoryBot.create(:user) } + can_browse :groups end context "as an organiser" do - before(:each) do - sign_in users(:organiser) - end + let(:user) { FactoryBot.create(:organiser) } + can_index :groups, params: {filter: "my"} end end diff --git a/spec/controllers/problem_sets_controller_spec.rb b/spec/controllers/problem_sets_controller_spec.rb index 939b6d8a..31ba2ecd 100644 --- a/spec/controllers/problem_sets_controller_spec.rb +++ b/spec/controllers/problem_sets_controller_spec.rb @@ -1,26 +1,23 @@ require "spec_helper" describe ProblemSetsController do - before(:all) do - @problem_set = FactoryBot.create(:problem_set) - end - after(:all) do - @problem_set.destroy + let(:problem_set) { FactoryBot.create(:problem_set) } + + before do + sign_in user end context "as admin" do - before(:each) do - sign_in users(:admin) - end + let(:user) { FactoryBot.create(:admin) } + can_index :problem_sets can_create :problem_set, attributes: {name: "A unique title"} can_manage :problem_set, attributes: {name: "A unique title"} end context "as an organiser" do - before(:each) do - sign_in users(:organiser) - end + let(:user) { FactoryBot.create(:organiser) } + can_index :problem_sets, params: {filter: "my"} end end diff --git a/spec/controllers/problems_controller_spec.rb b/spec/controllers/problems_controller_spec.rb index 5e1b7671..460dffbb 100644 --- a/spec/controllers/problems_controller_spec.rb +++ b/spec/controllers/problems_controller_spec.rb @@ -1,62 +1,48 @@ require "spec_helper" describe ProblemsController do - before(:all) do - @group = FactoryBot.create(:group, name: "Special Group", members: [users(:admin), users(:user)]) - @group_set = FactoryBot.create(:problem_set, name: "Set in Group", groups: [@group]) - @group_problem = FactoryBot.create(:adding_problem, problem_sets: [@group_set]) - end - after(:all) do - [@group, @group_set, @group_problem].reverse_each { |object| object.destroy } - end + let(:group) { FactoryBot.create(:group, name: "Special Group", members: [user]) } + let(:group_set) { FactoryBot.create(:problem_set, name: "Set in Group", groups: [group]) } + let(:group_problem) { FactoryBot.create(:adding_problem, problem_sets: [group_set]) } + + let(:owned_problem) { FactoryBot.create(:problem, owner: user) } + let(:unowned_problem) { FactoryBot.create(:problem) } + shared_examples "for any user" do can_index :problems, params: {filter: "my"} can_create :problem, attributes: {name: "A unique title", statement: "A unique statement"} can_manage :owned_problem, resource_name: :problem, attributes: {name: "A unique title", statement: "A unique statement"} end + before do + sign_in user + end + context "as admin" do - before(:all) do - @owned_problem = FactoryBot.create(:problem, owner: users(:admin)) - @unowned_problem = FactoryBot.create(:problem) - end - after(:all) do - @unowned_problem.destroy - @owned_problem.destroy - end - before(:each) do - sign_in users(:admin) - end + let(:user) { FactoryBot.create(:admin) } + include_examples "for any user" + can_manage :unowned_problem, resource_name: :problem, attributes: {name: "A unique title", statement: "A unique statement"} end context "as a normal user" do - before(:all) do - @owned_problem = FactoryBot.create(:problem, owner: users(:user)) - @unowned_problem = FactoryBot.create(:problem) - end - after(:all) do - @unowned_problem.destroy - @owned_problem.destroy - end - before(:each) do - sign_in users(:user) - end + let(:user) { FactoryBot.create(:user) } + include_examples "for any user" it "can get submit for group problem" do - get :submit, id: @group_problem.id + get :submit, id: group_problem.id expect(response).to be_success end it "can post submit for group problem" do expect_any_instance_of(Submission).to receive(:judge) # post multi-part form - post :submit, id: @group_problem.id, submission: {language_id: LanguageGroup.find_by_identifier("c++").current_language, source_file: fixture_file_upload("/files/adding.cpp", "text/plain")} + post :submit, id: group_problem.id, submission: {language_id: LanguageGroup.find_by_identifier("c++").current_language, source_file: fixture_file_upload("/files/adding.cpp", "text/plain")} expect(response).to redirect_to submission_path(assigns(:submission)) - expect(assigns(:submission).problem_id).to eq(@group_problem.id) - expect(assigns(:submission).user_id).to eq(users(:user).id) + expect(assigns(:submission).problem_id).to eq(group_problem.id) + expect(assigns(:submission).user_id).to eq(user.id) expect(assigns(:submission).language.group.identifier).to eq("c++") expect(assigns(:submission).source).not_to be_empty end diff --git a/spec/controllers/roles_controller_spec.rb b/spec/controllers/roles_controller_spec.rb index c9c8e561..2cbe1e12 100644 --- a/spec/controllers/roles_controller_spec.rb +++ b/spec/controllers/roles_controller_spec.rb @@ -1,26 +1,23 @@ require "spec_helper" describe RolesController do - before(:all) do - @role = FactoryBot.create(:role) - end - after(:all) do - @role.destroy + let(:role) { FactoryBot.create(:role) } + + before do + sign_in user end context "as superadmin" do - before(:each) do - sign_in users(:superadmin) - end + let(:user) { FactoryBot.create(:superadmin) } + can_index :roles can_create :role, attributes: {name: "A unique name"} can_manage :role, attributes: {name: "A unique name"} end context "as admin" do - before(:each) do - sign_in users(:admin) - end + let(:user) { FactoryBot.create(:admin) } + can_index :roles end end diff --git a/spec/controllers/settings_controller_spec.rb b/spec/controllers/settings_controller_spec.rb index 228c0386..99e67787 100644 --- a/spec/controllers/settings_controller_spec.rb +++ b/spec/controllers/settings_controller_spec.rb @@ -1,25 +1,26 @@ require "spec_helper" describe SettingsController do - before(:all) do - @setting = FactoryBot.create(:setting) - end - after(:all) do - @setting.destroy + let(:setting) { FactoryBot.create(:setting) } + + before do + sign_in user end context "as superadmin" do - before(:each) do - sign_in users(:superadmin) - end + let(:user) { FactoryBot.create(:superadmin) } + can_index :settings can_create :setting, attributes: {key: "A unique name", value: "Secret value setting"} can_manage :setting, attributes: {key: "A unique name", value: "Secret value setting"} end context "as a normal user" do - before(:each) do - sign_in users(:user) + let(:user) { FactoryBot.create(:user) } + + it "doesn't display" do + get :index + expect(response).to have_http_status(:forbidden) end end end diff --git a/spec/controllers/submissions_controller_spec.rb b/spec/controllers/submissions_controller_spec.rb index f329744a..7eeec92e 100644 --- a/spec/controllers/submissions_controller_spec.rb +++ b/spec/controllers/submissions_controller_spec.rb @@ -1,26 +1,25 @@ require "spec_helper" describe SubmissionsController do - before(:all) do - @submission = FactoryBot.create(:submission, problem: problems(:problem), user: users(:superadmin)) - end - after(:all) do - @submission.destroy + let(:problem) { FactoryBot.create(:problem) } + let(:user) { FactoryBot.create(:superadmin) } + let(:submission) { FactoryBot.create(:submission, problem: problem, user: user) } + + before do + sign_in user end context "as admin" do - before(:each) do - sign_in users(:admin) - end + let(:user) { FactoryBot.create(:admin) } + can_index :submissions can_show :submission can_destroy :submission end context "as a normal user" do - before(:each) do - sign_in users(:user) - end + let(:user) { FactoryBot.create(:user) } + can_index :submissions, params: {filter: "my"} end end diff --git a/spec/controllers/user_controller_spec.rb b/spec/controllers/user_controller_spec.rb index d5e3d64a..b6560b3f 100644 --- a/spec/controllers/user_controller_spec.rb +++ b/spec/controllers/user_controller_spec.rb @@ -1,23 +1,19 @@ require "spec_helper" describe UserController do - before(:each) do - @user = users(:user) - @superadmin = users(:superadmin) + before do + sign_in user end context "as admin" do - before(:each) do - sign_in users(:admin) - end + let(:user) { FactoryBot.create(:superadmin) } can_show :user can_update :user, attributes: {name: "A unique name"} end context "as a normal user" do - before(:each) do - sign_in users(:user) - end + let(:user) { FactoryBot.create(:user) } + can_show :user end end diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb index 45754b04..959fa9ec 100644 --- a/spec/controllers/users_controller_spec.rb +++ b/spec/controllers/users_controller_spec.rb @@ -1,22 +1,19 @@ require "spec_helper" describe UsersController do - before(:each) do - @user = users(:user) - @superadmin = users(:superadmin) + before do + sign_in user end context "as admin" do - before(:each) do - sign_in users(:admin) - end + let(:user) { FactoryBot.create(:admin) } + can_index :users end context "as a normal user" do - before(:each) do - sign_in users(:user) - end + let(:user) { FactoryBot.create(:user) } + can_index :users end end diff --git a/spec/features/invitation_and_join_request_spec.rb b/spec/features/invitation_and_join_request_spec.rb index 091e5dc1..866add22 100644 --- a/spec/features/invitation_and_join_request_spec.rb +++ b/spec/features/invitation_and_join_request_spec.rb @@ -1,89 +1,107 @@ require "spec_helper" feature "invitation and join request" do - scenario "group owner invites a user, and user accepts invitation to group" do - @group = FactoryBot.create(:group, owner: users(:organiser), visibility: Group::VISIBILITY[:private], membership: Group::MEMBERSHIP[:private]) - - login_as users(:organiser), scope: :user - visit invites_members_group_path(@group) - expect do - within "#invite_member_form" do - fill_in "username", with: users(:user).username - click_on "Invite" - end - end.to change { @group.invitations.pending.count }.by(1) - - @invitation = @group.invitations.pending.where(target_id: users(:user)).first - - expect(@group.members.exists?(users(:user).id)).to be false - - login_as users(:user), scope: :user - visit accounts_requests_path - expect do - within ".group_invitations_list" do - accept_link = find :xpath, "//a[@href = '#{accept_members_group_path(@group, @invitation)}']" - accept_link.click - end - end.to change { @group.invitations.pending.count }.by(-1) - - expect(@group.members.exists?(users(:user).id)).to be true + subject(:group) { FactoryBot.create(:group, owner: owner, members: members, visibility: visibility, membership: membership) } + + let(:owner) { FactoryBot.create(:organiser) } + let(:member) { FactoryBot.create(:user) } + let(:members) { [member] } + let(:visibility) { Group::VISIBILITY[:unlisted] } + let(:membership) { Group::MEMBERSHIP[:invitation] } + + let(:uninvited_user) { FactoryBot.create(:user) } + + context "when the group is private and invite-only" do + let(:membership) { Group::MEMBERSHIP[:private] } + let(:visibility) { Group::VISIBILITY[:private] } + + scenario "group owner invites a user, and user accepts invitation to group" do + login_as owner, scope: :user + visit invites_members_group_path(group) + expect do + within "#invite_member_form" do + fill_in "username", with: uninvited_user.username + click_on "Invite" + end + end.to change { group.invitations.pending.count }.by(1) + + invitation = group.invitations.pending.where(target: uninvited_user).first + + expect(group.members.exists?(uninvited_user.id)).to be false + + login_as uninvited_user, scope: :user + visit accounts_requests_path + + expect do + within ".group_invitations_list" do + accept_link = find :xpath, "//a[@href = '#{accept_members_group_path(group, invitation)}']" + accept_link.click + end + end.to change { group.invitations.pending.count }.by(-1) + + expect(group.members.exists?(uninvited_user.id)).to be true + end end - scenario "group member invites a user and cancels the invitation" do - @group = FactoryBot.create(:group, members: [users(:user)], visibility: Group::VISIBILITY[:private], membership: Group::MEMBERSHIP[:invitation]) + context "when the group unlisted and invite + apply" do + let(:membership) { Group::MEMBERSHIP[:invitation] } + let(:visibility) { Group::VISIBILITY[:unlisted] } - login_as users(:user), scope: :user - visit invites_members_group_path(@group) + scenario "group member invites a user and cancels the invitation" do + login_as member, scope: :user + visit invites_members_group_path(group) - expect do - within "#invite_member_form" do - fill_in "username", with: users(:organiser).username - click_on "Invite" - end - end.to change { @group.invitations.pending.count }.by(1) + expect do + within "#invite_member_form" do + fill_in "username", with: uninvited_user.username + click_on "Invite" + end + end.to change { group.invitations.pending.count }.by(1) - @invitation = @group.invitations.pending.where(target_id: users(:organiser)).first + invitation = group.invitations.pending.where(target_id: uninvited_user).first - visit invites_members_group_path(@group) - cancel_link = find :xpath, "//a[@href = '#{cancel_members_group_path(@group, @invitation)}']" - expect { cancel_link.click }.to change { @group.invitations.pending.count }.by(-1) - end - - scenario "user applies to join group, and group member accepts join request" do - @group = FactoryBot.create(:group, owner: users(:organiser), visibility: Group::VISIBILITY[:unlisted], membership: Group::MEMBERSHIP[:invitation]) + visit invites_members_group_path(group) + cancel_link = find :xpath, "//a[@href = '#{cancel_members_group_path(group, invitation)}']" + expect { cancel_link.click }.to change { group.invitations.pending.count }.by(-1) + end - login_as users(:user), scope: :user - visit group_path(@group) + scenario "uninvited user applies to join group, and group owner accepts join request" do + login_as uninvited_user, scope: :user + visit group_path(group) - apply_link = find :xpath, "//a[@href = '#{apply_group_path(@group)}']" - expect { apply_link.click }.to change { @group.join_requests.pending.count }.by(1) + apply_link = find :xpath, "//a[@href = '#{apply_group_path(group)}']" + expect { apply_link.click }.to change { group.join_requests.pending.count }.by(1) - expect(@group.members.exists?(users(:user).id)).to be false - @join_request = @group.join_requests.pending.where(subject_id: users(:user)).first + expect(group.members.exists?(uninvited_user.id)).to be false + join_request = group.join_requests.pending.where(subject_id: uninvited_user).first - login_as users(:organiser) - visit join_requests_members_group_path(@group) - accept_link = find :xpath, "//a[@href = '#{accept_members_group_path(@group, @join_request)}']" - expect { accept_link.click }.to change { @group.join_requests.pending.count }.by(-1) + login_as owner + visit join_requests_members_group_path(group) + accept_link = find :xpath, "//a[@href = '#{accept_members_group_path(group, join_request)}']" + expect { accept_link.click }.to change { group.join_requests.pending.count }.by(-1) - expect(@group.members.exists?(users(:user).id)).to be true + expect(group.members.exists?(uninvited_user.id)).to be true + end end - scenario "user applies to join group, and group owner rejects join request" do - @group = FactoryBot.create(:group, owner: users(:organiser), visibility: Group::VISIBILITY[:public], membership: Group::MEMBERSHIP[:application]) + context "when the group is public and allows applications" do + let(:visibility) { Group::VISIBILITY[:public] } + let(:membership) { Group::MEMBERSHIP[:application] } - login_as users(:user), scope: :user - visit group_path(@group) - apply_link = find :xpath, "//a[@href = '#{apply_group_path(@group)}']" - expect { apply_link.click }.to change { @group.join_requests.pending.count }.by(1) + scenario "user applies to join group, and group owner rejects join request" do + login_as uninvited_user, scope: :user + visit group_path(group) + apply_link = find :xpath, "//a[@href = '#{apply_group_path(group)}']" + expect { apply_link.click }.to change { group.join_requests.pending.count }.by(1) - @join_request = @group.join_requests.pending.where(subject_id: users(:user)).first + join_request = group.join_requests.pending.where(subject_id: uninvited_user).first - login_as users(:organiser) - visit join_requests_members_group_path(@group) - reject_link = find :xpath, "//a[@href = '#{reject_members_group_path(@group, @join_request)}']" - expect { reject_link.click }.to change { @group.join_requests.pending.count }.by(-1) + login_as owner + visit join_requests_members_group_path(group) + reject_link = find :xpath, "//a[@href = '#{reject_members_group_path(group, join_request)}']" + expect { reject_link.click }.to change { group.join_requests.pending.count }.by(-1) - expect(@group.members.exists?(users(:user).id)).to be false + expect(group.members.exists?(uninvited_user.id)).to be false + end end end diff --git a/spec/features/submissions_spec.rb b/spec/features/submissions_spec.rb index f46f8171..c5367f7b 100644 --- a/spec/features/submissions_spec.rb +++ b/spec/features/submissions_spec.rb @@ -1,12 +1,14 @@ require "spec_helper" feature "submission" do + let(:problem) { FactoryBot.create(:adding_problem) } + let!(:problem_set) { FactoryBot.create(:problem_set, problems: [problem], group_ids: [0]) } + let(:user) { FactoryBot.create(:user) } + scenario "submit code for a problem" do - @problem = FactoryBot.create(:adding_problem) - @problem_set = FactoryBot.create(:problem_set, problems: [@problem], group_ids: [0]) - login_as users(:user), scope: :user + login_as user, scope: :user - visit submit_problem_path(@problem) + visit submit_problem_path(problem) expect do within "#new_submission" do @@ -14,6 +16,6 @@ attach_file "submission_source_file", Rails.root.join("spec/fixtures/files/adding.cpp") click_on "Submit" end - end.to change { users(:user).submissions.count }.by(1) + end.to change { user.reload.submissions.count }.by(1) end end diff --git a/spec/models/contest_relation_spec.rb b/spec/models/contest_relation_spec.rb index 03885de1..deb6ab85 100644 --- a/spec/models/contest_relation_spec.rb +++ b/spec/models/contest_relation_spec.rb @@ -1,7 +1,6 @@ require "spec_helper" describe ContestRelation do - include FixturesSpecHelper RSpec::Matchers.define :finish_at_correct_time do def expected_time(relation) [relation.started_at.advance(hours: relation.contest.duration), relation.contest.end_time].min @@ -19,34 +18,34 @@ def expected_time(relation) "finish at the earlier of when contest duration runs out or when contest finishes" end end - before(:all) do - @contest = FactoryBot.create(:contest) - @relation = FactoryBot.create(:contest_relation, contest: @contest, user: users(:user)) - end - after(:all) do - @relation.destroy - @contest.destroy - end + + let(:user) { FactoryBot.create(:user) } + let(:contest) { FactoryBot.create(:contest) } + let(:relation) { FactoryBot.create(:contest_relation, contest: contest, user: user) } + it "updates finish_at when relation started_at changes" do - expect(@relation).to finish_at_correct_time - @relation.started_at = @contest.end_time.advance(hours: -1) - expect(@relation).to finish_at_correct_time + expect(relation).to finish_at_correct_time + relation.started_at = contest.end_time.advance(hours: -1) + expect(relation).to finish_at_correct_time end + it "updates finish_at when contest changes" do - @anothercontest = FactoryBot.build(:contest, start_time: @relation.started_at.advance(hours: -1), end_time: @relation.started_at.advance(hours: 1)) - @relation.contest = @anothercontest - expect(@relation).to finish_at_correct_time - @relation.contest_id = @contest.id - expect(@relation).to finish_at_correct_time + anothercontest = FactoryBot.build(:contest, start_time: relation.started_at.advance(hours: -1), end_time: relation.started_at.advance(hours: 1)) + relation.contest = anothercontest + expect(relation).to finish_at_correct_time + relation.contest_id = contest.id + expect(relation).to finish_at_correct_time end + it "updates finish_at when contest end_time changes" do - @contest.update_attributes(end_time: @relation.started_at.advance(hours: 1)) - expect(@relation).to finish_at_correct_time + contest.update_attributes(end_time: relation.started_at.advance(hours: 1)) + expect(relation.reload).to finish_at_correct_time end + it "updates finish_at when contest duration changes" do - @contest.update_attributes(duration: 1.0) - expect(@relation).to finish_at_correct_time - @contest.update_attributes(duration: 5.0) - expect(@relation).to finish_at_correct_time + contest.update_attributes(duration: 1.0) + expect(relation.reload).to finish_at_correct_time + contest.update_attributes(duration: 5.0) + expect(relation.reload).to finish_at_correct_time end end diff --git a/spec/models/submission_spec.rb b/spec/models/submission_spec.rb index 9dc1bf9d..ca34359f 100644 --- a/spec/models/submission_spec.rb +++ b/spec/models/submission_spec.rb @@ -1,43 +1,40 @@ require "spec_helper" describe Submission do - # pending "add some examples to (or delete) #{__FILE__}" context 'on "adding" problem' do - before(:all) do - @user = FactoryBot.create(:user) - @problem = FactoryBot.create(:adding_problem) - @submission = FactoryBot.create(:adding_submission, problem: @problem, user: @user) - @char_submission = FactoryBot.create(:adding_char_submission, problem: @problem, user: @user) - @unsigned_submission = FactoryBot.create(:adding_unsigned_submission, problem: @problem, user: @user) - end - after(:all) do - [@user, @problem, @submission, @char_submission, @unsigned_submission].reverse_each { |object| object.destroy } - end + let(:user) { FactoryBot.create(:user) } + let(:problem) { FactoryBot.create(:adding_problem) } + let(:submission) { FactoryBot.create(:adding_submission, problem: problem, user: user) } + let(:char_submission) { FactoryBot.create(:adding_char_submission, problem: problem, user: user) } + let(:unsigned_submission) { FactoryBot.create(:adding_unsigned_submission, problem: problem, user: user) } + it "judges submission" do - expect(@submission.score).to be_nil - @submission.judge - @submission.reload - expect(@submission.evaluation).to eq(1) + expect(submission.score).to be_nil + submission.judge + submission.reload + expect(submission.evaluation).to eq(1) end + it "judges submission on stdio problem" do problem = FactoryBot.create(:adding_problem_stdio) - submission = FactoryBot.create(:adding_submission_stdio, problem: problem, user: @user) + submission = FactoryBot.create(:adding_submission_stdio, problem: problem, user: user) expect(submission.evaluation).to be_nil submission.judge submission.reload expect(submission.evaluation).to eq(1) end + it "judges partially correct submissions correctly" do - expect(@char_submission.score).to be_nil - @char_submission.judge - @char_submission.reload - expect(@char_submission.points).to eq(2) - expect(@char_submission.maximum_points).to eq(4) + expect(char_submission.score).to be_nil + char_submission.judge + char_submission.reload + expect(char_submission.points).to eq(2) + expect(char_submission.maximum_points).to eq(4) - expect(@unsigned_submission.score).to be_nil - @unsigned_submission.judge - @unsigned_submission.reload - expect(@unsigned_submission.evaluation).to eq(0.75) + expect(unsigned_submission.score).to be_nil + unsigned_submission.judge + unsigned_submission.reload + expect(unsigned_submission.evaluation).to eq(0.75) end end end diff --git a/spec/policies/authorization_spec.rb b/spec/policies/authorization_spec.rb index f37e0305..46e4cc4d 100644 --- a/spec/policies/authorization_spec.rb +++ b/spec/policies/authorization_spec.rb @@ -1,150 +1,187 @@ require "spec_helper" describe "Authorization" do - include FixturesSpecHelper - before(:all) do - @superadmin = users(:superadmin) - @admin = users(:admin) - @organiser = users(:organiser) - @user = users(:user) - # various objects to test ability on - @member = FactoryBot.create(:user) - @group = FactoryBot.create(:group, members: [users(:user), users(:admin), users(:superadmin), @member]) - @organiser_group = FactoryBot.create(:group, owner: users(:organiser), members: [@member]) - @private_problem = FactoryBot.create(:problem) - @group_set = FactoryBot.create(:problem_set, groups: [@group]) - @everyone_set = FactoryBot.create(:problem_set, group_ids: [0]) - @group_problem = FactoryBot.create(:problem, problem_sets: [@group_set]) - @user_problem = FactoryBot.create(:problem, owner: users(:user)) - @admin_problem = FactoryBot.create(:problem, owner: users(:admin)) - @everyone_problem = FactoryBot.create(:problem, problem_sets: [@everyone_set]) - @contest_set = FactoryBot.create(:problem_set) - @contest = FactoryBot.create(:contest, name: "Contest", groups: [@group], problem_set: @contest_set, duration: 100, start_time: DateTime.now.advance(hours: -100), end_time: DateTime.now.advance(hours: 100)) - @contest_problem = FactoryBot.create(:problem, problem_sets: [@contest_set]) - @private_set = FactoryBot.create(:problem_set) - @private_contest = FactoryBot.create(:contest, problem_set: @contest_set) - @past_contest = FactoryBot.create(:contest, groups: [@group], problem_set: @contest_set, start_time: DateTime.now.advance(hours: -100), end_time: DateTime.now.advance(hours: -50)) - @future_contest = FactoryBot.create(:contest, groups: [@group], problem_set: @contest_set, start_time: DateTime.now.advance(hours: 100), end_time: DateTime.now.advance(hours: 200)) - @everyone_contest = FactoryBot.create(:contest, group_ids: [0], problem_set: @contest_set, start_time: DateTime.now.advance(hours: -100), end_time: DateTime.now.advance(hours: 100)) + let(:superadmin) { FactoryBot.create(:superadmin) } + let(:admin) { FactoryBot.create(:admin) } + let(:user) { FactoryBot.create(:user) } + let(:organiser) { FactoryBot.create(:organiser) } + + let(:group) { FactoryBot.create(:group, members: [user, admin, superadmin]) } + let(:organiser_group) { FactoryBot.create(:group, owner: organiser, members: [user]) } + + let(:group_set) { FactoryBot.create(:problem_set, groups: [group]) } + let(:everyone_set) { FactoryBot.create(:problem_set, group_ids: [0]) } + + let(:private_problem) { FactoryBot.create(:problem) } + let(:group_problem) { FactoryBot.create(:problem, problem_sets: [group_set]) } + let(:user_problem) { FactoryBot.create(:problem, owner: user) } + let(:admin_problem) { FactoryBot.create(:problem, owner: admin) } + let(:everyone_problem) { FactoryBot.create(:problem, problem_sets: [everyone_set]) } + let(:contest_problem) { FactoryBot.create(:problem, problem_sets: [contest_set]) } + + let(:contest_set) { FactoryBot.create(:problem_set) } + let(:private_set) { FactoryBot.create(:problem_set) } + + let(:private_contest) { FactoryBot.create(:contest, problem_set: contest_set) } + + let(:contest) do + FactoryBot.create( + :contest, + groups: [group], + problem_set: contest_set, + start_time: 100.hours.ago, + end_time: 100.hours.from_now + ) + end + + let(:past_contest) do + FactoryBot.create( + :contest, + groups: [group], + problem_set: contest_set, + start_time: 100.hours.ago, + end_time: 50.hours.ago + ) end - after(:all) do - [@member, @group, @organiser_group, @private_problem, @group_set, @everyone_set, @group_problem, @user_problem, @admin_problem, @everyone_problem, @contest_set, @contest, @contest_problem, @private_set, @private_contest, @past_contest, @future_contest, @everyone_contest].reverse_each { |obj| obj.destroy } + + let(:future_contest) do + FactoryBot.create( + :contest, + groups: [group], + problem_set: contest_set, + start_time: 100.hours.from_now, + end_time: 20.hours.from_now + ) + end + + let(:everyone_contest) do + FactoryBot.create( + :contest, + groups: [group], + problem_set: contest_set, + start_time: 100.hours.ago, + end_time: 100.hours.from_now + ) end + describe "on models" do it "superadmin can manage all objects" do - expect(@superadmin).to be_permitted_to :manage, [Problem, Setting, Role, User, Group, Evaluator, Contest] + expect(superadmin).to be_permitted_to :manage, [Problem, Setting, Role, User, Group, Evaluator, Contest] end + it "admin can manage most objects" do - expect(@admin).to be_permitted_to :manage, [Problem, Evaluator, Contest, Problem.new, Group.new, Contest.new, @user, @admin, @group] + expect(admin).to be_permitted_to :manage, [Problem, Evaluator, Contest, Problem.new, Group.new, Contest.new, user, admin, group] end + it "admin cannot manage Role, Setting, group for everyone, and superadmin user" do - expect(@admin).not_to be_permitted_to :manage, [Setting, Role, Group.find(0), @superadmin] + expect(admin).not_to be_permitted_to :manage, [Setting, Role, Group.find(0), superadmin] end + it "user cannot see Role or Setting" do - expect(@user).not_to be_permitted_to [:index, :show, :edit, :new], [Setting, Role] + expect(user).not_to be_permitted_to [:index, :show, :edit, :new], [Setting, Role] end end describe "on problems" do it "admin can :manage all problems" do - expect(@admin).to be_permitted_to [:show, :manage], [@private_problem, @group_problem, @user_problem, @admin_problem, @everyone_problem, @contest_problem] + expect(admin).to be_permitted_to [:show, :manage], [private_problem, group_problem, user_problem, admin_problem, everyone_problem, contest_problem] end it "user can read group or public problems" do - expect(@user).to be_permitted_to :show, @everyone_problem - expect(@user).to be_permitted_to :show, [@group_problem, @everyone_problem] + expect(user).to be_permitted_to :show, everyone_problem + expect(user).to be_permitted_to :show, [group_problem, everyone_problem] end it "user can read/update owned problem" do - expect(@user).to be_permitted_to [:index, :show, :edit, :update], @user_problem + expect(user).to be_permitted_to [:index, :show, :edit, :update], user_problem end it "user cannot read private problems" do - expect(@user).not_to be_permitted_to [:index, :show, :update], [@private_problem, @admin_problem, @contest_problem] + expect(user).not_to be_permitted_to [:index, :show, :update], [private_problem, admin_problem, contest_problem] end it "user can create problem" do - expect(@user).to be_permitted_to [:new, :create], Problem.new(owner_id: users(:user).id) + expect(user).to be_permitted_to [:new, :create], Problem.new(owner_id: user.id) end context "user in contest" do - before(:all) do - @relation = FactoryBot.create(:contest_relation, user_id: users(:user).id, contest_id: @contest.id, started_at: DateTime.now.advance(hours: -1)) - @contest_problem.reload - @contest_user = users(:user).reload - end - after(:all) do - @relation.destroy - @contest_problem.reload - @contest_user.reload + before do + FactoryBot.create(:contest_relation, user_id: user.id, contest_id: contest.id, started_at: DateTime.now.advance(hours: -1)) end + it "can read contest problem" do - expect(@contest_user).to be_permitted_to :show, [@contest_problem] + expect(user).to be_permitted_to :show, [contest_problem] end + it "cannot read/update other problems" do - expect(@contest_user).not_to be_permitted_to [:index, :show, :update], [@private_problem, @admin_problem, @user_problem] + expect(user).not_to be_permitted_to [:index, :show, :update], [private_problem, admin_problem, user_problem] end end end + describe "on problem sets" do it "admin can :manage all problem sets" do - expect(@admin).to be_permitted_to [:show, :manage], [@private_set, @group_set, @contest_set, @everyone_set] + expect(admin).to be_permitted_to [:show, :manage], [private_set, group_set, contest_set, everyone_set] end - # removed feature - # it 'user can read group or public problem sets' do - # expect(@user).to be_permitted_to :show, [@group_set, @everyone_set] - # end + it "user cannot read private or contest problem sets" do - expect(@user).not_to be_permitted_to [:index, :show, :update], [@private_set, @contest_set] + expect(user).not_to be_permitted_to [:index, :show, :update], [private_set, contest_set] end + context "user in contest" do - before(:all) do - @relation = FactoryBot.create(:contest_relation, user_id: users(:user).id, contest_id: @contest.id, started_at: DateTime.now.advance(hours: -1)) - @contest_user = users(:user).reload # refresh .competing? - end - after(:all) do - @relation.destroy - users(:user).reload + before do + FactoryBot.create(:contest_relation, user_id: user.id, contest_id: contest.id, started_at: DateTime.now.advance(hours: -1)) end + it "cannot read/update other problems" do - expect(@contest_user).not_to be_permitted_to [:index, :show, :update], [@private_set, @group_set, @everyone_set] + expect(user.reload).not_to be_permitted_to [:index, :show, :update], [private_set, group_set, everyone_set] end end end describe "on groups" do + let(:new_user) { FactoryBot.create(:user) } + it "members can invite users to open group" do - @organiser_group.update_attributes(visibility: Group::VISIBILITY[:public], membership: Group::MEMBERSHIP[:open]) - expect(@organiser).to be_permitted_to [:invite, :reject], @organiser_group - expect(@member).to be_permitted_to :invite, @organiser_group - expect(@member).not_to be_permitted_to :reject, @organiser_group - expect(@admin).to be_permitted_to [:invite, :reject], @organiser_group + organiser_group.update_attributes(visibility: Group::VISIBILITY[:public], membership: Group::MEMBERSHIP[:open]) + expect(organiser).to be_permitted_to [:invite, :reject], organiser_group + expect(user).to be_permitted_to :invite, organiser_group + expect(user).not_to be_permitted_to :reject, organiser_group + expect(admin).to be_permitted_to [:invite, :reject], organiser_group end + it "members can invite users if group membership is by invitation" do - @organiser_group.update_attributes(visibility: Group::VISIBILITY[:public], membership: Group::MEMBERSHIP[:invitation]) - expect(@member).to be_permitted_to :invite, @organiser_group - expect(@member).not_to be_permitted_to :reject, @organiser_group + organiser_group.update_attributes(visibility: Group::VISIBILITY[:public], membership: Group::MEMBERSHIP[:invitation]) + expect(user).to be_permitted_to :invite, organiser_group + expect(user).not_to be_permitted_to :reject, organiser_group end + it "members cannot invite users if group membership is by application" do - @organiser_group.update_attributes(visibility: Group::VISIBILITY[:public], membership: Group::MEMBERSHIP[:application]) - expect(@member).not_to be_permitted_to :invite, @organiser_group + organiser_group.update_attributes(visibility: Group::VISIBILITY[:public], membership: Group::MEMBERSHIP[:application]) + expect(user).not_to be_permitted_to :invite, organiser_group end - it "user can apply to join if group membership is by invitation" do - @organiser_group.update_attributes(visibility: Group::VISIBILITY[:public], membership: Group::MEMBERSHIP[:invitation]) - expect(@user).to be_permitted_to :apply, @organiser_group + + it "user can invite others to join if group membership is by invitation" do + organiser_group.update_attributes(visibility: Group::VISIBILITY[:public], membership: Group::MEMBERSHIP[:invitation]) + expect(user).to be_permitted_to :invite, organiser_group end + it "user can apply to join if group membership is by application" do - @organiser_group.update_attributes(visibility: Group::VISIBILITY[:public], membership: Group::MEMBERSHIP[:application]) - expect(@user).to be_permitted_to :apply, @organiser_group + organiser_group.update_attributes(visibility: Group::VISIBILITY[:public], membership: Group::MEMBERSHIP[:application]) + expect(new_user).to be_permitted_to :apply, organiser_group end + it "user cannot see, nor apply to private visibility groups, even if membership settings otherwise allow it" do - @organiser_group.update_attributes(visibility: Group::VISIBILITY[:private], membership: Group::MEMBERSHIP[:invitation]) - expect(@user).not_to be_permitted_to [:show, :apply], @organiser_group + organiser_group.update_attributes(visibility: Group::VISIBILITY[:private], membership: Group::MEMBERSHIP[:invitation]) + expect(new_user).not_to be_permitted_to [:show, :apply], organiser_group end end + describe "on contests" do it "user can index contest in group or for everyone" do - expect(@user).to be_permitted_to :index, [@contest, @everyone_contest, @past_contest, @future_contest] - expect(@user).to be_permitted_to :show, [@contest, @everyone_contest, @past_contest] + expect(user).to be_permitted_to :index, [contest, everyone_contest, past_contest, future_contest] + expect(user).to be_permitted_to :show, [contest, everyone_contest, past_contest] end + it "user cannot index private contests" do - expect(@user).not_to be_permitted_to [:index, :show], @private_contest + expect(user).not_to be_permitted_to [:index, :show], private_contest end + it "user can start active contest" do - expect(@user).to be_permitted_to :start, [@contest, @everyone_contest] + expect(user).to be_permitted_to :start, [contest, everyone_contest] end end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 090a5226..f872ada7 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -49,22 +49,13 @@ # config.infer_spec_type_from_file_location! - config.before(:suite) do - FixturesSpecHelper.initialize - end - config.after(:suite) do - FixturesSpecHelper.destroy - end config.include Devise::Test::ControllerHelpers, type: :controller config.include Devise::Test::IntegrationHelpers, type: :request - config.include FixturesSpecHelper, type: :controller # supply fixtures variables config.include ControllersSpecHelper, type: :controller # some macros for testing controllers config.render_views # don't stub views when testing controllers - config.include FixturesSpecHelper, type: :feature # supply fixture variables config.include RequestsSpecHelper, type: :feature # use warden to shortcut login - config.include FixturesSpecHelper, type: :presenter, file_path: %r{spec/presenters} # supply fixture variables config.include ActionView::TestCase::Behavior, type: :presenter, file_path: %r{spec/presenters} end diff --git a/spec/support/controllers_spec_helper.rb b/spec/support/controllers_spec_helper.rb index d603c237..512b682c 100644 --- a/spec/support/controllers_spec_helper.rb +++ b/spec/support/controllers_spec_helper.rb @@ -17,6 +17,14 @@ def process_hash hash hash end + def subject_object(resource) + if respond_to?(resource) + send(resource) + else + instance_variable_get("@#{resource}") + end + end + module ClassMethods def _process_options options options[:attributes] ||= {} @@ -62,8 +70,9 @@ def can_manage resource, options = {} def can_show resource, options = {} options = process_options resource, options + it "can show #{resource}" do - object = instance_variable_get "@#{resource}" + object = subject_object(resource) get :show, id: object.to_param expect(response).to be_success end @@ -71,14 +80,16 @@ def can_show resource, options = {} def can_update resource, options = {} options = process_options resource, options + it "can edit #{resource}" do - object = instance_variable_get "@#{resource}" + object = subject_object(resource) get :edit, id: object.to_param expect(response).to be_success assigns(options[:resource_name]).instance_of?(object.class) end + it "can update #{resource}" do - object = instance_variable_get "@#{resource}" + object = subject_object(resource) put :update, :id => object.to_param, options[:resource_name] => object.attributes.symbolize_keys.merge(options[:attributes]) expect(response).to redirect_to send "#{options[:resource_name]}_path", assigns(options[:resource_name]) expect(assigns(options[:resource_name])).to have_attributes(options[:attributes]) @@ -87,10 +98,12 @@ def can_update resource, options = {} def can_create resource, options = {} options = process_options resource, options + it "can get new #{resource}" do get :new expect(response).to be_success end + it "can create #{resource}" do expect do post :create, options[:resource_name] => options[:attributes] @@ -102,16 +115,19 @@ def can_create resource, options = {} def can_destroy resource, options = {} options = process_options resource, options + it "can destroy #{resource}" do - object = instance_variable_get "@#{resource}" + object = subject_object(resource) expect do delete :destroy, id: object.to_param - end.to change { object.class.count }.by(-1) + end.to change { object.class.count } + .by(-1) expect(response).to be_redirect end end end - # Instance methods + + # Instance methodss RSpec::Matchers.define :have_attributes do |expected| match do |actual| matching = true diff --git a/spec/support/fixtures_spec_helper.rb b/spec/support/fixtures_spec_helper.rb deleted file mode 100644 index 13aae4be..00000000 --- a/spec/support/fixtures_spec_helper.rb +++ /dev/null @@ -1,30 +0,0 @@ -module FixturesSpecHelper - @@table_names = [:users, :problems, :test_sets] - - def self.initialize - @@users = { - user: FactoryBot.create(:user), - organiser: FactoryBot.create(:organiser), - admin: FactoryBot.create(:admin), - superadmin: FactoryBot.create(:superadmin) - } - @@problems = {problem: FactoryBot.create(:problem)} - @@test_sets = {test_set: FactoryBot.create(:test_set, problem: @@problems[:problem])} - end - - def self.destroy - @@table_names.each do |table| - (class_variable_get "@@#{table}".to_sym).each { |k, v| - v.clear_association_cache # touching already destroyed association object causes `touch': can not touch on a new record object (ActiveRecord::ActiveRecordError) - v.destroy - } - end - end - - @@table_names.each do |table| - class_variable_set "@@#{table}".to_sym, {} - define_method table do |key| - (self.class.send :class_variable_get, "@@#{table}".to_sym)[key] - end - end -end