Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-01-06 21:15:26 +00:00
parent c38fb401d2
commit 35b5da5522
19 changed files with 317 additions and 230 deletions

View File

@ -488,6 +488,8 @@ That's all of the required database changes.
module Geo
class CoolWidgetState < ApplicationRecord
include EachBatch
self.primary_key = :cool_widget_id
belongs_to :cool_widget, inverse_of: :cool_widget_state

View File

@ -452,6 +452,8 @@ That's all of the required database changes.
``` ruby
module Geo
class CoolWidgetState < ApplicationRecord
include EachBatch
self.primary_key = :cool_widget_id
belongs_to :cool_widget, inverse_of: :cool_widget_state

View File

@ -2,7 +2,7 @@
source 'https://rubygems.org'
gem 'rails', '~> 6.1.4.1'
gem 'rails', '~> 6.1.4.4'
gem 'bootsnap', '~> 1.9.1', require: false

View File

@ -11,63 +11,63 @@ GEM
RedCloth (4.3.2)
acme-client (2.0.9)
faraday (>= 0.17, < 2.0.0)
actioncable (6.1.4.1)
actionpack (= 6.1.4.1)
activesupport (= 6.1.4.1)
actioncable (6.1.4.4)
actionpack (= 6.1.4.4)
activesupport (= 6.1.4.4)
nio4r (~> 2.0)
websocket-driver (>= 0.6.1)
actionmailbox (6.1.4.1)
actionpack (= 6.1.4.1)
activejob (= 6.1.4.1)
activerecord (= 6.1.4.1)
activestorage (= 6.1.4.1)
activesupport (= 6.1.4.1)
actionmailbox (6.1.4.4)
actionpack (= 6.1.4.4)
activejob (= 6.1.4.4)
activerecord (= 6.1.4.4)
activestorage (= 6.1.4.4)
activesupport (= 6.1.4.4)
mail (>= 2.7.1)
actionmailer (6.1.4.1)
actionpack (= 6.1.4.1)
actionview (= 6.1.4.1)
activejob (= 6.1.4.1)
activesupport (= 6.1.4.1)
actionmailer (6.1.4.4)
actionpack (= 6.1.4.4)
actionview (= 6.1.4.4)
activejob (= 6.1.4.4)
activesupport (= 6.1.4.4)
mail (~> 2.5, >= 2.5.4)
rails-dom-testing (~> 2.0)
actionpack (6.1.4.1)
actionview (= 6.1.4.1)
activesupport (= 6.1.4.1)
actionpack (6.1.4.4)
actionview (= 6.1.4.4)
activesupport (= 6.1.4.4)
rack (~> 2.0, >= 2.0.9)
rack-test (>= 0.6.3)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.2.0)
actiontext (6.1.4.1)
actionpack (= 6.1.4.1)
activerecord (= 6.1.4.1)
activestorage (= 6.1.4.1)
activesupport (= 6.1.4.1)
actiontext (6.1.4.4)
actionpack (= 6.1.4.4)
activerecord (= 6.1.4.4)
activestorage (= 6.1.4.4)
activesupport (= 6.1.4.4)
nokogiri (>= 1.8.5)
actionview (6.1.4.1)
activesupport (= 6.1.4.1)
actionview (6.1.4.4)
activesupport (= 6.1.4.4)
builder (~> 3.1)
erubi (~> 1.4)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.1, >= 1.2.0)
activejob (6.1.4.1)
activesupport (= 6.1.4.1)
activejob (6.1.4.4)
activesupport (= 6.1.4.4)
globalid (>= 0.3.6)
activemodel (6.1.4.1)
activesupport (= 6.1.4.1)
activerecord (6.1.4.1)
activemodel (= 6.1.4.1)
activesupport (= 6.1.4.1)
activemodel (6.1.4.4)
activesupport (= 6.1.4.4)
activerecord (6.1.4.4)
activemodel (= 6.1.4.4)
activesupport (= 6.1.4.4)
activerecord-explain-analyze (0.1.0)
activerecord (>= 4)
pg
activestorage (6.1.4.1)
actionpack (= 6.1.4.1)
activejob (= 6.1.4.1)
activerecord (= 6.1.4.1)
activesupport (= 6.1.4.1)
activestorage (6.1.4.4)
actionpack (= 6.1.4.4)
activejob (= 6.1.4.4)
activerecord (= 6.1.4.4)
activesupport (= 6.1.4.4)
marcel (~> 1.0.0)
mini_mime (>= 1.1.0)
activesupport (6.1.4.1)
activesupport (6.1.4.4)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 1.6, < 2)
minitest (>= 5.1)
@ -511,7 +511,7 @@ GEM
omniauth (~> 1.3)
pyu-ruby-sasl (>= 0.0.3.3, < 0.1)
rubyntlm (~> 0.5)
globalid (0.5.2)
globalid (1.0.0)
activesupport (>= 5.0)
gon (6.4.0)
actionpack (>= 3.0.20)
@ -739,7 +739,7 @@ GEM
lumberjack (1.2.7)
mail (2.7.1)
mini_mime (>= 0.1.1)
marcel (1.0.1)
marcel (1.0.2)
marginalia (1.10.0)
actionpack (>= 2.3)
activerecord (>= 2.3)
@ -971,20 +971,20 @@ GEM
rack-test (1.1.0)
rack (>= 1.0, < 3)
rack-timeout (0.5.2)
rails (6.1.4.1)
actioncable (= 6.1.4.1)
actionmailbox (= 6.1.4.1)
actionmailer (= 6.1.4.1)
actionpack (= 6.1.4.1)
actiontext (= 6.1.4.1)
actionview (= 6.1.4.1)
activejob (= 6.1.4.1)
activemodel (= 6.1.4.1)
activerecord (= 6.1.4.1)
activestorage (= 6.1.4.1)
activesupport (= 6.1.4.1)
rails (6.1.4.4)
actioncable (= 6.1.4.4)
actionmailbox (= 6.1.4.4)
actionmailer (= 6.1.4.4)
actionpack (= 6.1.4.4)
actiontext (= 6.1.4.4)
actionview (= 6.1.4.4)
activejob (= 6.1.4.4)
activemodel (= 6.1.4.4)
activerecord (= 6.1.4.4)
activestorage (= 6.1.4.4)
activesupport (= 6.1.4.4)
bundler (>= 1.15.0)
railties (= 6.1.4.1)
railties (= 6.1.4.4)
sprockets-rails (>= 2.0.0)
rails-controller-testing (1.0.5)
actionpack (>= 5.0.1.rc1)
@ -998,9 +998,9 @@ GEM
rails-i18n (6.0.0)
i18n (>= 0.7, < 2)
railties (>= 6.0.0, < 7)
railties (6.1.4.1)
actionpack (= 6.1.4.1)
activesupport (= 6.1.4.1)
railties (6.1.4.4)
actionpack (= 6.1.4.4)
activesupport (= 6.1.4.4)
method_source
rake (>= 0.13)
thor (~> 1.0)
@ -1381,7 +1381,7 @@ GEM
nokogiri (~> 1.8)
yajl-ruby (1.4.1)
yard (0.9.26)
zeitwerk (2.5.1)
zeitwerk (2.5.3)
PLATFORMS
ruby
@ -1589,7 +1589,7 @@ DEPENDENCIES
rack-oauth2 (~> 1.16.0)
rack-proxy (~> 0.6.0)
rack-timeout (~> 0.5.1)
rails (~> 6.1.4.1)
rails (~> 6.1.4.4)
rails-controller-testing
rails-i18n (~> 6.0)
rainbow (~> 3.0)

View File

@ -121,4 +121,4 @@ Please see [Getting help for GitLab](https://about.gitlab.com/getting-help/) on
## Is it awesome?
[These people](https://twitter.com/gitlab/likes) seem to like it.
[These people](https://twitter.com/gitlab/followers) seem to like it.

View File

@ -0,0 +1,52 @@
<script>
import { GlSprintf } from '@gitlab/ui';
import { sprintf } from '~/locale';
import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import { I18N_DETAILS_TITLE } from '../constants';
import RunnerTypeBadge from './runner_type_badge.vue';
import RunnerStatusBadge from './runner_status_badge.vue';
export default {
components: {
GlSprintf,
TimeAgo,
RunnerTypeBadge,
RunnerStatusBadge,
},
props: {
runner: {
type: Object,
required: true,
},
},
computed: {
paused() {
return !this.runner.active;
},
heading() {
const id = getIdFromGraphQLId(this.runner.id);
return sprintf(I18N_DETAILS_TITLE, { runner_id: id });
},
},
};
</script>
<template>
<div class="gl-py-5 gl-border-b-1 gl-border-b-solid gl-border-b-gray-100">
<runner-status-badge :runner="runner" />
<runner-type-badge v-if="runner" :type="runner.runnerType" />
<template v-if="runner.createdAt">
<gl-sprintf :message="__('%{runner} created %{timeago}')">
<template #runner>
<strong>{{ heading }}</strong>
</template>
<template #timeago>
<time-ago :time="runner.createdAt" />
</template>
</gl-sprintf>
</template>
<template v-else>
<strong>{{ heading }}</strong>
</template>
</div>
</template>

View File

@ -1,54 +0,0 @@
<script>
import { GlAlert, GlLink } from '@gitlab/ui';
import { helpPagePath } from '~/helpers/help_page_helper';
import { s__ } from '~/locale';
import { INSTANCE_TYPE, GROUP_TYPE, PROJECT_TYPE } from '../constants';
const ALERT_DATA = {
[INSTANCE_TYPE]: {
message: s__(
'Runners|This runner is available to all groups and projects in your GitLab instance.',
),
anchor: 'shared-runners',
},
[GROUP_TYPE]: {
message: s__('Runners|This runner is available to all projects and subgroups in a group.'),
anchor: 'group-runners',
},
[PROJECT_TYPE]: {
message: s__('Runners|This runner is associated with one or more projects.'),
anchor: 'specific-runners',
},
};
export default {
components: {
GlAlert,
GlLink,
},
props: {
type: {
type: String,
required: false,
default: null,
validator(type) {
return Boolean(ALERT_DATA[type]);
},
},
},
computed: {
alert() {
return ALERT_DATA[this.type];
},
helpHref() {
return helpPagePath('ci/runners/runners_scope', { anchor: this.alert.anchor });
},
},
};
</script>
<template>
<gl-alert v-if="alert" variant="info" :dismissible="false">
{{ alert.message }}
<gl-link :href="helpHref">{{ __('Learn more.') }}</gl-link>
</gl-alert>
</template>

View File

@ -9,4 +9,6 @@ fragment RunnerDetailsShared on CiRunner {
description
maximumTimeout
tagList
createdAt
status(legacyMode: null)
}

View File

@ -2,19 +2,16 @@
import createFlash from '~/flash';
import { TYPE_CI_RUNNER } from '~/graphql_shared/constants';
import { convertToGraphQLId } from '~/graphql_shared/utils';
import { sprintf } from '~/locale';
import RunnerTypeAlert from '../components/runner_type_alert.vue';
import RunnerTypeBadge from '../components/runner_type_badge.vue';
import RunnerHeader from '../components/runner_header.vue';
import RunnerUpdateForm from '../components/runner_update_form.vue';
import { I18N_DETAILS_TITLE, I18N_FETCH_ERROR } from '../constants';
import { I18N_FETCH_ERROR } from '../constants';
import getRunnerQuery from '../graphql/get_runner.query.graphql';
import { captureException } from '../sentry_utils';
export default {
name: 'RunnerDetailsApp',
components: {
RunnerTypeAlert,
RunnerTypeBadge,
RunnerHeader,
RunnerUpdateForm,
},
props: {
@ -43,11 +40,6 @@ export default {
},
},
},
computed: {
pageTitle() {
return sprintf(I18N_DETAILS_TITLE, { runner_id: this.runnerId });
},
},
errorCaptured(error) {
this.reportToSentry(error);
},
@ -60,12 +52,7 @@ export default {
</script>
<template>
<div>
<h2 class="page-title">
{{ pageTitle }} <runner-type-badge v-if="runner" :type="runner.runnerType" />
</h2>
<runner-type-alert v-if="runner" :type="runner.runnerType" />
<runner-header v-if="runner" :runner="runner" />
<runner-update-form :runner="runner" class="gl-my-5" />
</div>
</template>

View File

@ -5,6 +5,11 @@
- placeholder = local_assigns[:placeholder] || _('Search or filter results...')
- block_css_class = type != :productivity_analytics ? 'row-content-block second-block' : ''
- is_epic_board = board&.to_type == "EpicBoard"
- if @group.present?
- ff_resource = @group
- else
- ff_resource = board&.resource_parent&.group
- if is_epic_board
- user_can_admin_list = can?(current_user, :admin_epic_board_list, board.resource_parent)
- elsif board
@ -26,7 +31,7 @@
= check_box_tag checkbox_id, nil, false, class: "check-all-issues left"
- if is_epic_board
#js-board-filtered-search{ data: { full_path: @group&.full_path } }
- elsif Feature.enabled?(:issue_boards_filtered_search, board&.resource_parent) && board
- elsif Feature.enabled?(:issue_boards_filtered_search, ff_resource) && board
#js-issue-board-filtered-search
- else
.issues-other-filters.filtered-search-wrapper.d-flex.flex-column.flex-md-row

View File

@ -2,7 +2,7 @@
name: ci_retry_downstream_pipeline
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/76115
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/347424
milestone: '14.16'
milestone: '14.6'
type: development
group: group::pipeline authoring
default_enabled: false

View File

@ -876,6 +876,9 @@ msgstr ""
msgid "%{rotation} has been recalculated with the remaining participants. Please review the new setup for %{rotation}. It is recommended that you reach out to the current on-call responder to ensure continuity of on-call coverage."
msgstr ""
msgid "%{runner} created %{timeago}"
msgstr ""
msgid "%{scope} results for term '%{term}'"
msgstr ""
@ -30741,9 +30744,6 @@ msgstr ""
msgid "Runners|This runner has never contacted this instance"
msgstr ""
msgid "Runners|This runner is associated with one or more projects."
msgstr ""
msgid "Runners|This runner is associated with specific projects."
msgstr ""

View File

@ -468,9 +468,9 @@ RSpec.describe "Admin Runners" do
end
end
describe 'runner page title', :js do
it 'contains the runner id' do
expect(find('.page-title')).to have_content("Runner ##{runner.id}")
describe 'runner header', :js do
it 'contains the runner status, type and id' do
expect(page).to have_content("never contacted shared Runner ##{runner.id} created")
end
end

View File

@ -0,0 +1,93 @@
import { GlSprintf } from '@gitlab/ui';
import { mount, shallowMount } from '@vue/test-utils';
import { GROUP_TYPE, STATUS_ONLINE } from '~/runner/constants';
import { TYPE_CI_RUNNER } from '~/graphql_shared/constants';
import { convertToGraphQLId } from '~/graphql_shared/utils';
import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
import RunnerHeader from '~/runner/components/runner_header.vue';
import RunnerTypeBadge from '~/runner/components/runner_type_badge.vue';
import RunnerStatusBadge from '~/runner/components/runner_status_badge.vue';
import { runnerData } from '../mock_data';
const mockRunner = runnerData.data.runner;
describe('RunnerHeader', () => {
let wrapper;
const findRunnerTypeBadge = () => wrapper.findComponent(RunnerTypeBadge);
const findRunnerStatusBadge = () => wrapper.findComponent(RunnerStatusBadge);
const findTimeAgo = () => wrapper.findComponent(TimeAgo);
const createComponent = ({ runner = {}, mountFn = shallowMount } = {}) => {
wrapper = mountFn(RunnerHeader, {
propsData: {
runner: {
...mockRunner,
...runner,
},
},
stubs: {
GlSprintf,
TimeAgo,
},
});
};
afterEach(() => {
wrapper.destroy();
});
it('displays the runner status', () => {
createComponent({
mountFn: mount,
runner: {
status: STATUS_ONLINE,
},
});
expect(findRunnerStatusBadge().text()).toContain(`online`);
});
it('displays the runner type', () => {
createComponent({
mountFn: mount,
runner: {
runnerType: GROUP_TYPE,
},
});
expect(findRunnerTypeBadge().text()).toContain(`group`);
});
it('displays the runner id', () => {
createComponent({
runner: {
id: convertToGraphQLId(TYPE_CI_RUNNER, 99),
},
});
expect(wrapper.text()).toContain(`Runner #99`);
});
it('displays the runner creation time', () => {
createComponent();
expect(wrapper.text()).toMatch(/created .+/);
expect(findTimeAgo().props('time')).toBe(mockRunner.createdAt);
});
it('does not display runner creation time if createdAt missing', () => {
createComponent({
runner: {
id: convertToGraphQLId(TYPE_CI_RUNNER, 99),
createdAt: null,
},
});
expect(wrapper.text()).toContain(`Runner #99`);
expect(wrapper.text()).not.toMatch(/created .+/);
expect(findTimeAgo().exists()).toBe(false);
});
});

