summaryrefslogtreecommitdiff
path: root/spec/models/concerns
diff options
context:
space:
mode:
Diffstat (limited to 'spec/models/concerns')
-rw-r--r--spec/models/concerns/approvable_base_spec.rb28
-rw-r--r--spec/models/concerns/calloutable_spec.rb26
-rw-r--r--spec/models/concerns/featurable_spec.rb5
-rw-r--r--spec/models/concerns/issuable_spec.rb17
-rw-r--r--spec/models/concerns/loose_foreign_key_spec.rb83
-rw-r--r--spec/models/concerns/partitioned_table_spec.rb6
-rw-r--r--spec/models/concerns/sanitizable_spec.rb101
-rw-r--r--spec/models/concerns/taggable_queries_spec.rb9
8 files changed, 268 insertions, 7 deletions
diff --git a/spec/models/concerns/approvable_base_spec.rb b/spec/models/concerns/approvable_base_spec.rb
index c7ea2631a24..79053e98db7 100644
--- a/spec/models/concerns/approvable_base_spec.rb
+++ b/spec/models/concerns/approvable_base_spec.rb
@@ -60,6 +60,34 @@ RSpec.describe ApprovableBase do
end
end
+ describe '#can_be_unapproved_by?' do
+ subject { merge_request.can_be_unapproved_by?(user) }
+
+ before do
+ merge_request.project.add_developer(user)
+ end
+
+ it 'returns false' do
+ is_expected.to be_falsy
+ end
+
+ context 'when a user has approved' do
+ let!(:approval) { create(:approval, merge_request: merge_request, user: user) }
+
+ it 'returns true' do
+ is_expected.to be_truthy
+ end
+ end
+
+ context 'when a user is nil' do
+ let(:user) { nil }
+
+ it 'returns false' do
+ is_expected.to be_falsy
+ end
+ end
+ end
+
describe '.not_approved_by_users_with_usernames' do
subject { MergeRequest.not_approved_by_users_with_usernames([user.username, user2.username]) }
diff --git a/spec/models/concerns/calloutable_spec.rb b/spec/models/concerns/calloutable_spec.rb
new file mode 100644
index 00000000000..d847413de88
--- /dev/null
+++ b/spec/models/concerns/calloutable_spec.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Calloutable do
+ subject { build(:user_callout) }
+
+ describe "Associations" do
+ it { is_expected.to belong_to(:user) }
+ end
+
+ describe 'validations' do
+ it { is_expected.to validate_presence_of(:user) }
+ end
+
+ describe '#dismissed_after?' do
+ let(:some_feature_name) { UserCallout.feature_names.keys.second }
+ let(:callout_dismissed_month_ago) { create(:user_callout, feature_name: some_feature_name, dismissed_at: 1.month.ago )}
+ let(:callout_dismissed_day_ago) { create(:user_callout, feature_name: some_feature_name, dismissed_at: 1.day.ago )}
+
+ it 'returns whether a callout dismissed after specified date' do
+ expect(callout_dismissed_month_ago.dismissed_after?(15.days.ago)).to eq(false)
+ expect(callout_dismissed_day_ago.dismissed_after?(15.days.ago)).to eq(true)
+ end
+ end
+end
diff --git a/spec/models/concerns/featurable_spec.rb b/spec/models/concerns/featurable_spec.rb
index 295f3523dd5..453b6f7f29a 100644
--- a/spec/models/concerns/featurable_spec.rb
+++ b/spec/models/concerns/featurable_spec.rb
@@ -30,8 +30,11 @@ RSpec.describe Featurable do
describe '.set_available_features' do
let!(:klass) do
- Class.new do
+ Class.new(ApplicationRecord) do
include Featurable
+
+ self.table_name = 'project_features'
+
set_available_features %i(feature1 feature2)
def feature1_access_level
diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb
index 071e0dcba44..2a3f639a8ac 100644
--- a/spec/models/concerns/issuable_spec.rb
+++ b/spec/models/concerns/issuable_spec.rb
@@ -368,6 +368,23 @@ RSpec.describe Issuable do
expect(sorted_issue_ids).to eq(sorted_issue_ids.uniq)
end
end
+
+ context 'by title' do
+ let!(:issue1) { create(:issue, project: project, title: 'foo') }
+ let!(:issue2) { create(:issue, project: project, title: 'bar') }
+ let!(:issue3) { create(:issue, project: project, title: 'baz') }
+ let!(:issue4) { create(:issue, project: project, title: 'Baz 2') }
+
+ it 'sorts asc' do
+ issues = project.issues.sort_by_attribute('title_asc')
+ expect(issues).to eq([issue2, issue3, issue4, issue1])
+ end
+
+ it 'sorts desc' do
+ issues = project.issues.sort_by_attribute('title_desc')
+ expect(issues).to eq([issue1, issue4, issue3, issue2])
+ end
+ end
end
describe '#subscribed?' do
diff --git a/spec/models/concerns/loose_foreign_key_spec.rb b/spec/models/concerns/loose_foreign_key_spec.rb
new file mode 100644
index 00000000000..ce5e33261a9
--- /dev/null
+++ b/spec/models/concerns/loose_foreign_key_spec.rb
@@ -0,0 +1,83 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe LooseForeignKey do
+ let(:project_klass) do
+ Class.new(ApplicationRecord) do
+ include LooseForeignKey
+
+ self.table_name = 'projects'
+
+ loose_foreign_key :issues, :project_id, on_delete: :async_delete, gitlab_schema: :gitlab_main
+ loose_foreign_key 'merge_requests', 'project_id', 'on_delete' => 'async_nullify', 'gitlab_schema' => :gitlab_main
+ end
+ end
+
+ it 'exposes the loose foreign key definitions' do
+ definitions = project_klass.loose_foreign_key_definitions
+
+ tables = definitions.map(&:to_table)
+ expect(tables).to eq(%w[issues merge_requests])
+ end
+
+ it 'casts strings to symbol' do
+ definition = project_klass.loose_foreign_key_definitions.last
+
+ expect(definition.from_table).to eq('projects')
+ expect(definition.to_table).to eq('merge_requests')
+ expect(definition.column).to eq('project_id')
+ expect(definition.on_delete).to eq(:async_nullify)
+ expect(definition.options[:gitlab_schema]).to eq(:gitlab_main)
+ end
+
+ context 'validation' do
+ context 'on_delete validation' do
+ let(:invalid_class) do
+ Class.new(ApplicationRecord) do
+ include LooseForeignKey
+
+ self.table_name = 'projects'
+
+ loose_foreign_key :issues, :project_id, on_delete: :async_delete, gitlab_schema: :gitlab_main
+ loose_foreign_key :merge_requests, :project_id, on_delete: :async_nullify, gitlab_schema: :gitlab_main
+ loose_foreign_key :merge_requests, :project_id, on_delete: :destroy, gitlab_schema: :gitlab_main
+ end
+ end
+
+ it 'raises error when invalid `on_delete` option was given' do
+ expect { invalid_class }.to raise_error /Invalid on_delete option given: destroy/
+ end
+ end
+
+ context 'gitlab_schema validation' do
+ let(:invalid_class) do
+ Class.new(ApplicationRecord) do
+ include LooseForeignKey
+
+ self.table_name = 'projects'
+
+ loose_foreign_key :merge_requests, :project_id, on_delete: :async_nullify, gitlab_schema: :unknown
+ end
+ end
+
+ it 'raises error when invalid `gitlab_schema` option was given' do
+ expect { invalid_class }.to raise_error /Invalid gitlab_schema option given: unknown/
+ end
+ end
+
+ context 'inheritance validation' do
+ let(:inherited_project_class) do
+ Class.new(Project) do
+ include LooseForeignKey
+
+ loose_foreign_key :issues, :project_id, on_delete: :async_delete, gitlab_schema: :gitlab_main
+ end
+ end
+
+ it 'raises error when loose_foreign_key is defined in a child ActiveRecord model' do
+ expect { inherited_project_class }.to raise_error /Please define the loose_foreign_key on the Project class/
+ end
+ end
+ end
+end
diff --git a/spec/models/concerns/partitioned_table_spec.rb b/spec/models/concerns/partitioned_table_spec.rb
index c37fb81a1cf..714db4e21bd 100644
--- a/spec/models/concerns/partitioned_table_spec.rb
+++ b/spec/models/concerns/partitioned_table_spec.rb
@@ -35,11 +35,5 @@ RSpec.describe PartitionedTable do
expect(my_class.partitioning_strategy.partitioning_key).to eq(key)
end
-
- it 'registers itself with the PartitionCreator' do
- expect(Gitlab::Database::Partitioning::PartitionManager).to receive(:register).with(my_class)
-
- subject
- end
end
end
diff --git a/spec/models/concerns/sanitizable_spec.rb b/spec/models/concerns/sanitizable_spec.rb
new file mode 100644
index 00000000000..4a1d463d666
--- /dev/null
+++ b/spec/models/concerns/sanitizable_spec.rb
@@ -0,0 +1,101 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Sanitizable do
+ let_it_be(:klass) do
+ Class.new do
+ include ActiveModel::Model
+ include ActiveModel::Attributes
+ include ActiveModel::Validations
+ include ActiveModel::Validations::Callbacks
+ include Sanitizable
+
+ attribute :id, :integer
+ attribute :name, :string
+ attribute :description, :string
+ attribute :html_body, :string
+
+ sanitizes! :name, :description
+
+ def self.model_name
+ ActiveModel::Name.new(self, nil, 'SomeModel')
+ end
+ end
+ end
+
+ shared_examples 'noop' do
+ it 'has no effect' do
+ expect(subject).to eq(input)
+ end
+ end
+
+ shared_examples 'a sanitizable field' do |field|
+ let(:record) { klass.new(id: 1, name: input, description: input, html_body: input) }
+
+ before do
+ record.valid?
+ end
+
+ subject { record.public_send(field) }
+
+ describe field do
+ context 'when input is nil' do
+ let_it_be(:input) { nil }
+
+ it_behaves_like 'noop'
+ end
+
+ context 'when input does not contain any html' do
+ let_it_be(:input) { 'hello, world!' }
+
+ it_behaves_like 'noop'
+ end
+
+ context 'when input contains html' do
+ let_it_be(:input) { 'hello<script>alert(1)</script>' }
+
+ it 'sanitizes the input' do
+ expect(subject).to eq('hello')
+ end
+
+ context 'when input includes html entities' do
+ let(:input) { '<div>hello&world</div>' }
+
+ it 'does not escape them' do
+ expect(subject).to eq(' hello&world ')
+ end
+ end
+ end
+
+ context 'when input contains pre-escaped html entities' do
+ let_it_be(:input) { '&lt;script&gt;alert(1)&lt;/script&gt;' }
+
+ it_behaves_like 'noop'
+
+ it 'is not valid', :aggregate_failures do
+ expect(record).not_to be_valid
+ expect(record.errors.full_messages).to include('Name cannot contain escaped HTML entities')
+ end
+ end
+ end
+ end
+
+ shared_examples 'a non-sanitizable field' do |field, input|
+ describe field do
+ subject { klass.new(field => input).valid? }
+
+ it 'has no effect' do
+ expect(Sanitize).not_to receive(:fragment)
+
+ subject
+ end
+ end
+ end
+
+ it_behaves_like 'a non-sanitizable field', :id, 1
+ it_behaves_like 'a non-sanitizable field', :html_body, 'hello<script>alert(1)</script>'
+
+ it_behaves_like 'a sanitizable field', :name
+ it_behaves_like 'a sanitizable field', :description
+end
diff --git a/spec/models/concerns/taggable_queries_spec.rb b/spec/models/concerns/taggable_queries_spec.rb
new file mode 100644
index 00000000000..0d248c4636e
--- /dev/null
+++ b/spec/models/concerns/taggable_queries_spec.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe TaggableQueries do
+ it 'keeps MAX_TAGS_IDS in sync with TAGS_LIMIT' do
+ expect(described_class::MAX_TAGS_IDS).to eq(Gitlab::Ci::Config::Entry::Tags::TAGS_LIMIT)
+ end
+end