diff options
18 files changed, 198 insertions, 66 deletions
diff --git a/app/models/issue_email_participant.rb b/app/models/issue_email_participant.rb index 76a96151350..dd963bc9e7e 100644 --- a/app/models/issue_email_participant.rb +++ b/app/models/issue_email_participant.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class IssueEmailParticipant < ApplicationRecord + include BulkInsertSafe + belongs_to :issue validates :email, uniqueness: { scope: [:issue_id], case_sensitive: false } diff --git a/app/serializers/issue_entity.rb b/app/serializers/issue_entity.rb index 3d94d2e2e9d..82eef55205a 100644 --- a/app/serializers/issue_entity.rb +++ b/app/serializers/issue_entity.rb @@ -85,6 +85,11 @@ class IssueEntity < IssuableEntity end expose :issue_email_participants do |issue| + # TODO - This is a Temporary solution to avoid leaking participants' emails + # on public/internal projects when issue is not confidential. + # Should be removed when https://gitlab.com/gitlab-org/gitlab/-/issues/383448 is implemented. + next [] unless issue.confidential? + issue.issue_email_participants.map { |x| { email: x.email } } end diff --git a/app/services/issues/move_service.rb b/app/services/issues/move_service.rb index 6366ff4076b..f7f7d85611b 100644 --- a/app/services/issues/move_service.rb +++ b/app/services/issues/move_service.rb @@ -19,6 +19,7 @@ module Issues # to receive service desk emails on the new moved issue. update_service_desk_sent_notifications + copy_email_participants queue_copy_designs new_entity @@ -49,6 +50,18 @@ module Issues .sent_notifications.update_all(project_id: new_entity.project_id, noteable_id: new_entity.id) end + def copy_email_participants + new_attributes = { id: nil, issue_id: new_entity.id } + + new_participants = original_entity.issue_email_participants.dup + + new_participants.each do |participant| + participant.assign_attributes(new_attributes) + end + + IssueEmailParticipant.bulk_insert!(new_participants) + end + override :update_old_entity def update_old_entity super diff --git a/config/metrics/counts_28d/20210216184454_code_review_total_unique_counts_monthly.yml b/config/metrics/counts_28d/20210216184454_code_review_total_unique_counts_monthly.yml index d4d8158f25d..e7eb6a01f82 100644 --- a/config/metrics/counts_28d/20210216184454_code_review_total_unique_counts_monthly.yml +++ b/config/metrics/counts_28d/20210216184454_code_review_total_unique_counts_monthly.yml @@ -75,12 +75,6 @@ options: - i_code_review_merge_request_widget_license_compliance_warning - i_code_review_merge_request_widget_license_compliance_full_report_clicked - i_code_review_merge_request_widget_license_compliance_view - - i_code_review_merge_request_widget_load_performance_expand - - i_code_review_merge_request_widget_load_performance_expand_failed - - i_code_review_merge_request_widget_load_performance_expand_success - - i_code_review_merge_request_widget_load_performance_warning - - i_code_review_merge_request_widget_load_performance_full_report_clicked - - i_code_review_merge_request_widget_load_performance_view - i_code_review_mr_diffs - i_code_review_mr_single_file_diffs - i_code_review_mr_with_invalid_approvers diff --git a/config/metrics/counts_28d/20210427102618_code_review_category_monthly_active_users.yml b/config/metrics/counts_28d/20210427102618_code_review_category_monthly_active_users.yml index be5a1acc826..6f7b82ec793 100644 --- a/config/metrics/counts_28d/20210427102618_code_review_category_monthly_active_users.yml +++ b/config/metrics/counts_28d/20210427102618_code_review_category_monthly_active_users.yml @@ -132,9 +132,3 @@ options: - 'i_code_review_merge_request_widget_license_compliance_expand_success' - 'i_code_review_merge_request_widget_license_compliance_expand_warning' - 'i_code_review_merge_request_widget_license_compliance_expand_failed' - - 'i_code_review_merge_request_widget_load_performance_view' - - 'i_code_review_merge_request_widget_load_performance_full_report_clicked' - - 'i_code_review_merge_request_widget_load_performance_expand' - - 'i_code_review_merge_request_widget_load_performance_expand_success' - - 'i_code_review_merge_request_widget_load_performance_expand_warning' - - 'i_code_review_merge_request_widget_load_performance_expand_failed' diff --git a/config/metrics/counts_28d/20210427103119_code_review_group_monthly_active_users.yml b/config/metrics/counts_28d/20210427103119_code_review_group_monthly_active_users.yml index 2761eb0fe7c..ce14aa19f9c 100644 --- a/config/metrics/counts_28d/20210427103119_code_review_group_monthly_active_users.yml +++ b/config/metrics/counts_28d/20210427103119_code_review_group_monthly_active_users.yml @@ -136,9 +136,3 @@ options: - 'i_code_review_merge_request_widget_license_compliance_expand_success' - 'i_code_review_merge_request_widget_license_compliance_expand_warning' - 'i_code_review_merge_request_widget_license_compliance_expand_failed' - - 'i_code_review_merge_request_widget_load_performance_view' - - 'i_code_review_merge_request_widget_load_performance_full_report_clicked' - - 'i_code_review_merge_request_widget_load_performance_expand' - - 'i_code_review_merge_request_widget_load_performance_expand_success' - - 'i_code_review_merge_request_widget_load_performance_expand_warning' - - 'i_code_review_merge_request_widget_load_performance_expand_failed' diff --git a/config/metrics/counts_7d/20210216184452_code_review_total_unique_counts_weekly.yml b/config/metrics/counts_7d/20210216184452_code_review_total_unique_counts_weekly.yml index d82545a035f..420926c825c 100644 --- a/config/metrics/counts_7d/20210216184452_code_review_total_unique_counts_weekly.yml +++ b/config/metrics/counts_7d/20210216184452_code_review_total_unique_counts_weekly.yml @@ -75,12 +75,6 @@ options: - i_code_review_merge_request_widget_license_compliance_warning - i_code_review_merge_request_widget_license_compliance_full_report_clicked - i_code_review_merge_request_widget_license_compliance_view - - i_code_review_merge_request_widget_load_performance_expand - - i_code_review_merge_request_widget_load_performance_expand_failed - - i_code_review_merge_request_widget_load_performance_expand_success - - i_code_review_merge_request_widget_load_performance_warning - - i_code_review_merge_request_widget_load_performance_full_report_clicked - - i_code_review_merge_request_widget_load_performance_view - i_code_review_mr_diffs - i_code_review_mr_single_file_diffs - i_code_review_mr_with_invalid_approvers diff --git a/config/metrics/counts_7d/20210427103328_code_review_group_monthly_active_users.yml b/config/metrics/counts_7d/20210427103328_code_review_group_monthly_active_users.yml index 465664d0f8c..f29f4d0cf28 100644 --- a/config/metrics/counts_7d/20210427103328_code_review_group_monthly_active_users.yml +++ b/config/metrics/counts_7d/20210427103328_code_review_group_monthly_active_users.yml @@ -134,9 +134,3 @@ options: - 'i_code_review_merge_request_widget_license_compliance_expand_success' - 'i_code_review_merge_request_widget_license_compliance_expand_warning' - 'i_code_review_merge_request_widget_license_compliance_expand_failed' - - 'i_code_review_merge_request_widget_load_performance_view' - - 'i_code_review_merge_request_widget_load_performance_full_report_clicked' - - 'i_code_review_merge_request_widget_load_performance_expand' - - 'i_code_review_merge_request_widget_load_performance_expand_success' - - 'i_code_review_merge_request_widget_load_performance_expand_warning' - - 'i_code_review_merge_request_widget_load_performance_expand_failed' diff --git a/config/metrics/counts_7d/20210427103407_code_review_category_monthly_active_users.yml b/config/metrics/counts_7d/20210427103407_code_review_category_monthly_active_users.yml index 984cdb976ba..e40e7c7f19a 100644 --- a/config/metrics/counts_7d/20210427103407_code_review_category_monthly_active_users.yml +++ b/config/metrics/counts_7d/20210427103407_code_review_category_monthly_active_users.yml @@ -132,9 +132,3 @@ options: - 'i_code_review_merge_request_widget_license_compliance_expand_success' - 'i_code_review_merge_request_widget_license_compliance_expand_warning' - 'i_code_review_merge_request_widget_license_compliance_expand_failed' - - 'i_code_review_merge_request_widget_load_performance_view' - - 'i_code_review_merge_request_widget_load_performance_full_report_clicked' - - 'i_code_review_merge_request_widget_load_performance_expand' - - 'i_code_review_merge_request_widget_load_performance_expand_success' - - 'i_code_review_merge_request_widget_load_performance_expand_warning' - - 'i_code_review_merge_request_widget_load_performance_expand_failed' diff --git a/doc/user/application_security/dast_api/index.md b/doc/user/application_security/dast_api/index.md index 996338d54c4..9e0d174e8ee 100644 --- a/doc/user/application_security/dast_api/index.md +++ b/doc/user/application_security/dast_api/index.md @@ -1046,6 +1046,8 @@ can be added, removed, and modified by creating a custom configuration. |[`DAST_API_EXCLUDE_URLS`](#exclude-urls) | Exclude API URL from testing. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/357195) in GitLab 14.10. | |[`DAST_API_EXCLUDE_PARAMETER_ENV`](#exclude-parameters) | JSON string containing excluded parameters. | |[`DAST_API_EXCLUDE_PARAMETER_FILE`](#exclude-parameters) | Path to a JSON file containing excluded parameters. | +|[`DAST_API_REQUEST_HEADERS`](#request-headers) | A comma-separated (`,`) list of headers to include on each scan request. Consider using `DAST_API_REQUEST_HEADERS_BASE64` when storing secret header values in a [masked variable](../../../ci/variables/index.md#mask-a-cicd-variable), which has character set restrictions. | +|[`DAST_API_REQUEST_HEADERS_BASE64`](#request-headers) | A comma-separated (`,`) list of headers to include on each scan request, Base64-encoded. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/378440) in GitLab 15.6. | |[`DAST_API_OPENAPI`](#openapi-specification) | OpenAPI specification file or URL. | |[`DAST_API_OPENAPI_RELAXED_VALIDATION`](#openapi-specification) | Relax document validation. Default is disabled. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/345950) in GitLab 14.7. | |[`DAST_API_OPENAPI_ALL_MEDIA_TYPES`](#openapi-specification) | Use all supported media types instead of one when generating requests. Causes test duration to be longer. Default is disabled. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/333304) in GitLab 14.10. | @@ -1488,6 +1490,61 @@ variables: In the previous sample, you could use the script `user-pre-scan-set-up.sh` to also install new runtimes or applications that later on you could use in our overrides command. +## Request Headers + +The request headers feature lets you specify fixed values for the headers during the scan session. For example, you can use the configuration variable `DAST_API_REQUEST_HEADERS` to set a fixed value in the `Cache-Control` header. If the headers you need to set include sensitive values like the `Authorization` header, use the [masked variable](../../../ci/variables/index.md#mask-a-cicd-variable) feature along with the [variable `DAST_API_REQUEST_HEADERS_BASE64`](#base64). + +Note that if the `Authorization` header or any other header needs to get updated while the scan is in progress, consider using the [overrides](#overrides) feature. + +The variable `DAST_API_REQUEST_HEADERS` lets you specify a comma-separated (`,`) list of headers. These headers are included on each request that the scanner performs. Each header entry in the list consists of a name followed by a colon (`:`) and then by its value. Whitespace before the key or value is ignored. For example, to declare a header name `Cache-Control` with the value `max-age=604800`, the header entry is `Cache-Control: max-age=604800`. To use two headers, `Cache-Control: max-age=604800` and `Age: 100`, set `DAST_API_REQUEST_HEADERS` variable to `Cache-Control: max-age=604800, Age: 100`. + +The order in which the different headers are provided into the variable `DAST_API_REQUEST_HEADERS` does not affect the result. Setting `DAST_API_REQUEST_HEADERS` to `Cache-Control: max-age=604800, Age: 100` produces the same result as setting it to `Age: 100, Cache-Control: max-age=604800`. + +### Base64 + +The `DAST_API_REQUEST_HEADERS_BASE64` variable accepts the same list of headers as `DAST_API_REQUEST_HEADERS`, with the only difference that the entire value of the variable must be Base64-encoded. For example, to set `DAST_API_REQUEST_HEADERS_BASE64` variable to `Authorization: QmVhcmVyIFRPS0VO, Cache-control: bm8tY2FjaGU=`, ensure you convert the list to its Base64 equivalent: `QXV0aG9yaXphdGlvbjogUW1WaGNtVnlJRlJQUzBWTywgQ2FjaGUtY29udHJvbDogYm04dFkyRmphR1U9`, and the Base64-encoded value must be used. This is useful when storing secret header values in a [masked variable](../../../ci/variables/index.md#mask-a-cicd-variable), which has character set restrictions. + +WARNING: +Base64 is used to support the [masked variable](../../../ci/variables/index.md#mask-a-cicd-variable) feature. Base64 encoding is not by itself a security measure, because sensitive values can be easily decoded. + +### Example: Adding a list of headers on each request using plain text + +In the following example of a `.gitlab-ci.yml`, `DAST_API_REQUEST_HEADERS` configuration variable is set to provide two header values as explained in [request headers](#request-headers). + +```yaml +stages: + - dast + +include: + - template: DAST-API.gitlab-ci.yml + +variables: + DAST_API_PROFILE: Quick + DAST_API_OPENAPI: test-api-specification.json + DAST_API_TARGET_URL: http://test-deployment/ + DAST_API_REQUEST_HEADERS: 'Cache-control: no-cache, Save-Data: on' +``` + +### Example: Using a masked CI/CD variable + +The following `.gitlab-ci.yml` sample assumes the [masked variable](../../../ci/variables/index.md#mask-a-cicd-variable) `SECRET_REQUEST_HEADERS_BASE64` is defined as a [group or instance level CI/CD variable defined in the UI](../../../ci/variables/index.md#add-a-cicd-variable-to-an-instance). The value of `SECRET_REQUEST_HEADERS_BASE64` is set to `WC1BQ01FLVNlY3JldDogc31jcnt0ISwgWC1BQ01FLVRva2VuOiA3MDVkMTZmNWUzZmI=`, which is the Base64-encoded text version of `X-ACME-Secret: s3cr3t!, X-ACME-Token: 705d16f5e3fb`. Then, it can be used as follows: + +```yaml +stages: + - dast + +include: + - template: DAST-API.gitlab-ci.yml + +variables: + DAST_API_PROFILE: Quick + DAST_API_OPENAPI: test-api-specification.json + DAST_API_TARGET_URL: http://test-deployment/ + DAST_API_REQUEST_HEADERS_BASE64: $SECRET_REQUEST_HEADERS_BASE64 +``` + +Consider using `DAST_API_REQUEST_HEADERS_BASE64` when storing secret header values in a [masked variable](../../../ci/variables/index.md#mask-a-cicd-variable), which has character set restrictions. + ## Exclude Paths > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/211892) in GitLab 14.0. diff --git a/doc/user/project/releases/index.md b/doc/user/project/releases/index.md index da00be361f3..34cb8e9e092 100644 --- a/doc/user/project/releases/index.md +++ b/doc/user/project/releases/index.md @@ -184,20 +184,20 @@ is not available. ## Edit a release -Only users with at least the Developer role can edit releases. -Read more about [Release permissions](#release-permissions). +To edit the details of a release after it's created, you can use the +[Update a release API](../../../api/releases/index.md#update-a-release) or the UI. -To edit the details of a release: +Prerequisites: + +- You must have at least the Developer role. + +In the UI: 1. On the left sidebar, select **Deployments > Releases**. 1. In the top-right corner of the release you want to modify, select **Edit this release** (the pencil icon). 1. On the **Edit Release** page, change the release's details. 1. Select **Save changes**. -You can edit the release title, notes, associated milestones, and asset links. -To change the release date use the -[Releases API](../../../api/releases/index.md#update-a-release). - ## Delete a release > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/213862) in GitLab 15.2 diff --git a/doc/user/project/service_desk.md b/doc/user/project/service_desk.md index 199f25f1122..968a419ab52 100644 --- a/doc/user/project/service_desk.md +++ b/doc/user/project/service_desk.md @@ -360,12 +360,17 @@ does not count toward the license limit count. ### Moving a Service Desk issue +> [Changed](https://gitlab.com/gitlab-org/gitlab/-/issues/372246) in GitLab 15.7: customers continue receiving notifications when a Service Desk issue is moved. + Service Desk issues can be moved like any other issue in GitLab. You can move a Service Desk issue the same way you [move a regular issue](issues/managing_issues.md#move-an-issue) in GitLab. -If a Service Desk issue is moved to a different project the customer who created the issue stops receiving emails. +If a Service Desk issue is moved to a different project with Service Desk enabled, +the customer who created the issue continues to receive email notifications. +Because a moved issue is first closed, then copied, the customer is considered to be a participant +in both issues. They continue to receive any notifications in the old issue and the new one. ## Troubleshooting Service Desk diff --git a/lib/gitlab/usage_data_counters/merge_request_widget_extension_counter.rb b/lib/gitlab/usage_data_counters/merge_request_widget_extension_counter.rb index 93b7b7dd749..f88bbc41c70 100644 --- a/lib/gitlab/usage_data_counters/merge_request_widget_extension_counter.rb +++ b/lib/gitlab/usage_data_counters/merge_request_widget_extension_counter.rb @@ -5,8 +5,7 @@ module Gitlab class MergeRequestWidgetExtensionCounter < BaseCounter KNOWN_EVENTS = %w[view full_report_clicked expand expand_success expand_warning expand_failed].freeze PREFIX = 'i_code_review_merge_request_widget' - WIDGETS = %w[accessibility code_quality license_compliance load_performance status_checks terraform test_summary - metrics].freeze + WIDGETS = %w[accessibility code_quality license_compliance status_checks terraform test_summary metrics].freeze class << self private diff --git a/qa/qa/specs/features/browser_ui/3_create/merge_request/rebase_merge_request_spec.rb b/qa/qa/specs/features/browser_ui/3_create/merge_request/rebase_merge_request_spec.rb index 330cae575e4..236af93716f 100644 --- a/qa/qa/specs/features/browser_ui/3_create/merge_request/rebase_merge_request_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/merge_request/rebase_merge_request_spec.rb @@ -27,7 +27,7 @@ module QA merge_request.visit! Page::MergeRequest::Show.perform do |mr_page| - expect(mr_page).to have_content('Merge blocked: the source branch must be rebased onto the target branch.') + expect(mr_page).to have_content('Merge blocked: the source branch must be rebased onto the target branch.', wait: 20) expect(mr_page).to be_fast_forward_not_possible expect(mr_page).not_to have_merge_button expect(merge_request.project.commits.size).to eq(2) diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb index a7996c5266c..31e297e5773 100644 --- a/spec/controllers/projects/issues_controller_spec.rb +++ b/spec/controllers/projects/issues_controller_spec.rb @@ -141,18 +141,32 @@ RSpec.describe Projects::IssuesController do project.add_developer(user) end - it "returns issue attributes" do - participants = create_list(:issue_email_participant, 2, issue: issue) + context 'issue email participants' do + context 'when issue is confidential' do + let(:issue) { create(:issue, project: project, confidential: true) } + let!(:participants) { create_list(:issue_email_participant, 2, issue: issue) } - get :show, params: { namespace_id: project.namespace, project_id: project, id: issue.iid }, format: :json + it "returns issue email participants" do + get :show, params: { namespace_id: project.namespace, project_id: project, id: issue.iid }, format: :json - expect(response).to have_gitlab_http_status(:ok) - expect(json_response).to include( - 'issue_email_participants' => contain_exactly( - { "email" => participants[0].email }, { "email" => participants[1].email } - ), - 'type' => 'ISSUE' - ) + expect(response).to have_gitlab_http_status(:ok) + expect(json_response).to include( + 'issue_email_participants' => contain_exactly( + { "email" => participants[0].email }, { "email" => participants[1].email } + ), + 'type' => 'ISSUE' + ) + end + end + + context 'when issue is not confidential' do + it "returns empty email participants" do + get :show, params: { namespace_id: project.namespace, project_id: project, id: issue.iid }, format: :json + + expect(response).to have_gitlab_http_status(:ok) + expect(json_response).to include('issue_email_participants' => []) + end + end end context 'when issue is not a task and work items feature flag is enabled' do diff --git a/spec/features/projects/issues/email_participants_spec.rb b/spec/features/projects/issues/email_participants_spec.rb index 3ffe0a5ced8..4dedbff608e 100644 --- a/spec/features/projects/issues/email_participants_spec.rb +++ b/spec/features/projects/issues/email_participants_spec.rb @@ -2,16 +2,15 @@ require 'spec_helper' -RSpec.describe 'viewing an issue', :js do +RSpec.describe 'viewing an issue', :js, feature_category: :issue_email_participants do let_it_be(:user) { create(:user) } let_it_be(:project) { create(:project, :public) } - let_it_be(:issue) { create(:issue, project: project) } + let_it_be_with_refind(:issue) { create(:issue, project: project) } let_it_be(:note) { create(:note_on_issue, project: project, noteable: issue) } let_it_be(:participants) { create_list(:issue_email_participant, 4, issue: issue) } before do - sign_in(user) - visit project_issue_path(project, issue) + project.add_reporter(user) end shared_examples 'email participants warning' do |selector| @@ -20,15 +19,48 @@ RSpec.describe 'viewing an issue', :js do end end - context 'for a new note' do - it_behaves_like 'email participants warning', '.new-note' + shared_examples 'no email participants warning' do |selector| + it 'does not show email participants warning' do + expect(find(selector)).not_to have_content(", and 1 more will be notified of your comment") + end + end + + context 'when issue is confidential' do + before do + issue.update!(confidential: true) + sign_in(user) + visit project_issue_path(project, issue) + end + + context 'for a new note' do + it_behaves_like 'email participants warning', '.new-note' + end + + context 'for a reply form' do + before do + find('.js-reply-button').click + end + + it_behaves_like 'email participants warning', '.note-edit-form' + end end - context 'for a reply form' do + context 'when issue is not confidential' do before do - find('.js-reply-button').click + sign_in(user) + visit project_issue_path(project, issue) end - it_behaves_like 'email participants warning', '.note-edit-form' + context 'for a new note' do + it_behaves_like 'no email participants warning', '.new-note' + end + + context 'for a reply form' do + before do + find('.js-reply-button').click + end + + it_behaves_like 'no email participants warning', '.note-edit-form' + end end end diff --git a/spec/serializers/issue_entity_spec.rb b/spec/serializers/issue_entity_spec.rb index 6161d4d7ec2..9d53d8bb235 100644 --- a/spec/serializers/issue_entity_spec.rb +++ b/spec/serializers/issue_entity_spec.rb @@ -162,4 +162,24 @@ RSpec.describe IssueEntity do end it_behaves_like 'issuable entity current_user properties' + + context 'when issue has email participants' do + before do + resource.issue_email_participants.create!(email: 'any@email.com') + end + + context 'when issue is confidential' do + it 'returns email participants' do + resource.update!(confidential: true) + + expect(subject[:issue_email_participants]).to match_array([{ email: "any@email.com" }]) + end + end + + context 'when issue is not confidential' do + it 'returns empty array' do + expect(subject[:issue_email_participants]).to be_empty + end + end + end end diff --git a/spec/services/issues/move_service_spec.rb b/spec/services/issues/move_service_spec.rb index 655c5085fdc..324b2aa9fe2 100644 --- a/spec/services/issues/move_service_spec.rb +++ b/spec/services/issues/move_service_spec.rb @@ -508,4 +508,25 @@ RSpec.describe Issues::MoveService do end end end + + context 'copying email participants' do + let!(:participant1) { create(:issue_email_participant, email: 'user1@example.com', issue: old_issue) } + let!(:participant2) { create(:issue_email_participant, email: 'user2@example.com', issue: old_issue) } + let!(:participant3) { create(:issue_email_participant, email: 'other_project_customer@example.com') } + + include_context 'user can move issue' + + subject(:new_issue) do + move_service.execute(old_issue, new_project) + end + + it 'copies moved issue email participants' do + new_issue + + expect(participant1.reload.issue).to eq(old_issue) + expect(participant2.reload.issue).to eq(old_issue) + expect(new_issue.issue_email_participants.pluck(:email)) + .to match_array([participant1.email, participant2.email]) + end + end end |