diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-02-17 18:09:21 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-02-17 18:09:21 +0300 |
commit | c982bb363b3a0390a274197f410a1609a4667760 (patch) | |
tree | 8be9521106e8e9af432d179f03c5b3af11a0e207 /spec | |
parent | 75a4eaade04ee758bb3b253f27bf1c20c67991f0 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r-- | spec/factories/iteration_cadences.rb | 13 | ||||
-rw-r--r-- | spec/features/issues/gfm_autocomplete_spec.rb | 30 | ||||
-rw-r--r-- | spec/frontend/gfm_auto_complete_spec.js | 128 | ||||
-rw-r--r-- | spec/lib/gitlab/background_migration/set_default_iteration_cadences_spec.rb | 75 | ||||
-rw-r--r-- | spec/lib/gitlab/database/migrations/observers/query_statistics_spec.rb | 68 | ||||
-rw-r--r-- | spec/lib/gitlab/graphql/docs/renderer_spec.rb | 38 | ||||
-rw-r--r-- | spec/lib/gitlab/usage/docs/renderer_spec.rb | 2 | ||||
-rw-r--r-- | spec/lib/gitlab_spec.rb | 9 | ||||
-rw-r--r-- | spec/migrations/schedule_set_default_iteration_cadences_spec.rb | 41 | ||||
-rw-r--r-- | spec/models/concerns/protected_ref_spec.rb | 77 | ||||
-rw-r--r-- | spec/models/iteration_spec.rb | 97 | ||||
-rw-r--r-- | spec/models/iterations/cadence_spec.rb | 22 | ||||
-rw-r--r-- | spec/services/pages/legacy_storage_lease_spec.rb | 8 |
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)<img src=x onerror=alert(1)>' } 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 |