Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-02-17 18:09:21 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-02-17 18:09:21 +0300
commitc982bb363b3a0390a274197f410a1609a4667760 (patch)
tree8be9521106e8e9af432d179f03c5b3af11a0e207 /spec
parent75a4eaade04ee758bb3b253f27bf1c20c67991f0 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/factories/iteration_cadences.rb13
-rw-r--r--spec/features/issues/gfm_autocomplete_spec.rb30
-rw-r--r--spec/frontend/gfm_auto_complete_spec.js128
-rw-r--r--spec/lib/gitlab/background_migration/set_default_iteration_cadences_spec.rb75
-rw-r--r--spec/lib/gitlab/database/migrations/observers/query_statistics_spec.rb68
-rw-r--r--spec/lib/gitlab/graphql/docs/renderer_spec.rb38
-rw-r--r--spec/lib/gitlab/usage/docs/renderer_spec.rb2
-rw-r--r--spec/lib/gitlab_spec.rb9
-rw-r--r--spec/migrations/schedule_set_default_iteration_cadences_spec.rb41
-rw-r--r--spec/models/concerns/protected_ref_spec.rb77
-rw-r--r--spec/models/iteration_spec.rb97
-rw-r--r--spec/models/iterations/cadence_spec.rb22
-rw-r--r--spec/services/pages/legacy_storage_lease_spec.rb8
13 files changed, 466 insertions, 142 deletions
diff --git a/spec/factories/iteration_cadences.rb b/spec/factories/iteration_cadences.rb
new file mode 100644
index 00000000000..b36f15e3dd4
--- /dev/null
+++ b/spec/factories/iteration_cadences.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+FactoryBot.define do
+ sequence(:cadence_sequential_date) do |n|
+ n.days.from_now
+ end
+
+ factory :iterations_cadence, class: 'Iterations::Cadence' do
+ title
+ group
+ start_date { generate(:cadence_sequential_date) }
+ end
+end
diff --git a/spec/features/issues/gfm_autocomplete_spec.rb b/spec/features/issues/gfm_autocomplete_spec.rb
index e2087868035..a298aca2652 100644
--- a/spec/features/issues/gfm_autocomplete_spec.rb
+++ b/spec/features/issues/gfm_autocomplete_spec.rb
@@ -6,6 +6,7 @@ RSpec.describe 'GFM autocomplete', :js do
let_it_be(:user_xss_title) { 'eve <img src=x onerror=alert(2)&lt;img src=x onerror=alert(1)&gt;' }
let_it_be(:user_xss) { create(:user, name: user_xss_title, username: 'xss.user') }
let_it_be(:user) { create(:user, name: '💃speciąl someone💃', username: 'someone.special') }
+ let_it_be(:user2) { create(:user, name: 'Marge Simpson', username: 'msimpson') }
let_it_be(:group) { create(:group, name: 'Ancestor') }
let_it_be(:child_group) { create(:group, parent: group, name: 'My group') }
let_it_be(:project) { create(:project, group: child_group) }
@@ -16,6 +17,7 @@ RSpec.describe 'GFM autocomplete', :js do
before_all do
project.add_maintainer(user)
project.add_maintainer(user_xss)
+ project.add_maintainer(user2)
end
describe 'when tribute_autocomplete feature flag is off' do
@@ -86,11 +88,7 @@ RSpec.describe 'GFM autocomplete', :js do
wait_for_requests
- expect(page).to have_selector('.atwho-container')
-
- page.within '.atwho-container #at-view-users' do
- expect(find('li').text).to have_content(user_xss.username)
- end
+ expect(find_highlighted_autocomplete_item).to have_content(user_xss.username)
end
it 'opens autocomplete menu for Milestone when field starts with text with item escaping HTML characters' do
@@ -190,7 +188,23 @@ RSpec.describe 'GFM autocomplete', :js do
wait_for_requests
- expect(find('.atwho-view li', visible: true)).to have_content(user.name)
+ expect(find_highlighted_autocomplete_item).to have_content(user.name)
+ end
+
+ it 'shows names that start with the query as the top result' do
+ type(find('#note-body'), '@mar')
+
+ wait_for_requests
+
+ expect(find_highlighted_autocomplete_item).to have_content(user2.name)
+ end
+
+ it 'shows usernames that start with the query as the top result' do
+ type(find('#note-body'), '@msi')
+
+ wait_for_requests
+
+ expect(find_highlighted_autocomplete_item).to have_content(user2.name)
end
it 'selects the first item for non-assignee dropdowns if a query is entered' do
@@ -1004,4 +1018,8 @@ RSpec.describe 'GFM autocomplete', :js do
wait_for_requests
end
+
+ def find_highlighted_autocomplete_item
+ find('.atwho-view li.cur', visible: true)
+ end
end
diff --git a/spec/frontend/gfm_auto_complete_spec.js b/spec/frontend/gfm_auto_complete_spec.js
index 08368e1f2ca..fe1d44e034c 100644
--- a/spec/frontend/gfm_auto_complete_spec.js
+++ b/spec/frontend/gfm_auto_complete_spec.js
@@ -576,55 +576,95 @@ describe('GfmAutoComplete', () => {
});
});
- describe('Members.templateFunction', () => {
- it('should return html with avatarTag and username', () => {
- expect(
- GfmAutoComplete.Members.templateFunction({
- avatarTag: 'IMG',
- username: 'my-group',
- title: '',
- icon: '',
- availabilityStatus: '',
- }),
- ).toBe('<li>IMG my-group <small></small> </li>');
- });
+ describe('GfmAutoComplete.Members', () => {
+ const member = {
+ name: 'Marge Simpson',
+ username: 'msimpson',
+ search: 'MargeSimpson msimpson',
+ };
- it('should add icon if icon is set', () => {
- expect(
- GfmAutoComplete.Members.templateFunction({
- avatarTag: 'IMG',
- username: 'my-group',
- title: '',
- icon: '<i class="icon"/>',
- availabilityStatus: '',
- }),
- ).toBe('<li>IMG my-group <small></small> <i class="icon"/></li>');
+ describe('templateFunction', () => {
+ it('should return html with avatarTag and username', () => {
+ expect(
+ GfmAutoComplete.Members.templateFunction({
+ avatarTag: 'IMG',
+ username: 'my-group',
+ title: '',
+ icon: '',
+ availabilityStatus: '',
+ }),
+ ).toBe('<li>IMG my-group <small></small> </li>');
+ });
+
+ it('should add icon if icon is set', () => {
+ expect(
+ GfmAutoComplete.Members.templateFunction({
+ avatarTag: 'IMG',
+ username: 'my-group',
+ title: '',
+ icon: '<i class="icon"/>',
+ availabilityStatus: '',
+ }),
+ ).toBe('<li>IMG my-group <small></small> <i class="icon"/></li>');
+ });
+
+ it('should add escaped title if title is set', () => {
+ expect(
+ GfmAutoComplete.Members.templateFunction({
+ avatarTag: 'IMG',
+ username: 'my-group',
+ title: 'MyGroup+',
+ icon: '<i class="icon"/>',
+ availabilityStatus: '',
+ }),
+ ).toBe('<li>IMG my-group <small>MyGroup+</small> <i class="icon"/></li>');
+ });
+
+ it('should add user availability status if availabilityStatus is set', () => {
+ expect(
+ GfmAutoComplete.Members.templateFunction({
+ avatarTag: 'IMG',
+ username: 'my-group',
+ title: '',
+ icon: '<i class="icon"/>',
+ availabilityStatus: '<span class="gl-text-gray-500"> (Busy)</span>',
+ }),
+ ).toBe(
+ '<li>IMG my-group <small><span class="gl-text-gray-500"> (Busy)</span></small> <i class="icon"/></li>',
+ );
+ });
});
- it('should add escaped title if title is set', () => {
- expect(
- GfmAutoComplete.Members.templateFunction({
- avatarTag: 'IMG',
- username: 'my-group',
- title: 'MyGroup+',
- icon: '<i class="icon"/>',
- availabilityStatus: '',
- }),
- ).toBe('<li>IMG my-group <small>MyGroup+</small> <i class="icon"/></li>');
+ describe('nameOrUsernameStartsWith', () => {
+ it.each`
+ query | result
+ ${'mar'} | ${true}
+ ${'msi'} | ${true}
+ ${'margesimpson'} | ${true}
+ ${'msimpson'} | ${true}
+ ${'arge'} | ${false}
+ ${'rgesimp'} | ${false}
+ ${'maria'} | ${false}
+ ${'homer'} | ${false}
+ `('returns $result for $query', ({ query, result }) => {
+ expect(GfmAutoComplete.Members.nameOrUsernameStartsWith(member, query)).toBe(result);
+ });
});
- it('should add user availability status if availabilityStatus is set', () => {
- expect(
- GfmAutoComplete.Members.templateFunction({
- avatarTag: 'IMG',
- username: 'my-group',
- title: '',
- icon: '<i class="icon"/>',
- availabilityStatus: '<span class="gl-text-gray-500"> (Busy)</span>',
- }),
- ).toBe(
- '<li>IMG my-group <small><span class="gl-text-gray-500"> (Busy)</span></small> <i class="icon"/></li>',
- );
+ describe('nameOrUsernameIncludes', () => {
+ it.each`
+ query | result
+ ${'mar'} | ${true}
+ ${'msi'} | ${true}
+ ${'margesimpson'} | ${true}
+ ${'msimpson'} | ${true}
+ ${'arge'} | ${true}
+ ${'rgesimp'} | ${true}
+ ${'maria'} | ${false}
+ ${'homer'} | ${false}
+ `('returns $result for $query', ({ query, result }) => {
+ expect(GfmAutoComplete.Members.nameOrUsernameIncludes(member, query)).toBe(result);
+ });
});
});
diff --git a/spec/lib/gitlab/background_migration/set_default_iteration_cadences_spec.rb b/spec/lib/gitlab/background_migration/set_default_iteration_cadences_spec.rb
new file mode 100644
index 00000000000..d0e51593fd4
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/set_default_iteration_cadences_spec.rb
@@ -0,0 +1,75 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::BackgroundMigration::SetDefaultIterationCadences, schema: 20201231133921 do
+ let(:namespaces) { table(:namespaces) }
+ let(:iterations) { table(:sprints) }
+ let(:iterations_cadences) { table(:iterations_cadences) }
+
+ describe '#perform' do
+ context 'when no iteration cadences exists' do
+ let!(:group_1) { namespaces.create!(name: 'group 1', path: 'group-1') }
+ let!(:group_2) { namespaces.create!(name: 'group 2', path: 'group-2') }
+ let!(:group_3) { namespaces.create!(name: 'group 3', path: 'group-3') }
+
+ let!(:iteration_1) { iterations.create!(group_id: group_1.id, iid: 1, title: 'Iteration 1', start_date: 10.days.ago, due_date: 8.days.ago) }
+ let!(:iteration_2) { iterations.create!(group_id: group_3.id, iid: 1, title: 'Iteration 2', start_date: 10.days.ago, due_date: 8.days.ago) }
+ let!(:iteration_3) { iterations.create!(group_id: group_3.id, iid: 1, title: 'Iteration 3', start_date: 5.days.ago, due_date: 2.days.ago) }
+
+ before do
+ described_class.new.perform(group_1.id, group_2.id, group_3.id, namespaces.last.id + 1)
+ end
+
+ it 'creates iterations_cadence records for the requested groups' do
+ expect(iterations_cadences.count).to eq(2)
+ end
+
+ it 'assigns the iteration cadences to the iterations correctly' do
+ iterations_cadence = iterations_cadences.find_by(group_id: group_1.id)
+ iteration_records = iterations.where(iterations_cadence_id: iterations_cadence.id)
+
+ expect(iterations_cadence.start_date).to eq(iteration_1.start_date)
+ expect(iterations_cadence.last_run_date).to eq(iteration_1.start_date)
+ expect(iterations_cadence.title).to eq('group 1 Iterations')
+ expect(iteration_records.size).to eq(1)
+ expect(iteration_records.first.id).to eq(iteration_1.id)
+
+ iterations_cadence = iterations_cadences.find_by(group_id: group_3.id)
+ iteration_records = iterations.where(iterations_cadence_id: iterations_cadence.id)
+
+ expect(iterations_cadence.start_date).to eq(iteration_3.start_date)
+ expect(iterations_cadence.last_run_date).to eq(iteration_3.start_date)
+ expect(iterations_cadence.title).to eq('group 3 Iterations')
+ expect(iteration_records.size).to eq(2)
+ expect(iteration_records.first.id).to eq(iteration_2.id)
+ expect(iteration_records.second.id).to eq(iteration_3.id)
+ end
+ end
+
+ context 'when an iteration cadence exists for a group' do
+ let!(:group) { namespaces.create!(name: 'group', path: 'group') }
+
+ let!(:iterations_cadence_1) { iterations_cadences.create!(group_id: group.id, start_date: 5.days.ago, title: 'Cadence 1') }
+ let!(:iterations_cadence_2) { iterations_cadences.create!(group_id: group.id, start_date: 2.days.ago, title: 'Cadence 2') }
+
+ let!(:iteration_1) { iterations.create!(group_id: group.id, iid: 1, title: 'Iteration 1', start_date: 10.days.ago, due_date: 8.days.ago) }
+ let!(:iteration_2) { iterations.create!(group_id: group.id, iterations_cadence_id: iterations_cadence_1.id, iid: 2, title: 'Iteration 2', start_date: 5.days.ago, due_date: 3.days.ago) }
+ let!(:iteration_3) { iterations.create!(group_id: group.id, iterations_cadence_id: iterations_cadence_2.id, iid: 3, title: 'Iteration 3', start_date: 2.days.ago, due_date: 1.day.ago) }
+
+ subject { described_class.new.perform(group.id) }
+
+ it 'does not create a new iterations_cadence' do
+ expect { subject }.not_to change { iterations_cadences.count }
+ end
+
+ it 'assigns iteration cadences to iterations if needed' do
+ subject
+
+ expect(iteration_1.reload.iterations_cadence_id).to eq(iterations_cadence_1.id)
+ expect(iteration_2.reload.iterations_cadence_id).to eq(iterations_cadence_1.id)
+ expect(iteration_3.reload.iterations_cadence_id).to eq(iterations_cadence_2.id)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/database/migrations/observers/query_statistics_spec.rb b/spec/lib/gitlab/database/migrations/observers/query_statistics_spec.rb
new file mode 100644
index 00000000000..a3b03050b33
--- /dev/null
+++ b/spec/lib/gitlab/database/migrations/observers/query_statistics_spec.rb
@@ -0,0 +1,68 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+RSpec.describe Gitlab::Database::Migrations::Observers::QueryStatistics do
+ subject { described_class.new }
+
+ let(:connection) { ActiveRecord::Base.connection }
+
+ def mock_pgss(enabled: true)
+ if enabled
+ allow(subject).to receive(:function_exists?).with(:pg_stat_statements_reset).and_return(true)
+ allow(connection).to receive(:view_exists?).with(:pg_stat_statements).and_return(true)
+ else
+ allow(subject).to receive(:function_exists?).with(:pg_stat_statements_reset).and_return(false)
+ allow(connection).to receive(:view_exists?).with(:pg_stat_statements).and_return(false)
+ end
+ end
+
+ describe '#before' do
+ context 'with pgss available' do
+ it 'resets pg_stat_statements' do
+ mock_pgss(enabled: true)
+ expect(connection).to receive(:execute).with('select pg_stat_statements_reset()').once
+
+ subject.before
+ end
+ end
+
+ context 'without pgss available' do
+ it 'executes nothing' do
+ mock_pgss(enabled: false)
+ expect(connection).not_to receive(:execute)
+
+ subject.before
+ end
+ end
+ end
+
+ describe '#record' do
+ let(:observation) { Gitlab::Database::Migrations::Observation.new }
+ let(:result) { double }
+ let(:pgss_query) do
+ <<~SQL
+ SELECT query, calls, total_time, max_time, mean_time, rows
+ FROM pg_stat_statements
+ ORDER BY total_time DESC
+ SQL
+ end
+
+ context 'with pgss available' do
+ it 'fetches data from pg_stat_statements and stores on the observation' do
+ mock_pgss(enabled: true)
+ expect(connection).to receive(:execute).with(pgss_query).once.and_return(result)
+
+ expect { subject.record(observation) }.to change { observation.query_statistics }.from(nil).to(result)
+ end
+ end
+
+ context 'without pgss available' do
+ it 'executes nothing' do
+ mock_pgss(enabled: false)
+ expect(connection).not_to receive(:execute)
+
+ expect { subject.record(observation) }.not_to change { observation.query_statistics }
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/graphql/docs/renderer_spec.rb b/spec/lib/gitlab/graphql/docs/renderer_spec.rb
index 064e0c6828b..2923e589ff7 100644
--- a/spec/lib/gitlab/graphql/docs/renderer_spec.rb
+++ b/spec/lib/gitlab/graphql/docs/renderer_spec.rb
@@ -5,21 +5,25 @@ require 'spec_helper'
RSpec.describe Gitlab::Graphql::Docs::Renderer do
describe '#contents' do
# Returns a Schema that uses the given `type`
- def mock_schema(type)
+ def mock_schema(type, field_description)
query_type = Class.new(Types::BaseObject) do
- graphql_name 'QueryType'
+ graphql_name 'Query'
- field :foo, type, null: true
+ field :foo, type, null: true do
+ description field_description
+ argument :id, GraphQL::ID_TYPE, required: false, description: 'ID of the object.'
+ end
end
GraphQL::Schema.define(query: query_type)
end
let_it_be(:template) { Rails.root.join('lib/gitlab/graphql/docs/templates/', 'default.md.haml') }
+ let(:field_description) { 'List of objects.' }
subject(:contents) do
described_class.new(
- mock_schema(type).graphql_definition,
+ mock_schema(type, field_description).graphql_definition,
output_dir: nil,
template: template
).contents
@@ -45,6 +49,32 @@ RSpec.describe Gitlab::Graphql::Docs::Renderer do
is_expected.to include(expectation)
end
+
+ context 'query generation' do
+ let(:expectation) do
+ <<~DOC
+ ### Foo
+
+ List of objects.
+
+ | Name | Description | Type |
+ | ----- | ---- | ----------- |
+ | `id` | ID of the object. | ID |
+ DOC
+ end
+
+ it 'generates the query with arguments' do
+ expect(subject).to include(expectation)
+ end
+
+ context 'when description does not end with `.`' do
+ let(:field_description) { 'List of objects' }
+
+ it 'adds the `.` to the end' do
+ expect(subject).to include(expectation)
+ end
+ end
+ end
end
context 'A type with fields defined in reverse alphabetical order' do
diff --git a/spec/lib/gitlab/usage/docs/renderer_spec.rb b/spec/lib/gitlab/usage/docs/renderer_spec.rb
index 0677aa2d9d7..07f25cfcfa7 100644
--- a/spec/lib/gitlab/usage/docs/renderer_spec.rb
+++ b/spec/lib/gitlab/usage/docs/renderer_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe Gitlab::Usage::Docs::Renderer do
describe 'contents' do
let(:dictionary_path) { Gitlab::Usage::Docs::Renderer::DICTIONARY_PATH }
- let(:items) { Gitlab::Usage::MetricDefinition.definitions }
+ let(:items) { Gitlab::Usage::MetricDefinition.definitions.first(10).to_h }
it 'generates dictionary for given items' do
generated_dictionary = described_class.new(items).contents
diff --git a/spec/lib/gitlab_spec.rb b/spec/lib/gitlab_spec.rb
index 5f945d5b9fc..c5738ae730f 100644
--- a/spec/lib/gitlab_spec.rb
+++ b/spec/lib/gitlab_spec.rb
@@ -363,8 +363,13 @@ RSpec.describe Gitlab do
expect(described_class.maintenance_mode?).to eq(false)
end
- it 'returns false when maintenance mode feature flag is disabled' do
- stub_feature_flags(maintenance_mode: false)
+ it 'returns false when maintenance mode column is not present' do
+ stub_maintenance_mode_setting(true)
+
+ allow(::Gitlab::CurrentSettings.current_application_settings)
+ .to receive(:respond_to?)
+ .with(:maintenance_mode, false)
+ .and_return(false)
expect(described_class.maintenance_mode?).to eq(false)
end
diff --git a/spec/migrations/schedule_set_default_iteration_cadences_spec.rb b/spec/migrations/schedule_set_default_iteration_cadences_spec.rb
new file mode 100644
index 00000000000..9d7f1ac0dec
--- /dev/null
+++ b/spec/migrations/schedule_set_default_iteration_cadences_spec.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe ScheduleSetDefaultIterationCadences do
+ let(:namespaces) { table(:namespaces) }
+ let(:iterations) { table(:sprints) }
+
+ let(:group_1) { namespaces.create!(name: 'test_1', path: 'test_1') }
+ let!(:group_2) { namespaces.create!(name: 'test_2', path: 'test_2') }
+ let(:group_3) { namespaces.create!(name: 'test_3', path: 'test_3') }
+ let(:group_4) { namespaces.create!(name: 'test_4', path: 'test_4') }
+ let(:group_5) { namespaces.create!(name: 'test_5', path: 'test_5') }
+ let(:group_6) { namespaces.create!(name: 'test_6', path: 'test_6') }
+ let(:group_7) { namespaces.create!(name: 'test_7', path: 'test_7') }
+ let(:group_8) { namespaces.create!(name: 'test_8', path: 'test_8') }
+
+ let!(:iteration_1) { iterations.create!(iid: 1, title: 'iteration 1', group_id: group_1.id) }
+ let!(:iteration_2) { iterations.create!(iid: 1, title: 'iteration 2', group_id: group_3.id) }
+ let!(:iteration_3) { iterations.create!(iid: 1, title: 'iteration 2', group_id: group_4.id) }
+ let!(:iteration_4) { iterations.create!(iid: 1, title: 'iteration 2', group_id: group_5.id) }
+ let!(:iteration_5) { iterations.create!(iid: 1, title: 'iteration 2', group_id: group_6.id) }
+ let!(:iteration_6) { iterations.create!(iid: 1, title: 'iteration 2', group_id: group_7.id) }
+ let!(:iteration_7) { iterations.create!(iid: 1, title: 'iteration 2', group_id: group_8.id) }
+
+ around do |example|
+ freeze_time { Sidekiq::Testing.fake! { example.run } }
+ end
+
+ it 'schedules the background jobs', :aggregate_failures do
+ stub_const("#{described_class.name}::BATCH_SIZE", 3)
+
+ migrate!
+
+ expect(BackgroundMigrationWorker.jobs.size).to be(3)
+ expect(described_class::MIGRATION_CLASS).to be_scheduled_delayed_migration(2.minutes, group_1.id, group_3.id, group_4.id)
+ expect(described_class::MIGRATION_CLASS).to be_scheduled_delayed_migration(4.minutes, group_5.id, group_6.id, group_7.id)
+ expect(described_class::MIGRATION_CLASS).to be_scheduled_delayed_migration(6.minutes, group_8.id)
+ end
+end
diff --git a/spec/models/concerns/protected_ref_spec.rb b/spec/models/concerns/protected_ref_spec.rb
deleted file mode 100644
index 0a020736269..00000000000
--- a/spec/models/concerns/protected_ref_spec.rb
+++ /dev/null
@@ -1,77 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe ProtectedRef do
- using RSpec::Parameterized::TableSyntax
-
- let_it_be(:project) { create(:project, :repository) }
- let_it_be(:user) { create(:user, maintainer_projects: [project]) }
-
- where(:klass, :factory, :action) do
- ProtectedBranch | :protected_branch | :push
- ProtectedTag | :protected_tag | :create
- end
-
- with_them do
- describe '#protected_ref_accessible_to?' do
- subject do
- klass.protected_ref_accessible_to?('release', user, project: project, action: action)
- end
-
- it 'user cannot do action if rules do not exist' do
- is_expected.to be_falsy
- end
-
- context 'the ref is protected' do
- let!(:default_rule) { create(factory, :"developers_can_#{action}", project: project, name: 'release') }
-
- context 'all rules permit action' do
- let!(:maintainers_can) { create(factory, :"maintainers_can_#{action}", project: project, name: 'release*') }
-
- it 'user can do action' do
- is_expected.to be_truthy
- end
- end
-
- context 'one of the rules forbids action' do
- let!(:no_one_can) { create(factory, :"no_one_can_#{action}", project: project, name: 'release*') }
-
- it 'user cannot do action' do
- is_expected.to be_falsy
- end
- end
- end
- end
-
- describe '#developers_can?' do
- subject do
- klass.developers_can?(action, 'release')
- end
-
- it 'developers cannot do action if rules do not exist' do
- is_expected.to be_falsy
- end
-
- context 'the ref is protected' do
- let!(:default_rule) { create(factory, :"developers_can_#{action}", project: project, name: 'release') }
-
- context 'all rules permit developers to do action' do
- let!(:developers_can) { create(factory, :"developers_can_#{action}", project: project, name: 'release*') }
-
- it 'developers can do action' do
- is_expected.to be_truthy
- end
- end
-
- context 'one of the rules forbids developers to do action' do
- let!(:maintainers_can) { create(factory, :"maintainers_can_#{action}", project: project, name: 'release*') }
-
- it 'developers cannot do action' do
- is_expected.to be_falsy
- end
- end
- end
- end
- end
-end
diff --git a/spec/models/iteration_spec.rb b/spec/models/iteration_spec.rb
index e7ec5de0ef1..7241a07a215 100644
--- a/spec/models/iteration_spec.rb
+++ b/spec/models/iteration_spec.rb
@@ -5,6 +5,13 @@ require 'spec_helper'
RSpec.describe Iteration do
let_it_be(:project) { create(:project) }
let_it_be(:group) { create(:group) }
+ let(:set_cadence) { nil }
+
+ describe 'associations' do
+ it { is_expected.to belong_to(:project) }
+ it { is_expected.to belong_to(:group) }
+ it { is_expected.to belong_to(:iterations_cadence).inverse_of(:iterations) }
+ end
describe "#iid" do
it "is properly scoped on project and group" do
@@ -32,6 +39,59 @@ RSpec.describe Iteration do
end
end
+ describe 'setting iteration cadence' do
+ let_it_be(:iterations_cadence) { create(:iterations_cadence, group: group, start_date: 10.days.ago) }
+ let(:iteration) { create(:iteration, group: group, iterations_cadence: set_cadence, start_date: 2.days.from_now) }
+
+ context 'when iterations_cadence is set correctly' do
+ let(:set_cadence) { iterations_cadence}
+
+ it 'does not change the iterations_cadence' do
+ expect(iteration.iterations_cadence).to eq(iterations_cadence)
+ end
+ end
+
+ context 'when iterations_cadence exists for the group' do
+ let(:set_cadence) { nil }
+
+ it 'sets the iterations_cadence to the existing record' do
+ expect(iteration.iterations_cadence).to eq(iterations_cadence)
+ end
+ end
+
+ context 'when iterations_cadence does not exists for the group' do
+ let_it_be(:group) { create(:group, name: 'Test group')}
+ let(:iteration) { build(:iteration, group: group, iterations_cadence: set_cadence) }
+
+ it 'creates a default iterations_cadence and uses it for the iteration' do
+ expect { iteration.save! }.to change { Iterations::Cadence.count }.by(1)
+ end
+
+ it 'sets the newly created iterations_cadence to the reecord' do
+ iteration.save!
+
+ expect(iteration.iterations_cadence).to eq(Iterations::Cadence.last)
+ end
+
+ it 'creates the iterations_cadence with the correct attributes' do
+ iteration.save!
+
+ cadence = Iterations::Cadence.last
+
+ expect(cadence.reload.start_date).to eq(iteration.start_date)
+ expect(cadence.title).to eq('Test group Iterations')
+ end
+ end
+
+ context 'when iteration is a project iteration' do
+ it 'does not set the iterations_cadence' do
+ iteration = create(:iteration, iterations_cadence: nil, project: project, skip_project_validation: true)
+
+ expect(iteration.reload.iterations_cadence).to be_nil
+ end
+ end
+ end
+
describe '.filter_by_state' do
let_it_be(:closed_iteration) { create(:iteration, :closed, :skip_future_date_validation, group: group, start_date: 8.days.ago, due_date: 2.days.ago) }
let_it_be(:started_iteration) { create(:iteration, :started, :skip_future_date_validation, group: group, start_date: 1.day.ago, due_date: 6.days.from_now) }
@@ -307,6 +367,43 @@ RSpec.describe Iteration do
end
end
+ describe '#validate_group' do
+ let_it_be(:iterations_cadence) { create(:iterations_cadence, group: group) }
+
+ context 'when the iteration and iteration cadence groups are same' do
+ it 'is valid' do
+ iteration = build(:iteration, group: group, iterations_cadence: iterations_cadence)
+
+ expect(iteration).to be_valid
+ end
+ end
+
+ context 'when the iteration and iteration cadence groups are different' do
+ it 'is invalid' do
+ other_group = create(:group)
+ iteration = build(:iteration, group: other_group, iterations_cadence: iterations_cadence)
+
+ expect(iteration).not_to be_valid
+ end
+ end
+
+ context 'when the iteration belongs to a project and the iteration cadence is set' do
+ it 'is invalid' do
+ iteration = build(:iteration, project: project, iterations_cadence: iterations_cadence, skip_project_validation: true)
+
+ expect(iteration).to be_invalid
+ end
+ end
+
+ context 'when the iteration belongs to a project and the iteration cadence is not set' do
+ it 'is valid' do
+ iteration = build(:iteration, project: project, skip_project_validation: true)
+
+ expect(iteration).to be_valid
+ end
+ end
+ end
+
describe '.within_timeframe' do
let_it_be(:now) { Time.current }
let_it_be(:project) { create(:project, :empty_repo) }
diff --git a/spec/models/iterations/cadence_spec.rb b/spec/models/iterations/cadence_spec.rb
new file mode 100644
index 00000000000..cdeeef97580
--- /dev/null
+++ b/spec/models/iterations/cadence_spec.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Iterations::Cadence do
+ describe 'associations' do
+ subject { build(:iterations_cadence) }
+
+ it { is_expected.to belong_to(:group) }
+ it { is_expected.to have_many(:iterations).inverse_of(:iterations_cadence) }
+ end
+
+ describe 'validations' do
+ subject { build(:iterations_cadence) }
+
+ it { is_expected.to validate_presence_of(:title) }
+ it { is_expected.to validate_presence_of(:start_date) }
+ it { is_expected.to validate_presence_of(:group_id) }
+ it { is_expected.to validate_presence_of(:active) }
+ it { is_expected.to validate_presence_of(:automatic) }
+ end
+end
diff --git a/spec/services/pages/legacy_storage_lease_spec.rb b/spec/services/pages/legacy_storage_lease_spec.rb
index c022da6f47f..092dce093ff 100644
--- a/spec/services/pages/legacy_storage_lease_spec.rb
+++ b/spec/services/pages/legacy_storage_lease_spec.rb
@@ -47,14 +47,6 @@ RSpec.describe ::Pages::LegacyStorageLease do
expect(service.execute).to eq(nil)
end
-
- it 'runs guarded method if feature flag is disabled' do
- stub_feature_flags(pages_use_legacy_storage_lease: false)
-
- expect(service).to receive(:execute_unsafe).and_call_original
-
- expect(service.execute).to eq(true)
- end
end
context 'when another service holds the lease for the different project' do