diff options
Diffstat (limited to 'spec/requests')
-rw-r--r-- | spec/requests/api/lint_spec.rb | 168 | ||||
-rw-r--r-- | spec/requests/api/merge_request_approvals_spec.rb | 6 | ||||
-rw-r--r-- | spec/requests/api/merge_request_diffs_spec.rb | 12 | ||||
-rw-r--r-- | spec/requests/api/merge_requests_spec.rb | 30 | ||||
-rw-r--r-- | spec/requests/api/npm_instance_packages_spec.rb | 2 | ||||
-rw-r--r-- | spec/requests/api/npm_project_packages_spec.rb | 34 | ||||
-rw-r--r-- | spec/requests/api/project_attributes.yml | 149 | ||||
-rw-r--r-- | spec/requests/api/projects_spec.rb | 51 | ||||
-rw-r--r-- | spec/requests/api/terraform/state_spec.rb | 2 | ||||
-rw-r--r-- | spec/requests/api/todos_spec.rb | 8 |
10 files changed, 393 insertions, 69 deletions
diff --git a/spec/requests/api/lint_spec.rb b/spec/requests/api/lint_spec.rb index 2653653c896..2316e702c3e 100644 --- a/spec/requests/api/lint_spec.rb +++ b/spec/requests/api/lint_spec.rb @@ -4,91 +4,136 @@ require 'spec_helper' RSpec.describe API::Lint do describe 'POST /ci/lint' do - context 'with valid .gitlab-ci.yaml content' do - let(:yaml_content) do - File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml')) - end + context 'when signup settings are disabled' do + Gitlab::CurrentSettings.signup_enabled = false - it 'passes validation without warnings or errors' do - post api('/ci/lint'), params: { content: yaml_content } + context 'when unauthenticated' do + it 'returns authentication error' do + post api('/ci/lint'), params: { content: 'content' } - expect(response).to have_gitlab_http_status(:ok) - expect(json_response).to be_an Hash - expect(json_response['status']).to eq('valid') - expect(json_response['warnings']).to eq([]) - expect(json_response['errors']).to eq([]) + expect(response).to have_gitlab_http_status(:unauthorized) + end end - it 'outputs expanded yaml content' do - post api('/ci/lint'), params: { content: yaml_content, include_merged_yaml: true } + context 'when authenticated' do + it 'returns unauthorized error' do + post api('/ci/lint'), params: { content: 'content' } - expect(response).to have_gitlab_http_status(:ok) - expect(json_response).to have_key('merged_yaml') + expect(response).to have_gitlab_http_status(:unauthorized) + end end end - context 'with valid .gitlab-ci.yaml with warnings' do - let(:yaml_content) { { job: { script: 'ls', rules: [{ when: 'always' }] } }.to_yaml } + context 'when signup settings are enabled' do + Gitlab::CurrentSettings.signup_enabled = true - it 'passes validation but returns warnings' do - post api('/ci/lint'), params: { content: yaml_content } + context 'when unauthenticated' do + it 'returns authentication error' do + post api('/ci/lint'), params: { content: 'content' } - expect(response).to have_gitlab_http_status(:ok) - expect(json_response['status']).to eq('valid') - expect(json_response['warnings']).not_to be_empty - expect(json_response['status']).to eq('valid') - expect(json_response['errors']).to eq([]) + expect(response).to have_gitlab_http_status(:unauthorized) + end + end + + context 'when authenticated' do + let_it_be(:api_user) { create(:user) } + it 'returns authentication success' do + post api('/ci/lint', api_user), params: { content: 'content' } + + expect(response).to have_gitlab_http_status(:ok) + end end end - context 'with an invalid .gitlab_ci.yml' do - context 'with invalid syntax' do - let(:yaml_content) { 'invalid content' } + context 'when authenticated' do + let_it_be(:api_user) { create(:user) } - it 'responds with errors about invalid syntax' do - post api('/ci/lint'), params: { content: yaml_content } + context 'with valid .gitlab-ci.yaml content' do + let(:yaml_content) do + File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml')) + end + + it 'passes validation without warnings or errors' do + post api('/ci/lint', api_user), params: { content: yaml_content } expect(response).to have_gitlab_http_status(:ok) - expect(json_response['status']).to eq('invalid') + expect(json_response).to be_an Hash + expect(json_response['status']).to eq('valid') expect(json_response['warnings']).to eq([]) - expect(json_response['errors']).to eq(['Invalid configuration format']) + expect(json_response['errors']).to eq([]) end it 'outputs expanded yaml content' do - post api('/ci/lint'), params: { content: yaml_content, include_merged_yaml: true } + post api('/ci/lint', api_user), params: { content: yaml_content, include_merged_yaml: true } expect(response).to have_gitlab_http_status(:ok) expect(json_response).to have_key('merged_yaml') end end - context 'with invalid configuration' do - let(:yaml_content) { '{ image: "ruby:2.7", services: ["postgres"], invalid }' } + context 'with valid .gitlab-ci.yaml with warnings' do + let(:yaml_content) { { job: { script: 'ls', rules: [{ when: 'always' }] } }.to_yaml } - it 'responds with errors about invalid configuration' do - post api('/ci/lint'), params: { content: yaml_content } + it 'passes validation but returns warnings' do + post api('/ci/lint', api_user), params: { content: yaml_content } expect(response).to have_gitlab_http_status(:ok) - expect(json_response['status']).to eq('invalid') - expect(json_response['warnings']).to eq([]) - expect(json_response['errors']).to eq(['jobs invalid config should implement a script: or a trigger: keyword', 'jobs config should contain at least one visible job']) + expect(json_response['status']).to eq('valid') + expect(json_response['warnings']).not_to be_empty + expect(json_response['status']).to eq('valid') + expect(json_response['errors']).to eq([]) end + end - it 'outputs expanded yaml content' do - post api('/ci/lint'), params: { content: yaml_content, include_merged_yaml: true } + context 'with an invalid .gitlab_ci.yml' do + context 'with invalid syntax' do + let(:yaml_content) { 'invalid content' } - expect(response).to have_gitlab_http_status(:ok) - expect(json_response).to have_key('merged_yaml') + it 'responds with errors about invalid syntax' do + post api('/ci/lint', api_user), params: { content: yaml_content } + + expect(response).to have_gitlab_http_status(:ok) + expect(json_response['status']).to eq('invalid') + expect(json_response['warnings']).to eq([]) + expect(json_response['errors']).to eq(['Invalid configuration format']) + end + + it 'outputs expanded yaml content' do + post api('/ci/lint', api_user), params: { content: yaml_content, include_merged_yaml: true } + + expect(response).to have_gitlab_http_status(:ok) + expect(json_response).to have_key('merged_yaml') + end + end + + context 'with invalid configuration' do + let(:yaml_content) { '{ image: "ruby:2.7", services: ["postgres"] }' } + + it 'responds with errors about invalid configuration' do + post api('/ci/lint', api_user), params: { content: yaml_content } + + expect(response).to have_gitlab_http_status(:ok) + expect(json_response['status']).to eq('invalid') + expect(json_response['warnings']).to eq([]) + expect(json_response['errors']).to eq(['jobs config should contain at least one visible job']) + end + + it 'outputs expanded yaml content' do + post api('/ci/lint', api_user), params: { content: yaml_content, include_merged_yaml: true } + + expect(response).to have_gitlab_http_status(:ok) + expect(json_response).to have_key('merged_yaml') + end end end - end - context 'without the content parameter' do - it 'responds with validation error about missing content' do - post api('/ci/lint') + context 'without the content parameter' do + it 'responds with validation error about missing content' do + post api('/ci/lint', api_user) - expect(response).to have_gitlab_http_status(:bad_request) - expect(json_response['error']).to eq('content is missing') + expect(response).to have_gitlab_http_status(:bad_request) + expect(json_response['error']).to eq('content is missing') + end end end end @@ -364,6 +409,18 @@ RSpec.describe API::Lint do expect(response).to have_gitlab_http_status(:not_found) end + + context 'when project is public' do + before do + project.update!(visibility_level: Gitlab::VisibilityLevel::PUBLIC) + end + + it 'returns authentication error' do + ci_lint + + expect(response).to have_gitlab_http_status(:forbidden) + end + end end context 'when authenticated as non-member' do @@ -387,13 +444,10 @@ RSpec.describe API::Lint do context 'when running as dry run' do let(:dry_run) { true } - it 'returns pipeline creation error' do + it 'returns authentication error' do ci_lint - expect(response).to have_gitlab_http_status(:ok) - expect(json_response['merged_yaml']).to eq(nil) - expect(json_response['valid']).to eq(false) - expect(json_response['errors']).to eq(['Insufficient permissions to create a new pipeline']) + expect(response).to have_gitlab_http_status(:forbidden) end end @@ -410,7 +464,11 @@ RSpec.describe API::Lint do ) end - it_behaves_like 'valid project config' + it 'returns authentication error' do + ci_lint + + expect(response).to have_gitlab_http_status(:forbidden) + end end end end diff --git a/spec/requests/api/merge_request_approvals_spec.rb b/spec/requests/api/merge_request_approvals_spec.rb index fad5c3fb60e..b18f3017e03 100644 --- a/spec/requests/api/merge_request_approvals_spec.rb +++ b/spec/requests/api/merge_request_approvals_spec.rb @@ -21,6 +21,12 @@ RSpec.describe API::MergeRequestApprovals do expect(response).to have_gitlab_http_status(:ok) end + + context 'when merge request author has only guest access' do + it_behaves_like 'rejects user from accessing merge request info' do + let(:url) { "/projects/#{project.id}/merge_requests/#{merge_request.iid}/approvals" } + end + end end describe 'POST :id/merge_requests/:merge_request_iid/approve' do diff --git a/spec/requests/api/merge_request_diffs_spec.rb b/spec/requests/api/merge_request_diffs_spec.rb index 2e6cbe7bee7..971fb5e991c 100644 --- a/spec/requests/api/merge_request_diffs_spec.rb +++ b/spec/requests/api/merge_request_diffs_spec.rb @@ -35,6 +35,12 @@ RSpec.describe API::MergeRequestDiffs, 'MergeRequestDiffs' do get api("/projects/#{project.id}/merge_requests/0/versions", user) expect(response).to have_gitlab_http_status(:not_found) end + + context 'when merge request author has only guest access' do + it_behaves_like 'rejects user from accessing merge request info' do + let(:url) { "/projects/#{project.id}/merge_requests/#{merge_request.iid}/versions" } + end + end end describe 'GET /projects/:id/merge_requests/:merge_request_iid/versions/:version_id' do @@ -63,5 +69,11 @@ RSpec.describe API::MergeRequestDiffs, 'MergeRequestDiffs' do get api("/projects/#{project.id}/merge_requests/#{non_existing_record_iid}/versions/#{merge_request_diff.id}", user) expect(response).to have_gitlab_http_status(:not_found) end + + context 'when merge request author has only guest access' do + it_behaves_like 'rejects user from accessing merge request info' do + let(:url) { "/projects/#{project.id}/merge_requests/#{merge_request.iid}/versions/#{merge_request_diff.id}" } + end + end end end diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb index b459d3cd8d3..ad8e21bf4c1 100644 --- a/spec/requests/api/merge_requests_spec.rb +++ b/spec/requests/api/merge_requests_spec.rb @@ -1226,6 +1226,12 @@ RSpec.describe API::MergeRequests do end end + context 'when merge request author has only guest access' do + it_behaves_like 'rejects user from accessing merge request info' do + let(:url) { "/projects/#{project.id}/merge_requests/#{merge_request.iid}" } + end + end + context 'merge_request_metrics' do let(:pipeline) { create(:ci_empty_pipeline) } @@ -1411,6 +1417,12 @@ RSpec.describe API::MergeRequests do it_behaves_like 'issuable participants endpoint' do let(:entity) { create(:merge_request, :simple, milestone: milestone1, author: user, assignees: [user], source_project: project, target_project: project, source_branch: 'markdown', title: "Test", created_at: base_time) } end + + context 'when merge request author has only guest access' do + it_behaves_like 'rejects user from accessing merge request info' do + let(:url) { "/projects/#{project.id}/merge_requests/#{merge_request.iid}/participants" } + end + end end describe 'GET /projects/:id/merge_requests/:merge_request_iid/commits' do @@ -1436,6 +1448,12 @@ RSpec.describe API::MergeRequests do expect(response).to have_gitlab_http_status(:not_found) end + + context 'when merge request author has only guest access' do + it_behaves_like 'rejects user from accessing merge request info' do + let(:url) { "/projects/#{project.id}/merge_requests/#{merge_request.iid}/commits" } + end + end end describe 'GET /projects/:id/merge_requests/:merge_request_iid/:context_commits' do @@ -1511,6 +1529,12 @@ RSpec.describe API::MergeRequests do expect(response).to have_gitlab_http_status(:not_found) end + context 'when merge request author has only guest access' do + it_behaves_like 'rejects user from accessing merge request info' do + let(:url) { "/projects/#{project.id}/merge_requests/#{merge_request.iid}/changes" } + end + end + it_behaves_like 'find an existing merge request' it_behaves_like 'accesses diffs via raw_diffs' @@ -1600,6 +1624,12 @@ RSpec.describe API::MergeRequests do expect(response).to have_gitlab_http_status(:forbidden) end end + + context 'when merge request author has only guest access' do + it_behaves_like 'rejects user from accessing merge request info' do + let(:url) { "/projects/#{project.id}/merge_requests/#{merge_request.iid}/pipelines" } + end + end end describe 'POST /projects/:id/merge_requests/:merge_request_iid/pipelines' do diff --git a/spec/requests/api/npm_instance_packages_spec.rb b/spec/requests/api/npm_instance_packages_spec.rb index 8299717b5c7..70c76067a6e 100644 --- a/spec/requests/api/npm_instance_packages_spec.rb +++ b/spec/requests/api/npm_instance_packages_spec.rb @@ -6,7 +6,7 @@ RSpec.describe API::NpmInstancePackages do include_context 'npm api setup' describe 'GET /api/v4/packages/npm/*package_name' do - it_behaves_like 'handling get metadata requests' do + it_behaves_like 'handling get metadata requests', scope: :instance do let(:url) { api("/packages/npm/#{package_name}") } end end diff --git a/spec/requests/api/npm_project_packages_spec.rb b/spec/requests/api/npm_project_packages_spec.rb index 1421f20ac28..7ea238c0607 100644 --- a/spec/requests/api/npm_project_packages_spec.rb +++ b/spec/requests/api/npm_project_packages_spec.rb @@ -6,25 +6,25 @@ RSpec.describe API::NpmProjectPackages do include_context 'npm api setup' describe 'GET /api/v4/projects/:id/packages/npm/*package_name' do - it_behaves_like 'handling get metadata requests' do + it_behaves_like 'handling get metadata requests', scope: :project do let(:url) { api("/projects/#{project.id}/packages/npm/#{package_name}") } end end describe 'GET /api/v4/projects/:id/packages/npm/-/package/*package_name/dist-tags' do - it_behaves_like 'handling get dist tags requests' do + it_behaves_like 'handling get dist tags requests', scope: :project do let(:url) { api("/projects/#{project.id}/packages/npm/-/package/#{package_name}/dist-tags") } end end describe 'PUT /api/v4/projects/:id/packages/npm/-/package/*package_name/dist-tags/:tag' do - it_behaves_like 'handling create dist tag requests' do + it_behaves_like 'handling create dist tag requests', scope: :project do let(:url) { api("/projects/#{project.id}/packages/npm/-/package/#{package_name}/dist-tags/#{tag_name}") } end end describe 'DELETE /api/v4/projects/:id/packages/npm/-/package/*package_name/dist-tags/:tag' do - it_behaves_like 'handling delete dist tag requests' do + it_behaves_like 'handling delete dist tag requests', scope: :project do let(:url) { api("/projects/#{project.id}/packages/npm/-/package/#{package_name}/dist-tags/#{tag_name}") } end end @@ -32,10 +32,14 @@ RSpec.describe API::NpmProjectPackages do describe 'GET /api/v4/projects/:id/packages/npm/*package_name/-/*file_name' do let_it_be(:package_file) { package.package_files.first } - let(:params) { {} } - let(:url) { api("/projects/#{project.id}/packages/npm/#{package_file.package.name}/-/#{package_file.file_name}") } + let(:headers) { {} } + let(:url) { api("/projects/#{project.id}/packages/npm/#{package.name}/-/#{package_file.file_name}") } - subject { get(url, params: params) } + subject { get(url, headers: headers) } + + before do + project.add_developer(user) + end shared_examples 'a package file that requires auth' do it 'denies download with no token' do @@ -45,7 +49,7 @@ RSpec.describe API::NpmProjectPackages do end context 'with access token' do - let(:params) { { access_token: token.token } } + let(:headers) { build_token_auth_header(token.token) } it 'returns the file' do subject @@ -56,7 +60,7 @@ RSpec.describe API::NpmProjectPackages do end context 'with job token' do - let(:params) { { job_token: job.token } } + let(:headers) { build_token_auth_header(job.token) } it 'returns the file' do subject @@ -86,7 +90,7 @@ RSpec.describe API::NpmProjectPackages do it_behaves_like 'a package file that requires auth' context 'with guest' do - let(:params) { { access_token: token.token } } + let(:headers) { build_token_auth_header(token.token) } it 'denies download when not enough permissions' do project.add_guest(user) @@ -108,7 +112,11 @@ RSpec.describe API::NpmProjectPackages do end describe 'PUT /api/v4/projects/:id/packages/npm/:package_name' do - RSpec.shared_examples 'handling invalid record with 400 error' do + before do + project.add_developer(user) + end + + shared_examples 'handling invalid record with 400 error' do it 'handles an ActiveRecord::RecordInvalid exception with 400 error' do expect { upload_package_with_token(package_name, params) } .not_to change { project.packages.count } @@ -261,7 +269,9 @@ RSpec.describe API::NpmProjectPackages do end def upload_package(package_name, params = {}) - put api("/projects/#{project.id}/packages/npm/#{package_name.sub('/', '%2f')}"), params: params + token = params.delete(:access_token) || params.delete(:job_token) + headers = build_token_auth_header(token) + put api("/projects/#{project.id}/packages/npm/#{package_name.sub('/', '%2f')}"), params: params, headers: headers end def upload_package_with_token(package_name, params = {}) diff --git a/spec/requests/api/project_attributes.yml b/spec/requests/api/project_attributes.yml new file mode 100644 index 00000000000..181fcafd577 --- /dev/null +++ b/spec/requests/api/project_attributes.yml @@ -0,0 +1,149 @@ +--- +itself: # project + unexposed_attributes: + - bfg_object_map + - delete_error + - detected_repository_languages + - disable_overriding_approvers_per_merge_request + - external_authorization_classification_label + - external_webhook_token + - has_external_issue_tracker + - has_external_wiki + - import_source + - import_type + - import_url + - issues_template + - jobs_cache_index + - last_repository_check_at + - last_repository_check_failed + - last_repository_updated_at + - marked_for_deletion_at + - marked_for_deletion_by_user_id + - max_artifacts_size + - max_pages_size + - merge_requests_author_approval + - merge_requests_disable_committers_approval + - merge_requests_rebase_enabled + - merge_requests_template + - mirror_last_successful_update_at + - mirror_last_update_at + - mirror_overwrites_diverged_branches + - mirror_trigger_builds + - mirror_user_id + - only_mirror_protected_branches + - pages_https_only + - pending_delete + - pool_repository_id + - pull_mirror_available_overridden + - pull_mirror_branch_prefix + - remote_mirror_available_overridden + - repository_read_only + - repository_size_limit + - require_password_to_approve + - reset_approvals_on_push + - runners_token_encrypted + - storage_version + - updated_at + remapped_attributes: + avatar: avatar_url + build_allow_git_fetch: build_git_strategy + merge_requests_ff_only_enabled: merge_method + namespace_id: namespace + public_builds: public_jobs + visibility_level: visibility + computed_attributes: + - _links + - can_create_merge_request_in + - compliance_frameworks + - container_expiration_policy + - default_branch + - empty_repo + - forks_count + - http_url_to_repo + - name_with_namespace + - open_issues_count + - owner + - path_with_namespace + - permissions + - readme_url + - shared_with_groups + - ssh_url_to_repo + - web_url + +build_auto_devops: # auto_devops + unexposed_attributes: + - id + - project_id + - created_at + - updated_at + remapped_attributes: + enabled: auto_devops_enabled + deploy_strategy: auto_devops_deploy_strategy + +ci_cd_settings: + unexposed_attributes: + - id + - project_id + - group_runners_enabled + - keep_latest_artifact + - merge_pipelines_enabled + - merge_trains_enabled + - auto_rollback_enabled + remapped_attributes: + default_git_depth: ci_default_git_depth + forward_deployment_enabled: ci_forward_deployment_enabled + +build_import_state: # import_state + unexposed_attributes: + - id + - project_id + - retry_count + - last_update_started_at + - last_update_scheduled_at + - next_execution_timestamp + - jid + - last_update_at + - last_successful_update_at + - correlation_id_value + remapped_attributes: + status: import_status + last_error: import_error + +project_feature: + unexposed_attributes: + - id + - created_at + - metrics_dashboard_access_level + - project_id + - requirements_access_level + - security_and_compliance_access_level + - updated_at + computed_attributes: + - issues_enabled + - jobs_enabled + - merge_requests_enabled + - requirements_enabled + - security_and_compliance_enabled + - snippets_enabled + - wiki_enabled + +project_setting: + unexposed_attributes: + - allow_editing_commit_messages + - created_at + - has_confluence + - has_vulnerabilities + - prevent_merge_without_jira_issue + - project_id + - push_rule_id + - show_default_award_emojis + - squash_option + - updated_at + +build_service_desk_setting: # service_desk_setting + unexposed_attributes: + - project_id + - issue_template_key + - outgoing_name + remapped_attributes: + project_key: service_desk_address diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index 6d8cdde2c4f..ad36777184a 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -886,6 +886,7 @@ RSpec.describe API::Projects do merge_method: 'ff' }).tap do |attrs| attrs[:operations_access_level] = 'disabled' + attrs[:analytics_access_level] = 'disabled' end post api('/projects', user), params: project @@ -1539,6 +1540,35 @@ RSpec.describe API::Projects do end context 'when authenticated as an admin' do + let(:project_attributes_file) { 'spec/requests/api/project_attributes.yml' } + let(:project_attributes) { YAML.load_file(project_attributes_file) } + + let(:expected_keys) do + keys = project_attributes.map do |relation, relation_config| + begin + actual_keys = project.send(relation).attributes.keys + rescue NoMethodError + actual_keys = ["#{relation} is nil"] + end + unexposed_attributes = relation_config['unexposed_attributes'] || [] + remapped_attributes = relation_config['remapped_attributes'] || {} + computed_attributes = relation_config['computed_attributes'] || [] + actual_keys - unexposed_attributes - remapped_attributes.keys + remapped_attributes.values + computed_attributes + end.flatten + + unless Gitlab.ee? + keys -= %w[ + approvals_before_merge + compliance_frameworks + mirror + requirements_enabled + security_and_compliance_enabled + ] + end + + keys + end + it 'returns a project by id' do project project_member @@ -1587,6 +1617,27 @@ RSpec.describe API::Projects do expect(json_response['only_allow_merge_if_all_discussions_are_resolved']).to eq(project.only_allow_merge_if_all_discussions_are_resolved) expect(json_response['operations_access_level']).to be_present end + + it 'exposes all necessary attributes' do + create(:project_group_link, project: project) + + get api("/projects/#{project.id}", admin) + + diff = Set.new(json_response.keys) ^ Set.new(expected_keys) + + expect(diff).to be_empty, failure_message(diff) + end + + def failure_message(diff) + <<~MSG + It looks like project's set of exposed attributes is different from the expected set. + + The following attributes are missing or newly added: + #{diff.to_a.to_sentence} + + Please update #{project_attributes_file} file" + MSG + end end context 'when authenticated as a regular user' do diff --git a/spec/requests/api/terraform/state_spec.rb b/spec/requests/api/terraform/state_spec.rb index bfdb5458fd1..2cb3c8e9ab5 100644 --- a/spec/requests/api/terraform/state_spec.rb +++ b/spec/requests/api/terraform/state_spec.rb @@ -39,7 +39,7 @@ RSpec.describe API::Terraform::State do context 'with maintainer permissions' do let(:current_user) { maintainer } - it_behaves_like 'tracking unique hll events', :usage_data_p_terraform_state_api_unique_users do + it_behaves_like 'tracking unique hll events' do let(:target_id) { 'p_terraform_state_api_unique_users' } let(:expected_type) { instance_of(Integer) } end diff --git a/spec/requests/api/todos_spec.rb b/spec/requests/api/todos_spec.rb index eaffa49fc9d..00de1ef5964 100644 --- a/spec/requests/api/todos_spec.rb +++ b/spec/requests/api/todos_spec.rb @@ -331,6 +331,14 @@ RSpec.describe API::Todos do expect(response).to have_gitlab_http_status(:not_found) end end + + it 'returns an error if the issuable author does not have access' do + project_1.add_guest(issuable.author) + + post api("/projects/#{project_1.id}/#{issuable_type}/#{issuable.iid}/todo", issuable.author) + + expect(response).to have_gitlab_http_status(:not_found) + end end describe 'POST :id/issuable_type/:issueable_id/todo' do |