View File

@ -1,61 +0,0 @@
import { GlAlert, GlLink } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import RunnerTypeAlert from '~/runner/components/runner_type_alert.vue';
import { INSTANCE_TYPE, GROUP_TYPE, PROJECT_TYPE } from '~/runner/constants';
describe('RunnerTypeAlert', () => {
let wrapper;
const findAlert = () => wrapper.findComponent(GlAlert);
const findLink = () => wrapper.findComponent(GlLink);
const createComponent = ({ props = {} } = {}) => {
wrapper = shallowMount(RunnerTypeAlert, {
propsData: {
type: INSTANCE_TYPE,
...props,
},
});
};
afterEach(() => {
wrapper.destroy();
});
describe.each`
type | exampleText | anchor
${INSTANCE_TYPE} | ${'This runner is available to all groups and projects'} | ${'#shared-runners'}
${GROUP_TYPE} | ${'This runner is available to all projects and subgroups in a group'} | ${'#group-runners'}
${PROJECT_TYPE} | ${'This runner is associated with one or more projects'} | ${'#specific-runners'}
`('When it is an $type level runner', ({ type, exampleText, anchor }) => {
beforeEach(() => {
createComponent({ props: { type } });
});
it('Describes runner type', () => {
expect(wrapper.text()).toMatch(exampleText);
});
it(`Shows an "info" variant`, () => {
expect(findAlert().props('variant')).toBe('info');
});
it(`Links to anchor "${anchor}"`, () => {
expect(findLink().attributes('href')).toBe(`/help/ci/runners/runners_scope${anchor}`);
});
});
describe('When runner type is not correct', () => {
it('Does not render content when type is missing', () => {
createComponent({ props: { type: undefined } });
expect(wrapper.html()).toBe('');
});
it('Validation fails for an incorrect type', () => {
expect(() => {
createComponent({ props: { type: 'NOT_A_TYPE' } });
}).toThrow();
});
});
});

