diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-11-05 00:09:16 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-11-05 00:09:16 +0000 |
commit | 4fd77e112fac07c5b904668b7d5f500589f8d2d5 (patch) | |
tree | a7f82bdc0eaba09f1aaafc484509bfe4b664c954 /spec | |
parent | 8a55c3263f1f37fdc9ee772bc0d38133dfe94495 (diff) | |
download | gitlab-ce-4fd77e112fac07c5b904668b7d5f500589f8d2d5.tar.gz |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r-- | spec/frontend/vue_shared/components/filtered_search_bar/tokens/milestone_token_spec.js | 4 | ||||
-rw-r--r-- | spec/graphql/types/group_invitation_type_spec.rb | 19 | ||||
-rw-r--r-- | spec/graphql/types/invitation_interface_spec.rb | 43 | ||||
-rw-r--r-- | spec/graphql/types/project_invitation_type_spec.rb | 19 | ||||
-rw-r--r-- | spec/lib/api/validations/validators/email_or_email_list_spec.rb | 28 | ||||
-rw-r--r-- | spec/lib/gitlab/path_regex_spec.rb | 4 | ||||
-rw-r--r-- | spec/lib/gitlab/search/sort_options_spec.rb | 34 | ||||
-rw-r--r-- | spec/lib/gitlab/usage_data_spec.rb | 4 | ||||
-rw-r--r-- | spec/migrations/rename_sitemap_namespace_spec.rb | 30 | ||||
-rw-r--r-- | spec/models/concerns/from_union_spec.rb | 10 | ||||
-rw-r--r-- | spec/requests/api/invitations_spec.rb | 207 | ||||
-rw-r--r-- | spec/requests/api/search_spec.rb | 54 | ||||
-rw-r--r-- | spec/services/members/invite_service_spec.rb | 66 |
13 files changed, 510 insertions, 12 deletions
diff --git a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/milestone_token_spec.js b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/milestone_token_spec.js index 0ec814e3f15..5d91eafbabf 100644 --- a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/milestone_token_spec.js +++ b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/milestone_token_spec.js @@ -120,7 +120,9 @@ describe('MilestoneToken', () => { wrapper.vm.fetchMilestoneBySearchTerm('foo'); return waitForPromises().then(() => { - expect(createFlash).toHaveBeenCalledWith('There was a problem fetching milestones.'); + expect(createFlash).toHaveBeenCalledWith({ + message: 'There was a problem fetching milestones.', + }); }); }); diff --git a/spec/graphql/types/group_invitation_type_spec.rb b/spec/graphql/types/group_invitation_type_spec.rb new file mode 100644 index 00000000000..dab2d43fc90 --- /dev/null +++ b/spec/graphql/types/group_invitation_type_spec.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Types::GroupInvitationType do + specify { expect(described_class).to expose_permissions_using(Types::PermissionTypes::Group) } + + specify { expect(described_class.graphql_name).to eq('GroupInvitation') } + + specify { expect(described_class).to require_graphql_authorizations(:read_group) } + + it 'has the expected fields' do + expected_fields = %w[ + email access_level created_by created_at updated_at expires_at group + ] + + expect(described_class).to include_graphql_fields(*expected_fields) + end +end diff --git a/spec/graphql/types/invitation_interface_spec.rb b/spec/graphql/types/invitation_interface_spec.rb new file mode 100644 index 00000000000..8f345c58ca3 --- /dev/null +++ b/spec/graphql/types/invitation_interface_spec.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Types::InvitationInterface do + it 'exposes the expected fields' do + expected_fields = %i[ + email + access_level + created_by + created_at + updated_at + expires_at + user + ] + + expect(described_class).to have_graphql_fields(*expected_fields) + end + + describe '.resolve_type' do + subject { described_class.resolve_type(object, {}) } + + context 'for project member' do + let(:object) { build(:project_member) } + + it { is_expected.to be Types::ProjectInvitationType } + end + + context 'for group member' do + let(:object) { build(:group_member) } + + it { is_expected.to be Types::GroupInvitationType } + end + + context 'for an unknown type' do + let(:object) { build(:user) } + + it 'raises an error' do + expect { subject }.to raise_error(Gitlab::Graphql::Errors::BaseError) + end + end + end +end diff --git a/spec/graphql/types/project_invitation_type_spec.rb b/spec/graphql/types/project_invitation_type_spec.rb new file mode 100644 index 00000000000..148a763a5fa --- /dev/null +++ b/spec/graphql/types/project_invitation_type_spec.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Types::ProjectInvitationType do + specify { expect(described_class).to expose_permissions_using(Types::PermissionTypes::Project) } + + specify { expect(described_class.graphql_name).to eq('ProjectInvitation') } + + specify { expect(described_class).to require_graphql_authorizations(:read_project) } + + it 'has the expected fields' do + expected_fields = %w[ + access_level created_by created_at updated_at expires_at project user + ] + + expect(described_class).to include_graphql_fields(*expected_fields) + end +end diff --git a/spec/lib/api/validations/validators/email_or_email_list_spec.rb b/spec/lib/api/validations/validators/email_or_email_list_spec.rb new file mode 100644 index 00000000000..ac3111c2319 --- /dev/null +++ b/spec/lib/api/validations/validators/email_or_email_list_spec.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe API::Validations::Validators::EmailOrEmailList do + include ApiValidatorsHelpers + + subject do + described_class.new(['email'], {}, false, scope.new) + end + + context 'with valid email addresses' do + it 'does not raise a validation error' do + expect_no_validation_error('test' => 'test@example.org') + expect_no_validation_error('test' => 'test1@example.com,test2@example.org') + expect_no_validation_error('test' => 'test1@example.com,test2@example.org,test3@example.co.uk') + end + end + + context 'including any invalid email address' do + it 'raises a validation error' do + expect_validation_error('test' => 'not') + expect_validation_error('test' => '@example.com') + expect_validation_error('test' => 'test1@example.com,asdf') + expect_validation_error('test' => 'asdf,testa1@example.com,asdf') + end + end +end diff --git a/spec/lib/gitlab/path_regex_spec.rb b/spec/lib/gitlab/path_regex_spec.rb index a3977528537..9427fdfc0fe 100644 --- a/spec/lib/gitlab/path_regex_spec.rb +++ b/spec/lib/gitlab/path_regex_spec.rb @@ -107,7 +107,7 @@ RSpec.describe Gitlab::PathRegex do end let(:sitemap_words) do - %w(sitemap.xml sitemap.xml.gz) + %w(sitemap sitemap.xml sitemap.xml.gz) end let(:ee_top_level_words) do @@ -177,7 +177,7 @@ RSpec.describe Gitlab::PathRegex do # We ban new items in this list, see https://gitlab.com/gitlab-org/gitlab/-/issues/215362 it 'does not allow expansion' do - expect(described_class::TOP_LEVEL_ROUTES.size).to eq(43) + expect(described_class::TOP_LEVEL_ROUTES.size).to eq(44) end end diff --git a/spec/lib/gitlab/search/sort_options_spec.rb b/spec/lib/gitlab/search/sort_options_spec.rb new file mode 100644 index 00000000000..2044fdfc894 --- /dev/null +++ b/spec/lib/gitlab/search/sort_options_spec.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +require 'fast_spec_helper' +require 'gitlab/search/sort_options' + +RSpec.describe ::Gitlab::Search::SortOptions do + describe '.sort_and_direction' do + context 'using order_by and sort' do + it 'returns matched options' do + expect(described_class.sort_and_direction('created_at', 'asc')).to eq(:created_at_asc) + expect(described_class.sort_and_direction('created_at', 'desc')).to eq(:created_at_desc) + end + end + + context 'using just sort' do + it 'returns matched options' do + expect(described_class.sort_and_direction(nil, 'created_asc')).to eq(:created_at_asc) + expect(described_class.sort_and_direction(nil, 'created_desc')).to eq(:created_at_desc) + end + end + + context 'when unknown option' do + it 'returns unknown' do + expect(described_class.sort_and_direction(nil, 'foo_asc')).to eq(:unknown) + expect(described_class.sort_and_direction(nil, 'bar_desc')).to eq(:unknown) + expect(described_class.sort_and_direction(nil, 'created_bar')).to eq(:unknown) + + expect(described_class.sort_and_direction('created_at', 'foo')).to eq(:unknown) + expect(described_class.sort_and_direction('foo', 'desc')).to eq(:unknown) + expect(described_class.sort_and_direction('created_at', nil)).to eq(:unknown) + end + end + end +end diff --git a/spec/lib/gitlab/usage_data_spec.rb b/spec/lib/gitlab/usage_data_spec.rb index d3a3f24732c..2e2c1806e51 100644 --- a/spec/lib/gitlab/usage_data_spec.rb +++ b/spec/lib/gitlab/usage_data_spec.rb @@ -308,6 +308,7 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do projects_with_tracing_enabled: 2, projects_with_error_tracking_enabled: 2 ) + expect(described_class.usage_activity_by_stage_monitor(described_class.last_28_days_time_period)).to include( clusters: 1, clusters_applications_prometheus: 1, @@ -470,6 +471,7 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do expect(count_data[:projects_with_prometheus_alerts]).to eq(2) expect(count_data[:projects_with_terraform_reports]).to eq(2) expect(count_data[:projects_with_terraform_states]).to eq(2) + expect(count_data[:projects_with_alerts_created]).to eq(1) expect(count_data[:protected_branches]).to eq(2) expect(count_data[:protected_branches_except_default]).to eq(1) expect(count_data[:terraform_reports]).to eq(6) @@ -611,6 +613,7 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do create(:deployment, :success, deployment_options) create(:project_snippet, project: project, created_at: n.days.ago) create(:personal_snippet, created_at: n.days.ago) + create(:alert_management_alert, project: project, created_at: n.days.ago) end stub_application_setting(self_monitoring_project: project) @@ -631,6 +634,7 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do expect(counts_monthly[:snippets]).to eq(2) expect(counts_monthly[:personal_snippets]).to eq(1) expect(counts_monthly[:project_snippets]).to eq(1) + expect(counts_monthly[:projects_with_alerts_created]).to eq(1) expect(counts_monthly[:packages]).to eq(1) expect(counts_monthly[:promoted_issues]).to eq(1) end diff --git a/spec/migrations/rename_sitemap_namespace_spec.rb b/spec/migrations/rename_sitemap_namespace_spec.rb new file mode 100644 index 00000000000..83f0721c600 --- /dev/null +++ b/spec/migrations/rename_sitemap_namespace_spec.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +require 'spec_helper' +require Rails.root.join('db', 'post_migrate', '20201102112206_rename_sitemap_namespace.rb') + +RSpec.describe RenameSitemapNamespace do + let(:namespaces) { table(:namespaces) } + let(:routes) { table(:routes) } + let(:sitemap_path) { 'sitemap' } + + it 'correctly run #up and #down' do + create_namespace(sitemap_path) + + reversible_migration do |migration| + migration.before -> { + expect(namespaces.pluck(:path)).to contain_exactly(sitemap_path) + } + + migration.after -> { + expect(namespaces.pluck(:path)).to contain_exactly(sitemap_path + '0') + } + end + end + + def create_namespace(path) + namespaces.create!(name: path, path: path).tap do |namespace| + routes.create!(path: namespace.path, name: namespace.name, source_id: namespace.id, source_type: 'Namespace') + end + end +end diff --git a/spec/models/concerns/from_union_spec.rb b/spec/models/concerns/from_union_spec.rb index bd2893090a8..4f4d948fe48 100644 --- a/spec/models/concerns/from_union_spec.rb +++ b/spec/models/concerns/from_union_spec.rb @@ -3,13 +3,5 @@ require 'spec_helper' RSpec.describe FromUnion do - [true, false].each do |sql_set_operator| - context "when sql-set-operators feature flag is #{sql_set_operator}" do - before do - stub_feature_flags(sql_set_operators: sql_set_operator) - end - - it_behaves_like 'from set operator', Gitlab::SQL::Union - end - end + it_behaves_like 'from set operator', Gitlab::SQL::Union end diff --git a/spec/requests/api/invitations_spec.rb b/spec/requests/api/invitations_spec.rb new file mode 100644 index 00000000000..9befd4f533a --- /dev/null +++ b/spec/requests/api/invitations_spec.rb @@ -0,0 +1,207 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe API::Invitations do + let(:maintainer) { create(:user, username: 'maintainer_user') } + let(:developer) { create(:user) } + let(:access_requester) { create(:user) } + let(:stranger) { create(:user) } + let(:email) { 'email@example.org' } + + let(:project) do + create(:project, :public, creator_id: maintainer.id, namespace: maintainer.namespace) do |project| + project.add_developer(developer) + project.add_maintainer(maintainer) + project.request_access(access_requester) + end + end + + let!(:group) do + create(:group, :public) do |group| + group.add_developer(developer) + group.add_owner(maintainer) + group.request_access(access_requester) + end + end + + def invitations_url(source, user) + api("/#{source.model_name.plural}/#{source.id}/invitations", user) + end + + shared_examples 'POST /:source_type/:id/invitations' do |source_type| + context "with :source_type == #{source_type.pluralize}" do + it_behaves_like 'a 404 response when source is private' do + let(:route) do + post invitations_url(source, stranger), + params: { email: email, access_level: Member::MAINTAINER } + end + end + + context 'when authenticated as a non-member or member with insufficient rights' do + %i[access_requester stranger developer].each do |type| + context "as a #{type}" do + it 'returns 403' do + user = public_send(type) + + post invitations_url(source, user), params: { email: email, access_level: Member::MAINTAINER } + + expect(response).to have_gitlab_http_status(:forbidden) + end + end + end + end + + context 'when authenticated as a maintainer/owner' do + context 'and new member is already a requester' do + it 'does not transform the requester into a proper member' do + expect do + post api("/#{source_type.pluralize}/#{source.id}/invitations", maintainer), + params: { email: email, access_level: Member::MAINTAINER } + + expect(response).to have_gitlab_http_status(:created) + end.not_to change { source.members.count } + end + end + + it 'invites a new member' do + expect do + post api("/#{source_type.pluralize}/#{source.id}/invitations", maintainer), + params: { email: email, access_level: Member::DEVELOPER } + + expect(response).to have_gitlab_http_status(:created) + end.to change { source.requesters.count }.by(1) + end + + it 'invites a list of new email addresses' do + expect do + email_list = 'email1@example.com,email2@example.com' + + post api("/#{source_type.pluralize}/#{source.id}/invitations", maintainer), + params: { email: email_list, access_level: Member::DEVELOPER } + + expect(response).to have_gitlab_http_status(:created) + end.to change { source.requesters.count }.by(2) + end + end + + context 'access levels' do + it 'does not create the member if group level is higher' do + parent = create(:group) + + group.update!(parent: parent) + project.update!(group: group) + parent.add_developer(stranger) + + post api("/#{source_type.pluralize}/#{source.id}/invitations", maintainer), + params: { email: stranger.email, access_level: Member::REPORTER } + + expect(response).to have_gitlab_http_status(:created) + expect(json_response['message'][stranger.email]).to eq("Access level should be greater than or equal to Developer inherited membership from group #{parent.name}") + end + + it 'creates the member if group level is lower' do + parent = create(:group) + + group.update!(parent: parent) + project.update!(group: group) + parent.add_developer(stranger) + + post api("/#{source_type.pluralize}/#{source.id}/invitations", maintainer), + params: { email: stranger.email, access_level: Member::MAINTAINER } + + expect(response).to have_gitlab_http_status(:created) + end + end + + context 'access expiry date' do + subject do + post api("/#{source_type.pluralize}/#{source.id}/invitations", maintainer), + params: { email: email, access_level: Member::DEVELOPER, expires_at: expires_at } + end + + context 'when set to a date in the past' do + let(:expires_at) { 2.days.ago.to_date } + + it 'does not create a member' do + expect do + subject + end.not_to change { source.members.count } + + expect(response).to have_gitlab_http_status(:created) + expect(json_response['message'][email]).to eq('Expires at cannot be a date in the past') + end + end + + context 'when set to a date in the future' do + let(:expires_at) { 2.days.from_now.to_date } + + it 'invites a member' do + expect do + subject + end.to change { source.requesters.count }.by(1) + + expect(response).to have_gitlab_http_status(:created) + end + end + end + + it "returns a message if member already exists" do + post api("/#{source_type.pluralize}/#{source.id}/invitations", maintainer), + params: { email: maintainer.email, access_level: Member::MAINTAINER } + + expect(response).to have_gitlab_http_status(:created) + expect(json_response['message'][maintainer.email]).to eq("Already a member of #{source.name}") + end + + it 'returns 404 when the email is not valid' do + post api("/#{source_type.pluralize}/#{source.id}/invitations", maintainer), + params: { email: '', access_level: Member::MAINTAINER } + + expect(response).to have_gitlab_http_status(:created) + expect(json_response['message']).to eq('Email cannot be blank') + end + + it 'returns 404 when the email list is not a valid format' do + post api("/#{source_type.pluralize}/#{source.id}/invitations", maintainer), + params: { email: 'email1@example.com,not-an-email', access_level: Member::MAINTAINER } + + expect(response).to have_gitlab_http_status(:bad_request) + expect(json_response['error']).to eq('email contains an invalid email address') + end + + it 'returns 400 when email is not given' do + post api("/#{source_type.pluralize}/#{source.id}/invitations", maintainer), + params: { access_level: Member::MAINTAINER } + + expect(response).to have_gitlab_http_status(:bad_request) + end + + it 'returns 400 when access_level is not given' do + post api("/#{source_type.pluralize}/#{source.id}/invitations", maintainer), + params: { email: email } + + expect(response).to have_gitlab_http_status(:bad_request) + end + + it 'returns 400 when access_level is not valid' do + post invitations_url(source, maintainer), + params: { email: email, access_level: non_existing_record_access_level } + + expect(response).to have_gitlab_http_status(:bad_request) + end + end + end + + describe 'POST /projects/:id/invitations' do + it_behaves_like 'POST /:source_type/:id/invitations', 'project' do + let(:source) { project } + end + end + + describe 'POST /groups/:id/invitations' do + it_behaves_like 'POST /:source_type/:id/invitations', 'group' do + let(:source) { group } + end + end +end diff --git a/spec/requests/api/search_spec.rb b/spec/requests/api/search_spec.rb index de1aedc67a6..8012892a571 100644 --- a/spec/requests/api/search_spec.rb +++ b/spec/requests/api/search_spec.rb @@ -23,6 +23,48 @@ RSpec.describe API::Search do end end + shared_examples 'orderable by created_at' do |scope:| + it 'allows ordering results by created_at asc' do + get api(endpoint, user), params: { scope: scope, search: 'sortable', order_by: 'created_at', sort: 'asc' } + + expect(response).to have_gitlab_http_status(:success) + expect(json_response.count).to be > 1 + + created_ats = json_response.map { |r| Time.parse(r['created_at']) } + expect(created_ats.uniq.count).to be > 1 + + expect(created_ats).to eq(created_ats.sort) + end + + it 'allows ordering results by created_at desc' do + get api(endpoint, user), params: { scope: scope, search: 'sortable', order_by: 'created_at', sort: 'desc' } + + expect(response).to have_gitlab_http_status(:success) + expect(json_response.count).to be > 1 + + created_ats = json_response.map { |r| Time.parse(r['created_at']) } + expect(created_ats.uniq.count).to be > 1 + + expect(created_ats).to eq(created_ats.sort.reverse) + end + end + + shared_examples 'issues orderable by created_at' do + before do + create_list(:issue, 3, title: 'sortable item', project: project) + end + + it_behaves_like 'orderable by created_at', scope: :issues + end + + shared_examples 'merge_requests orderable by created_at' do + before do + create_list(:merge_request, 3, :unique_branches, title: 'sortable item', target_project: repo_project, source_project: repo_project) + end + + it_behaves_like 'orderable by created_at', scope: :merge_requests + end + shared_examples 'pagination' do |scope:, search: ''| it 'returns a different result for each page' do get api(endpoint, user), params: { scope: scope, search: search, page: 1, per_page: 1 } @@ -121,6 +163,8 @@ RSpec.describe API::Search do it_behaves_like 'ping counters', scope: :issues + it_behaves_like 'issues orderable by created_at' + describe 'pagination' do before do create(:issue, project: project, title: 'another issue') @@ -181,6 +225,8 @@ RSpec.describe API::Search do it_behaves_like 'ping counters', scope: :merge_requests + it_behaves_like 'merge_requests orderable by created_at' + describe 'pagination' do before do create(:merge_request, source_project: repo_project, title: 'another mr', target_branch: 'another_branch') @@ -354,6 +400,8 @@ RSpec.describe API::Search do it_behaves_like 'ping counters', scope: :issues + it_behaves_like 'issues orderable by created_at' + describe 'pagination' do before do create(:issue, project: project, title: 'another issue') @@ -374,6 +422,8 @@ RSpec.describe API::Search do it_behaves_like 'ping counters', scope: :merge_requests + it_behaves_like 'merge_requests orderable by created_at' + describe 'pagination' do before do create(:merge_request, source_project: repo_project, title: 'another mr', target_branch: 'another_branch') @@ -506,6 +556,8 @@ RSpec.describe API::Search do it_behaves_like 'ping counters', scope: :issues + it_behaves_like 'issues orderable by created_at' + describe 'pagination' do before do create(:issue, project: project, title: 'another issue') @@ -536,6 +588,8 @@ RSpec.describe API::Search do it_behaves_like 'ping counters', scope: :merge_requests + it_behaves_like 'merge_requests orderable by created_at' + describe 'pagination' do before do create(:merge_request, source_project: repo_project, title: 'another mr', target_branch: 'another_branch') diff --git a/spec/services/members/invite_service_spec.rb b/spec/services/members/invite_service_spec.rb new file mode 100644 index 00000000000..12a1a54696b --- /dev/null +++ b/spec/services/members/invite_service_spec.rb @@ -0,0 +1,66 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Members::InviteService do + let(:project) { create(:project) } + let(:user) { create(:user) } + let(:project_user) { create(:user) } + + before do + project.add_maintainer(user) + end + + it 'adds an existing user to members' do + params = { email: project_user.email.to_s, access_level: Gitlab::Access::GUEST } + result = described_class.new(user, params).execute(project) + + expect(result[:status]).to eq(:success) + expect(project.users).to include project_user + end + + it 'creates a new user for an unknown email address' do + params = { email: 'email@example.org', access_level: Gitlab::Access::GUEST } + result = described_class.new(user, params).execute(project) + + expect(result[:status]).to eq(:success) + end + + it 'limits the number of emails to 100' do + emails = Array.new(101).map { |n| "email#{n}@example.com" } + params = { email: emails, access_level: Gitlab::Access::GUEST } + + result = described_class.new(user, params).execute(project) + + expect(result[:status]).to eq(:error) + expect(result[:message]).to eq('Too many users specified (limit is 100)') + end + + it 'does not invite an invalid email' do + params = { email: project_user.id.to_s, access_level: Gitlab::Access::GUEST } + result = described_class.new(user, params).execute(project) + + expect(result[:status]).to eq(:error) + expect(result[:message][project_user.id.to_s]).to eq("Invite email is invalid") + expect(project.users).not_to include project_user + end + + it 'does not invite to an invalid access level' do + params = { email: project_user.email, access_level: -1 } + result = described_class.new(user, params).execute(project) + + expect(result[:status]).to eq(:error) + expect(result[:message][project_user.email]).to eq("Access level is not included in the list") + end + + it 'does not add a member with an existing invite' do + invited_member = create(:project_member, :invited, project: project) + + params = { email: invited_member.invite_email, + access_level: Gitlab::Access::GUEST } + result = described_class.new(user, params).execute(project) + + expect(result[:status]).to eq(:error) + expect(result[:message][invited_member.invite_email]).to eq("Member already invited to #{project.name}") + end +end |