diff --git a/app/controllers/import/source_users_controller.rb b/app/controllers/import/source_users_controller.rb index ded2e9f3bfc..3800bedd4d3 100644 --- a/app/controllers/import/source_users_controller.rb +++ b/app/controllers/import/source_users_controller.rb @@ -15,9 +15,9 @@ module Import if result.success? flash[:raw] = banner('accept_invite') - redirect_to(dashboard_groups_path) + redirect_to(root_path) else - redirect_to(dashboard_groups_path, alert: s_('UserMapping|The invitation could not be accepted.')) + redirect_to(root_path, alert: s_('UserMapping|The invitation could not be accepted.')) end end @@ -26,9 +26,9 @@ module Import if result.success? flash[:raw] = banner('reject_invite') - redirect_to(dashboard_groups_path) + redirect_to(root_path) else - redirect_to(dashboard_groups_path, alert: s_('UserMapping|The invitation could not be declined.')) + redirect_to(root_path, alert: s_('UserMapping|The invitation could not be declined.')) end end @@ -40,7 +40,7 @@ module Import return if source_user.awaiting_approval? && current_user_matches_invite? flash[:raw] = banner('invalid_invite') - redirect_to(dashboard_groups_path) + redirect_to(root_path) end def current_user_matches_invite? diff --git a/config/gitlab_loose_foreign_keys.yml b/config/gitlab_loose_foreign_keys.yml index 7d52d6deed0..7255c21d4bf 100644 --- a/config/gitlab_loose_foreign_keys.yml +++ b/config/gitlab_loose_foreign_keys.yml @@ -584,10 +584,6 @@ vulnerability_scanners: - table: projects column: project_id on_delete: async_delete -vulnerability_state_transitions: - - table: ci_pipelines - column: state_changed_at_pipeline_id - on_delete: async_nullify vulnerability_statistics: - table: ci_pipelines column: latest_pipeline_id diff --git a/doc/.vale/gitlab_base/CodeBlockNesting.yml b/doc/.vale/gitlab_base/CodeBlockNesting.yml index 576895a4d9e..a216032d556 100644 --- a/doc/.vale/gitlab_base/CodeBlockNesting.yml +++ b/doc/.vale/gitlab_base/CodeBlockNesting.yml @@ -4,7 +4,7 @@ # Ensures content nested in lists are spaced correctly. # extends: existence -message: "Use three spaces for lines under ordered lists, and two spaces under unordered lists" +message: "Items under an ordered list must be indented three spaces. Items under an unordered list must be indented two spaces." link: https://docs.gitlab.com/ee/development/documentation/styleguide/#nesting-inside-a-list-item level: error nonword: true diff --git a/doc/administration/repository_storage_paths.md b/doc/administration/repository_storage_paths.md index 62dca60fb96..f1d4b97ea0c 100644 --- a/doc/administration/repository_storage_paths.md +++ b/doc/administration/repository_storage_paths.md @@ -28,6 +28,7 @@ For more information on: ## Hashed storage +> - Support for legacy storage, where repository paths were generated based on the project path, has been completely removed in GitLab 14.0. > - **Storage name** field [renamed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/128416) from **Gitaly storage name** and **Relative path** field [renamed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/128416) from **Gitaly relative path** in GitLab 16.3. Hashed storage stores projects on disk in a location based on a hash of the project's ID. This makes the folder diff --git a/doc/api/environments.md b/doc/api/environments.md index 49df65ca9a6..f86349a5529 100644 --- a/doc/api/environments.md +++ b/doc/api/environments.md @@ -163,6 +163,21 @@ Example of response "runner": null, "artifacts_expire_at": null } + }, + "cluster_agent": { + "id": 1, + "name": "agent-1", + "config_project": { + "id": 20, + "description": "", + "name": "test", + "name_with_namespace": "Administrator / test", + "path": "test", + "path_with_namespace": "root/test", + "created_at": "2022-03-20T20:42:40.221Z" + }, + "created_at": "2022-04-20T20:42:40.221Z", + "created_by_user_id": 42 } } ``` diff --git a/doc/development/file_storage.md b/doc/development/file_storage.md index 6e413551fa8..158dbe21d94 100644 --- a/doc/development/file_storage.md +++ b/doc/development/file_storage.md @@ -45,7 +45,7 @@ they are still not 100% standardized. You can see them below: | User snippet attachments | yes | `uploads/-/system/personal_snippet/:id/:random_hex/:filename` | `PersonalFileUploader` | Snippet | | Project avatars | yes | `uploads/-/system/project/avatar/:id/:filename` | `AvatarUploader` | Project | | Topic avatars | yes | `uploads/-/system/projects/topic/avatar/:id/:filename` | `AvatarUploader` | Topic | -| Issues/MR/Notes Markdown attachments | yes | `uploads/:project_path_with_namespace/:random_hex/:filename` | `FileUploader` | Project | +| Issues/MR/Notes Markdown attachments | yes | `uploads/:hash_project_id/:random_hex/:filename` | `FileUploader` | Project | | Issues/MR/Notes Legacy Markdown attachments | no | `uploads/-/system/note/attachment/:id/:filename` | `AttachmentUploader` | Note | | Design Management design thumbnails | yes | `uploads/-/system/design_management/action/image_v432x230/:id/:filename` | `DesignManagement::DesignV432x230Uploader` | DesignManagement::Action | | CI Artifacts (CE) | yes | `shared/artifacts/:disk_hash[0..1]/:disk_hash[2..3]/:disk_hash/:year_:month_:date/:job_id/:job_artifact_id` (`:disk_hash` is SHA256 digest of `project_id`) | `JobArtifactUploader` | Ci::JobArtifact | @@ -56,9 +56,8 @@ they are still not 100% standardized. You can see them below: CI Artifacts and LFS Objects behave differently in CE and EE. In CE they inherit the `GitlabUploader` while in EE they inherit the `ObjectStorage` and store files in and S3 API compatible object store. -In the case of Issues/MR/Notes Markdown attachments, there is a different approach using the [Hashed Storage](../administration/repository_storage_paths.md) layout, -instead of basing the path into a mutable variable `:project_path_with_namespace`, it's possible to use the -hash of the project ID instead, if project migrates to the new approach (introduced in 10.2). +Attachments for issues, merge requests (MR), and notes in Markdown use +[hashed storage](../administration/repository_storage_paths.md) with the hash of the project ID. We provide an [all-in-one Rake task](../administration/raketasks/uploads/migrate.md) to migrate all uploads to object storage in one go. If a new Uploader class or model diff --git a/doc/development/git_object_deduplication.md b/doc/development/git_object_deduplication.md index 0498563f677..eb9fb743364 100644 --- a/doc/development/git_object_deduplication.md +++ b/doc/development/git_object_deduplication.md @@ -98,9 +98,6 @@ are as follows: ### Assumptions -- All repositories in a pool must use [hashed storage](../administration/repository_storage_paths.md). - This is so that we don't have to ever worry about updating paths in - `object/info/alternates` files. - All repositories in a pool must be on the same Gitaly storage shard. The Git alternates mechanism relies on direct disk access across multiple repositories, and we can only assume direct disk access to diff --git a/doc/user/application_security/policies/pipeline_execution_policies.md b/doc/user/application_security/policies/pipeline_execution_policies.md index 352776691d7..c195878f5f7 100644 --- a/doc/user/application_security/policies/pipeline_execution_policies.md +++ b/doc/user/application_security/policies/pipeline_execution_policies.md @@ -192,6 +192,14 @@ The strategy takes precedence over other policies using the `inject_ci` strategy This strategy allows users to include the project CI/CD configuration in the pipeline execution policy configuration, enabling them to customize the policy jobs. For example, by combining policy and project CI/CD configuration into one YAML file, users can override `before_script` configuration. +NOTE: +When a pipeline execution policy uses workflow rules that prevent policy jobs from running, the +project's original CI/CD configuration remains in effect instead of being overridden. You can +conditionally apply pipeline execution policies to control when the policy impacts the project's +CI/CD configuration. For example, if you set a workflow rule `if: $CI_PIPELINE_SOURCE == +"merge_request_event"`, the project's CI configuration is only overridden when the pipeline source +is a merge request event. + ### Include a project's CI/CD configuration in the pipeline execution policy configuration When using `override_project_ci` strategy, the project configuration can be included into the pipeline execution policy configuration: diff --git a/doc/user/project/issues/design_management.md b/doc/user/project/issues/design_management.md index b3f5dcdc1ad..35d8cd9fff5 100644 --- a/doc/user/project/issues/design_management.md +++ b/doc/user/project/issues/design_management.md @@ -36,8 +36,6 @@ For a video overview, see [Design Management](https://www.youtube.com/watch?v=CC Image thumbnails are stored as other uploads, and are not associated with a project but rather with a specific design model. - Newly created projects use hashed storage by default. - A GitLab administrator can verify the relative path of a hashed-stored project by going to **Admin area > Projects** and then selecting the project in question. The **Relative path** field contains `@hashed` in its value. diff --git a/lib/api/entities/environment.rb b/lib/api/entities/environment.rb index 92a87f92d6b..156a91bfa5a 100644 --- a/lib/api/entities/environment.rb +++ b/lib/api/entities/environment.rb @@ -10,6 +10,15 @@ module API expose :last_deployment, using: Entities::Deployment, if: { last_deployment: true } expose :state, documentation: { type: 'string', example: 'available' } expose :auto_stop_at, documentation: { type: 'dateTime', example: '2019-05-25T18:55:13.252Z' } + expose :cluster_agent, using: Entities::Clusters::Agent, if: ->(_, _) { can_read_cluster_agent? } + + private + + def can_read_cluster_agent? + return unless object.cluster_agent.present? + + Ability.allowed?(options[:current_user], :read_cluster_agent, object.cluster_agent) + end end end end diff --git a/lib/import/placeholder_references/alias_resolver.rb b/lib/import/placeholder_references/alias_resolver.rb index 3338af9cf44..586a82ed8e7 100644 --- a/lib/import/placeholder_references/alias_resolver.rb +++ b/lib/import/placeholder_references/alias_resolver.rb @@ -3,8 +3,13 @@ module Import module PlaceholderReferences module AliasResolver + extend self + MissingAlias = Class.new(StandardError) + NOTE_COLUMNS = { "author_id" => "author_id", "updated_by_id" => "updated_by_id", + "resolved_by_id" => "resolved_by_id" }.freeze + # A new version for a model should be defined when new entries must be # mapped in a different way to data that already exists in the database. # Context: https://gitlab.com/gitlab-org/gitlab/-/issues/478501 @@ -30,7 +35,7 @@ module Import "Ci::Build" => { 1 => { model: Ci::Build, - columns: { "user_id" => "user_id" } + columns: { "user_id" => "user_id", "erased_by_id" => "erased_by_id" } } }, "Ci::Pipeline" => { @@ -39,28 +44,60 @@ module Import columns: { "user_id" => "user_id" } } }, + "Ci::PipelineSchedule" => { + 1 => { + model: Ci::PipelineSchedule, + columns: { "owner_id" => "owner_id" } + } + }, "DesignManagement::Version" => { 1 => { model: DesignManagement::Version, columns: { "author_id" => "author_id" } } }, + "DiffNote" => { + 1 => { + model: DiffNote, + columns: NOTE_COLUMNS + } + }, + "DiscussionNote" => { + 1 => { + model: DiscussionNote, + columns: NOTE_COLUMNS + } + }, "Event" => { 1 => { model: Event, columns: { "author_id" => "author_id" } } }, + "Epic" => { + 1 => { + model: Epic, + columns: { "author_id" => "author_id", "assignee_id" => "assignee_id", "updated_by_id" => "updated_by_id", + "last_edited_by_id" => "last_edited_by_id", "closed_by_id" => "closed_by_id" } + } + }, "GenericCommitStatus" => { 1 => { model: GenericCommitStatus, columns: { "user_id" => "user_id" } } }, + "LegacyDiffNote" => { + 1 => { + model: LegacyDiffNote, + columns: NOTE_COLUMNS + } + }, "Issue" => { 1 => { model: Issue, - columns: { "author_id" => "author_id" } + columns: { "author_id" => "author_id", "updated_by_id" => "updated_by_id", + "closed_by_id" => "closed_by_id", "last_edited_by_id" => "last_edited_by_id" } } }, "IssueAssignee" => { @@ -69,16 +106,23 @@ module Import columns: { "user_id" => "user_id", "issue_id" => "issue_id" } } }, + "List" => { + 1 => { + model: List, + columns: { "user_id" => "user_id" } + } + }, "MergeRequest" => { 1 => { model: MergeRequest, - columns: { "author_id" => "author_id" } + columns: { "author_id" => "author_id", "updated_by_id" => "updated_by_id", + "last_edited_by_id" => "last_edited_by_id", "merge_user_id" => "merge_user_id" } } }, "MergeRequest::Metrics" => { 1 => { model: MergeRequest::Metrics, - columns: { "merged_by_id" => "merged_by_id" } + columns: { "merged_by_id" => "merged_by_id", "latest_closed_by_id" => "latest_closed_by_id" } } }, "MergeRequestAssignee" => { @@ -96,6 +140,36 @@ module Import "Note" => { 1 => { model: Note, + columns: NOTE_COLUMNS + } + }, + "ProtectedTag::CreateAccessLevel" => { + 1 => { + model: ProtectedTag::CreateAccessLevel, + columns: { "user_id" => "user_id" } + } + }, + "ProtectedBranch::MergeAccessLevel" => { + 1 => { + model: ProtectedBranch::MergeAccessLevel, + columns: { "user_id" => "user_id" } + } + }, + "ProtectedBranch::PushAccessLevel" => { + 1 => { + model: ProtectedBranch::PushAccessLevel, + columns: { "user_id" => "user_id" } + } + }, + "ProjectSnippet" => { + 1 => { + model: ProjectSnippet, + columns: { "author_id" => "author_id" } + } + }, + "Release" => { + 1 => { + model: Release, columns: { "author_id" => "author_id" } } }, @@ -117,6 +191,12 @@ module Import columns: { "user_id" => "user_id" } } }, + "Snippet" => { + 1 => { + model: Snippet, + columns: { "author_id" => "author_id" } + } + }, "Timelog" => { 1 => { model: Timelog, @@ -125,16 +205,22 @@ module Import } }.freeze - def self.version_for_model(model) - return ALIASES[model].keys.max if ALIASES[model] + private_constant :ALIASES + + def aliases + ALIASES + end + + def version_for_model(model) + return aliases[model].keys.max if aliases[model] track_error_for_missing(model: model) 1 end - def self.aliased_model(model, version:) - aliased_model = ALIASES.dig(model, version, :model) + def aliased_model(model, version:) + aliased_model = aliases.dig(model, version, :model) return aliased_model if aliased_model.present? track_error_for_missing(model: model, version: version) @@ -142,8 +228,8 @@ module Import model.safe_constantize end - def self.aliased_column(model, column, version:) - aliased_column = ALIASES.dig(model, version, :columns, column) + def aliased_column(model, column, version:) + aliased_column = aliases.dig(model, version, :columns, column) return aliased_column if aliased_column.present? track_error_for_missing(model: model, column: column, version: version) @@ -151,8 +237,8 @@ module Import column end - def self.models_with_columns - ALIASES.values + def models_with_columns + aliases.values .map { |versions| versions[versions.keys.max] } .map { |data| [data[:model], data[:columns].values] } end @@ -166,3 +252,5 @@ module Import end end end + +Import::PlaceholderReferences::AliasResolver.extend_mod diff --git a/spec/db/schema_spec.rb b/spec/db/schema_spec.rb index 46571efd187..c550b101983 100644 --- a/spec/db/schema_spec.rb +++ b/spec/db/schema_spec.rb @@ -179,6 +179,7 @@ RSpec.describe 'Database schema', feature_category: :database do vulnerability_identifiers: %w[external_id], vulnerability_occurrence_identifiers: %w[project_id], vulnerability_scanners: %w[external_id], + vulnerability_state_transitions: %w[state_changed_at_pipeline_id], security_scans: %w[pipeline_id project_id], # foreign key is not added as ci_pipeline table will be moved into different db soon dependency_list_exports: %w[pipeline_id], # foreign key is not added as ci_pipeline table is in different db vulnerability_reads: %w[cluster_agent_id namespace_id], # namespace_id is a denormalization of `project.namespace` diff --git a/spec/fixtures/api/schemas/public_api/v4/environment.json b/spec/fixtures/api/schemas/public_api/v4/environment.json index a7455be9251..385c10f9111 100644 --- a/spec/fixtures/api/schemas/public_api/v4/environment.json +++ b/spec/fixtures/api/schemas/public_api/v4/environment.json @@ -60,6 +60,16 @@ }, "project": { "$ref": "project.json" + }, + "cluster_agent": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "agent.json" + } + ] } }, "additionalProperties": false diff --git a/spec/lib/import/placeholder_references/alias_resolver_spec.rb b/spec/lib/import/placeholder_references/alias_resolver_spec.rb index 7991ecc6d16..2ef49cdfcc7 100644 --- a/spec/lib/import/placeholder_references/alias_resolver_spec.rb +++ b/spec/lib/import/placeholder_references/alias_resolver_spec.rb @@ -3,7 +3,21 @@ require "spec_helper" RSpec.describe Import::PlaceholderReferences::AliasResolver, feature_category: :importers do - describe "ALIASES" do + describe ".aliases" do + def missing_attribute_message(model, attribute) + <<-MSG + #{model}##{attribute} references a user and it is not defined in #{described_class}::ALIASES. + Please add the attribute in the columns key in the #{described_class}::ALIASES['#{model}'] hash. + MSG + end + + def missing_alias_message(model) + <<-MSG + #{model} models references a user and it is not defined in #{described_class}::ALIASES. + Please define the mapping in #{described_class}::ALIASES. + MSG + end + it "points to real columns" do def failure_message(model, column) <<-MSG @@ -13,7 +27,7 @@ RSpec.describe Import::PlaceholderReferences::AliasResolver, feature_category: : MSG end - described_class::ALIASES.each_value.flat_map(&:values).each do |model_alias| + described_class.aliases.each_value.flat_map(&:values).each do |model_alias| model = model_alias[:model] column_names = model.columns.map(&:name) @@ -22,6 +36,86 @@ RSpec.describe Import::PlaceholderReferences::AliasResolver, feature_category: : end end end + + shared_examples 'define aliases' do + def relation_class(relation_key) + relation_key.to_s.classify.constantize + rescue NameError + relation_key.to_s.constantize + end + + def extract_relation_names(hash, keys = []) + keys += hash.keys + hash.each_value do |value| + keys += extract_relation_names(value, keys) + end + keys.uniq + end + + it "defines aliases for imported resources that references users", :eager_load do + relation_names = extract_relation_names(config_tree).reject { |name| ignore_relations.include?(name) } + relation_names.each do |relation_name| + relation_name = overrides[relation_name] || relation_name + model_class = relation_class(relation_name) + table_columns = model_class.columns.collect(&:name) + user_associations = model_class.reflect_on_all_associations(:belongs_to) + .reject(&:polymorphic?) + .filter { |association| association.klass == User } + .reject { |association| table_columns.exclude?(association.foreign_key) } + + next unless user_associations.any? + + expect(described_class.aliases[model_class.to_s]).to be_present, missing_alias_message(model_class) + + user_associations.each do |association| + foreign_key = association.foreign_key + last_version = described_class.aliases[model_class.to_s].keys.max + alias_definition = described_class.aliases[model_class.to_s][last_version] + expect(alias_definition[:columns]).to include(foreign_key), + missing_attribute_message(model_class, foreign_key) + end + end + end + end + + describe 'group aliases' do + let(:overrides) { Gitlab::ImportExport::Group::RelationFactory.overrides } + let(:ignore_relations) { %i[members user_contributions author user] } + let(:config_tree) do + Gitlab::ImportExport::Config.new(config: Gitlab::ImportExport.group_config_file).to_h[:tree][:group] + end + + it_behaves_like 'define aliases' + end + + describe 'project aliases' do + let(:overrides) { Gitlab::ImportExport::Project::RelationFactory.overrides } + let(:ignore_relations) { %i[project_members user_contributions author user] } + let(:config_tree) do + Gitlab::ImportExport::Config.new(config: Gitlab::ImportExport.config_file).to_h[:tree][:project] + end + + it_behaves_like 'define aliases' + end + + it "defines aliases for all note descendants apart from synthetic notes" do + user_associations = Note.reflect_on_all_associations(:belongs_to) + .reject(&:polymorphic?) + .filter { |association| association.klass == User } + .reject { |association| Note.columns.collect(&:name).exclude?(association.foreign_key) } + + (Note.descendants - SyntheticNote.descendants - [SyntheticNote]).each do |descendant| + expect(described_class.aliases[descendant.to_s]).to be_present, missing_alias_message(descendant) + + user_associations.each do |association| + foreign_key = association.foreign_key + last_version = described_class.aliases[descendant.to_s].keys.max + alias_definition = described_class.aliases[descendant.to_s][last_version] + expect(alias_definition[:columns]).to include(foreign_key), + missing_attribute_message(descendant, foreign_key) + end + end + end end describe ".version_for_model" do @@ -41,7 +135,7 @@ RSpec.describe Import::PlaceholderReferences::AliasResolver, feature_category: : end before do - stub_const("#{described_class}::ALIASES", aliases) + allow(described_class).to receive(:aliases).and_return(aliases) end it "returns the max version available for the model" do @@ -80,7 +174,7 @@ RSpec.describe Import::PlaceholderReferences::AliasResolver, feature_category: : end before do - stub_const("#{described_class}::ALIASES", aliases) + allow(described_class).to receive(:aliases).and_return(aliases) end it "returns the value for the right version" do @@ -106,7 +200,7 @@ RSpec.describe Import::PlaceholderReferences::AliasResolver, feature_category: : end before do - stub_const("#{described_class}::ALIASES", aliases) + allow(described_class).to receive(:aliases).and_return(aliases) end it "returns the new model name" do @@ -160,7 +254,7 @@ RSpec.describe Import::PlaceholderReferences::AliasResolver, feature_category: : end before do - stub_const("#{described_class}::ALIASES", aliases) + allow(described_class).to receive(:aliases).and_return(aliases) end it "returns the value for the right version" do @@ -184,7 +278,7 @@ RSpec.describe Import::PlaceholderReferences::AliasResolver, feature_category: : end before do - stub_const("#{described_class}::ALIASES", aliases) + allow(described_class).to receive(:aliases).and_return(aliases) end it "returns the new column name" do @@ -229,7 +323,7 @@ RSpec.describe Import::PlaceholderReferences::AliasResolver, feature_category: : end before do - stub_const("#{described_class}::ALIASES", aliases) + allow(described_class).to receive(:aliases).and_return(aliases) end it "only includes the last version" do diff --git a/spec/requests/api/environments_spec.rb b/spec/requests/api/environments_spec.rb index 954d7b86101..3c2c92510e0 100644 --- a/spec/requests/api/environments_spec.rb +++ b/spec/requests/api/environments_spec.rb @@ -6,7 +6,8 @@ RSpec.describe API::Environments, feature_category: :continuous_delivery do let_it_be(:user) { create(:user) } let_it_be(:developer) { create(:user) } let_it_be(:non_member) { create(:user) } - let_it_be(:project) { create(:project, :private, :repository, namespace: user.namespace, maintainers: user, developers: developer) } + let_it_be(:reporter) { create(:user) } + let_it_be(:project) { create(:project, :private, :repository, namespace: user.namespace, maintainers: user, developers: developer, reporters: reporter) } let_it_be_with_reload(:environment) { create(:environment, project: project) } describe 'GET /projects/:id/environments', :aggregate_failures do @@ -383,11 +384,14 @@ RSpec.describe API::Environments, feature_category: :continuous_delivery do deployable: job ) + environment.update!(cluster_agent: create(:cluster_agent, project: project)) + get api("/projects/#{project.id}/environments/#{environment.id}", user) expect(response).to have_gitlab_http_status(:ok) expect(response).to match_response_schema('public_api/v4/environment') expect(json_response['last_deployment']).to be_present + expect(json_response['cluster_agent']).to be_present end end @@ -443,6 +447,18 @@ RSpec.describe API::Environments, feature_category: :continuous_delivery do end end + context 'as a reporter' do + it 'does not expose the cluster agent' do + environment.update!(cluster_agent: create(:cluster_agent, project: project)) + + get api("/projects/#{project.id}/environments/#{environment.id}", reporter) + + expect(response).to have_gitlab_http_status(:ok) + expect(response).to match_response_schema('public_api/v4/environment') + expect(json_response['cluster_agent']).not_to be_present + end + end + context 'as non member' do shared_examples 'environment will not be found' do it 'returns a 404 status code' do @@ -518,12 +534,6 @@ RSpec.describe API::Environments, feature_category: :continuous_delivery do end context "as a reporter" do - let(:reporter) { create(:user) } - - before do - project.add_reporter(reporter) - end - it "rejects the request" do delete api("/projects/#{project.id}/environments/review_apps", reporter) diff --git a/spec/requests/import/source_users_controller_spec.rb b/spec/requests/import/source_users_controller_spec.rb index 804c8a1b61d..ea3915f1458 100644 --- a/spec/requests/import/source_users_controller_spec.rb +++ b/spec/requests/import/source_users_controller_spec.rb @@ -31,7 +31,7 @@ RSpec.describe Import::SourceUsersController, feature_category: :importers do expect { subject }.not_to change { source_user.reload.status } - expect(response).to redirect_to(dashboard_groups_path) + expect(response).to redirect_to(root_path) expect(flash[:raw]).to match(/Reassignment not available/) end end @@ -42,7 +42,7 @@ RSpec.describe Import::SourceUsersController, feature_category: :importers do expect { subject }.not_to change { source_user.reload.status } - expect(response).to redirect_to(dashboard_groups_path) + expect(response).to redirect_to(root_path) expect(flash[:raw]).to match(/Reassignment not available/) end end @@ -72,7 +72,7 @@ RSpec.describe Import::SourceUsersController, feature_category: :importers do it 'redirects with a notice when accepted' do accept_invite - expect(response).to redirect_to(dashboard_groups_path) + expect(response).to redirect_to(root_path) expect(flash[:raw]).to match(/Reassignment approved/) end @@ -82,7 +82,7 @@ RSpec.describe Import::SourceUsersController, feature_category: :importers do accept_invite - expect(response).to redirect_to(dashboard_groups_path) + expect(response).to redirect_to(root_path) expect(flash[:alert]).to match(/The invitation could not be accepted/) end @@ -112,7 +112,7 @@ RSpec.describe Import::SourceUsersController, feature_category: :importers do it 'redirects with a notice' do reject_invite - expect(response).to redirect_to(dashboard_groups_path) + expect(response).to redirect_to(root_path) expect(flash[:raw]).to match(/Reassignment rejected/) end @@ -122,7 +122,7 @@ RSpec.describe Import::SourceUsersController, feature_category: :importers do reject_invite - expect(response).to redirect_to(dashboard_groups_path) + expect(response).to redirect_to(root_path) expect(flash[:alert]).to match(/The invitation could not be declined/) end