View File

@ -127,7 +127,7 @@ describe('RunnerUpdateForm', () => {
await submitFormAndWait();
// Some fields are not submitted
const { ipAddress, runnerType, ...submitted } = mockRunner;
const { ipAddress, runnerType, createdAt, status, ...submitted } = mockRunner;
expectToHaveSubmittedRunnerContaining(submitted);
});

View File

@ -5,7 +5,7 @@ import waitForPromises from 'helpers/wait_for_promises';
import createFlash from '~/flash';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import RunnerTypeBadge from '~/runner/components/runner_type_badge.vue';
import RunnerHeader from '~/runner/components/runner_header.vue';
import getRunnerQuery from '~/runner/graphql/get_runner.query.graphql';
import RunnerDetailsApp from '~/runner/runner_details/runner_details_app.vue';
import { captureException } from '~/runner/sentry_utils';
@ -25,7 +25,7 @@ describe('RunnerDetailsApp', () => {
let wrapper;
let mockRunnerQuery;
const findRunnerTypeBadge = () => wrapper.findComponent(RunnerTypeBadge);
const findRunnerHeader = () => wrapper.findComponent(RunnerHeader);
const createComponentWithApollo = ({ props = {}, mountFn = shallowMount } = {}) => {
wrapper = mountFn(RunnerDetailsApp, {
@ -40,7 +40,7 @@ describe('RunnerDetailsApp', () => {
return waitForPromises();
};
beforeEach(async () => {
beforeEach(() => {
mockRunnerQuery = jest.fn().mockResolvedValue(runnerData);
});
@ -56,15 +56,16 @@ describe('RunnerDetailsApp', () => {
});
it('displays the runner id', async () => {
await createComponentWithApollo();
expect(wrapper.text()).toContain(`Runner #${mockRunnerId}`);
});
it('displays the runner type', async () => {
await createComponentWithApollo({ mountFn: mount });
expect(findRunnerTypeBadge().text()).toBe('shared');
expect(findRunnerHeader().text()).toContain(`Runner #${mockRunnerId} created`);
});
it('displays the runner type and status', async () => {
await createComponentWithApollo({ mountFn: mount });
expect(findRunnerHeader().text()).toContain(`never contacted`);
expect(findRunnerHeader().text()).toContain(`shared`);
});
describe('When there is an error', () => {
@ -73,14 +74,14 @@ describe('RunnerDetailsApp', () => {
await createComponentWithApollo();
});
it('error is reported to sentry', async () => {
it('error is reported to sentry', () => {
expect(captureException).toHaveBeenCalledWith({
error: new Error('Network error: Error!'),
component: 'RunnerDetailsApp',
});
});
it('error is shown to the user', async () => {
it('error is shown to the user', () => {
expect(createFlash).toHaveBeenCalled();
});
});

View File

@ -1,12 +1,34 @@
# frozen_string_literal: true
RSpec.shared_examples 'permission level for issue mutation is correctly verified' do |raises_for_all_errors = false|
before do
issue.assignees = []
issue.author = user
let_it_be(:other_user_author) { create(:user) }
def issue_attributes(issue)
issue.attributes.except(
# Description and title can be updated by authors and assignees of the issues
'description',
'title',
# Those fields are calculated or expected to be modified during the mutations
'author_id',
'updated_at',
'updated_by_id',
'last_edited_at',
'last_edited_by_id',
'lock_version',
# There were spec failures due to nano-second comparisons
# this property isn't changed by any mutation so we don't have to verify it
'created_at'
)
end
shared_examples_for 'when the user does not have access to the resource' do |raise_for_assigned|
let(:expected) { issue_attributes(issue) }
shared_examples_for 'when the user does not have access to the resource' do |raise_for_assigned_and_author|
before do
issue.assignees = []
issue.update!(author: other_user_author)
end
it 'raises an error' do
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
end
@ -17,21 +39,25 @@ RSpec.shared_examples 'permission level for issue mutation is correctly verified
end
it 'does not modify issue' do
if raises_for_all_errors || raise_for_assigned
if raises_for_all_errors || raise_for_assigned_and_author
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
else
expect(subject[:issue]).to eq issue
expect(issue_attributes(subject[:issue])).to eq expected
end
end
end
context 'even if author of the issue' do
before do
issue.author = user
issue.update!(author: user)
end
it 'raises an error' do
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
it 'does not modify issue' do
if raises_for_all_errors || raise_for_assigned_and_author
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
else
expect(issue_attributes(subject[:issue])).to eq expected
end
end
end
end

View File

@ -1,13 +1,39 @@
# frozen_string_literal: true
RSpec.shared_examples 'permission level for merge request mutation is correctly verified' do
before do
merge_request.assignees = []
merge_request.reviewers = []
merge_request.author = nil
let(:other_user_author) { create(:user) }
def mr_attributes(mr)
mr.attributes.except(
# Authors and assignees can edit title, description, target branch and draft status
'title',
'description',
'target_branch',
'draft',
# Those fields are calculated or expected to be modified during the mutations
'author_id',
'latest_merge_request_diff_id',
'last_edited_at',
'last_edited_by_id',
'lock_version',
'updated_at',
'updated_by_id',
'merge_status',
# There were spec failures due to nano-second comparisons
# this property isn't changed by any mutation so we don't have to verify it
'created_at'
)
end
shared_examples_for 'when the user does not have access to the resource' do |raise_for_assigned|
let(:expected) { mr_attributes(merge_request) }
shared_examples_for 'when the user does not have access to the resource' do |raise_for_assigned_and_author|
before do
merge_request.assignees = []
merge_request.reviewers = []
merge_request.update!(author: other_user_author)
end
it 'raises an error' do
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
end
@ -18,12 +44,12 @@ RSpec.shared_examples 'permission level for merge request mutation is correctly
end
it 'does not modify merge request' do
if raise_for_assigned
if raise_for_assigned_and_author
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
else
# In some cases we simply do nothing instead of raising
# https://gitlab.com/gitlab-org/gitlab/-/issues/196241
expect(subject[:merge_request]).to eq merge_request
expect(mr_attributes(subject[:merge_request])).to eq expected
end
end
end
@ -40,11 +66,17 @@ RSpec.shared_examples 'permission level for merge request mutation is correctly
context 'even if author of the merge request' do
before do
merge_request.author = user
merge_request.update!(author: user)
end
it 'raises an error' do
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
if raise_for_assigned_and_author
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
else
# In some cases we simply do nothing instead of raising
# https://gitlab.com/gitlab-org/gitlab/-/issues/196241
expect(mr_attributes(subject[:merge_request])).to eq expected
end
end
end
end