diff --git a/Gemfile b/Gemfile index f980a082..4bdc5e8d 100644 --- a/Gemfile +++ b/Gemfile @@ -73,6 +73,7 @@ group :test do gem 'nyan-cat-formatter' gem 'rspec-autotest' gem 'rspec-rails' + gem 'shoulda-matchers' gem 'simplecov', require: false gem 'webmock' end diff --git a/Gemfile.lock b/Gemfile.lock index fb93dbf4..e3afa6a6 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -365,6 +365,8 @@ GEM selenium-webdriver (3.142.6) childprocess (>= 0.5, < 4.0) rubyzip (>= 1.2.2) + shoulda-matchers (4.1.1) + activesupport (>= 4.2.0) simplecov (0.17.1) docile (~> 1.1) json (>= 1.8, < 3) @@ -491,6 +493,7 @@ DEPENDENCIES rubyzip sass-rails selenium-webdriver + shoulda-matchers simplecov slim-rails sorcery diff --git a/app/models/exercise.rb b/app/models/exercise.rb index 71ffd97a..86b402d8 100644 --- a/app/models/exercise.rb +++ b/app/models/exercise.rb @@ -31,12 +31,12 @@ class Exercise < ApplicationRecord validate :valid_main_file? validates :description, presence: true - validates :execution_environment_id, presence: true, if: -> { !unpublished? } + validates :execution_environment, presence: true, if: -> { !unpublished? } validates :public, boolean_presence: true validates :unpublished, boolean_presence: true validates :title, presence: true validates :token, presence: true, uniqueness: true - validates_uniqueness_of :uuid + validates_uniqueness_of :uuid, if: -> { uuid.present? } @working_time_statistics = nil attr_reader :working_time_statistics diff --git a/app/policies/codeharbor_link_policy.rb b/app/policies/codeharbor_link_policy.rb index e83f9b75..21b1dab2 100644 --- a/app/policies/codeharbor_link_policy.rb +++ b/app/policies/codeharbor_link_policy.rb @@ -1,5 +1,3 @@ -# frozen_string_literal: true - class CodeharborLinkPolicy < ApplicationPolicy def index? false @@ -18,14 +16,20 @@ class CodeharborLinkPolicy < ApplicationPolicy end def edit? - teacher? + owner? end def update? - teacher? + owner? end def destroy? - teacher? + owner? + end + + private + + def owner? + @record.reload.user == @user end end diff --git a/spec/models/codeharbor_link_spec.rb b/spec/models/codeharbor_link_spec.rb new file mode 100644 index 00000000..df1ddba4 --- /dev/null +++ b/spec/models/codeharbor_link_spec.rb @@ -0,0 +1,16 @@ +require 'rails_helper' + +describe CodeharborLink do + it { is_expected.to validate_presence_of(:check_uuid_url) } + it { is_expected.to validate_presence_of(:push_url) } + it { is_expected.to validate_presence_of(:api_key) } + it { is_expected.to belong_to(:user) } + + describe '#to_s' do + subject { codeharbor_link.to_s } + + let(:codeharbor_link) { FactoryBot.create(:codeharbor_link) } + + it { is_expected.to eql codeharbor_link.id.to_s } + end +end diff --git a/spec/models/exercise_spec.rb b/spec/models/exercise_spec.rb index edfbd1cc..3576d3a5 100644 --- a/spec/models/exercise_spec.rb +++ b/spec/models/exercise_spec.rb @@ -22,10 +22,6 @@ describe Exercise do expect(exercise.errors[:description]).to be_present end - it 'validates the presence of an execution environment' do - expect(exercise.errors[:execution_environment_id]).to be_present - end - it 'validates the presence of the public flag' do expect(exercise.errors[:public]).to be_present exercise.update(public: false) @@ -45,6 +41,30 @@ describe Exercise do expect(exercise.errors[:user_type]).to be_present end + context 'when exercise is unpublished' do + subject { FactoryBot.build(:dummy, unpublished: true) } + + it { is_expected.not_to validate_presence_of(:execution_environment) } + end + + context 'when exercise is not unpublished' do + subject { FactoryBot.build(:dummy, unpublished: false) } + + it { is_expected.to validate_presence_of(:execution_environment) } + end + + context 'with uuid' do + subject { FactoryBot.build(:dummy, uuid: SecureRandom.uuid) } + + it { is_expected.to validate_uniqueness_of(:uuid).case_insensitive } + end + + context 'without uuid' do + subject { FactoryBot.build(:dummy, uuid: nil) } + + it { is_expected.not_to validate_uniqueness_of(:uuid) } + end + describe '#average_percentage' do let(:exercise) { FactoryBot.create(:fibonacci) } diff --git a/spec/policies/codeharbor_link_policy_spec.rb b/spec/policies/codeharbor_link_policy_spec.rb new file mode 100644 index 00000000..9569dd90 --- /dev/null +++ b/spec/policies/codeharbor_link_policy_spec.rb @@ -0,0 +1,45 @@ +require 'rails_helper' + +describe CodeharborLinkPolicy do + subject(:policy) { described_class } + + let(:codeharbor_link) { FactoryBot.create(:codeharbor_link) } + + %i[index? show?].each do |action| + permissions(action) do + it 'does not grant access any user' do + %i[external_user teacher admin].each do |factory_name| + expect(policy).not_to permit(FactoryBot.create(factory_name), codeharbor_link) + end + end + end + end + + %i[new? create?].each do |action| + permissions(action) do + it 'grants access to teachers' do + expect(policy).to permit(FactoryBot.create(:teacher), codeharbor_link) + end + + it 'does not grant access to all other users' do + %i[external_user admin].each do |factory_name| + expect(policy).not_to permit(FactoryBot.create(factory_name), codeharbor_link) + end + end + end + end + + %i[destroy? edit? update?].each do |action| + permissions(action) do + it 'grants access to the owner of the link' do + expect(policy).to permit(codeharbor_link.user, codeharbor_link) + end + + it 'does not grant access to arbitrary users' do + %i[external_user admin teacher].each do |factory_name| + expect(policy).not_to permit(FactoryBot.create(factory_name), codeharbor_link) + end + end + end + end +end diff --git a/spec/policies/exercise_policy_spec.rb b/spec/policies/exercise_policy_spec.rb index 4b4acd4e..4de6a9f4 100644 --- a/spec/policies/exercise_policy_spec.rb +++ b/spec/policies/exercise_policy_spec.rb @@ -4,7 +4,7 @@ describe ExercisePolicy do subject { described_class } let(:exercise) { FactoryBot.build(:dummy) } - + permissions :batch_update? do it 'grants access to admins only' do expect(subject).to permit(FactoryBot.build(:admin), exercise) @@ -30,7 +30,7 @@ let(:exercise) { FactoryBot.build(:dummy) } end end - [:clone?, :destroy?, :edit?, :update?].each do |action| + [:clone?, :destroy?, :edit?, :update?, :export_external_check?, :export_external_confirm?].each do |action| permissions(action) do it 'grants access to admins' do expect(subject).to permit(FactoryBot.build(:admin), exercise) diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index faf011d2..735761ff 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -39,3 +39,10 @@ RSpec.configure do |config| # https://relishapp.com/rspec/rspec-rails/docs config.infer_spec_type_from_file_location! end + +Shoulda::Matchers.configure do |config| + config.integrate do |with| + with.test_framework :rspec + with.library :rails + end +end