diff options
Diffstat (limited to 'spec')
34 files changed, 621 insertions, 67 deletions
diff --git a/spec/finders/milestones_finder_spec.rb b/spec/finders/milestones_finder_spec.rb index 3545ff35ed8..3402eb39b3b 100644 --- a/spec/finders/milestones_finder_spec.rb +++ b/spec/finders/milestones_finder_spec.rb @@ -3,13 +3,14 @@ require 'spec_helper' describe MilestonesFinder do + let(:now) { Time.now } let(:group) { create(:group) } let(:project_1) { create(:project, namespace: group) } let(:project_2) { create(:project, namespace: group) } - let!(:milestone_1) { create(:milestone, group: group, title: 'one test', due_date: Date.today) } - let!(:milestone_2) { create(:milestone, group: group) } - let!(:milestone_3) { create(:milestone, project: project_1, state: 'active', due_date: Date.tomorrow) } - let!(:milestone_4) { create(:milestone, project: project_2, state: 'active') } + let!(:milestone_1) { create(:milestone, group: group, title: 'one test', start_date: now - 1.day, due_date: now) } + let!(:milestone_2) { create(:milestone, group: group, start_date: now + 1.day, due_date: now + 2.days) } + let!(:milestone_3) { create(:milestone, project: project_1, state: 'active', start_date: now + 2.days, due_date: now + 3.days) } + let!(:milestone_4) { create(:milestone, project: project_2, state: 'active', start_date: now + 4.days, due_date: now + 5.days) } it 'returns milestones for projects' do result = described_class.new(project_ids: [project_1.id, project_2.id], state: 'all').execute @@ -33,8 +34,11 @@ describe MilestonesFinder do end it 'orders milestones by due date' do - expect(result.first).to eq(milestone_1) - expect(result.second).to eq(milestone_3) + milestone = create(:milestone, group: group, due_date: now - 2.days) + + expect(result.first).to eq(milestone) + expect(result.second).to eq(milestone_1) + expect(result.third).to eq(milestone_2) end end @@ -77,6 +81,34 @@ describe MilestonesFinder do expect(result.to_a).to contain_exactly(milestone_1) end + + context 'by timeframe' do + it 'returns milestones with start_date and due_date between timeframe' do + params.merge!(start_date: now - 1.day, end_date: now + 3.days) + + milestones = described_class.new(params).execute + + expect(milestones).to match_array([milestone_1, milestone_2, milestone_3]) + end + + it 'returns milestones which starts before the timeframe' do + milestone = create(:milestone, project: project_2, start_date: now - 5.days) + params.merge!(start_date: now - 3.days, end_date: now - 2.days) + + milestones = described_class.new(params).execute + + expect(milestones).to match_array([milestone]) + end + + it 'returns milestones which ends after the timeframe' do + milestone = create(:milestone, project: project_2, due_date: now + 6.days) + params.merge!(start_date: now + 6.days, end_date: now + 7.days) + + milestones = described_class.new(params).execute + + expect(milestones).to match_array([milestone]) + end + end end describe '#find_by' do diff --git a/spec/frontend/monitoring/components/charts/stacked_column_spec.js b/spec/frontend/monitoring/components/charts/stacked_column_spec.js new file mode 100644 index 00000000000..abb89ac15ef --- /dev/null +++ b/spec/frontend/monitoring/components/charts/stacked_column_spec.js @@ -0,0 +1,45 @@ +import { shallowMount } from '@vue/test-utils'; +import { GlStackedColumnChart } from '@gitlab/ui/dist/charts'; +import StackedColumnChart from '~/monitoring/components/charts/stacked_column.vue'; +import { stackedColumnMockedData } from '../../mock_data'; + +jest.mock('~/lib/utils/icon_utils', () => ({ + getSvgIconPathContent: jest.fn().mockResolvedValue('mockSvgPathContent'), +})); + +describe('Stacked column chart component', () => { + let wrapper; + const glStackedColumnChart = () => wrapper.find(GlStackedColumnChart); + + beforeEach(() => { + wrapper = shallowMount(StackedColumnChart, { + propsData: { + graphData: stackedColumnMockedData, + }, + }); + }); + + afterEach(() => { + wrapper.destroy(); + }); + + describe('with graphData present', () => { + it('is a Vue instance', () => { + expect(glStackedColumnChart().exists()).toBe(true); + }); + + it('should contain the same number of elements in the seriesNames computed prop as the graphData metrics prop', () => + wrapper.vm + .$nextTick() + .then(expect(wrapper.vm.seriesNames).toHaveLength(stackedColumnMockedData.metrics.length))); + + it('should contain the same number of elements in the groupBy computed prop as the graphData result prop', () => + wrapper.vm + .$nextTick() + .then( + expect(wrapper.vm.groupBy).toHaveLength( + stackedColumnMockedData.metrics[0].result[0].values.length, + ), + )); + }); +}); diff --git a/spec/frontend/monitoring/mock_data.js b/spec/frontend/monitoring/mock_data.js index 5fd73b73e0d..0c985ba4fca 100644 --- a/spec/frontend/monitoring/mock_data.js +++ b/spec/frontend/monitoring/mock_data.js @@ -665,3 +665,50 @@ export const graphDataPrometheusQueryRangeMultiTrack = { }, ], }; + +export const stackedColumnMockedData = { + title: 'memories', + type: 'stacked-column', + x_label: 'x label', + y_label: 'y label', + metrics: [ + { + label: 'memory_1024', + unit: 'count', + series_name: 'group 1', + prometheus_endpoint_path: + '/root/autodevops-deploy-6/-/environments/24/prometheus/api/v1/query_range?query=avg%28sum%28container_memory_usage_bytes%7Bcontainer_name%21%3D%22POD%22%2Cpod_name%3D~%22%5E%25%7Bci_environment_slug%7D-%28%5B%5Ec%5D.%2A%7Cc%28%5B%5Ea%5D%7Ca%28%5B%5En%5D%7Cn%28%5B%5Ea%5D%7Ca%28%5B%5Er%5D%7Cr%5B%5Ey%5D%29%29%29%29.%2A%7C%29-%28.%2A%29%22%2Cnamespace%3D%22%25%7Bkube_namespace%7D%22%7D%29+by+%28job%29%29+without+%28job%29+%2F+count%28avg%28container_memory_usage_bytes%7Bcontainer_name%21%3D%22POD%22%2Cpod_name%3D~%22%5E%25%7Bci_environment_slug%7D-%28%5B%5Ec%5D.%2A%7Cc%28%5B%5Ea%5D%7Ca%28%5B%5En%5D%7Cn%28%5B%5Ea%5D%7Ca%28%5B%5Er%5D%7Cr%5B%5Ey%5D%29%29%29%29.%2A%7C%29-%28.%2A%29%22%2Cnamespace%3D%22%25%7Bkube_namespace%7D%22%7D%29+without+%28job%29%29+%2F1024%2F1024', + metric_id: 'undefined_metric_of_ages_1024', + metricId: 'undefined_metric_of_ages_1024', + result: [ + { + metric: {}, + values: [ + ['2020-01-30 12:00:00', '5'], + ['2020-01-30 12:01:00', '10'], + ['2020-01-30 12:02:00', '15'], + ], + }, + ], + }, + { + label: 'memory_1000', + unit: 'count', + series_name: 'group 2', + prometheus_endpoint_path: + '/root/autodevops-deploy-6/-/environments/24/prometheus/api/v1/query_range?query=avg%28sum%28container_memory_usage_bytes%7Bcontainer_name%21%3D%22POD%22%2Cpod_name%3D~%22%5E%25%7Bci_environment_slug%7D-%28%5B%5Ec%5D.%2A%7Cc%28%5B%5Ea%5D%7Ca%28%5B%5En%5D%7Cn%28%5B%5Ea%5D%7Ca%28%5B%5Er%5D%7Cr%5B%5Ey%5D%29%29%29%29.%2A%7C%29-%28.%2A%29%22%2Cnamespace%3D%22%25%7Bkube_namespace%7D%22%7D%29+by+%28job%29%29+without+%28job%29+%2F+count%28avg%28container_memory_usage_bytes%7Bcontainer_name%21%3D%22POD%22%2Cpod_name%3D~%22%5E%25%7Bci_environment_slug%7D-%28%5B%5Ec%5D.%2A%7Cc%28%5B%5Ea%5D%7Ca%28%5B%5En%5D%7Cn%28%5B%5Ea%5D%7Ca%28%5B%5Er%5D%7Cr%5B%5Ey%5D%29%29%29%29.%2A%7C%29-%28.%2A%29%22%2Cnamespace%3D%22%25%7Bkube_namespace%7D%22%7D%29+without+%28job%29%29+%2F1024%2F1024', + metric_id: 'undefined_metric_of_ages_1000', + metricId: 'undefined_metric_of_ages_1000', + result: [ + { + metric: {}, + values: [ + ['2020-01-30 12:00:00', '20'], + ['2020-01-30 12:01:00', '25'], + ['2020-01-30 12:02:00', '30'], + ], + }, + ], + }, + ], +}; diff --git a/spec/graphql/resolvers/milestone_resolver_spec.rb b/spec/graphql/resolvers/milestone_resolver_spec.rb new file mode 100644 index 00000000000..297130c2027 --- /dev/null +++ b/spec/graphql/resolvers/milestone_resolver_spec.rb @@ -0,0 +1,93 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Resolvers::MilestoneResolver do + include GraphqlHelpers + + describe '#resolve' do + let_it_be(:current_user) { create(:user) } + + context 'for group milestones' do + let_it_be(:now) { Time.now } + let_it_be(:group) { create(:group, :private) } + + def resolve_group_milestones(args = {}, context = { current_user: current_user }) + resolve(described_class, obj: group, args: args, ctx: context) + end + + before do + group.add_developer(current_user) + end + + it 'calls MilestonesFinder#execute' do + expect_next_instance_of(MilestonesFinder) do |finder| + expect(finder).to receive(:execute) + end + + resolve_group_milestones + end + + context 'without parameters' do + it 'calls MilestonesFinder to retrieve all milestones' do + expect(MilestonesFinder).to receive(:new) + .with(group_ids: group.id, state: 'all', start_date: nil, end_date: nil) + .and_call_original + + resolve_group_milestones + end + end + + context 'with parameters' do + it 'calls MilestonesFinder with correct parameters' do + start_date = now + end_date = start_date + 1.hour + + expect(MilestonesFinder).to receive(:new) + .with(group_ids: group.id, state: 'closed', start_date: start_date, end_date: end_date) + .and_call_original + + resolve_group_milestones(start_date: start_date, end_date: end_date, state: 'closed') + end + end + + context 'by timeframe' do + context 'when start_date and end_date are present' do + context 'when start date is after end_date' do + it 'raises error' do + expect do + resolve_group_milestones(start_date: now, end_date: now - 2.days) + end.to raise_error(Gitlab::Graphql::Errors::ArgumentError, "startDate is after endDate") + end + end + end + + context 'when only start_date is present' do + it 'raises error' do + expect do + resolve_group_milestones(start_date: now) + end.to raise_error(Gitlab::Graphql::Errors::ArgumentError, /Both startDate and endDate/) + end + end + + context 'when only end_date is present' do + it 'raises error' do + expect do + resolve_group_milestones(end_date: now) + end.to raise_error(Gitlab::Graphql::Errors::ArgumentError, /Both startDate and endDate/) + end + end + end + + context 'when user cannot read milestones' do + it 'raises error' do + unauthorized_user = create(:user) + + expect do + resolve_group_milestones({}, { current_user: unauthorized_user }) + end.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable) + end + end + end + end +end diff --git a/spec/lib/gitlab/search/found_blob_spec.rb b/spec/lib/gitlab/search/found_blob_spec.rb index 07842faa638..ce6a54100a5 100644 --- a/spec/lib/gitlab/search/found_blob_spec.rb +++ b/spec/lib/gitlab/search/found_blob_spec.rb @@ -156,4 +156,14 @@ describe Gitlab::Search::FoundBlob do end end end + + describe 'policy' do + let(:project) { build(:project, :repository) } + + subject { described_class.new(project: project) } + + it 'works with policy' do + expect(Ability.allowed?(project.creator, :read_blob, subject)).to be_truthy + end + end end diff --git a/spec/lib/gitlab/search/found_wiki_page_spec.rb b/spec/lib/gitlab/search/found_wiki_page_spec.rb new file mode 100644 index 00000000000..e8b6728aba5 --- /dev/null +++ b/spec/lib/gitlab/search/found_wiki_page_spec.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Gitlab::Search::FoundWikiPage do + let(:project) { create(:project, :public, :repository) } + + describe 'policy' do + let(:project) { build(:project, :repository) } + let(:found_blob) { Gitlab::Search::FoundBlob.new(project: project) } + + subject { described_class.new(found_blob) } + + it 'works with policy' do + expect(Ability.allowed?(project.creator, :read_wiki_page, subject)).to be_truthy + end + end +end diff --git a/spec/models/abuse_report_spec.rb b/spec/models/abuse_report_spec.rb index 814df472389..2c4fa398636 100644 --- a/spec/models/abuse_report_spec.rb +++ b/spec/models/abuse_report_spec.rb @@ -3,8 +3,8 @@ require 'spec_helper' describe AbuseReport do - set(:report) { create(:abuse_report) } - set(:user) { create(:admin) } + let_it_be(:report, reload: true) { create(:abuse_report) } + let_it_be(:user, reload: true) { create(:admin) } subject { report } it { expect(subject).to be_valid } diff --git a/spec/models/award_emoji_spec.rb b/spec/models/award_emoji_spec.rb index b15b26b1630..b2d58dd95ad 100644 --- a/spec/models/award_emoji_spec.rb +++ b/spec/models/award_emoji_spec.rb @@ -45,8 +45,8 @@ describe AwardEmoji do end describe 'scopes' do - set(:thumbsup) { create(:award_emoji, name: 'thumbsup') } - set(:thumbsdown) { create(:award_emoji, name: 'thumbsdown') } + let_it_be(:thumbsup) { create(:award_emoji, name: 'thumbsup') } + let_it_be(:thumbsdown) { create(:award_emoji, name: 'thumbsdown') } describe '.upvotes' do it { expect(described_class.upvotes).to contain_exactly(thumbsup) } diff --git a/spec/models/blob_viewer/gitlab_ci_yml_spec.rb b/spec/models/blob_viewer/gitlab_ci_yml_spec.rb index 02993052124..e645733e02d 100644 --- a/spec/models/blob_viewer/gitlab_ci_yml_spec.rb +++ b/spec/models/blob_viewer/gitlab_ci_yml_spec.rb @@ -6,9 +6,8 @@ describe BlobViewer::GitlabCiYml do include FakeBlobHelpers include RepoHelpers - set(:project) { create(:project, :repository) } - set(:user) { create(:user) } - + let_it_be(:project) { create(:project, :repository) } + let_it_be(:user) { create(:user) } let(:data) { File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml')) } let(:blob) { fake_blob(path: '.gitlab-ci.yml', data: data) } let(:sha) { sample_commit.id } diff --git a/spec/models/ci/artifact_blob_spec.rb b/spec/models/ci/artifact_blob_spec.rb index f63816fd92a..8a66b55cc15 100644 --- a/spec/models/ci/artifact_blob_spec.rb +++ b/spec/models/ci/artifact_blob_spec.rb @@ -3,8 +3,8 @@ require 'spec_helper' describe Ci::ArtifactBlob do - set(:project) { create(:project, :public) } - set(:build) { create(:ci_build, :artifacts, project: project) } + let_it_be(:project) { create(:project, :public) } + let_it_be(:build) { create(:ci_build, :artifacts, project: project) } let(:entry) { build.artifacts_metadata_entry('other_artifacts_0.1.2/another-subdirectory/banana_sample.gif') } subject { described_class.new(entry) } diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb index 013581c0d94..09d6d661d81 100644 --- a/spec/models/ci/pipeline_spec.rb +++ b/spec/models/ci/pipeline_spec.rb @@ -7,7 +7,7 @@ describe Ci::Pipeline, :mailer do include StubRequests let(:user) { create(:user) } - set(:project) { create(:project) } + let_it_be(:project) { create(:project) } let(:pipeline) do create(:ci_empty_pipeline, status: :created, project: project) @@ -231,7 +231,7 @@ describe Ci::Pipeline, :mailer do describe '#legacy_detached_merge_request_pipeline?' do subject { pipeline.legacy_detached_merge_request_pipeline? } - set(:merge_request) { create(:merge_request) } + let_it_be(:merge_request) { create(:merge_request) } let(:ref) { 'feature' } let(:target_sha) { nil } diff --git a/spec/models/clusters/cluster_spec.rb b/spec/models/clusters/cluster_spec.rb index 44ca4a06e2d..85860da38ad 100644 --- a/spec/models/clusters/cluster_spec.rb +++ b/spec/models/clusters/cluster_spec.rb @@ -544,7 +544,7 @@ describe Clusters::Cluster, :use_clean_rails_memory_store_caching do end describe '#applications' do - set(:cluster) { create(:cluster) } + let_it_be(:cluster, reload: true) { create(:cluster) } subject { cluster.applications } diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb index 782d1ac4552..c09f5bc4f4d 100644 --- a/spec/models/commit_spec.rb +++ b/spec/models/commit_spec.rb @@ -17,7 +17,7 @@ describe Commit do end describe '.lazy' do - set(:project) { create(:project, :repository) } + let_it_be(:project) { create(:project, :repository) } context 'when the commits are found' do let(:oids) do diff --git a/spec/models/commit_status_spec.rb b/spec/models/commit_status_spec.rb index 40652614101..e1a748da7fd 100644 --- a/spec/models/commit_status_spec.rb +++ b/spec/models/commit_status_spec.rb @@ -3,9 +3,9 @@ require 'spec_helper' describe CommitStatus do - set(:project) { create(:project, :repository) } + let_it_be(:project) { create(:project, :repository) } - set(:pipeline) do + let_it_be(:pipeline) do create(:ci_pipeline, project: project, sha: project.commit.id) end diff --git a/spec/models/concerns/batch_destroy_dependent_associations_spec.rb b/spec/models/concerns/batch_destroy_dependent_associations_spec.rb index 1fe90d3cc9a..d2373926802 100644 --- a/spec/models/concerns/batch_destroy_dependent_associations_spec.rb +++ b/spec/models/concerns/batch_destroy_dependent_associations_spec.rb @@ -15,7 +15,7 @@ describe BatchDestroyDependentAssociations do end describe '#dependent_associations_to_destroy' do - set(:project) { TestProject.new } + let_it_be(:project) { TestProject.new } it 'returns the right associations' do expect(project.dependent_associations_to_destroy.map(&:name)).to match_array([:builds]) @@ -23,9 +23,9 @@ describe BatchDestroyDependentAssociations do end describe '#destroy_dependent_associations_in_batches' do - set(:project) { create(:project) } - set(:build) { create(:ci_build, project: project) } - set(:notification_setting) { create(:notification_setting, project: project) } + let_it_be(:project) { create(:project) } + let_it_be(:build) { create(:ci_build, project: project) } + let_it_be(:notification_setting) { create(:notification_setting, project: project) } let!(:todos) { create(:todo, project: project) } it 'destroys multiple builds' do diff --git a/spec/models/identity_spec.rb b/spec/models/identity_spec.rb index 74ddc2d6284..9f120775a3c 100644 --- a/spec/models/identity_spec.rb +++ b/spec/models/identity_spec.rb @@ -13,7 +13,7 @@ describe Identity do end describe 'validations' do - set(:user) { create(:user) } + let_it_be(:user) { create(:user) } context 'with existing user and provider' do before do diff --git a/spec/models/label_note_spec.rb b/spec/models/label_note_spec.rb index dd2c702a7a9..34560acfa9e 100644 --- a/spec/models/label_note_spec.rb +++ b/spec/models/label_note_spec.rb @@ -3,20 +3,20 @@ require 'spec_helper' describe LabelNote do - set(:project) { create(:project, :repository) } - set(:user) { create(:user) } - set(:label) { create(:label, project: project) } - set(:label2) { create(:label, project: project) } + let_it_be(:project) { create(:project, :repository) } + let_it_be(:user) { create(:user) } + let_it_be(:label) { create(:label, project: project) } + let_it_be(:label2) { create(:label, project: project) } let(:resource_parent) { project } context 'when resource is issue' do - set(:resource) { create(:issue, project: project) } + let_it_be(:resource) { create(:issue, project: project) } it_behaves_like 'label note created from events' end context 'when resource is merge request' do - set(:resource) { create(:merge_request, source_project: project, target_project: project) } + let_it_be(:resource) { create(:merge_request, source_project: project, target_project: project) } it_behaves_like 'label note created from events' end diff --git a/spec/models/lfs_file_lock_spec.rb b/spec/models/lfs_file_lock_spec.rb index a42346c341d..0a47ded43fb 100644 --- a/spec/models/lfs_file_lock_spec.rb +++ b/spec/models/lfs_file_lock_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe LfsFileLock do - set(:lfs_file_lock) { create(:lfs_file_lock) } + let_it_be(:lfs_file_lock, reload: true) { create(:lfs_file_lock) } subject { lfs_file_lock } it { is_expected.to belong_to(:project) } diff --git a/spec/models/lfs_object_spec.rb b/spec/models/lfs_object_spec.rb index 44445429d3e..51713906d06 100644 --- a/spec/models/lfs_object_spec.rb +++ b/spec/models/lfs_object_spec.rb @@ -44,8 +44,8 @@ describe LfsObject do end describe '#project_allowed_access?' do - set(:lfs_object) { create(:lfs_objects_project).lfs_object } - set(:project) { create(:project) } + let_it_be(:lfs_object) { create(:lfs_objects_project).lfs_object } + let_it_be(:project, reload: true) { create(:project) } it 'returns true when project is linked' do create(:lfs_objects_project, lfs_object: lfs_object, project: project) @@ -58,9 +58,9 @@ describe LfsObject do end context 'when project is a member of a fork network' do - set(:fork_network) { create(:fork_network) } - set(:fork_network_root_project) { fork_network.root_project } - set(:fork_network_membership) { create(:fork_network_member, project: project, fork_network: fork_network) } + let_it_be(:fork_network) { create(:fork_network) } + let_it_be(:fork_network_root_project, reload: true) { fork_network.root_project } + let_it_be(:fork_network_membership) { create(:fork_network_member, project: project, fork_network: fork_network) } it 'returns true for all members when forked project is linked' do create(:lfs_objects_project, lfs_object: lfs_object, project: project) diff --git a/spec/models/lfs_objects_project_spec.rb b/spec/models/lfs_objects_project_spec.rb index e320f873989..31300828a43 100644 --- a/spec/models/lfs_objects_project_spec.rb +++ b/spec/models/lfs_objects_project_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe LfsObjectsProject do - set(:project) { create(:project) } + let_it_be(:project) { create(:project) } subject do create(:lfs_objects_project, project: project) diff --git a/spec/models/merge_request_diff_spec.rb b/spec/models/merge_request_diff_spec.rb index 78b9e8bc217..8167241faa8 100644 --- a/spec/models/merge_request_diff_spec.rb +++ b/spec/models/merge_request_diff_spec.rb @@ -54,20 +54,20 @@ describe MergeRequestDiff do end describe '.ids_for_external_storage_migration' do - set(:merge_request) { create(:merge_request) } - set(:outdated) { merge_request.merge_request_diff } - set(:latest) { merge_request.create_merge_request_diff } + let_it_be(:merge_request) { create(:merge_request) } + let_it_be(:outdated) { merge_request.merge_request_diff } + let_it_be(:latest) { merge_request.create_merge_request_diff } - set(:closed_mr) { create(:merge_request, :closed_last_month) } + let_it_be(:closed_mr) { create(:merge_request, :closed_last_month) } let(:closed) { closed_mr.merge_request_diff } - set(:merged_mr) { create(:merge_request, :merged_last_month) } + let_it_be(:merged_mr) { create(:merge_request, :merged_last_month) } let(:merged) { merged_mr.merge_request_diff } - set(:recently_closed_mr) { create(:merge_request, :closed) } + let_it_be(:recently_closed_mr) { create(:merge_request, :closed) } let(:closed_recently) { recently_closed_mr.merge_request_diff } - set(:recently_merged_mr) { create(:merge_request, :merged) } + let_it_be(:recently_merged_mr) { create(:merge_request, :merged) } let(:merged_recently) { recently_merged_mr.merge_request_diff } before do diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index 470b67afe07..08348e3767a 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -1091,8 +1091,8 @@ describe MergeRequest do end describe '#can_remove_source_branch?' do - set(:user) { create(:user) } - set(:merge_request) { create(:merge_request, :simple) } + let_it_be(:user) { create(:user) } + let_it_be(:merge_request, reload: true) { create(:merge_request, :simple) } subject { merge_request } diff --git a/spec/models/milestone_spec.rb b/spec/models/milestone_spec.rb index d84a8665dc8..04587ef4240 100644 --- a/spec/models/milestone_spec.rb +++ b/spec/models/milestone_spec.rb @@ -197,6 +197,15 @@ describe Milestone do end end + it_behaves_like 'within_timeframe scope' do + let_it_be(:now) { Time.now } + let_it_be(:project) { create(:project, :empty_repo) } + let_it_be(:resource_1) { create(:milestone, project: project, start_date: now - 1.day, due_date: now + 1.day) } + let_it_be(:resource_2) { create(:milestone, project: project, start_date: now + 2.days, due_date: now + 3.days) } + let_it_be(:resource_3) { create(:milestone, project: project, due_date: now) } + let_it_be(:resource_4) { create(:milestone, project: project, start_date: now) } + end + describe "#percent_complete" do it "does not count open issues" do milestone.issues << issue @@ -517,9 +526,9 @@ describe Milestone do end describe '.sort_by_attribute' do - set(:milestone_1) { create(:milestone, title: 'Foo') } - set(:milestone_2) { create(:milestone, title: 'Bar') } - set(:milestone_3) { create(:milestone, title: 'Zoo') } + let_it_be(:milestone_1) { create(:milestone, title: 'Foo') } + let_it_be(:milestone_2) { create(:milestone, title: 'Bar') } + let_it_be(:milestone_3) { create(:milestone, title: 'Zoo') } context 'ordering by name ascending' do it 'sorts by title ascending' do @@ -555,7 +564,7 @@ describe Milestone do end it 'returns the quantity of milestones in each possible state' do - expected_count = { opened: 5, closed: 6, all: 11 } + expected_count = { opened: 2, closed: 6, all: 8 } count = described_class.states_count(Project.all, Group.all) expect(count).to eq(expected_count) diff --git a/spec/models/project_auto_devops_spec.rb b/spec/models/project_auto_devops_spec.rb index 2a821b20aa8..5af25ac1437 100644 --- a/spec/models/project_auto_devops_spec.rb +++ b/spec/models/project_auto_devops_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe ProjectAutoDevops do - set(:project) { build(:project) } + let_it_be(:project) { build(:project) } it_behaves_like 'having unique enum values' diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 679e6142416..fdacfbe24d1 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -40,7 +40,7 @@ describe Repository do end describe '#branch_names_contains' do - set(:project) { create(:project, :repository) } + let_it_be(:project) { create(:project, :repository) } let(:repository) { project.repository } subject { repository.branch_names_contains(sample_commit.id) } @@ -328,7 +328,7 @@ describe Repository do end describe '#new_commits' do - set(:project) { create(:project, :repository) } + let_it_be(:project) { create(:project, :repository) } let(:repository) { project.repository } subject { repository.new_commits(rev) } @@ -356,7 +356,7 @@ describe Repository do end describe '#commits_by' do - set(:project) { create(:project, :repository) } + let_it_be(:project) { create(:project, :repository) } let(:oids) { TestEnv::BRANCH_SHA.values } subject { project.repository.commits_by(oids: oids) } @@ -2575,7 +2575,7 @@ describe Repository do end describe 'commit cache' do - set(:project) { create(:project, :repository) } + let_it_be(:project) { create(:project, :repository) } it 'caches based on SHA' do # Gets the commit oid, and warms the cache @@ -2723,7 +2723,7 @@ describe Repository do end describe '#merge_base' do - set(:project) { create(:project, :repository) } + let_it_be(:project) { create(:project, :repository) } subject(:repository) { project.repository } it 'only makes one gitaly call' do @@ -2782,7 +2782,7 @@ describe Repository do end describe "#blobs_metadata" do - set(:project) { create(:project, :repository) } + let_it_be(:project) { create(:project, :repository) } let(:repository) { project.repository } def expect_metadata_blob(thing) diff --git a/spec/models/sent_notification_spec.rb b/spec/models/sent_notification_spec.rb index 7539bf1e957..fedaae372c4 100644 --- a/spec/models/sent_notification_spec.rb +++ b/spec/models/sent_notification_spec.rb @@ -3,8 +3,8 @@ require 'spec_helper' describe SentNotification do - set(:user) { create(:user) } - set(:project) { create(:project) } + let_it_be(:user) { create(:user) } + let_it_be(:project) { create(:project) } describe 'validation' do describe 'note validity' do diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index d14de7e99de..f2b95e00b5e 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -2196,7 +2196,7 @@ describe User, :do_not_mock_admin_mode do describe '.find_by_private_commit_email' do context 'with email' do - set(:user) { create(:user) } + let_it_be(:user) { create(:user) } it 'returns user through private commit email' do expect(described_class.find_by_private_commit_email(user.private_commit_email)).to eq(user) diff --git a/spec/presenters/milestone_presenter_spec.rb b/spec/presenters/milestone_presenter_spec.rb new file mode 100644 index 00000000000..3d7b3ad6d78 --- /dev/null +++ b/spec/presenters/milestone_presenter_spec.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe MilestonePresenter do + let_it_be(:user) { create(:user) } + let_it_be(:group) { create(:group) } + let_it_be(:milestone) { create(:milestone, group: group) } + let_it_be(:presenter) { described_class.new(milestone, current_user: user) } + + before do + group.add_developer(user) + end + + describe '#milestone_path' do + it 'returns correct path' do + expect(presenter.milestone_path).to eq("/groups/#{group.full_path}/-/milestones/#{milestone.iid}") + end + end +end diff --git a/spec/requests/api/graphql/group/milestones_spec.rb b/spec/requests/api/graphql/group/milestones_spec.rb new file mode 100644 index 00000000000..84b14470fd1 --- /dev/null +++ b/spec/requests/api/graphql/group/milestones_spec.rb @@ -0,0 +1,85 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'Milestones through GroupQuery' do + include GraphqlHelpers + + let_it_be(:user) { create(:user) } + let_it_be(:now) { Time.now } + let_it_be(:group) { create(:group, :private) } + let_it_be(:milestone_1) { create(:milestone, group: group) } + let_it_be(:milestone_2) { create(:milestone, group: group, state: :closed, start_date: now, due_date: now + 1.day) } + let_it_be(:milestone_3) { create(:milestone, group: group, start_date: now, due_date: now + 2.days) } + let_it_be(:milestone_4) { create(:milestone, group: group, state: :closed, start_date: now - 2.days, due_date: now - 1.day) } + let_it_be(:milestone_from_other_group) { create(:milestone, group: create(:group)) } + + let(:milestone_data) { graphql_data['group']['milestones']['edges'] } + + describe 'Get list of milestones from a group' do + before do + group.add_developer(user) + end + + context 'when the request is correct' do + before do + fetch_milestones(user) + end + + it_behaves_like 'a working graphql query' + + it 'returns milestones successfully' do + expect(response).to have_gitlab_http_status(200) + expect(graphql_errors).to be_nil + expect_array_response(milestone_1.to_global_id.to_s, milestone_2.to_global_id.to_s, milestone_3.to_global_id.to_s, milestone_4.to_global_id.to_s) + end + end + + context 'when filtering by timeframe' do + it 'fetches milestones between start_date and due_date' do + fetch_milestones(user, { start_date: now.to_s, end_date: (now + 2.days).to_s }) + + expect_array_response(milestone_2.to_global_id.to_s, milestone_3.to_global_id.to_s) + end + end + + context 'when filtering by state' do + it 'returns milestones with given state' do + fetch_milestones(user, { state: :active }) + + expect_array_response(milestone_1.to_global_id.to_s, milestone_3.to_global_id.to_s) + end + end + + def fetch_milestones(user = nil, args = {}) + post_graphql(milestones_query(args), current_user: user) + end + + def milestones_query(args = {}) + milestone_node = <<~NODE + edges { + node { + id + title + state + } + } + NODE + + graphql_query_for("group", + { full_path: group.full_path }, + [query_graphql_field("milestones", args, milestone_node)] + ) + end + + def expect_array_response(*items) + expect(response).to have_gitlab_http_status(:success) + expect(milestone_data).to be_an Array + expect(milestone_node_array('id')).to match_array(items) + end + + def milestone_node_array(extract_attribute = nil) + node_array(milestone_data, extract_attribute) + end + end +end diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index e88209081d4..2b9ec4319f5 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -1402,6 +1402,7 @@ describe API::Projects do expect(json_response['merge_requests_access_level']).to be_present expect(json_response['wiki_access_level']).to be_present expect(json_response['builds_access_level']).to be_present + expect(json_response).to have_key('emails_disabled') expect(json_response['resolve_outdated_diff_discussions']).to eq(project.resolve_outdated_diff_discussions) expect(json_response['remove_source_branch_after_merge']).to be_truthy expect(json_response['container_registry_enabled']).to be_present @@ -1412,18 +1413,18 @@ describe API::Projects do expect(json_response['namespace']).to be_present expect(json_response['import_status']).to be_present expect(json_response).to include("import_error") - expect(json_response['avatar_url']).to be_nil + expect(json_response).to have_key('avatar_url') expect(json_response['star_count']).to be_present expect(json_response['forks_count']).to be_present expect(json_response['public_jobs']).to be_present - expect(json_response['ci_config_path']).to be_nil + expect(json_response).to have_key('ci_config_path') expect(json_response['shared_with_groups']).to be_an Array expect(json_response['shared_with_groups'].length).to eq(1) expect(json_response['shared_with_groups'][0]['group_id']).to eq(group.id) expect(json_response['shared_with_groups'][0]['group_name']).to eq(group.name) expect(json_response['shared_with_groups'][0]['group_full_path']).to eq(group.full_path) expect(json_response['shared_with_groups'][0]['group_access_level']).to eq(link.group_access) - expect(json_response['shared_with_groups'][0]['expires_at']).to be_nil + expect(json_response['shared_with_groups'][0]).to have_key('expires_at') expect(json_response['only_allow_merge_if_pipeline_succeeds']).to eq(project.only_allow_merge_if_pipeline_succeeds) expect(json_response['only_allow_merge_if_all_discussions_are_resolved']).to eq(project.only_allow_merge_if_all_discussions_are_resolved) expect(json_response['ci_default_git_depth']).to eq(project.ci_default_git_depth) @@ -2243,6 +2244,16 @@ describe API::Projects do expect(json_response['pages_access_level']).to eq('private') end + it 'updates emails_disabled' do + project_param = { emails_disabled: true } + + put api("/projects/#{project3.id}", user), params: project_param + + expect(response).to have_gitlab_http_status(200) + + expect(json_response['emails_disabled']).to eq(true) + end + it 'updates build_git_strategy' do project_param = { build_git_strategy: 'clone' } diff --git a/spec/requests/api/runner_spec.rb b/spec/requests/api/runner_spec.rb index e3ba366dfcc..23ce17b14fc 100644 --- a/spec/requests/api/runner_spec.rb +++ b/spec/requests/api/runner_spec.rb @@ -1607,7 +1607,7 @@ describe API::Runner, :clean_gitlab_redis_shared_state do it_behaves_like 'successful artifacts upload' end - context 'for file stored remotelly' do + context 'for file stored remotely' do let!(:fog_connection) do stub_artifacts_object_storage(direct_upload: true) end @@ -1894,6 +1894,46 @@ describe API::Runner, :clean_gitlab_redis_shared_state do end end + context 'when artifacts already exist for the job' do + let(:params) do + { + artifact_type: :archive, + artifact_format: :zip, + 'file.sha256' => uploaded_sha256 + } + end + + let(:existing_sha256) { '0' * 64 } + + let!(:existing_artifact) do + create(:ci_job_artifact, :archive, file_sha256: existing_sha256, job: job) + end + + context 'when sha256 is the same of the existing artifact' do + let(:uploaded_sha256) { existing_sha256 } + + it 'ignores the new artifact' do + upload_artifacts(file_upload, headers_with_token, params) + + expect(response).to have_gitlab_http_status(:created) + expect(job.reload.job_artifacts_archive).to eq(existing_artifact) + end + end + + context 'when sha256 is different than the existing artifact' do + let(:uploaded_sha256) { '1' * 64 } + + it 'logs and returns an error' do + expect(Gitlab::ErrorTracking).to receive(:track_exception) + + upload_artifacts(file_upload, headers_with_token, params) + + expect(response).to have_gitlab_http_status(:bad_request) + expect(job.reload.job_artifacts_archive).to eq(existing_artifact) + end + end + end + context 'when artifacts are being stored outside of tmp path' do let(:new_tmpdir) { Dir.mktmpdir } diff --git a/spec/services/ci/create_job_artifacts_service_spec.rb b/spec/services/ci/create_job_artifacts_service_spec.rb new file mode 100644 index 00000000000..e1146fc3df6 --- /dev/null +++ b/spec/services/ci/create_job_artifacts_service_spec.rb @@ -0,0 +1,121 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Ci::CreateJobArtifactsService do + let(:service) { described_class.new } + let(:job) { create(:ci_build) } + let(:artifacts_sha256) { '0' * 64 } + let(:metadata_file) { nil } + + let(:artifacts_file) do + file_to_upload('spec/fixtures/ci_build_artifacts.zip', sha256: artifacts_sha256) + end + + let(:params) do + { + 'artifact_type' => 'archive', + 'artifact_format' => 'zip' + } + end + + def file_to_upload(path, params = {}) + upload = Tempfile.new('upload') + FileUtils.copy(path, upload.path) + + UploadedFile.new(upload.path, params) + end + + describe '#execute' do + subject { service.execute(job, artifacts_file, params, metadata_file: metadata_file) } + + context 'when artifacts file is uploaded' do + it 'saves artifact for the given type' do + expect { subject }.to change { Ci::JobArtifact.count }.by(1) + + new_artifact = job.job_artifacts.last + expect(new_artifact.project).to eq(job.project) + expect(new_artifact.file).to be_present + expect(new_artifact.file_type).to eq(params['artifact_type']) + expect(new_artifact.file_format).to eq(params['artifact_format']) + expect(new_artifact.file_sha256).to eq(artifacts_sha256) + end + + context 'when metadata file is also uploaded' do + let(:metadata_file) do + file_to_upload('spec/fixtures/ci_build_artifacts_metadata.gz', sha256: artifacts_sha256) + end + + before do + stub_application_setting(default_artifacts_expire_in: '1 day') + end + + it 'saves metadata artifact' do + expect { subject }.to change { Ci::JobArtifact.count }.by(2) + + new_artifact = job.job_artifacts.last + expect(new_artifact.project).to eq(job.project) + expect(new_artifact.file).to be_present + expect(new_artifact.file_type).to eq('metadata') + expect(new_artifact.file_format).to eq('gzip') + expect(new_artifact.file_sha256).to eq(artifacts_sha256) + end + + it 'sets expiration date according to application settings' do + expected_expire_at = 1.day.from_now + + expect(subject).to be_truthy + archive_artifact, metadata_artifact = job.job_artifacts.last(2) + + expect(job.artifacts_expire_at).to be_within(1.minute).of(expected_expire_at) + expect(archive_artifact.expire_at).to be_within(1.minute).of(expected_expire_at) + expect(metadata_artifact.expire_at).to be_within(1.minute).of(expected_expire_at) + end + + context 'when expire_in params is set' do + before do + params.merge!('expire_in' => '2 hours') + end + + it 'sets expiration date according to the parameter' do + expected_expire_at = 2.hours.from_now + + expect(subject).to be_truthy + archive_artifact, metadata_artifact = job.job_artifacts.last(2) + + expect(job.artifacts_expire_at).to be_within(1.minute).of(expected_expire_at) + expect(archive_artifact.expire_at).to be_within(1.minute).of(expected_expire_at) + expect(metadata_artifact.expire_at).to be_within(1.minute).of(expected_expire_at) + end + end + end + end + + context 'when artifacts file already exists' do + let!(:existing_artifact) do + create(:ci_job_artifact, :archive, file_sha256: existing_sha256, job: job) + end + + context 'when sha256 of uploading artifact is the same of the existing one' do + let(:existing_sha256) { artifacts_sha256 } + + it 'ignores the changes' do + expect { subject }.not_to change { Ci::JobArtifact.count } + expect(subject).to be_truthy + end + end + + context 'when sha256 of uploading artifact is different than the existing one' do + let(:existing_sha256) { '1' * 64 } + + it 'returns false and logs the error' do + expect(Gitlab::ErrorTracking).to receive(:track_exception).and_call_original + + expect { subject }.not_to change { Ci::JobArtifact.count } + expect(subject).to be_falsey + expect(job.errors[:base]).to contain_exactly('another artifact of the same type already exists') + end + end + end + end +end diff --git a/spec/support/helpers/graphql_helpers.rb b/spec/support/helpers/graphql_helpers.rb index 8dc99e4e042..35b1b802f35 100644 --- a/spec/support/helpers/graphql_helpers.rb +++ b/spec/support/helpers/graphql_helpers.rb @@ -185,12 +185,13 @@ module GraphqlHelpers end # Fairly dumb Ruby => GraphQL rendering function. Only suitable for testing. - # Missing support for Enums (feel free to add if you need it). + # Use symbol for Enum values def as_graphql_literal(value) case value when Array then "[#{value.map { |v| as_graphql_literal(v) }.join(',')}]" when Integer, Float then value.to_s when String then "\"#{value.gsub(/"/, '\\"')}\"" + when Symbol then value when nil then 'null' when true then 'true' when false then 'false' diff --git a/spec/support/shared_examples/policies/within_timeframe_shared_examples.rb b/spec/support/shared_examples/policies/within_timeframe_shared_examples.rb new file mode 100644 index 00000000000..918db6886d3 --- /dev/null +++ b/spec/support/shared_examples/policies/within_timeframe_shared_examples.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +RSpec.shared_examples 'within_timeframe scope' do + describe '.within_timeframe' do + it 'returns resources with start_date and/or end_date between timeframe' do + resources = described_class.within_timeframe(now + 2.days, now + 3.days) + + expect(resources).to match_array([resource_2, resource_4]) + end + + it 'returns resources which starts before the timeframe' do + resources = described_class.within_timeframe(now, now + 1.day) + + expect(resources).to match_array([resource_1, resource_3, resource_4]) + end + + it 'returns resources which ends after the timeframe' do + resources = described_class.within_timeframe(now + 3.days, now + 5.days) + + expect(resources).to match_array([resource_2, resource_4]) + end + end +end |