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-12-16 18:10:28 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-12-16 18:10:28 +0300
commit6aaec2fc6c3e3f96f443b96fd53ae9ed5e7979af (patch)
tree17336eb6c5d10d904310218c72b3b0bf9b78a180 /spec
parenta32fd79d1e34ca4da1d5390c0aaf91d660e03fc8 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/finders/packages/build_infos_finder_spec.rb64
-rw-r--r--spec/frontend/crm/mock_data.js25
-rw-r--r--spec/frontend/crm/new_organization_form_spec.js109
-rw-r--r--spec/frontend/crm/organizations_root_spec.js104
-rw-r--r--spec/graphql/resolvers/package_pipelines_resolver_spec.rb84
-rw-r--r--spec/graphql/types/packages/package_details_type_spec.rb9
-rw-r--r--spec/models/packages/build_info_spec.rb42
-rw-r--r--spec/requests/api/ci/runners_spec.rb1
-rw-r--r--spec/requests/api/graphql/packages/package_spec.rb64
-rw-r--r--spec/requests/groups/crm/organizations_controller_spec.rb91
10 files changed, 571 insertions, 22 deletions
diff --git a/spec/finders/packages/build_infos_finder_spec.rb b/spec/finders/packages/build_infos_finder_spec.rb
new file mode 100644
index 00000000000..23425de4316
--- /dev/null
+++ b/spec/finders/packages/build_infos_finder_spec.rb
@@ -0,0 +1,64 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe ::Packages::BuildInfosFinder do
+ using RSpec::Parameterized::TableSyntax
+
+ let_it_be(:package) { create(:package) }
+ let_it_be(:build_infos) { create_list(:package_build_info, 5, :with_pipeline, package: package) }
+ let_it_be(:build_info_with_empty_pipeline) { create(:package_build_info, package: package) }
+
+ let(:finder) { described_class.new(package, params) }
+ let(:params) do
+ {
+ first: first,
+ last: last,
+ after: after,
+ before: before,
+ max_page_size: max_page_size,
+ support_next_page: support_next_page
+ }
+ end
+
+ describe '#execute' do
+ subject { finder.execute }
+
+ where(:first, :last, :after_index, :before_index, :max_page_size, :support_next_page, :expected_build_infos_indexes) do
+ # F L AI BI MPS SNP
+ nil | nil | nil | nil | nil | false | [4, 3, 2, 1, 0]
+ nil | nil | nil | nil | 10 | false | [4, 3, 2, 1, 0]
+ nil | nil | nil | nil | 2 | false | [4, 3]
+ 2 | nil | nil | nil | nil | false | [4, 3]
+ 2 | nil | nil | nil | nil | true | [4, 3, 2]
+ 2 | nil | 3 | nil | nil | false | [2, 1]
+ 2 | nil | 3 | nil | nil | true | [2, 1, 0]
+ 3 | nil | 4 | nil | 2 | false | [3, 2]
+ 3 | nil | 4 | nil | 2 | true | [3, 2, 1]
+ nil | 2 | nil | nil | nil | false | [0, 1]
+ nil | 2 | nil | nil | nil | true | [0, 1, 2]
+ nil | 2 | nil | 1 | nil | false | [2, 3]
+ nil | 2 | nil | 1 | nil | true | [2, 3, 4]
+ nil | 3 | nil | 0 | 2 | false | [1, 2]
+ nil | 3 | nil | 0 | 2 | true | [1, 2, 3]
+ end
+
+ with_them do
+ let(:expected_build_infos) do
+ expected_build_infos_indexes.map do |idx|
+ build_infos[idx]
+ end
+ end
+
+ let(:after) do
+ build_infos[after_index].pipeline_id if after_index
+ end
+
+ let(:before) do
+ build_infos[before_index].pipeline_id if before_index
+ end
+
+ it { is_expected.to eq(expected_build_infos) }
+ end
+ end
+end
diff --git a/spec/frontend/crm/mock_data.js b/spec/frontend/crm/mock_data.js
index 3abbc488081..f7af2ccdb72 100644
--- a/spec/frontend/crm/mock_data.js
+++ b/spec/frontend/crm/mock_data.js
@@ -134,3 +134,28 @@ export const updateContactMutationErrorResponse = {
},
},
};
+
+export const createOrganizationMutationResponse = {
+ data: {
+ customerRelationsOrganizationCreate: {
+ __typeName: 'CustomerRelationsOrganizationCreatePayload',
+ organization: {
+ __typename: 'CustomerRelationsOrganization',
+ id: 'gid://gitlab/CustomerRelations::Organization/2',
+ name: 'A',
+ defaultRate: null,
+ description: null,
+ },
+ errors: [],
+ },
+ },
+};
+
+export const createOrganizationMutationErrorResponse = {
+ data: {
+ customerRelationsOrganizationCreate: {
+ organization: null,
+ errors: ['Name cannot be blank.'],
+ },
+ },
+};
diff --git a/spec/frontend/crm/new_organization_form_spec.js b/spec/frontend/crm/new_organization_form_spec.js
new file mode 100644
index 00000000000..976b626f35f
--- /dev/null
+++ b/spec/frontend/crm/new_organization_form_spec.js
@@ -0,0 +1,109 @@
+import { GlAlert } from '@gitlab/ui';
+import Vue from 'vue';
+import VueApollo from 'vue-apollo';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import waitForPromises from 'helpers/wait_for_promises';
+import NewOrganizationForm from '~/crm/components/new_organization_form.vue';
+import createOrganizationMutation from '~/crm/components/queries/create_organization.mutation.graphql';
+import getGroupOrganizationsQuery from '~/crm/components/queries/get_group_organizations.query.graphql';
+import {
+ createOrganizationMutationErrorResponse,
+ createOrganizationMutationResponse,
+ getGroupOrganizationsQueryResponse,
+} from './mock_data';
+
+describe('Customer relations organizations root app', () => {
+ Vue.use(VueApollo);
+ let wrapper;
+ let fakeApollo;
+ let queryHandler;
+
+ const findCreateNewOrganizationButton = () =>
+ wrapper.findByTestId('create-new-organization-button');
+ const findCancelButton = () => wrapper.findByTestId('cancel-button');
+ const findForm = () => wrapper.find('form');
+ const findError = () => wrapper.findComponent(GlAlert);
+
+ const mountComponent = () => {
+ fakeApollo = createMockApollo([[createOrganizationMutation, queryHandler]]);
+ fakeApollo.clients.defaultClient.cache.writeQuery({
+ query: getGroupOrganizationsQuery,
+ variables: { groupFullPath: 'flightjs' },
+ data: getGroupOrganizationsQueryResponse.data,
+ });
+ wrapper = shallowMountExtended(NewOrganizationForm, {
+ provide: { groupId: 26, groupFullPath: 'flightjs' },
+ apolloProvider: fakeApollo,
+ propsData: { drawerOpen: true },
+ });
+ };
+
+ beforeEach(() => {
+ queryHandler = jest.fn().mockResolvedValue(createOrganizationMutationResponse);
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ fakeApollo = null;
+ });
+
+ describe('Create new organization button', () => {
+ it('should be disabled by default', () => {
+ mountComponent();
+
+ expect(findCreateNewOrganizationButton().attributes('disabled')).toBeTruthy();
+ });
+
+ it('should not be disabled when first, last and email have values', async () => {
+ mountComponent();
+
+ wrapper.find('#organization-name').vm.$emit('input', 'A');
+ await waitForPromises();
+
+ expect(findCreateNewOrganizationButton().attributes('disabled')).toBeFalsy();
+ });
+ });
+
+ it("should emit 'close' when cancel button is clicked", () => {
+ mountComponent();
+
+ findCancelButton().vm.$emit('click');
+
+ expect(wrapper.emitted().close).toBeTruthy();
+ });
+
+ describe('when query is successful', () => {
+ it("should emit 'close'", async () => {
+ mountComponent();
+
+ findForm().trigger('submit');
+ await waitForPromises();
+
+ expect(wrapper.emitted().close).toBeTruthy();
+ });
+ });
+
+ describe('when query fails', () => {
+ it('should show error on reject', async () => {
+ queryHandler = jest.fn().mockRejectedValue('ERROR');
+ mountComponent();
+
+ findForm().trigger('submit');
+ await waitForPromises();
+
+ expect(findError().exists()).toBe(true);
+ });
+
+ it('should show error on error response', async () => {
+ queryHandler = jest.fn().mockResolvedValue(createOrganizationMutationErrorResponse);
+ mountComponent();
+
+ findForm().trigger('submit');
+ await waitForPromises();
+
+ expect(findError().exists()).toBe(true);
+ expect(findError().text()).toBe('Name cannot be blank.');
+ });
+ });
+});
diff --git a/spec/frontend/crm/organizations_root_spec.js b/spec/frontend/crm/organizations_root_spec.js
index cfaeb8ae7e3..aef417964f4 100644
--- a/spec/frontend/crm/organizations_root_spec.js
+++ b/spec/frontend/crm/organizations_root_spec.js
@@ -1,38 +1,59 @@
import { GlAlert, GlLoadingIcon } from '@gitlab/ui';
import Vue from 'vue';
import VueApollo from 'vue-apollo';
+import VueRouter from 'vue-router';
import { mountExtended, shallowMountExtended } from 'helpers/vue_test_utils_helper';
import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import OrganizationsRoot from '~/crm/components/organizations_root.vue';
+import NewOrganizationForm from '~/crm/components/new_organization_form.vue';
+import { NEW_ROUTE_NAME } from '~/crm/constants';
+import routes from '~/crm/routes';
import getGroupOrganizationsQuery from '~/crm/components/queries/get_group_organizations.query.graphql';
import { getGroupOrganizationsQueryResponse } from './mock_data';
describe('Customer relations organizations root app', () => {
Vue.use(VueApollo);
+ Vue.use(VueRouter);
let wrapper;
let fakeApollo;
+ let router;
const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
const findRowByName = (rowName) => wrapper.findAllByRole('row', { name: rowName });
const findIssuesLinks = () => wrapper.findAllByTestId('issues-link');
+ const findNewOrganizationButton = () => wrapper.findByTestId('new-organization-button');
+ const findNewOrganizationForm = () => wrapper.findComponent(NewOrganizationForm);
const findError = () => wrapper.findComponent(GlAlert);
const successQueryHandler = jest.fn().mockResolvedValue(getGroupOrganizationsQueryResponse);
+ const basePath = '/groups/flightjs/-/crm/organizations';
+
const mountComponent = ({
queryHandler = successQueryHandler,
mountFunction = shallowMountExtended,
+ canAdminCrmOrganization = true,
} = {}) => {
fakeApollo = createMockApollo([[getGroupOrganizationsQuery, queryHandler]]);
wrapper = mountFunction(OrganizationsRoot, {
- provide: { groupFullPath: 'flightjs', groupIssuesPath: '/issues' },
+ router,
+ provide: { canAdminCrmOrganization, groupFullPath: 'flightjs', groupIssuesPath: '/issues' },
apolloProvider: fakeApollo,
});
};
+ beforeEach(() => {
+ router = new VueRouter({
+ base: basePath,
+ mode: 'history',
+ routes,
+ });
+ });
+
afterEach(() => {
wrapper.destroy();
fakeApollo = null;
+ router = null;
});
it('should render loading spinner', () => {
@@ -41,6 +62,56 @@ describe('Customer relations organizations root app', () => {
expect(findLoadingIcon().exists()).toBe(true);
});
+ describe('new organization button', () => {
+ it('should exist when user has permission', () => {
+ mountComponent();
+
+ expect(findNewOrganizationButton().exists()).toBe(true);
+ });
+
+ it('should not exist when user has no permission', () => {
+ mountComponent({ canAdminCrmOrganization: false });
+
+ expect(findNewOrganizationButton().exists()).toBe(false);
+ });
+ });
+
+ describe('new organization form', () => {
+ it('should not exist by default', async () => {
+ mountComponent();
+ await waitForPromises();
+
+ expect(findNewOrganizationForm().exists()).toBe(false);
+ });
+
+ it('should exist when user clicks new contact button', async () => {
+ mountComponent();
+
+ findNewOrganizationButton().vm.$emit('click');
+ await waitForPromises();
+
+ expect(findNewOrganizationForm().exists()).toBe(true);
+ });
+
+ it('should exist when user navigates directly to /new', async () => {
+ router.replace({ name: NEW_ROUTE_NAME });
+ mountComponent();
+ await waitForPromises();
+
+ expect(findNewOrganizationForm().exists()).toBe(true);
+ });
+
+ it('should not exist when form emits close', async () => {
+ router.replace({ name: NEW_ROUTE_NAME });
+ mountComponent();
+
+ findNewOrganizationForm().vm.$emit('close');
+ await waitForPromises();
+
+ expect(findNewOrganizationForm().exists()).toBe(false);
+ });
+ });
+
it('should render error message on reject', async () => {
mountComponent({ queryHandler: jest.fn().mockRejectedValue('ERROR') });
await waitForPromises();
@@ -48,18 +119,27 @@ describe('Customer relations organizations root app', () => {
expect(findError().exists()).toBe(true);
});
- it('renders correct results', async () => {
- mountComponent({ mountFunction: mountExtended });
- await waitForPromises();
+ describe('on successful load', () => {
+ it('should not render error', async () => {
+ mountComponent();
+ await waitForPromises();
- expect(findRowByName(/Test Inc/i)).toHaveLength(1);
- expect(findRowByName(/VIP/i)).toHaveLength(1);
- expect(findRowByName(/120/i)).toHaveLength(1);
+ expect(findError().exists()).toBe(false);
+ });
- const issueLink = findIssuesLinks().at(0);
- expect(issueLink.exists()).toBe(true);
- expect(issueLink.attributes('href')).toBe(
- '/issues?scope=all&state=opened&crm_organization_id=2',
- );
+ it('renders correct results', async () => {
+ mountComponent({ mountFunction: mountExtended });
+ await waitForPromises();
+
+ expect(findRowByName(/Test Inc/i)).toHaveLength(1);
+ expect(findRowByName(/VIP/i)).toHaveLength(1);
+ expect(findRowByName(/120/i)).toHaveLength(1);
+
+ const issueLink = findIssuesLinks().at(0);
+ expect(issueLink.exists()).toBe(true);
+ expect(issueLink.attributes('href')).toBe(
+ '/issues?scope=all&state=opened&crm_organization_id=2',
+ );
+ });
});
});
diff --git a/spec/graphql/resolvers/package_pipelines_resolver_spec.rb b/spec/graphql/resolvers/package_pipelines_resolver_spec.rb
new file mode 100644
index 00000000000..d48d4d8ae01
--- /dev/null
+++ b/spec/graphql/resolvers/package_pipelines_resolver_spec.rb
@@ -0,0 +1,84 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Resolvers::PackagePipelinesResolver do
+ include GraphqlHelpers
+
+ let_it_be_with_reload(:package) { create(:package) }
+ let_it_be(:pipelines) { create_list(:ci_pipeline, 3, project: package.project) }
+
+ let(:user) { package.project.owner }
+ let(:args) { {} }
+
+ describe '#resolve' do
+ subject { resolve(described_class, obj: package, args: args, ctx: { current_user: user }) }
+
+ before do
+ package.pipelines = pipelines
+ package.save!
+ end
+
+ it { is_expected.to contain_exactly(*pipelines) }
+
+ context 'with invalid after' do
+ let(:args) { { first: 1, after: 'not_json_string' } }
+
+ it 'raises argument error' do
+ expect { subject }.to raise_error(Gitlab::Graphql::Errors::ArgumentError)
+ end
+ end
+
+ context 'with invalid after key' do
+ let(:args) { { first: 1, after: encode_cursor(foo: 3) } }
+
+ it 'raises argument error' do
+ expect { subject }.to raise_error(Gitlab::Graphql::Errors::ArgumentError)
+ end
+ end
+
+ context 'with invalid before' do
+ let(:args) { { last: 1, before: 'not_json_string' } }
+
+ it 'raises argument error' do
+ expect { subject }.to raise_error(Gitlab::Graphql::Errors::ArgumentError)
+ end
+ end
+
+ context 'with invalid before key' do
+ let(:args) { { last: 1, before: encode_cursor(foo: 3) } }
+
+ it 'raises argument error' do
+ expect { subject }.to raise_error(Gitlab::Graphql::Errors::ArgumentError)
+ end
+ end
+
+ context 'field options' do
+ let(:field) do
+ field_options = described_class.field_options.merge(
+ owner: resolver_parent,
+ name: 'dummy_field'
+ )
+ ::Types::BaseField.new(**field_options)
+ end
+
+ it 'sets them properly' do
+ expect(field).not_to be_connection
+ expect(field.extras).to match_array([:lookahead])
+ end
+ end
+
+ context 'with unauthorized user' do
+ let_it_be(:user) { create(:user) }
+
+ it { is_expected.to be_nil }
+ end
+
+ def encode_cursor(json)
+ GitlabSchema.cursor_encoder.encode(
+ Gitlab::Json.dump(json),
+ nonce: true
+ )
+ end
+ end
+end
diff --git a/spec/graphql/types/packages/package_details_type_spec.rb b/spec/graphql/types/packages/package_details_type_spec.rb
index 7e1103d8aa0..f0b684d6b07 100644
--- a/spec/graphql/types/packages/package_details_type_spec.rb
+++ b/spec/graphql/types/packages/package_details_type_spec.rb
@@ -10,4 +10,13 @@ RSpec.describe GitlabSchema.types['PackageDetailsType'] do
expect(described_class).to include_graphql_fields(*expected_fields)
end
+
+ it 'overrides the pipelines field' do
+ field = described_class.fields['pipelines']
+
+ expect(field).to have_graphql_type(Types::Ci::PipelineType.connection_type)
+ expect(field).to have_graphql_extension(Gitlab::Graphql::Extensions::ExternallyPaginatedArrayExtension)
+ expect(field).to have_graphql_resolver(Resolvers::PackagePipelinesResolver)
+ expect(field).not_to be_connection
+ end
end
diff --git a/spec/models/packages/build_info_spec.rb b/spec/models/packages/build_info_spec.rb
index a4369c56fe2..db8ac605d72 100644
--- a/spec/models/packages/build_info_spec.rb
+++ b/spec/models/packages/build_info_spec.rb
@@ -6,4 +6,46 @@ RSpec.describe Packages::BuildInfo, type: :model do
it { is_expected.to belong_to(:package) }
it { is_expected.to belong_to(:pipeline) }
end
+
+ context 'with some build infos' do
+ let_it_be(:package) { create(:package) }
+ let_it_be(:build_infos) { create_list(:package_build_info, 3, :with_pipeline, package: package) }
+ let_it_be(:build_info_with_no_pipeline) { create(:package_build_info) }
+
+ describe '.pluck_pipeline_ids' do
+ subject { package.build_infos.pluck_pipeline_ids.sort }
+
+ it { is_expected.to eq(build_infos.map(&:pipeline_id).sort) }
+ end
+
+ describe '.without_empty_pipelines' do
+ subject { package.build_infos.without_empty_pipelines }
+
+ it { is_expected.to contain_exactly(*build_infos) }
+ end
+
+ describe '.order_by_pipeline_id asc' do
+ subject { package.build_infos.order_by_pipeline_id(:asc) }
+
+ it { is_expected.to eq(build_infos) }
+ end
+
+ describe '.order_by_pipeline_id desc' do
+ subject { package.build_infos.order_by_pipeline_id(:desc) }
+
+ it { is_expected.to eq(build_infos.reverse) }
+ end
+
+ describe '.with_pipeline_id_less_than' do
+ subject { package.build_infos.with_pipeline_id_less_than(build_infos[1].pipeline_id) }
+
+ it { is_expected.to contain_exactly(build_infos[0]) }
+ end
+
+ describe '.with_pipeline_id_greater_than' do
+ subject { package.build_infos.with_pipeline_id_greater_than(build_infos[1].pipeline_id) }
+
+ it { is_expected.to contain_exactly(build_infos[2]) }
+ end
+ end
end
diff --git a/spec/requests/api/ci/runners_spec.rb b/spec/requests/api/ci/runners_spec.rb
index 20acdd892e0..6ca380a3cb9 100644
--- a/spec/requests/api/ci/runners_spec.rb
+++ b/spec/requests/api/ci/runners_spec.rb
@@ -254,6 +254,7 @@ RSpec.describe API::Ci::Runners do
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['description']).to eq(shared_runner.description)
expect(json_response['maximum_timeout']).to be_nil
+ expect(json_response['status']).to eq("not_connected")
end
end
diff --git a/spec/requests/api/graphql/packages/package_spec.rb b/spec/requests/api/graphql/packages/package_spec.rb
index c75b86f85bd..a9019a7611a 100644
--- a/spec/requests/api/graphql/packages/package_spec.rb
+++ b/spec/requests/api/graphql/packages/package_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
RSpec.describe 'package details' do
include GraphqlHelpers
- let_it_be(:project) { create(:project) }
+ let_it_be_with_reload(:project) { create(:project) }
let_it_be(:composer_package) { create(:composer_package, project: project) }
let_it_be(:composer_json) { { name: 'name', type: 'type', license: 'license', version: 1 } }
let_it_be(:composer_metadatum) do
@@ -97,8 +97,23 @@ RSpec.describe 'package details' do
end
end
- context 'when loading pipelines ordered by ID DESC' do
+ context 'with unauthorized user' do
+ let_it_be(:user) { create(:user) }
+
+ before do
+ project.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
+ end
+
+ it 'returns no packages' do
+ subject
+
+ expect(graphql_data_at(:package)).to be_nil
+ end
+ end
+
+ context 'pipelines field', :aggregate_failures do
let(:pipelines) { create_list(:ci_pipeline, 6, project: project) }
+ let(:pipeline_gids) { pipelines.sort_by(&:id).map(&:to_gid).map(&:to_s).reverse }
before do
composer_package.pipelines = pipelines
@@ -111,6 +126,7 @@ RSpec.describe 'package details' do
id
}
pageInfo {
+ startCursor
endCursor
}
QUERY
@@ -119,20 +135,48 @@ RSpec.describe 'package details' do
post_graphql(query, current_user: user)
end
- it 'loads the second page correctly' do
- run_query({ first: 2 })
+ it 'loads the second page with pagination first correctly' do
+ run_query(first: 2)
+ pipeline_ids = graphql_data.dig('package', 'pipelines', 'nodes').pluck('id')
+
+ expect(pipeline_ids).to eq(pipeline_gids[0..1])
+
cursor = graphql_data.dig('package', 'pipelines', 'pageInfo', 'endCursor')
- run_query({ first: 2, after: cursor })
+ run_query(first: 2, after: cursor)
- expected_pipeline_ids = pipelines
- .sort_by(&:id)
- .reverse[2..3] # second page
- .map { |pipeline| pipeline.to_gid.to_s }
+ pipeline_ids = graphql_data.dig('package', 'pipelines', 'nodes').pluck('id')
+ expect(pipeline_ids).to eq(pipeline_gids[2..3])
+ end
+
+ it 'loads the second page with pagination last correctly' do
+ run_query(last: 2)
pipeline_ids = graphql_data.dig('package', 'pipelines', 'nodes').pluck('id')
- expect(pipeline_ids).to eq(expected_pipeline_ids)
+ expect(pipeline_ids).to eq(pipeline_gids[4..5])
+
+ cursor = graphql_data.dig('package', 'pipelines', 'pageInfo', 'startCursor')
+
+ run_query(last: 2, before: cursor)
+
+ pipeline_ids = graphql_data.dig('package', 'pipelines', 'nodes').pluck('id')
+
+ expect(pipeline_ids).to eq(pipeline_gids[2..3])
+ end
+
+ context 'with unauthorized user' do
+ let_it_be(:user) { create(:user) }
+
+ before do
+ project.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
+ end
+
+ it 'returns no packages' do
+ run_query(first: 2)
+
+ expect(graphql_data_at(:package)).to be_nil
+ end
end
end
end
diff --git a/spec/requests/groups/crm/organizations_controller_spec.rb b/spec/requests/groups/crm/organizations_controller_spec.rb
new file mode 100644
index 00000000000..7595950350d
--- /dev/null
+++ b/spec/requests/groups/crm/organizations_controller_spec.rb
@@ -0,0 +1,91 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Groups::Crm::OrganizationsController do
+ let_it_be(:user) { create(:user) }
+
+ shared_examples 'response with 404 status' do
+ it 'returns 404' do
+ subject
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+
+ shared_examples 'ok response with index template' do
+ it 'renders the index template' do
+ subject
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to render_template(:index)
+ end
+ end
+
+ shared_examples 'ok response with index template if authorized' do
+ context 'private group' do
+ let(:group) { create(:group, :private) }
+
+ context 'with authorized user' do
+ before do
+ group.add_reporter(user)
+ sign_in(user)
+ end
+
+ context 'when feature flag is enabled' do
+ it_behaves_like 'ok response with index template'
+ end
+
+ context 'when feature flag is not enabled' do
+ before do
+ stub_feature_flags(customer_relations: false)
+ end
+
+ it_behaves_like 'response with 404 status'
+ end
+ end
+
+ context 'with unauthorized user' do
+ before do
+ sign_in(user)
+ end
+
+ it_behaves_like 'response with 404 status'
+ end
+
+ context 'with anonymous user' do
+ it 'blah' do
+ subject
+
+ expect(response).to have_gitlab_http_status(:found)
+ expect(response).to redirect_to(new_user_session_path)
+ end
+ end
+ end
+
+ context 'public group' do
+ let(:group) { create(:group, :public) }
+
+ context 'with anonymous user' do
+ it_behaves_like 'ok response with index template'
+ end
+ end
+ end
+
+ describe 'GET #index' do
+ subject do
+ get group_crm_organizations_path(group)
+ response
+ end
+
+ it_behaves_like 'ok response with index template if authorized'
+ end
+
+ describe 'GET #new' do
+ subject do
+ get new_group_crm_organization_path(group)
+ end
+
+ it_behaves_like 'ok response with index template if authorized'
+ end
+end