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

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-02-15 15:09:29 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-02-15 15:09:29 +0300
commit6986c1adc235859111e45593bb0bd61e70892d3c (patch)
tree99912b55cd4c39c7ce24120269dfc870ff8f3705 /spec
parentc5b1e86b43f136d8a43cab867ddc49a02751c45a (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/features/projects/terraform_spec.rb2
-rw-r--r--spec/frontend/issuable_list/components/issuable_item_spec.js46
-rw-r--r--spec/frontend/terraform/components/states_table_actions_spec.js75
-rw-r--r--spec/frontend/terraform/components/states_table_spec.js27
-rw-r--r--spec/frontend/vue_shared/components/toggle_button_spec.js96
-rw-r--r--spec/graphql/resolvers/group_labels_resolver_spec.rb96
-rw-r--r--spec/graphql/resolvers/labels_resolver_spec.rb96
-rw-r--r--spec/graphql/types/group_type_spec.rb4
-rw-r--r--spec/graphql/types/project_type_spec.rb4
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/template_usage_spec.rb2
-rw-r--r--spec/lib/gitlab/url_blocker_spec.rb41
-rw-r--r--spec/lib/gitlab/usage_data_counters/ci_template_unique_counter_spec.rb86
-rw-r--r--spec/support/helpers/dns_helpers.rb35
-rw-r--r--spec/support/helpers/stub_requests.rb19
-rw-r--r--spec/support/shared_examples/graphql/label_fields.rb2
15 files changed, 464 insertions, 167 deletions
diff --git a/spec/features/projects/terraform_spec.rb b/spec/features/projects/terraform_spec.rb
index dfa4dad8490..55b906c2bc5 100644
--- a/spec/features/projects/terraform_spec.rb
+++ b/spec/features/projects/terraform_spec.rb
@@ -68,7 +68,7 @@ RSpec.describe 'Terraform', :js do
fill_in "terraform-state-remove-input-#{additional_state.name}", with: additional_state.name
click_button 'Remove'
- expect(page).not_to have_content(additional_state.name)
+ expect(page).to have_content("#{additional_state.name} successfully removed")
expect { additional_state.reload }.to raise_error ActiveRecord::RecordNotFound
end
end
diff --git a/spec/frontend/issuable_list/components/issuable_item_spec.js b/spec/frontend/issuable_list/components/issuable_item_spec.js
index d6e6816086e..987acf559e3 100644
--- a/spec/frontend/issuable_list/components/issuable_item_spec.js
+++ b/spec/frontend/issuable_list/components/issuable_item_spec.js
@@ -18,6 +18,8 @@ const createComponent = ({ issuableSymbol = '#', issuable = mockIssuable, slots
slots,
});
+const MOCK_GITLAB_URL = 'http://0.0.0.0:3000';
+
describe('IssuableItem', () => {
// The mock data is dependent that this is after our default date
useFakeDate(2020, 11, 11);
@@ -28,7 +30,7 @@ describe('IssuableItem', () => {
let wrapper;
beforeEach(() => {
- gon.gitlab_url = 'http://0.0.0.0:3000';
+ gon.gitlab_url = MOCK_GITLAB_URL;
wrapper = createComponent();
});
@@ -73,11 +75,11 @@ describe('IssuableItem', () => {
describe('isIssuableUrlExternal', () => {
it.each`
- issuableWebUrl | urlType | returnValue
- ${'/gitlab-org/gitlab-test/-/issues/2'} | ${'relative'} | ${false}
- ${'http://0.0.0.0:3000/gitlab-org/gitlab-test/-/issues/1'} | ${'absolute and internal'} | ${false}
- ${'http://jira.atlassian.net/browse/IG-1'} | ${'external'} | ${true}
- ${'https://github.com/gitlabhq/gitlabhq/issues/1'} | ${'external'} | ${true}
+ issuableWebUrl | urlType | returnValue
+ ${'/gitlab-org/gitlab-test/-/issues/2'} | ${'relative'} | ${false}
+ ${`${MOCK_GITLAB_URL}/gitlab-org/gitlab-test/-/issues/1`} | ${'absolute and internal'} | ${false}
+ ${'http://jira.atlassian.net/browse/IG-1'} | ${'external'} | ${true}
+ ${'https://github.com/gitlabhq/gitlabhq/issues/1'} | ${'external'} | ${true}
`(
'returns $returnValue when `issuable.webUrl` is $urlType',
async ({ issuableWebUrl, returnValue }) => {
@@ -217,14 +219,32 @@ describe('IssuableItem', () => {
});
describe('template', () => {
- it('renders issuable title', () => {
- const titleEl = wrapper.find('[data-testid="issuable-title"]');
+ it.each`
+ gitlabWebUrl | webUrl | expectedHref | expectedTarget
+ ${undefined} | ${`${MOCK_GITLAB_URL}/issue`} | ${`${MOCK_GITLAB_URL}/issue`} | ${undefined}
+ ${undefined} | ${'https://jira.com/issue'} | ${'https://jira.com/issue'} | ${'_blank'}
+ ${'/gitlab-org/issue'} | ${'https://jira.com/issue'} | ${'/gitlab-org/issue'} | ${undefined}
+ `(
+ 'renders issuable title correctly when `gitlabWebUrl` is `$gitlabWebUrl` and webUrl is `$webUrl`',
+ async ({ webUrl, gitlabWebUrl, expectedHref, expectedTarget }) => {
+ wrapper.setProps({
+ issuable: {
+ ...mockIssuable,
+ webUrl,
+ gitlabWebUrl,
+ },
+ });
- expect(titleEl.exists()).toBe(true);
- expect(titleEl.find(GlLink).attributes('href')).toBe(mockIssuable.webUrl);
- expect(titleEl.find(GlLink).attributes('target')).not.toBeDefined();
- expect(titleEl.find(GlLink).text()).toBe(mockIssuable.title);
- });
+ await wrapper.vm.$nextTick();
+
+ const titleEl = wrapper.find('[data-testid="issuable-title"]');
+
+ expect(titleEl.exists()).toBe(true);
+ expect(titleEl.find(GlLink).attributes('href')).toBe(expectedHref);
+ expect(titleEl.find(GlLink).attributes('target')).toBe(expectedTarget);
+ expect(titleEl.find(GlLink).text()).toBe(mockIssuable.title);
+ },
+ );
it('renders checkbox when `showCheckbox` prop is true', async () => {
wrapper.setProps({
diff --git a/spec/frontend/terraform/components/states_table_actions_spec.js b/spec/frontend/terraform/components/states_table_actions_spec.js
index 7449ddd3d2a..61f6e9f0f7b 100644
--- a/spec/frontend/terraform/components/states_table_actions_spec.js
+++ b/spec/frontend/terraform/components/states_table_actions_spec.js
@@ -14,6 +14,7 @@ localVue.use(VueApollo);
describe('StatesTableActions', () => {
let lockResponse;
let removeResponse;
+ let toast;
let unlockResponse;
let updateStateResponse;
let wrapper;
@@ -59,10 +60,13 @@ describe('StatesTableActions', () => {
const createComponent = (propsData = defaultProps) => {
const apolloProvider = createMockApolloProvider();
+ toast = jest.fn();
+
wrapper = shallowMount(StateActions, {
apolloProvider,
localVue,
propsData,
+ mocks: { $toast: { show: toast } },
stubs: { GlDropdown, GlModal, GlSprintf },
});
@@ -83,6 +87,7 @@ describe('StatesTableActions', () => {
afterEach(() => {
lockResponse = null;
removeResponse = null;
+ toast = null;
unlockResponse = null;
updateStateResponse = null;
wrapper.destroy();
@@ -243,7 +248,6 @@ describe('StatesTableActions', () => {
describe('when clicking the remove button', () => {
beforeEach(() => {
findRemoveBtn().vm.$emit('click');
-
return waitForPromises();
});
@@ -254,21 +258,70 @@ describe('StatesTableActions', () => {
});
describe('when submitting the remove modal', () => {
- it('does not call the remove mutation when state name is missing', async () => {
- findRemoveModal().vm.$emit('ok');
- await wrapper.vm.$nextTick();
+ describe('when state name is missing', () => {
+ beforeEach(() => {
+ findRemoveModal().vm.$emit('ok');
+ return waitForPromises();
+ });
- expect(removeResponse).not.toHaveBeenCalledWith();
+ it('does not call the remove mutation', () => {
+ expect(removeResponse).not.toHaveBeenCalledWith();
+ });
});
- it('calls the remove mutation when state name is present', async () => {
- await wrapper.setData({ removeConfirmText: defaultProps.state.name });
+ describe('when state name is present', () => {
+ beforeEach(async () => {
+ await wrapper.setData({ removeConfirmText: defaultProps.state.name });
+
+ findRemoveModal().vm.$emit('ok');
- findRemoveModal().vm.$emit('ok');
- await wrapper.vm.$nextTick();
+ await waitForPromises();
+ });
+
+ it('calls the remove mutation', () => {
+ expect(removeResponse).toHaveBeenCalledWith({ stateID: defaultProps.state.id });
+ });
+
+ it('calls the toast action', () => {
+ expect(toast).toHaveBeenCalledWith(`${defaultProps.state.name} successfully removed`);
+ });
- expect(removeResponse).toHaveBeenCalledWith({
- stateID: defaultProps.state.id,
+ it('calls mutations to set loading and errors', () => {
+ // loading update
+ expect(updateStateResponse).toHaveBeenNthCalledWith(
+ 1,
+ {},
+ {
+ terraformState: {
+ ...defaultProps.state,
+ _showDetails: false,
+ errorMessages: [],
+ loadingLock: false,
+ loadingRemove: true,
+ },
+ },
+ // Apollo fields
+ expect.any(Object),
+ expect.any(Object),
+ );
+
+ // final update
+ expect(updateStateResponse).toHaveBeenNthCalledWith(
+ 2,
+ {},
+ {
+ terraformState: {
+ ...defaultProps.state,
+ _showDetails: false,
+ errorMessages: [],
+ loadingLock: false,
+ loadingRemove: false,
+ },
+ },
+ // Apollo fields
+ expect.any(Object),
+ expect.any(Object),
+ );
});
});
});
diff --git a/spec/frontend/terraform/components/states_table_spec.js b/spec/frontend/terraform/components/states_table_spec.js
index a9aeba202e0..100e577f514 100644
--- a/spec/frontend/terraform/components/states_table_spec.js
+++ b/spec/frontend/terraform/components/states_table_spec.js
@@ -1,4 +1,4 @@
-import { GlIcon, GlTooltip } from '@gitlab/ui';
+import { GlIcon, GlLoadingIcon, GlTooltip } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
import { useFakeDate } from 'helpers/fake_date';
import StatesTable from '~/terraform/components/states_table.vue';
@@ -92,6 +92,17 @@ describe('StatesTable', () => {
},
},
},
+ {
+ _showDetails: false,
+ errorMessages: [],
+ name: 'state-5',
+ loadingLock: false,
+ loadingRemove: true,
+ lockedAt: null,
+ lockedByUser: null,
+ updatedAt: '2020-10-10T00:00:00Z',
+ latestVersion: null,
+ },
],
};
@@ -112,14 +123,15 @@ describe('StatesTable', () => {
});
it.each`
- name | toolTipText | locked | lineNumber
- ${'state-1'} | ${'Locked by user-1 2 days ago'} | ${true} | ${0}
- ${'state-2'} | ${'Locking state'} | ${false} | ${1}
- ${'state-3'} | ${'Unlocking state'} | ${false} | ${2}
- ${'state-4'} | ${'Locked by Unknown User 5 days ago'} | ${true} | ${3}
+ name | toolTipText | locked | loading | lineNumber
+ ${'state-1'} | ${'Locked by user-1 2 days ago'} | ${true} | ${false} | ${0}
+ ${'state-2'} | ${'Locking state'} | ${false} | ${true} | ${1}
+ ${'state-3'} | ${'Unlocking state'} | ${false} | ${true} | ${2}
+ ${'state-4'} | ${'Locked by Unknown User 5 days ago'} | ${true} | ${false} | ${3}
+ ${'state-5'} | ${'Removing'} | ${false} | ${true} | ${4}
`(
'displays the name and locked information "$name" for line "$lineNumber"',
- ({ name, toolTipText, locked, lineNumber }) => {
+ ({ name, toolTipText, locked, loading, lineNumber }) => {
const states = wrapper.findAll('[data-testid="terraform-states-table-name"]');
const state = states.at(lineNumber);
@@ -127,6 +139,7 @@ describe('StatesTable', () => {
expect(state.text()).toContain(name);
expect(state.find(GlIcon).exists()).toBe(locked);
+ expect(state.find(GlLoadingIcon).exists()).toBe(loading);
expect(toolTip.exists()).toBe(locked);
if (locked) {
diff --git a/spec/frontend/vue_shared/components/toggle_button_spec.js b/spec/frontend/vue_shared/components/toggle_button_spec.js
deleted file mode 100644
index 632e648aadc..00000000000
--- a/spec/frontend/vue_shared/components/toggle_button_spec.js
+++ /dev/null
@@ -1,96 +0,0 @@
-import { GlIcon } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import ToggleButton from '~/vue_shared/components/toggle_button.vue';
-
-describe('Toggle Button component', () => {
- let wrapper;
-
- function createComponent(propsData = {}) {
- wrapper = shallowMount(ToggleButton, {
- propsData,
- });
- }
-
- const findInput = () => wrapper.find('input');
- const findButton = () => wrapper.find('button');
- const findToggleIcon = () => wrapper.find(GlIcon);
-
- afterEach(() => {
- wrapper.destroy();
- wrapper = null;
- });
-
- it('renders input with provided name', () => {
- createComponent({
- name: 'foo',
- });
-
- expect(findInput().attributes('name')).toBe('foo');
- });
-
- describe.each`
- value | iconName
- ${true} | ${'status_success_borderless'}
- ${false} | ${'status_failed_borderless'}
- `('when `value` prop is `$value`', ({ value, iconName }) => {
- beforeEach(() => {
- createComponent({
- value,
- name: 'foo',
- });
- });
-
- it('renders input with correct value attribute', () => {
- expect(findInput().attributes('value')).toBe(`${value}`);
- });
-
- it('renders correct icon', () => {
- const icon = findToggleIcon();
- expect(icon.isVisible()).toBe(true);
- expect(icon.props('name')).toBe(iconName);
- expect(findButton().classes('is-checked')).toBe(value);
- });
-
- describe('when clicked', () => {
- it('emits `change` event with correct event', async () => {
- findButton().trigger('click');
- await wrapper.vm.$nextTick();
-
- expect(wrapper.emitted('change')).toStrictEqual([[!value]]);
- });
- });
- });
-
- describe('when `disabledInput` prop is `true`', () => {
- beforeEach(() => {
- createComponent({
- value: true,
- disabledInput: true,
- });
- });
-
- it('renders disabled button', () => {
- expect(findButton().classes()).toContain('is-disabled');
- });
-
- it('does not emit change event when clicked', async () => {
- findButton().trigger('click');
- await wrapper.vm.$nextTick();
-
- expect(wrapper.emitted('change')).toBeFalsy();
- });
- });
-
- describe('when `isLoading` prop is `true`', () => {
- beforeEach(() => {
- createComponent({
- value: true,
- isLoading: true,
- });
- });
-
- it('renders loading class', () => {
- expect(findButton().classes()).toContain('is-loading');
- });
- });
-});
diff --git a/spec/graphql/resolvers/group_labels_resolver_spec.rb b/spec/graphql/resolvers/group_labels_resolver_spec.rb
new file mode 100644
index 00000000000..ed94f12502a
--- /dev/null
+++ b/spec/graphql/resolvers/group_labels_resolver_spec.rb
@@ -0,0 +1,96 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Resolvers::GroupLabelsResolver do
+ include GraphqlHelpers
+
+ using RSpec::Parameterized::TableSyntax
+
+ let_it_be(:current_user) { create(:user) }
+ let_it_be(:group, reload: true) { create(:group, :private) }
+ let_it_be(:subgroup, reload: true) { create(:group, :private, parent: group) }
+ let_it_be(:sub_subgroup, reload: true) { create(:group, :private, parent: subgroup) }
+ let_it_be(:project, reload: true) { create(:project, :private, group: sub_subgroup) }
+ let_it_be(:label1) { create(:label, project: project, name: 'project feature') }
+ let_it_be(:label2) { create(:label, project: project, name: 'new project feature') }
+ let_it_be(:group_label1) { create(:group_label, group: group, name: 'group feature') }
+ let_it_be(:group_label2) { create(:group_label, group: group, name: 'new group feature') }
+ let_it_be(:subgroup_label1) { create(:group_label, group: subgroup, name: 'subgroup feature') }
+ let_it_be(:subgroup_label2) { create(:group_label, group: subgroup, name: 'new subgroup feature') }
+ let_it_be(:sub_subgroup_label1) { create(:group_label, group: sub_subgroup, name: 'sub_subgroup feature') }
+ let_it_be(:sub_subgroup_label2) { create(:group_label, group: sub_subgroup, name: 'new sub_subgroup feature') }
+
+ specify do
+ expect(described_class).to have_nullable_graphql_type(Types::LabelType.connection_type)
+ end
+
+ describe '#resolve' do
+ context 'with unauthorized user' do
+ it 'raises error' do
+ expect { resolve_labels(subgroup) }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
+ end
+ end
+
+ context 'with authorized user' do
+ it 'does not raise error' do
+ group.add_guest(current_user)
+
+ expect { resolve_labels(subgroup) }.not_to raise_error
+ end
+ end
+
+ context 'without parent' do
+ it 'returns no labels' do
+ expect(resolve_labels(nil)).to eq(Label.none)
+ end
+ end
+
+ context 'at group level' do
+ before_all do
+ group.add_developer(current_user)
+ end
+
+ # because :include_ancestor_groups, :include_descendant_groups, :only_group_labels default to false
+ # the `nil` value would be equivalent to passing in `false` so just check for `nil` option
+ where(:include_ancestor_groups, :include_descendant_groups, :only_group_labels, :search_term, :test) do
+ nil | nil | nil | nil | -> { expect(subject).to contain_exactly(subgroup_label1, subgroup_label2) }
+ nil | nil | true | nil | -> { expect(subject).to contain_exactly(subgroup_label1, subgroup_label2) }
+ nil | true | nil | nil | -> { expect(subject).to contain_exactly(subgroup_label1, subgroup_label2, sub_subgroup_label1, sub_subgroup_label2, label1, label2) }
+ nil | true | true | nil | -> { expect(subject).to contain_exactly(subgroup_label1, subgroup_label2, sub_subgroup_label1, sub_subgroup_label2) }
+ true | nil | nil | nil | -> { expect(subject).to contain_exactly(group_label1, group_label2, subgroup_label1, subgroup_label2) }
+ true | nil | true | nil | -> { expect(subject).to contain_exactly(group_label1, group_label2, subgroup_label1, subgroup_label2) }
+ true | true | nil | nil | -> { expect(subject).to contain_exactly(group_label1, group_label2, subgroup_label1, subgroup_label2, sub_subgroup_label1, sub_subgroup_label2, label1, label2) }
+ true | true | true | nil | -> { expect(subject).to contain_exactly(group_label1, group_label2, subgroup_label1, subgroup_label2, sub_subgroup_label1, sub_subgroup_label2) }
+
+ nil | nil | nil | 'new' | -> { expect(subject).to contain_exactly(subgroup_label2) }
+ nil | nil | true | 'new' | -> { expect(subject).to contain_exactly(subgroup_label2) }
+ nil | true | nil | 'new' | -> { expect(subject).to contain_exactly(subgroup_label2, sub_subgroup_label2, label2) }
+ nil | true | true | 'new' | -> { expect(subject).to contain_exactly(subgroup_label2, sub_subgroup_label2) }
+ true | nil | nil | 'new' | -> { expect(subject).to contain_exactly(group_label2, subgroup_label2) }
+ true | nil | true | 'new' | -> { expect(subject).to contain_exactly(group_label2, subgroup_label2) }
+ true | true | nil | 'new' | -> { expect(subject).to contain_exactly(group_label2, subgroup_label2, sub_subgroup_label2, label2) }
+ true | true | true | 'new' | -> { expect(subject).to contain_exactly(group_label2, subgroup_label2, sub_subgroup_label2) }
+ end
+
+ with_them do
+ let(:params) do
+ {
+ include_ancestor_groups: include_ancestor_groups,
+ include_descendant_groups: include_descendant_groups,
+ only_group_labels: only_group_labels,
+ search_term: search_term
+ }
+ end
+
+ subject { resolve_labels(subgroup, params) }
+
+ it { self.instance_exec(&test) }
+ end
+ end
+ end
+
+ def resolve_labels(parent, args = {}, context = { current_user: current_user })
+ resolve(described_class, obj: parent, args: args, ctx: context)
+ end
+end
diff --git a/spec/graphql/resolvers/labels_resolver_spec.rb b/spec/graphql/resolvers/labels_resolver_spec.rb
new file mode 100644
index 00000000000..3d027a6c8d5
--- /dev/null
+++ b/spec/graphql/resolvers/labels_resolver_spec.rb
@@ -0,0 +1,96 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Resolvers::LabelsResolver do
+ include GraphqlHelpers
+
+ using RSpec::Parameterized::TableSyntax
+
+ let_it_be(:current_user) { create(:user) }
+ let_it_be(:group, reload: true) { create(:group, :private) }
+ let_it_be(:subgroup, reload: true) { create(:group, :private, parent: group) }
+ let_it_be(:sub_subgroup, reload: true) { create(:group, :private, parent: subgroup) }
+ let_it_be(:project, reload: true) { create(:project, :private, group: subgroup) }
+ let_it_be(:label1) { create(:label, project: project, name: 'project feature') }
+ let_it_be(:label2) { create(:label, project: project, name: 'new project feature') }
+ let_it_be(:group_label1) { create(:group_label, group: group, name: 'group feature') }
+ let_it_be(:group_label2) { create(:group_label, group: group, name: 'new group feature') }
+ let_it_be(:subgroup_label1) { create(:group_label, group: subgroup, name: 'subgroup feature') }
+ let_it_be(:subgroup_label2) { create(:group_label, group: subgroup, name: 'new subgroup feature') }
+ let_it_be(:sub_subgroup_label1) { create(:group_label, group: sub_subgroup, name: 'sub_subgroup feature') }
+ let_it_be(:sub_subgroup_label2) { create(:group_label, group: sub_subgroup, name: 'new sub_subgroup feature') }
+
+ specify do
+ expect(described_class).to have_nullable_graphql_type(Types::LabelType.connection_type)
+ end
+
+ describe '#resolve' do
+ context 'with unauthorized user' do
+ it 'returns no labels' do
+ expect { resolve_labels(project) }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
+ end
+ end
+
+ context 'with authorized user' do
+ it 'returns no labels' do
+ group.add_guest(current_user)
+
+ expect { resolve_labels(project) }.not_to raise_error
+ end
+ end
+
+ context 'without parent' do
+ it 'returns no labels' do
+ expect(resolve_labels(nil)).to eq(Label.none)
+ end
+ end
+
+ context 'at project level' do
+ before_all do
+ group.add_developer(current_user)
+ end
+
+ # because :include_ancestor_groups, :include_descendant_groups, :only_group_labels default to false
+ # the `nil` value would be equivalent to passing in `false` so just check for `nil` option
+ where(:include_ancestor_groups, :include_descendant_groups, :only_group_labels, :search_term, :test) do
+ nil | nil | nil | nil | -> { expect(subject).to contain_exactly(label1, label2, subgroup_label1, subgroup_label2) }
+ nil | nil | true | nil | -> { expect(subject).to contain_exactly(label1, label2, subgroup_label1, subgroup_label2) }
+ nil | true | nil | nil | -> { expect(subject).to contain_exactly(label1, label2, subgroup_label1, subgroup_label2, sub_subgroup_label1, sub_subgroup_label2) }
+ nil | true | true | nil | -> { expect(subject).to contain_exactly(label1, label2, subgroup_label1, subgroup_label2, sub_subgroup_label1, sub_subgroup_label2) }
+ true | nil | nil | nil | -> { expect(subject).to contain_exactly(label1, label2, group_label1, group_label2, subgroup_label1, subgroup_label2) }
+ true | nil | true | nil | -> { expect(subject).to contain_exactly(label1, label2, group_label1, group_label2, subgroup_label1, subgroup_label2) }
+ true | true | nil | nil | -> { expect(subject).to contain_exactly(label1, label2, group_label1, group_label2, subgroup_label1, subgroup_label2, sub_subgroup_label1, sub_subgroup_label2) }
+ true | true | true | nil | -> { expect(subject).to contain_exactly(label1, label2, group_label1, group_label2, subgroup_label1, subgroup_label2, sub_subgroup_label1, sub_subgroup_label2) }
+
+ nil | nil | nil | 'new' | -> { expect(subject).to contain_exactly(label2, subgroup_label2) }
+ nil | nil | true | 'new' | -> { expect(subject).to contain_exactly(label2, subgroup_label2) }
+ nil | true | nil | 'new' | -> { expect(subject).to contain_exactly(label2, subgroup_label2, sub_subgroup_label2) }
+ nil | true | true | 'new' | -> { expect(subject).to contain_exactly(label2, subgroup_label2, sub_subgroup_label2) }
+ true | nil | nil | 'new' | -> { expect(subject).to contain_exactly(label2, group_label2, subgroup_label2) }
+ true | nil | true | 'new' | -> { expect(subject).to contain_exactly(label2, group_label2, subgroup_label2) }
+ true | true | nil | 'new' | -> { expect(subject).to contain_exactly(label2, group_label2, subgroup_label2, sub_subgroup_label2) }
+ true | true | true | 'new' | -> { expect(subject).to contain_exactly(label2, group_label2, subgroup_label2, sub_subgroup_label2) }
+ end
+
+ with_them do
+ let(:params) do
+ {
+ include_ancestor_groups: include_ancestor_groups,
+ include_descendant_groups: include_descendant_groups,
+ only_group_labels: only_group_labels,
+ search_term: search_term
+ }
+ end
+
+ subject { resolve_labels(project, params) }
+
+ it { self.instance_exec(&test) }
+ end
+ end
+ end
+
+ def resolve_labels(parent, args = {}, context = { current_user: current_user })
+ resolve(described_class, obj: parent, args: args, ctx: context)
+ end
+end
diff --git a/spec/graphql/types/group_type_spec.rb b/spec/graphql/types/group_type_spec.rb
index de19e8b602a..bba702ba3e9 100644
--- a/spec/graphql/types/group_type_spec.rb
+++ b/spec/graphql/types/group_type_spec.rb
@@ -38,5 +38,7 @@ RSpec.describe GitlabSchema.types['Group'] do
it { is_expected.to have_graphql_resolver(Resolvers::GroupMembersResolver) }
end
- it_behaves_like 'a GraphQL type with labels'
+ it_behaves_like 'a GraphQL type with labels' do
+ let(:labels_resolver_arguments) { [:search_term, :includeAncestorGroups, :includeDescendantGroups, :onlyGroupLabels] }
+ end
end
diff --git a/spec/graphql/types/project_type_spec.rb b/spec/graphql/types/project_type_spec.rb
index 95c835773e1..9579ef8b99b 100644
--- a/spec/graphql/types/project_type_spec.rb
+++ b/spec/graphql/types/project_type_spec.rb
@@ -332,7 +332,9 @@ RSpec.describe GitlabSchema.types['Project'] do
it { is_expected.to have_graphql_resolver(Resolvers::Terraform::StatesResolver) }
end
- it_behaves_like 'a GraphQL type with labels'
+ it_behaves_like 'a GraphQL type with labels' do
+ let(:labels_resolver_arguments) { [:search_term, :includeAncestorGroups] }
+ end
describe 'jira_imports' do
subject { resolve_field(:jira_imports, project) }
diff --git a/spec/lib/gitlab/ci/pipeline/chain/template_usage_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/template_usage_spec.rb
index 3616461d94f..cd868a57bbc 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/template_usage_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/template_usage_spec.rb
@@ -27,7 +27,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::TemplateUsage do
%w(Template-1 Template-2).each do |expected_template|
expect(Gitlab::UsageDataCounters::CiTemplateUniqueCounter).to(
receive(:track_unique_project_event)
- .with(project_id: project.id, template: expected_template)
+ .with(project_id: project.id, template: expected_template, config_source: pipeline.config_source)
)
end
diff --git a/spec/lib/gitlab/url_blocker_spec.rb b/spec/lib/gitlab/url_blocker_spec.rb
index fa01d4e48df..20a8f2f6a41 100644
--- a/spec/lib/gitlab/url_blocker_spec.rb
+++ b/spec/lib/gitlab/url_blocker_spec.rb
@@ -160,6 +160,47 @@ RSpec.describe Gitlab::UrlBlocker, :stub_invalid_dns_only do
end
end
end
+
+ context 'when resolving runs into a timeout' do
+ let(:import_url) { 'http://example.com' }
+
+ subject { described_class.validate!(import_url, dns_rebind_protection: dns_rebind_protection) }
+
+ before do
+ skip 'timeout is not available' unless timeout_available?
+
+ stub_env('RSPEC_ALLOW_INVALID_URLS', 'false')
+ stub_const("#{described_class}::GETADDRINFO_TIMEOUT_SECONDS", 0)
+ end
+
+ context 'with dns rebinding enabled' do
+ let(:dns_rebind_protection) { true }
+
+ it 'raises an error due to DNS timeout' do
+ expect { subject }.to raise_error(described_class::BlockedUrlError)
+ end
+ end
+
+ context 'with dns rebinding disabled' do
+ let(:dns_rebind_protection) { false }
+
+ it_behaves_like 'validates URI and hostname' do
+ let(:expected_uri) { import_url }
+ let(:expected_hostname) { nil }
+ end
+ end
+
+ # Detect whether the timeout option is available.
+ #
+ # See https://bugs.ruby-lang.org/issues/15553
+ def timeout_available?
+ Addrinfo.getaddrinfo('localhost', nil, timeout: 0)
+
+ false
+ rescue SocketError
+ true
+ end
+ end
end
describe '#blocked_url?' do
diff --git a/spec/lib/gitlab/usage_data_counters/ci_template_unique_counter_spec.rb b/spec/lib/gitlab/usage_data_counters/ci_template_unique_counter_spec.rb
index 4b07f9143b5..b1d5d106082 100644
--- a/spec/lib/gitlab/usage_data_counters/ci_template_unique_counter_spec.rb
+++ b/spec/lib/gitlab/usage_data_counters/ci_template_unique_counter_spec.rb
@@ -3,28 +3,88 @@
require 'spec_helper'
RSpec.describe Gitlab::UsageDataCounters::CiTemplateUniqueCounter do
- let(:project_id) { 1 }
-
describe '.track_unique_project_event' do
- described_class::TEMPLATE_TO_EVENT.keys.each do |template|
- context "when given template #{template}" do
- it_behaves_like 'tracking unique hll events' do
- subject(:request) { described_class.track_unique_project_event(project_id: project_id, template: template) }
+ using RSpec::Parameterized::TableSyntax
+
+ where(:template, :config_source, :expected_event) do
+ # Implicit Auto DevOps usage
+ 'Auto-DevOps.gitlab-ci.yml' | :auto_devops_source | 'p_ci_templates_implicit_auto_devops'
+ 'Jobs/Build.gitlab-ci.yml' | :auto_devops_source | 'p_ci_templates_implicit_auto_devops_build'
+ 'Jobs/Deploy.gitlab-ci.yml' | :auto_devops_source | 'p_ci_templates_implicit_auto_devops_deploy'
+ 'Security/SAST.gitlab-ci.yml' | :auto_devops_source | 'p_ci_templates_implicit_security_sast'
+ 'Security/Secret-Detection.gitlab-ci.yml' | :auto_devops_source | 'p_ci_templates_implicit_security_secret_detection'
+ # Explicit include:template usage
+ '5-Minute-Production-App.gitlab-ci.yml' | :repository_source | 'p_ci_templates_5_min_production_app'
+ 'Auto-DevOps.gitlab-ci.yml' | :repository_source | 'p_ci_templates_auto_devops'
+ 'AWS/CF-Provision-and-Deploy-EC2.gitlab-ci.yml' | :repository_source | 'p_ci_templates_aws_cf_deploy_ec2'
+ 'AWS/Deploy-ECS.gitlab-ci.yml' | :repository_source | 'p_ci_templates_aws_deploy_ecs'
+ 'Jobs/Build.gitlab-ci.yml' | :repository_source | 'p_ci_templates_auto_devops_build'
+ 'Jobs/Deploy.gitlab-ci.yml' | :repository_source | 'p_ci_templates_auto_devops_deploy'
+ 'Jobs/Deploy.latest.gitlab-ci.yml' | :repository_source | 'p_ci_templates_auto_devops_deploy_latest'
+ 'Security/SAST.gitlab-ci.yml' | :repository_source | 'p_ci_templates_security_sast'
+ 'Security/Secret-Detection.gitlab-ci.yml' | :repository_source | 'p_ci_templates_security_secret_detection'
+ 'Terraform/Base.latest.gitlab-ci.yml' | :repository_source | 'p_ci_templates_terraform_base_latest'
+ end
+
+ with_them do
+ it_behaves_like 'tracking unique hll events' do
+ subject(:request) { described_class.track_unique_project_event(project_id: project_id, template: template, config_source: config_source) }
+
+ let(:project_id) { 1 }
+ let(:target_id) { expected_event }
+ let(:expected_type) { instance_of(Integer) }
+ end
+ end
+
+ context 'known_events coverage tests' do
+ let(:project_id) { 1 }
+ let(:config_source) { :repository_source }
- let(:target_id) { "p_ci_templates_#{described_class::TEMPLATE_TO_EVENT[template]}" }
- let(:expected_type) { instance_of(Integer) }
+ # These tests help guard against missing "explicit" events in known_events/ci_templates.yml
+ context 'explicit include:template events' do
+ described_class::TEMPLATE_TO_EVENT.keys.each do |template|
+ it "does not raise error for #{template}" do
+ expect do
+ described_class.track_unique_project_event(project_id: project_id, template: template, config_source: config_source)
+ end.not_to raise_error
+ end
+ end
+ end
+
+ # This test is to help guard against missing "implicit" events in known_events/ci_templates.yml
+ it 'does not raise error for any template in an implicit Auto DevOps pipeline' do
+ project = create(:project, :auto_devops)
+ pipeline = double(project: project)
+ command = double
+ result = Gitlab::Ci::YamlProcessor.new(
+ Gitlab::Ci::Pipeline::Chain::Config::Content::AutoDevops.new(pipeline, command).content,
+ project: project,
+ user: double,
+ sha: double
+ ).execute
+
+ config_source = :auto_devops_source
+
+ result.included_templates.each do |template|
+ expect do
+ described_class.track_unique_project_event(project_id: project.id, template: template, config_source: config_source)
+ end.not_to raise_error
end
end
end
- it 'does not track templates outside of TEMPLATE_TO_EVENT' do
- expect(Gitlab::UsageDataCounters::HLLRedisCounter).not_to(
- receive(:track_event)
- )
+ context 'templates outside of TEMPLATE_TO_EVENT' do
+ let(:project_id) { 1 }
+ let(:config_source) { :repository_source }
+
Dir.glob(File.join('lib', 'gitlab', 'ci', 'templates', '**'), base: Rails.root) do |template|
next if described_class::TEMPLATE_TO_EVENT.key?(template)
- described_class.track_unique_project_event(project_id: 1, template: template)
+ it "does not track #{template}" do
+ expect(Gitlab::UsageDataCounters::HLLRedisCounter).not_to(receive(:track_event))
+
+ described_class.track_unique_project_event(project_id: project_id, template: template, config_source: config_source)
+ end
end
end
end
diff --git a/spec/support/helpers/dns_helpers.rb b/spec/support/helpers/dns_helpers.rb
index 1795b0a9ac3..52c708e77a5 100644
--- a/spec/support/helpers/dns_helpers.rb
+++ b/spec/support/helpers/dns_helpers.rb
@@ -12,19 +12,38 @@ module DnsHelpers
end
def stub_all_dns!
- allow(Addrinfo).to receive(:getaddrinfo).with(anything, anything, nil, :STREAM).and_return([])
- allow(Addrinfo).to receive(:getaddrinfo).with(anything, anything, nil, :STREAM, anything, anything).and_return([])
+ allow(Addrinfo).to receive(:getaddrinfo).and_return([])
end
def stub_invalid_dns!
- allow(Addrinfo).to receive(:getaddrinfo).with(/\Afoobar\.\w|(\d{1,3}\.){4,}\d{1,3}\z/i, anything, nil, :STREAM) do
- raise SocketError.new("getaddrinfo: Name or service not known")
- end
+ invalid_addresses = %r{
+ \A
+ (?:
+ foobar\.\w |
+ (?:\d{1,3}\.){4,}\d{1,3}
+ )
+ \z
+ }ix
+
+ allow(Addrinfo).to receive(:getaddrinfo)
+ .with(invalid_addresses, any_args)
+ .and_raise(SocketError, 'getaddrinfo: Name or service not known')
end
def permit_local_dns!
- local_addresses = /\A(127|10)\.0\.0\.\d{1,3}|(192\.168|172\.16)\.\d{1,3}\.\d{1,3}|0\.0\.0\.0|localhost\z/i
- allow(Addrinfo).to receive(:getaddrinfo).with(local_addresses, anything, nil, :STREAM).and_call_original
- allow(Addrinfo).to receive(:getaddrinfo).with(local_addresses, anything, nil, :STREAM, anything, anything, any_args).and_call_original
+ local_addresses = %r{
+ \A
+ (?:
+ (?:127|10)\.0\.0\.\d{1,3} |
+ (?:192\.168|172\.16)\.\d{1,3}\.\d{1,3} |
+ 0\.0\.0\.0 |
+ localhost
+ )
+ \z
+ }ix
+
+ allow(Addrinfo).to receive(:getaddrinfo)
+ .with(local_addresses, any_args)
+ .and_call_original
end
end
diff --git a/spec/support/helpers/stub_requests.rb b/spec/support/helpers/stub_requests.rb
index 473f07dd413..81872b1c908 100644
--- a/spec/support/helpers/stub_requests.rb
+++ b/spec/support/helpers/stub_requests.rb
@@ -18,14 +18,13 @@ module StubRequests
end
def stub_dns(url, ip_address:, port: 80)
- url = parse_url(url)
+ url = URI(url)
socket = Socket.sockaddr_in(port, ip_address)
addr = Addrinfo.new(socket)
- # See Gitlab::UrlBlocker
allow(Addrinfo).to receive(:getaddrinfo)
- .with(url.hostname, url.port, nil, :STREAM)
- .and_return([addr])
+ .with(url.hostname, url.port, any_args)
+ .and_return([addr])
end
def stub_all_dns(url, ip_address:)
@@ -34,22 +33,14 @@ module StubRequests
socket = Socket.sockaddr_in(port, ip_address)
addr = Addrinfo.new(socket)
- # See Gitlab::UrlBlocker
- allow(Addrinfo).to receive(:getaddrinfo).and_call_original
allow(Addrinfo).to receive(:getaddrinfo)
- .with(url.hostname, anything, nil, :STREAM)
+ .with(url.hostname, any_args)
.and_return([addr])
end
def stubbed_hostname(url, hostname: IP_ADDRESS_STUB)
- url = parse_url(url)
+ url = URI(url)
url.hostname = hostname
url.to_s
end
-
- private
-
- def parse_url(url)
- url.is_a?(URI) ? url : URI(url)
- end
end
diff --git a/spec/support/shared_examples/graphql/label_fields.rb b/spec/support/shared_examples/graphql/label_fields.rb
index 0d09ab391f1..4159e4e03ab 100644
--- a/spec/support/shared_examples/graphql/label_fields.rb
+++ b/spec/support/shared_examples/graphql/label_fields.rb
@@ -18,7 +18,7 @@ RSpec.shared_examples 'a GraphQL type with labels' do
subject { described_class.fields['labels'] }
it { is_expected.to have_graphql_type(Types::LabelType.connection_type) }
- it { is_expected.to have_graphql_arguments(:search_term) }
+ it { is_expected.to have_graphql_arguments(labels_resolver_arguments) }
end
end