diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-07-09 09:09:27 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-07-09 09:09:27 +0000 |
commit | c3b45354d720654215eb0e7b8e718ba6ea2d7a96 (patch) | |
tree | 359066e91bad08ae8e404bb43316705b7b53993e /spec | |
parent | 734708924b0f86ad3c23636bd6a8942d679daaf2 (diff) | |
download | gitlab-ce-c3b45354d720654215eb0e7b8e718ba6ea2d7a96.tar.gz |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r-- | spec/factories/projects.rb | 8 | ||||
-rw-r--r-- | spec/factories/service_desk_settings.rb | 7 | ||||
-rw-r--r-- | spec/frontend/monitoring/components/__snapshots__/dashboard_template_spec.js.snap | 4 | ||||
-rw-r--r-- | spec/frontend/monitoring/components/charts/time_series_spec.js | 85 | ||||
-rw-r--r-- | spec/frontend/monitoring/components/dashboard_header_spec.js | 48 | ||||
-rw-r--r-- | spec/frontend/monitoring/graph_data.js | 70 | ||||
-rw-r--r-- | spec/graphql/types/project_type_spec.rb | 2 | ||||
-rw-r--r-- | spec/helpers/environments_helper_spec.rb | 18 | ||||
-rw-r--r-- | spec/helpers/todos_helper_spec.rb | 20 | ||||
-rw-r--r-- | spec/lib/gitlab/service_desk_email_spec.rb | 59 | ||||
-rw-r--r-- | spec/lib/gitlab/service_desk_spec.rb | 56 | ||||
-rw-r--r-- | spec/lib/gitlab/usage_data_spec.rb | 34 | ||||
-rw-r--r-- | spec/models/issue_spec.rb | 26 | ||||
-rw-r--r-- | spec/models/project_spec.rb | 56 | ||||
-rw-r--r-- | spec/models/service_desk_setting_spec.rb | 37 | ||||
-rw-r--r-- | spec/support/helpers/stub_configuration.rb | 4 | ||||
-rw-r--r-- | spec/support/shared_examples/policies/project_policy_shared_examples.rb | 28 |
17 files changed, 498 insertions, 64 deletions
diff --git a/spec/factories/projects.rb b/spec/factories/projects.rb index dca8d70a166..46ed50932b7 100644 --- a/spec/factories/projects.rb +++ b/spec/factories/projects.rb @@ -316,6 +316,14 @@ FactoryBot.define do end end + trait :service_desk_disabled do + service_desk_enabled { nil } + end + + trait(:service_desk_enabled) do + service_desk_enabled { true } + end + # Project with empty repository # # This is a case when you just created a project diff --git a/spec/factories/service_desk_settings.rb b/spec/factories/service_desk_settings.rb new file mode 100644 index 00000000000..abdbc5f1ea0 --- /dev/null +++ b/spec/factories/service_desk_settings.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :service_desk_setting do + project + end +end diff --git a/spec/frontend/monitoring/components/__snapshots__/dashboard_template_spec.js.snap b/spec/frontend/monitoring/components/__snapshots__/dashboard_template_spec.js.snap index cb3f74ca73c..0a85dbc32d5 100644 --- a/spec/frontend/monitoring/components/__snapshots__/dashboard_template_spec.js.snap +++ b/spec/frontend/monitoring/components/__snapshots__/dashboard_template_spec.js.snap @@ -129,6 +129,10 @@ exports[`Dashboard template matches the default snapshot 1`] = ` <!----> <!----> + + <!----> + + <!----> </div> <duplicate-dashboard-modal-stub diff --git a/spec/frontend/monitoring/components/charts/time_series_spec.js b/spec/frontend/monitoring/components/charts/time_series_spec.js index 3cb27be30ac..c4d6c25e18d 100644 --- a/spec/frontend/monitoring/components/charts/time_series_spec.js +++ b/spec/frontend/monitoring/components/charts/time_series_spec.js @@ -9,18 +9,12 @@ import { GlChartSeriesLabel, GlChartLegend, } from '@gitlab/ui/dist/charts'; -import { cloneDeep } from 'lodash'; import { shallowWrapperContainsSlotText } from 'helpers/vue_test_utils_helper'; -import { createStore } from '~/monitoring/stores'; import { panelTypes, chartHeight } from '~/monitoring/constants'; import TimeSeries from '~/monitoring/components/charts/time_series.vue'; -import * as types from '~/monitoring/stores/mutation_types'; -import { deploymentData, mockProjectDir, annotationsData, metricsResult } from '../../mock_data'; -import { - metricsDashboardPayload, - metricsDashboardViewModel, - metricResultStatus, -} from '../../fixture_data'; +import { deploymentData, mockProjectDir, annotationsData } from '../../mock_data'; + +import { timeSeriesGraphData } from '../../graph_data'; jest.mock('lodash/throttle', () => // this throttle mock executes immediately @@ -35,23 +29,21 @@ jest.mock('~/lib/utils/icon_utils', () => ({ })); describe('Time series component', () => { - let mockGraphData; - let store; + const defaultGraphData = timeSeriesGraphData(); let wrapper; const createWrapper = ( - { graphData = mockGraphData, ...props } = {}, + { graphData = defaultGraphData, ...props } = {}, mountingMethod = shallowMount, ) => { wrapper = mountingMethod(TimeSeries, { propsData: { graphData, - deploymentData: store.state.monitoringDashboard.deploymentData, - annotations: store.state.monitoringDashboard.annotations, + deploymentData, + annotations: annotationsData, projectPath: `${TEST_HOST}${mockProjectDir}`, ...props, }, - store, stubs: { GlPopover: true, }, @@ -59,27 +51,15 @@ describe('Time series component', () => { }); }; - describe('With a single time series', () => { - beforeEach(() => { - setTestTimeout(1000); - - store = createStore(); - - store.commit( - `monitoringDashboard/${types.RECEIVE_METRICS_DASHBOARD_SUCCESS}`, - metricsDashboardPayload, - ); - - store.commit(`monitoringDashboard/${types.RECEIVE_DEPLOYMENTS_DATA_SUCCESS}`, deploymentData); + beforeEach(() => { + setTestTimeout(1000); + }); - store.commit( - `monitoringDashboard/${types.RECEIVE_METRIC_RESULT_SUCCESS}`, - metricResultStatus, - ); - // dashboard is a dynamically generated fixture and stored at environment_metrics_dashboard.json - [mockGraphData] = store.state.monitoringDashboard.dashboard.panelGroups[1].panels; - }); + afterEach(() => { + wrapper.destroy(); + }); + describe('With a single time series', () => { describe('general functions', () => { const findChart = () => wrapper.find({ ref: 'chart' }); @@ -88,10 +68,6 @@ describe('Time series component', () => { return wrapper.vm.$nextTick(); }); - afterEach(() => { - wrapper.destroy(); - }); - it('allows user to override legend label texts using props', () => { const legendRelatedProps = { legendMinText: 'legendMinText', @@ -231,19 +207,20 @@ describe('Time series component', () => { }); it('formats tooltip content', () => { - const name = 'Status Code'; + const name = 'Metric 1'; const value = '5.556'; const dataIndex = 0; const seriesLabel = wrapper.find(GlChartSeriesLabel); expect(seriesLabel.vm.color).toBe(''); + expect(shallowWrapperContainsSlotText(seriesLabel, 'default', name)).toBe(true); expect(wrapper.vm.tooltip.content).toEqual([ { name, value, dataIndex, color: undefined }, ]); expect( - shallowWrapperContainsSlotText(wrapper.find(GlAreaChart), 'tooltipContent', value), + shallowWrapperContainsSlotText(wrapper.find(GlLineChart), 'tooltipContent', value), ).toBe(true); }); @@ -385,10 +362,8 @@ describe('Time series component', () => { }); it('utilizes all data points', () => { - const { values } = mockGraphData.metrics[0].result[0]; - expect(chartData.length).toBe(1); - expect(seriesData().data.length).toBe(values.length); + expect(seriesData().data.length).toBe(3); }); it('creates valid data', () => { @@ -602,14 +577,10 @@ describe('Time series component', () => { it('constructs a label for the chart y-axis', () => { const { yAxis } = getChartOptions(); - expect(yAxis[0].name).toBe('Requests / Sec'); + expect(yAxis[0].name).toBe('Y Axis'); }); }); }); - - afterEach(() => { - wrapper.destroy(); - }); }); describe('wrapped components', () => { @@ -630,7 +601,7 @@ describe('Time series component', () => { beforeEach(() => { createWrapper( - { graphData: { ...mockGraphData, type: dynamicComponent.chartType } }, + { graphData: timeSeriesGraphData({ type: dynamicComponent.chartType }) }, mount, ); return wrapper.vm.$nextTick(); @@ -700,18 +671,12 @@ describe('Time series component', () => { describe('with multiple time series', () => { describe('General functions', () => { beforeEach(() => { - store = createStore(); - const graphData = cloneDeep(metricsDashboardViewModel.panelGroups[0].panels[3]); - graphData.metrics.forEach(metric => Object.assign(metric, { result: metricsResult })); + const graphData = timeSeriesGraphData({ type: panelTypes.AREA_CHART, multiMetric: true }); - createWrapper({ graphData: { ...graphData, type: 'area-chart' } }, mount); + createWrapper({ graphData }, mount); return wrapper.vm.$nextTick(); }); - afterEach(() => { - wrapper.destroy(); - }); - describe('Color match', () => { let lineColors; @@ -752,14 +717,10 @@ describe('Time series component', () => { const findLegend = () => wrapper.find(GlChartLegend); beforeEach(() => { - createWrapper(mockGraphData, mount); + createWrapper({}, mount); return wrapper.vm.$nextTick(); }); - afterEach(() => { - wrapper.destroy(); - }); - it('should render a tabular legend layout by default', () => { expect(findLegend().props('layout')).toBe('table'); }); diff --git a/spec/frontend/monitoring/components/dashboard_header_spec.js b/spec/frontend/monitoring/components/dashboard_header_spec.js index 5af7b4049d1..7522fe247b7 100644 --- a/spec/frontend/monitoring/components/dashboard_header_spec.js +++ b/spec/frontend/monitoring/components/dashboard_header_spec.js @@ -157,4 +157,52 @@ describe('Dashboard header', () => { }); }); }); + + describe('metrics settings button', () => { + const findSettingsButton = () => wrapper.find('[data-testid="metrics-settings-button"]'); + const url = 'https://path/to/project/settings'; + + beforeEach(() => { + createShallowWrapper(); + + store.state.monitoringDashboard.canAccessOperationsSettings = false; + store.state.monitoringDashboard.operationsSettingsPath = ''; + }); + + it('is rendered when the user can access the project settings and path to settings is available', () => { + store.state.monitoringDashboard.canAccessOperationsSettings = true; + store.state.monitoringDashboard.operationsSettingsPath = url; + + return wrapper.vm.$nextTick(() => { + expect(findSettingsButton().exists()).toBe(true); + }); + }); + + it('is not rendered when the user can not access the project settings', () => { + store.state.monitoringDashboard.canAccessOperationsSettings = false; + store.state.monitoringDashboard.operationsSettingsPath = url; + + return wrapper.vm.$nextTick(() => { + expect(findSettingsButton().exists()).toBe(false); + }); + }); + + it('is not rendered when the path to settings is unavailable', () => { + store.state.monitoringDashboard.canAccessOperationsSettings = false; + store.state.monitoringDashboard.operationsSettingsPath = ''; + + return wrapper.vm.$nextTick(() => { + expect(findSettingsButton().exists()).toBe(false); + }); + }); + + it('leads to the project settings page', () => { + store.state.monitoringDashboard.canAccessOperationsSettings = true; + store.state.monitoringDashboard.operationsSettingsPath = url; + + return wrapper.vm.$nextTick(() => { + expect(findSettingsButton().attributes('href')).toBe(url); + }); + }); + }); }); diff --git a/spec/frontend/monitoring/graph_data.js b/spec/frontend/monitoring/graph_data.js new file mode 100644 index 00000000000..4874e558d06 --- /dev/null +++ b/spec/frontend/monitoring/graph_data.js @@ -0,0 +1,70 @@ +import { mapPanelToViewModel, normalizeQueryResponseData } from '~/monitoring/stores/utils'; +import { panelTypes, metricStates } from '~/monitoring/constants'; + +const initTime = 1435781451.781; + +const makeValues = vals => vals.map((val, i) => [initTime + 15 * i, val]); + +// Normalized Prometheus Responses + +const matrixSingleResult = ({ values = ['1', '2', '3'] } = {}) => + normalizeQueryResponseData({ + resultType: 'matrix', + result: [ + { + metric: {}, + values: makeValues(values), + }, + ], + }); + +const matrixMultiResult = ({ values1 = ['1', '2', '3'], values2 = ['4', '5', '6'] } = {}) => + normalizeQueryResponseData({ + resultType: 'matrix', + result: [ + { + metric: { + __name__: 'up', + job: 'prometheus', + instance: 'localhost:9090', + }, + values: makeValues(values1), + }, + { + metric: { + __name__: 'up', + job: 'node', + instance: 'localhost:9091', + }, + values: makeValues(values2), + }, + ], + }); + +// GraphData factory + +/** + * Generate mock graph data according to options + * + * @param {Object} panelOptions - Panel options as in YML. + * @param {Object} dataOptions + * @param {Object} dataOptions.metricCount + * @param {Object} dataOptions.isMultiSeries + */ +// eslint-disable-next-line import/prefer-default-export +export const timeSeriesGraphData = (panelOptions = {}, dataOptions = {}) => { + const { metricCount = 1, isMultiSeries = false } = dataOptions; + + return mapPanelToViewModel({ + title: 'Time Series Panel', + type: panelTypes.LINE_CHART, + x_label: 'X Axis', + y_label: 'Y Axis', + metrics: Array.from(Array(metricCount), (_, i) => ({ + label: `Metric ${i + 1}`, + state: metricStates.OK, + result: isMultiSeries ? matrixMultiResult() : matrixSingleResult(), + })), + ...panelOptions, + }); +}; diff --git a/spec/graphql/types/project_type_spec.rb b/spec/graphql/types/project_type_spec.rb index 49e2cfd45e5..ea88ed6a3f5 100644 --- a/spec/graphql/types/project_type_spec.rb +++ b/spec/graphql/types/project_type_spec.rb @@ -26,7 +26,7 @@ RSpec.describe GitlabSchema.types['Project'] do grafanaIntegration autocloseReferencedIssues suggestion_commit_message environments boards jira_import_status jira_imports services releases release alert_management_alerts alert_management_alert alert_management_alert_status_counts - container_expiration_policy sast_ci_configuration + container_expiration_policy sast_ci_configuration service_desk_enabled service_desk_address ] expect(described_class).to include_graphql_fields(*expected_fields) diff --git a/spec/helpers/environments_helper_spec.rb b/spec/helpers/environments_helper_spec.rb index c083eb89cc0..9386ec2ac71 100644 --- a/spec/helpers/environments_helper_spec.rb +++ b/spec/helpers/environments_helper_spec.rb @@ -42,10 +42,26 @@ RSpec.describe EnvironmentsHelper do 'custom-metrics-available' => 'true', 'alerts-endpoint' => project_prometheus_alerts_path(project, environment_id: environment.id, format: :json), 'prometheus-alerts-available' => 'true', - 'custom-dashboard-base-path' => Metrics::Dashboard::CustomDashboardService::DASHBOARD_ROOT + 'custom-dashboard-base-path' => Metrics::Dashboard::CustomDashboardService::DASHBOARD_ROOT, + 'operations-settings-path' => project_settings_operations_path(project), + 'can-access-operations-settings' => 'true' ) end + context 'without admin_operations permission' do + before do + allow(helper).to receive(:can?) + .with(user, :admin_operations, project) + .and_return(false) + end + + specify do + expect(metrics_data).to include( + 'can-access-operations-settings' => 'false' + ) + end + end + context 'without read_prometheus_alerts permission' do before do allow(helper).to receive(:can?) diff --git a/spec/helpers/todos_helper_spec.rb b/spec/helpers/todos_helper_spec.rb index 5b9168fb655..6b658a475b1 100644 --- a/spec/helpers/todos_helper_spec.rb +++ b/spec/helpers/todos_helper_spec.rb @@ -222,4 +222,24 @@ RSpec.describe TodosHelper do end end end + + describe '#todo_author_display?' do + using RSpec::Parameterized::TableSyntax + + subject { helper.todo_author_display?(alert_todo) } + + where(:action, :result) do + Todo::BUILD_FAILED | false + Todo::UNMERGEABLE | false + Todo::ASSIGNED | true + end + + with_them do + before do + alert_todo.action = action + end + + it { is_expected.to eq(result) } + end + end end diff --git a/spec/lib/gitlab/service_desk_email_spec.rb b/spec/lib/gitlab/service_desk_email_spec.rb new file mode 100644 index 00000000000..23e2b2ff3cf --- /dev/null +++ b/spec/lib/gitlab/service_desk_email_spec.rb @@ -0,0 +1,59 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::ServiceDeskEmail do + describe '.enabled?' do + context 'when service_desk_email is enabled and address is set' do + before do + stub_service_desk_email_setting(enabled: true, address: 'foo') + end + + it 'returns true' do + expect(described_class.enabled?).to be_truthy + end + end + + context 'when service_desk_email is disabled' do + before do + stub_service_desk_email_setting(enabled: false, address: 'foo') + end + + it 'returns false' do + expect(described_class.enabled?).to be_falsey + end + end + + context 'when service desk address is not set' do + before do + stub_service_desk_email_setting(enabled: true, address: nil) + end + + it 'returns false' do + expect(described_class.enabled?).to be_falsey + end + end + end + + describe '.key_from_address' do + context 'when service desk address is set' do + before do + stub_service_desk_email_setting(address: 'address+%{key}@example.com') + end + + it 'returns key' do + expect(described_class.key_from_address('address+key@example.com')).to eq('key') + end + end + + context 'when service desk address is not set' do + before do + stub_service_desk_email_setting(address: nil) + end + + it 'returns nil' do + expect(described_class.key_from_address('address+key@example.com')).to be_nil + end + end + end +end diff --git a/spec/lib/gitlab/service_desk_spec.rb b/spec/lib/gitlab/service_desk_spec.rb new file mode 100644 index 00000000000..f554840ec78 --- /dev/null +++ b/spec/lib/gitlab/service_desk_spec.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::ServiceDesk do + before do + allow(Gitlab::IncomingEmail).to receive(:enabled?).and_return(true) + allow(Gitlab::IncomingEmail).to receive(:supports_wildcard?).and_return(true) + end + + describe 'enabled?' do + let_it_be(:project) { create(:project) } + + subject { described_class.enabled?(project: project) } + + it { is_expected.to be_truthy } + + context 'when service desk is not supported' do + before do + allow(described_class).to receive(:supported?).and_return(false) + end + + it { is_expected.to be_falsy } + end + + context 'when service desk is disabled for project' do + before do + project.update!(service_desk_enabled: false) + end + + it { is_expected.to be_falsy } + end + end + + describe 'supported?' do + subject { described_class.supported? } + + it { is_expected.to be_truthy } + + context 'when incoming emails are disabled' do + before do + allow(Gitlab::IncomingEmail).to receive(:enabled?).and_return(false) + end + + it { is_expected.to be_falsy } + end + + context 'when email key is not supported' do + before do + allow(Gitlab::IncomingEmail).to receive(:supports_wildcard?).and_return(false) + end + + it { is_expected.to be_falsy } + end + end +end diff --git a/spec/lib/gitlab/usage_data_spec.rb b/spec/lib/gitlab/usage_data_spec.rb index 5156e6bcb0d..cdd10f89a58 100644 --- a/spec/lib/gitlab/usage_data_spec.rb +++ b/spec/lib/gitlab/usage_data_spec.rb @@ -174,6 +174,40 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do :merge_requests_users ) end + + it 'includes accurate usage_activity_by_stage data' do + for_defined_days_back do + user = create(:user) + project = create(:project, :repository_private, + :test_repo, :remote_mirror, creator: user) + create(:merge_request, source_project: project) + create(:deploy_key, user: user) + create(:key, user: user) + create(:project, creator: user, disable_overriding_approvers_per_merge_request: true) + create(:project, creator: user, disable_overriding_approvers_per_merge_request: false) + create(:remote_mirror, project: project) + create(:snippet, author: user) + end + + expect(described_class.uncached_data[:usage_activity_by_stage][:create]).to include( + deploy_keys: 2, + keys: 2, + merge_requests: 2, + projects_with_disable_overriding_approvers_per_merge_request: 2, + projects_without_disable_overriding_approvers_per_merge_request: 4, + remote_mirrors: 2, + snippets: 2 + ) + expect(described_class.uncached_data[:usage_activity_by_stage_monthly][:create]).to include( + deploy_keys: 1, + keys: 1, + merge_requests: 1, + projects_with_disable_overriding_approvers_per_merge_request: 1, + projects_without_disable_overriding_approvers_per_merge_request: 2, + remote_mirrors: 1, + snippets: 1 + ) + end end context 'for monitor' do diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb index 4221b5e5f7a..fe6f2ac6f4d 100644 --- a/spec/models/issue_spec.rb +++ b/spec/models/issue_spec.rb @@ -406,6 +406,22 @@ RSpec.describe Issue do end end + describe '#from_service_desk?' do + subject { issue.from_service_desk? } + + context 'when issue author is support bot' do + let(:issue) { create(:issue, author: ::User.support_bot) } + + it { is_expected.to be_truthy } + end + + context 'when issue author is not support bot' do + let(:issue) { create(:issue) } + + it { is_expected.to be_falsey } + end + end + describe '#suggested_branch_name' do let(:repository) { double } @@ -1002,6 +1018,16 @@ RSpec.describe Issue do end end + describe '.service_desk' do + it 'returns the service desk issue' do + service_desk_issue = create(:issue, author: ::User.support_bot) + regular_issue = create(:issue) + + expect(described_class.service_desk).to include(service_desk_issue) + expect(described_class.service_desk).not_to include(regular_issue) + end + end + it_behaves_like 'throttled touch' do subject { create(:issue, updated_at: 1.hour.ago) } end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index c1c7892e2da..75336446c7e 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -1378,6 +1378,62 @@ RSpec.describe Project do end end + describe '.service_desk_enabled' do + it 'returns the correct project' do + project_with_service_desk_enabled = create(:project) + project_with_service_desk_disabled = create(:project, :service_desk_disabled) + + expect(described_class.service_desk_enabled).to include(project_with_service_desk_enabled) + expect(described_class.service_desk_enabled).not_to include(project_with_service_desk_disabled) + end + end + + describe '#service_desk_enabled?' do + let_it_be(:namespace) { create(:namespace) } + + subject(:project) { build(:project, :private, namespace: namespace, service_desk_enabled: true) } + + before do + allow(Gitlab::IncomingEmail).to receive(:enabled?).and_return(true) + allow(Gitlab::IncomingEmail).to receive(:supports_wildcard?).and_return(true) + end + + it 'is enabled' do + expect(project.service_desk_enabled?).to be_truthy + expect(project.service_desk_enabled).to be_truthy + end + end + + describe '#service_desk_address' do + let_it_be(:project) { create(:project, service_desk_enabled: true) } + + before do + allow(Gitlab::ServiceDesk).to receive(:enabled?).and_return(true) + allow(Gitlab.config.incoming_email).to receive(:enabled).and_return(true) + allow(Gitlab.config.incoming_email).to receive(:address).and_return("test+%{key}@mail.com") + end + + it 'uses project full path as service desk address key' do + expect(project.service_desk_address).to eq("test+#{project.full_path_slug}-#{project.project_id}-issue-@mail.com") + end + end + + describe '.find_by_service_desk_project_key' do + it 'returns the correct project' do + project1 = create(:project) + project2 = create(:project) + create(:service_desk_setting, project: project1, project_key: 'key1') + create(:service_desk_setting, project: project2, project_key: 'key2') + + expect(Project.find_by_service_desk_project_key('key1')).to eq(project1) + expect(Project.find_by_service_desk_project_key('key2')).to eq(project2) + end + + it 'returns nil if there is no project with the key' do + expect(Project.find_by_service_desk_project_key('some_key')).to be_nil + end + end + context 'repository storage by default' do let(:project) { build(:project) } diff --git a/spec/models/service_desk_setting_spec.rb b/spec/models/service_desk_setting_spec.rb new file mode 100644 index 00000000000..ca57a5d4087 --- /dev/null +++ b/spec/models/service_desk_setting_spec.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe ServiceDeskSetting do + describe 'validations' do + subject(:service_desk_setting) { create(:service_desk_setting) } + + it { is_expected.to validate_presence_of(:project_id) } + it { is_expected.to validate_length_of(:outgoing_name).is_at_most(255) } + it { is_expected.to validate_length_of(:project_key).is_at_most(255) } + it { is_expected.to allow_value('abc123_').for(:project_key) } + it { is_expected.not_to allow_value('abc 12').for(:project_key) } + it { is_expected.not_to allow_value('Big val').for(:project_key) } + + describe '.valid_issue_template' do + let_it_be(:project) { create(:project, :custom_repo, files: { '.gitlab/issue_templates/service_desk.md' => 'template' }) } + + it 'is not valid if template does not exist' do + settings = build(:service_desk_setting, project: project, issue_template_key: 'invalid key') + + expect(settings).not_to be_valid + expect(settings.errors[:issue_template_key].first).to eq('is empty or does not exist') + end + + it 'is valid if template exists' do + settings = build(:service_desk_setting, project: project, issue_template_key: 'service_desk') + + expect(settings).to be_valid + end + end + end + + describe 'associations' do + it { is_expected.to belong_to(:project) } + end +end diff --git a/spec/support/helpers/stub_configuration.rb b/spec/support/helpers/stub_configuration.rb index 6a832ca97d1..83d4fa1aa2d 100644 --- a/spec/support/helpers/stub_configuration.rb +++ b/spec/support/helpers/stub_configuration.rb @@ -113,6 +113,10 @@ module StubConfiguration allow(Gitlab.config.rack_attack.git_basic_auth).to receive_messages(to_settings(messages)) end + def stub_service_desk_email_setting(messages) + allow(::Gitlab.config.service_desk_email).to receive_messages(to_settings(messages)) + end + private # Modifies stubbed messages to also stub possible predicate versions diff --git a/spec/support/shared_examples/policies/project_policy_shared_examples.rb b/spec/support/shared_examples/policies/project_policy_shared_examples.rb index 4dd0152e3d1..f8526ec68dc 100644 --- a/spec/support/shared_examples/policies/project_policy_shared_examples.rb +++ b/spec/support/shared_examples/policies/project_policy_shared_examples.rb @@ -41,6 +41,28 @@ RSpec.shared_examples 'archived project policies' do end end +RSpec.shared_examples 'project private features with read_all_resources ability' do + subject { described_class.new(user, project) } + + before do + project.project_feature.update!( + repository_access_level: ProjectFeature::PRIVATE, + merge_requests_access_level: ProjectFeature::PRIVATE, + builds_access_level: ProjectFeature::PRIVATE + ) + end + + [:public, :internal, :private].each do |visibility| + context "for #{visibility} projects" do + let(:project) { create(:project, visibility, namespace: owner.namespace) } + + it 'allows the download_code ability' do + expect_allowed(:download_code) + end + end + end +end + RSpec.shared_examples 'project policies as anonymous' do context 'abilities for public projects' do context 'when a project has pending invites' do @@ -231,6 +253,12 @@ RSpec.shared_examples 'project policies as admin with admin mode' do let(:regular_abilities) { owner_permissions } end end + + context 'abilities for all project visibility', :enable_admin_mode do + it_behaves_like 'project private features with read_all_resources ability' do + let(:user) { admin } + end + end end RSpec.shared_examples 'project policies as admin without admin mode' do |