From fb8ebc84b8a6a8e14aace8db94f5726b919eeaed Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Wed, 24 Jul 2024 03:10:24 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- .gitlab/issue_templates/Deprecations.md | 85 ++++++++++--------- app/assets/stylesheets/components/avatar.scss | 10 +-- doc/administration/object_storage.md | 4 +- doc/administration/secure_files.md | 37 ++++++-- doc/ci/jobs/index.md | 4 + doc/subscriptions/subscription-add-ons.md | 2 +- doc/user/gitlab_duo/experiments.md | 24 ------ doc/user/gitlab_duo/index.md | 15 +++- doc/user/gitlab_duo/use_cases.md | 4 +- doc/user/gitlab_duo_chat/examples.md | 31 ++++++- gems/csv_builder/lib/csv_builder.rb | 10 ++- gems/csv_builder/lib/csv_builder/builder.rb | 24 ++++-- .../csv_builder/spec/csv_builder/gzip_spec.rb | 2 +- .../spec/csv_builder/stream_spec.rb | 2 +- gems/csv_builder/spec/csv_builder_spec.rb | 40 ++++++++- .../DAST-Default-Branch-Deploy.gitlab-ci.yml | 2 +- .../ci/templates/Jobs/Deploy.gitlab-ci.yml | 2 +- .../Jobs/Deploy.latest.gitlab-ci.yml | 2 +- .../partitioning/partition_manager.rb | 14 +++ lib/gitlab/pagination/gitaly_keyset_pager.rb | 6 +- spec/graphql/mutations/groups/update_spec.rb | 7 +- .../timeline_event/create_spec.rb | 10 ++- .../timeline_event/destroy_spec.rb | 9 +- .../timeline_event/promote_from_note_spec.rb | 9 +- .../timeline_event/update_spec.rb | 11 ++- .../timeline_event_tag/create_spec.rb | 9 +- spec/graphql/mutations/issues/create_spec.rb | 8 +- .../mutations/issues/link_alerts_spec.rb | 5 +- spec/graphql/mutations/issues/move_spec.rb | 9 +- .../mutations/issues/set_assignees_spec.rb | 8 +- .../mutations/issues/set_confidential_spec.rb | 7 +- .../mutations/issues/set_due_date_spec.rb | 8 +- .../issues/set_escalation_status_spec.rb | 8 +- .../mutations/issues/set_locked_spec.rb | 8 +- .../mutations/issues/set_severity_spec.rb | 8 +- .../mutations/issues/unlink_alert_spec.rb | 6 +- spec/graphql/mutations/issues/update_spec.rb | 6 +- spec/graphql/mutations/labels/create_spec.rb | 8 +- .../members/bulk_update_base_spec.rb | 4 +- .../mutations/merge_requests/accept_spec.rb | 15 ++-- .../mutations/merge_requests/create_spec.rb | 11 +-- .../merge_requests/set_assignees_spec.rb | 7 +- .../merge_requests/set_draft_spec.rb | 7 +- .../merge_requests/set_labels_spec.rb | 8 +- .../merge_requests/set_locked_spec.rb | 8 +- .../merge_requests/set_milestone_spec.rb | 8 +- .../merge_requests/set_reviewers_spec.rb | 8 +- .../mutations/merge_requests/update_spec.rb | 6 +- .../pagination/gitaly_keyset_pager_spec.rb | 25 ++++++ .../resolves_subscription_shared_examples.rb | 12 ++- .../set_assignees_shared_examples.rb | 8 +- ...metrics_instrumentation_shared_examples.rb | 19 +++++ 52 files changed, 434 insertions(+), 176 deletions(-) diff --git a/.gitlab/issue_templates/Deprecations.md b/.gitlab/issue_templates/Deprecations.md index 30d01c5f5c4..f6152709828 100644 --- a/.gitlab/issue_templates/Deprecations.md +++ b/.gitlab/issue_templates/Deprecations.md @@ -1,6 +1,5 @@ -For guidance on the overall deprecations, removals and breaking changes workflow, please visit [Breaking changes, deprecations, and removing features](https://about.gitlab.com/handbook/product/gitlab-the-product/#deprecations-removals-and-breaking-changes) - + ### Deprecation Summary @@ -15,12 +14,17 @@ The description of the deprecation should state what actions the user should tak --> -### Breaking Change +### Breaking Change? + + + ### Affected Topology -### Checklists - -**Labels** - -- [ ] This issue is labeled ~deprecation, and with the relevant `~devops::`, `~group::`, and `~Category:` labels. -- [ ] This issue is labeled ~"breaking change" if the removal of the deprecated item will be a [breaking change](https://about.gitlab.com/handbook/product/gitlab-the-product/#examples-of-breaking-changes). - -**Timeline** - -Please add links to the relevant merge requests. - -- As soon as possible, but no later than the third milestone preceding the major release (for example, given the following release schedule: `14.8, 14.9, 14.10, 15.0` – `14.8` is the third milestone preceding the major release): - - [ ] A [deprecation announcement entry](https://about.gitlab.com/handbook/marketing/blog/release-posts/#creating-the-announcement) has been created so the deprecation will appear in release posts and on the [general deprecation page](https://docs.gitlab.com/ee/update/deprecations). - - [ ] Documentation has been updated to mark the feature as [deprecated](https://docs.gitlab.com/ee/development/documentation/versions.html#deprecations-and-removals). -- [ ] On or before the major milestone: A [removal entry](https://about.gitlab.com/handbook/marketing/blog/release-posts/#creating-the-announcement-1) has been created so the removal will appear on the [removals by milestones](https://docs.gitlab.com/ee/update/removals) page and be announced in the release post. -- On the major milestone: - - [ ] The deprecated item has been removed. - - [ ] If the removal of the deprecated item is a [breaking change](https://about.gitlab.com/handbook/product/gitlab-the-product/#examples-of-breaking-changes), the merge request is labeled ~"breaking change". - -**Mentions** - -- [ ] Your stage's stable counterparts have been `@mentioned` on this issue. For example, Customer Support, Customer Success (Technical Account Manager), Product Marketing Manager. - - To see who the stable counterparts are for a product team visit [product categories](https://about.gitlab.com/handbook/product/categories/) - - If there is no stable counterpart listed for Sales/CS please mention `@timtams` - - If there is no stable counterpart listed for Support please mention `@gitlab-com/support/managers` - - If there is no stable counterpart listed for Marketing please mention `@cfoster3` -- [ ] Your GPM has been `@mentioned` so that they are aware of planned deprecations. The goal is to have reviews happen at least two releases before the final removal of the feature or introduction of a breaking change. + +/label ~"GitLab Free" ~"GitLab Premium" ~"GitLab Ultimate" ### Deprecation Milestone - + ### Planned Removal Milestone - + ### Links @@ -83,19 +62,47 @@ issues and MRs related to this deprecation/removal to this issue. This can inclu issues that were created ahead of time, and the MRs doing the actual deprecation/removal work. --> +### Checklists + +
Click to expand + +**Labels** + + +/label ~devops:: ~group: ~Category: + +- [ ] This issue is labeled ~deprecation, and with the relevant `~devops::`, `~group::`, and `~Category:` labels. +- [ ] This issue is labeled ~"breaking change" if the removal of the deprecated item will be a [breaking change](https://about.gitlab.com/handbook/product/gitlab-the-product/#examples-of-breaking-changes). + +**Timeline** + +Please add links to the relevant merge requests. + +- As soon as possible, but no later than the third milestone preceding the major release (for example, given the following release schedule: `14.8, 14.9, 14.10, 15.0` – `14.8` is the third milestone preceding the major release): + - [ ] A [deprecation announcement entry](https://about.gitlab.com/handbook/marketing/blog/release-posts/#creating-the-announcement) has been created so the deprecation will appear in release posts and on the [general deprecation page](https://docs.gitlab.com/ee/update/deprecations). + - [ ] Documentation has been updated to mark the feature as [deprecated](https://docs.gitlab.com/ee/development/documentation/versions.html#deprecations-and-removals). +- On the major milestone: + - [ ] The deprecated item has been removed. + - [ ] If the removal of the deprecated item is a [breaking change](https://about.gitlab.com/handbook/product/gitlab-the-product/#examples-of-breaking-changes), the merge request is labeled ~"breaking change". + +**Mentions** + +- [ ] Your stage's stable counterparts have been `@mentioned` on this issue. For example, Customer Support, Customer Success (Technical Account Manager), Product Marketing Manager. + - To see who the stable counterparts are for a product team visit [product categories](https://handbook.gitlab.com/handbook/product/categories/) + - If there is no stable counterpart listed for Sales/CS please mention `@timtams` + - If there is no stable counterpart listed for Support please mention `@gitlab-com/support/managers` + - If there is no stable counterpart listed for Marketing please mention `@cfoster3` +- [ ] Your GPM or Director has been `@mentioned` so that they are aware of planned deprecations. + +
+ + - -/label ~devops:: ~group: ~Category: - - -/label ~"GitLab Free" ~"GitLab Premium" ~"GitLab Ultimate" - /label ~"deprecation" - diff --git a/app/assets/stylesheets/components/avatar.scss b/app/assets/stylesheets/components/avatar.scss index 110ec90043f..cc8e73f3e83 100644 --- a/app/assets/stylesheets/components/avatar.scss +++ b/app/assets/stylesheets/components/avatar.scss @@ -85,9 +85,9 @@ $avatar-sizes: ( width: 40px; height: 40px; padding: 0; - background: $gray-10; + background: var(--gl-background-color-subtle); overflow: hidden; - box-shadow: inset 0 0 0 1px rgba($gray-950, $gl-avatar-border-opacity); + box-shadow: inset 0 0 0 1px var(--gl-avatar-border-color-default); &.avatar-inline { float: none; @@ -116,8 +116,8 @@ $avatar-sizes: ( .identicon { text-align: center; vertical-align: top; - color: $gray-900; - background-color: $gray-50; + color: var(--gl-text-color-strong); + background-color: var(--gl-avatar-fallback-background-color-neutral); // Sizes @each $size, $size-config in $avatar-sizes { @@ -208,7 +208,7 @@ $avatar-sizes: ( &.user-popover-cannot-merge { .popover-header { - background-color: $orange-50; + background-color: var(--gl-feedback-warning-background-color); } } } diff --git a/doc/administration/object_storage.md b/doc/administration/object_storage.md index ad8a8217536..05762ce89ae 100644 --- a/doc/administration/object_storage.md +++ b/doc/administration/object_storage.md @@ -119,6 +119,7 @@ The following table lists the valid `objects` that can be used: | `dependency_proxy` | [Dependency Proxy](packages/dependency_proxy.md) | | `terraform_state` | [Terraform state files](terraform_state.md) | | `pages` | [Pages](pages/index.md) | +| `ci_secure_files` | [Secure files](secure_files.md) | Within each object type, three parameters can be defined: @@ -166,11 +167,11 @@ supported by the consolidated form, refer to the following guides: | Object storage type | Supported by consolidated form? | |---------------------|------------------------------------------| -| [Secure Files](secure_files.md#using-object-storage) | **{dotted-circle}** No | | [Backups](../administration/backup_restore/backup_gitlab.md#upload-backups-to-a-remote-cloud-storage) | **{dotted-circle}** No | | [Container registry](packages/container_registry.md#use-object-storage) (optional feature) | **{dotted-circle}** No | | [Mattermost](https://docs.mattermost.com/configure/file-storage-configuration-settings.html)| **{dotted-circle}** No | | [Autoscale runner caching](https://docs.gitlab.com/runner/configuration/autoscale.html#distributed-runners-caching) (optional for improved performance) | **{dotted-circle}** No | +| [Secure Files](secure_files.md#using-object-storage) | **{check-circle}** Yes | | [Job artifacts](job_artifacts.md#using-object-storage) including archived job logs | **{check-circle}** Yes | | [LFS objects](lfs/index.md#storing-lfs-objects-in-remote-object-storage) | **{check-circle}** Yes | | [Uploads](uploads.md#using-object-storage) | **{check-circle}** Yes | @@ -483,6 +484,7 @@ The following example uses AWS S3 to enable object storage for all supported ser gitlab_rails['object_store']['objects']['packages']['bucket'] = 'gitlab-packages' gitlab_rails['object_store']['objects']['dependency_proxy']['bucket'] = 'gitlab-dependency-proxy' gitlab_rails['object_store']['objects']['terraform_state']['bucket'] = 'gitlab-terraform-state' + gitlab_rails['object_store']['objects']['ci_secure_files']['bucket'] = 'gitlab-ci-secure-files' gitlab_rails['object_store']['objects']['pages']['bucket'] = 'gitlab-pages' ``` diff --git a/doc/administration/secure_files.md b/doc/administration/secure_files.md index e5d52917d09..51febd8300f 100644 --- a/doc/administration/secure_files.md +++ b/doc/administration/secure_files.md @@ -97,13 +97,14 @@ DETAILS: Instead of storing Secure Files on disk, you should use [one of the supported object storage options](object_storage.md#supported-object-storage-providers). This configuration relies on valid credentials to be configured already. -[Read more about using object storage with GitLab](object_storage.md). +### Consolidated object storage -NOTE: -This feature is not supported by consolidated object storage configuration. -Adding support is proposed in [issue 414673](https://gitlab.com/gitlab-org/gitlab/-/issues/414673). +> - Support for consolidated object storage was [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/149873) in GitLab 17.0. -### Object storage settings +Using the [consolidated form](object_storage.md#configure-a-single-storage-connection-for-all-object-types-consolidated-form) +of the object storage is recommended. + +### Storage-specific object storage The following settings are: @@ -120,7 +121,9 @@ The following settings are: See [the available connection settings for different providers](object_storage.md#configure-the-connection-settings). -**For Linux package installations:** +::Tabs + +:::TabTitle Linux package (Omnibus) 1. Edit `/etc/gitlab/gitlab.rb` and add the following lines, but using the values you want: @@ -147,10 +150,15 @@ See [the available connection settings for different providers](object_storage.m } ``` -1. Save the file and [reconfigure GitLab](restart_gitlab.md#reconfigure-a-linux-package-installation). +1. Save the file and reconfigure GitLab: + + ```shell + sudo gitlab-ctl reconfigure + ``` + 1. [Migrate any existing local states to the object storage](#migrate-to-object-storage). -**For self-compiled installations** +:::TabTitle Self-compiled (source) 1. Edit `/home/git/gitlab/config/gitlab.yml` and add or amend the following lines: @@ -167,9 +175,20 @@ See [the available connection settings for different providers](object_storage.m region: eu-central-1 ``` -1. Save the file and [restart GitLab](restart_gitlab.md#self-compiled-installations) for the changes to take effect. +1. Save the file and restart GitLab: + + ```shell + # For systems running systemd + sudo systemctl restart gitlab.target + + # For systems running SysV init + sudo service gitlab restart + ``` + 1. [Migrate any existing local states to the object storage](#migrate-to-object-storage). +::EndTabs + ### Migrate to object storage > - [Introduced](https://gitlab.com/gitlab-org/incubation-engineering/mobile-devops/readme/-/issues/125) in GitLab 16.1. diff --git a/doc/ci/jobs/index.md b/doc/ci/jobs/index.md index 4eb94a219ee..0b63ed42984 100644 --- a/doc/ci/jobs/index.md +++ b/doc/ci/jobs/index.md @@ -73,6 +73,10 @@ In each place, if you hover over the failed job you can see the reason it failed You can also see the reason it failed on the Job detail page. +### Troubleshoot a failed job with root cause analysis + +You can use root cause analysis in GitLab Duo Chat to [troubleshoot failed CI/CD jobs](../../user/gitlab_duo_chat/examples.md#troubleshoot-failed-cicd-jobs-with-root-cause-analysis). + ## The order of jobs in a pipeline The order of jobs in a pipeline depends on the type of pipeline graph. diff --git a/doc/subscriptions/subscription-add-ons.md b/doc/subscriptions/subscription-add-ons.md index d285848fc49..b196177c0f8 100644 --- a/doc/subscriptions/subscription-add-ons.md +++ b/doc/subscriptions/subscription-add-ons.md @@ -116,7 +116,7 @@ Prerequisites: - You must be an administrator. 1. Sign in to the [GitLab Customers Portal](https://customers.gitlab.com/). -1. On the **GitLab Duo Pro** section of your subscription card click **Add seats** button. +1. On the **GitLab Duo Pro** section of your subscription card select **Add seats**. 1. Enter the number of seats. The amount cannot be higher than the number of seats in the subscription. 1. Review the **Purchase summary** section. 1. From the **Payment method** dropdown list, select your payment method. diff --git a/doc/user/gitlab_duo/experiments.md b/doc/user/gitlab_duo/experiments.md index e90d312b8c4..71f36326dcf 100644 --- a/doc/user/gitlab_duo/experiments.md +++ b/doc/user/gitlab_duo/experiments.md @@ -74,30 +74,6 @@ is displayed. Provide feedback on this experimental feature in [issue 416833](https://gitlab.com/gitlab-org/gitlab/-/issues/416833). -## Troubleshoot failed CI/CD jobs with Root cause analysis - -DETAILS: -**Tier:** For a limited time, Ultimate. In the future, [GitLab Duo Enterprise](../../subscriptions/subscription-add-ons.md). -**Offering:** GitLab.com -**Status:** Experiment - -> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/123692) in GitLab 16.2 as an [experiment](../../policy/experiment-beta-support.md#experiment). - -Determine the root cause of a CI/CD job failure by analyzing the logs. - -Prerequisites: - -- You must belong to at least one group with the [experiment and beta features setting](turn_on_off.md#turn-on-beta-and-experimental-features) enabled. -- You must have permission to view the CI/CD job. - -To view root cause analysis: - -1. Open a merge request. -1. On the **Pipelines** tab, select the failed CI/CD job. -1. Above the job output, select **Troubleshoot**. - -An analysis of the reasons for the failure is displayed. - ## Summarize an issue with Issue description generation DETAILS: diff --git a/doc/user/gitlab_duo/index.md b/doc/user/gitlab_duo/index.md index eac7aad5801..a5aaa4e58c4 100644 --- a/doc/user/gitlab_duo/index.md +++ b/doc/user/gitlab_duo/index.md @@ -43,6 +43,19 @@ the [Duo Chat examples](../gitlab_duo_chat_examples.md). For self-managed, the models also depend on your GitLab version. For the most benefit, use the latest GitLab version whenever possible. +#### Root cause analysis + +DETAILS: +**Tier:** Ultimate with [GitLab Duo Enterprise](../../subscriptions/subscription-add-ons.md). +**Offering:** GitLab.com, Self-managed, GitLab Dedicated + +> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/123692) in GitLab 16.2 as an [experiment](../../policy/experiment-beta-support.md#experiment) on GitLab.com. +> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/441681) and moved to GitLab Duo Chat in GitLab 17.3. + +- Helps you determine the root cause for a CI/CD job failure by analyzing the logs. +- LLM: Anthropic's [Claude 3.5 Sonnet](https://console.cloud.google.com/vertex-ai/publishers/anthropic/model-garden/claude-3-5-sonnet) +- [View documentation](../gitlab_duo_chat/examples.md#troubleshoot-failed-cicd-jobs-with-root-cause-analysis) + ### Code Suggestions DETAILS: @@ -190,7 +203,7 @@ DETAILS: - Helps you determine the root cause for a CI/CD job failure by analyzing the logs. - LLM: Vertex AI Codey [`text-bison`](https://console.cloud.google.com/vertex-ai/publishers/google/model-garden/text-bison) -- [View documentation](experiments.md#troubleshoot-failed-cicd-jobs-with-root-cause-analysis). +- [View documentation](../gitlab_duo_chat/examples.md#troubleshoot-failed-cicd-jobs-with-root-cause-analysis). ### Vulnerability resolution diff --git a/doc/user/gitlab_duo/use_cases.md b/doc/user/gitlab_duo/use_cases.md index ced85d70044..db74902c93f 100644 --- a/doc/user/gitlab_duo/use_cases.md +++ b/doc/user/gitlab_duo/use_cases.md @@ -55,8 +55,8 @@ you need to configure CI/CD. Please show a .gitignore and .gitlab-ci.yml configuration for a C# project. ``` -- If your CI/CD job fails, [Root Cause Analysis](../../user/gitlab_duo/experiments.md#troubleshoot-failed-cicd-jobs-with-root-cause-analysis) - can help understand the problem. Alternatively, you can copy the error message into +- If your CI/CD job fails, use root cause analysis to [troubleshoot failed CI/CD jobs](../gitlab_duo_chat/examples.md#troubleshoot-failed-cicd-jobs-with-root-cause-analysis). + Alternatively, you can copy the error message into GitLab Duo Chat, and ask for help: ```markdown diff --git a/doc/user/gitlab_duo_chat/examples.md b/doc/user/gitlab_duo_chat/examples.md index 700b94e1d4f..3ecee5c63f4 100644 --- a/doc/user/gitlab_duo_chat/examples.md +++ b/doc/user/gitlab_duo_chat/examples.md @@ -80,7 +80,35 @@ You can also ask to explain specific job errors by copy-pasting the error messag - `Please explain this CI/CD job error message in the context of a Go project: build.sh: line 14: go command not found` -Alternatively, you can use [root cause analysis in CI/CD](../gitlab_duo/experiments.md#troubleshoot-failed-cicd-jobs-with-root-cause-analysis). +Alternatively, you can use root cause analysis to [troubleshoot failed CI/CD jobs](#troubleshoot-failed-cicd-jobs-with-root-cause-analysis). + +## Troubleshoot failed CI/CD jobs with root cause analysis + +DETAILS: +**Tier:** Ultimate with [GitLab Duo Enterprise](../../subscriptions/subscription-add-ons.md). +**Offering:** GitLab.com, Self-managed, GitLab Dedicated + +> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/123692) in GitLab 16.2 as an [experiment](../../policy/experiment-beta-support.md#experiment) on GitLab.com. +> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/441681) and moved to GitLab Duo Chat in GitLab 17.3. + +You can ask GitLab Duo Chat to determine the root cause of a CI/CD job failure by analyzing the logs. + +Prerequisites: + +- Have permission to view the CI/CD job. +- Have a paid GitLab Duo Enterprise seat. + +To troubleshoot a failed CI/CD job: + +1. On the left sidebar, select **Search or go to** and find your project. +1. Select **Build > Jobs**. +1. Select the failed CI/CD job. +1. From the job log page, do one of the following: + + - Above the job log, select **Troubleshoot**. + - Open GitLab Duo Chat and type `/troubleshoot`. + +An analysis of the reasons for the failure and an example fix is displayed. ## Explain a vulnerability @@ -276,4 +304,5 @@ Use the following commands to quickly accomplish specific tasks. | /explain | [Explain code](../gitlab_duo_chat/examples.md#explain-code-in-the-ide) | | /vulnerability_explain | [Explain current vulnerability](../gitlab_duo/index.md#vulnerability-explanation) | | /refactor | [Refactor the code](../gitlab_duo_chat/examples.md#refactor-code-in-the-ide) | +| /troubleshoot | [Troubleshoot failed CI/CD jobs with root cause analysis](#troubleshoot-failed-cicd-jobs-with-root-cause-analysis) | | /fix | [Fix the code](../gitlab_duo_chat/examples.md#fix-code-in-the-ide) | diff --git a/gems/csv_builder/lib/csv_builder.rb b/gems/csv_builder/lib/csv_builder.rb index 86b682939dc..0f5aa538633 100644 --- a/gems/csv_builder/lib/csv_builder.rb +++ b/gems/csv_builder/lib/csv_builder.rb @@ -28,11 +28,17 @@ module CsvBuilder # * +collection+ - The data collection to be used # * +header_to_value_hash+ - A hash of 'Column Heading' => 'value_method'. # * +associations_to_preload+ - An array of records to preload with a batch of records. + # * +replace_newlines+ - default: false - If true, replaces newline characters with a literal "\n" # # The value method will be called once for each object in the collection, to # determine the value for that row. It can either be the name of a method on # the object, or a lamda to call passing in the object. - def self.new(collection, header_to_value_hash, associations_to_preload = []) - CsvBuilder::Builder.new(collection, header_to_value_hash, associations_to_preload) + def self.new(collection, header_to_value_hash, associations_to_preload = [], replace_newlines: false) + CsvBuilder::Builder.new( + collection, + header_to_value_hash, + associations_to_preload, + replace_newlines: replace_newlines + ) end end diff --git a/gems/csv_builder/lib/csv_builder/builder.rb b/gems/csv_builder/lib/csv_builder/builder.rb index 51aaf2132cf..c270db77f84 100644 --- a/gems/csv_builder/lib/csv_builder/builder.rb +++ b/gems/csv_builder/lib/csv_builder/builder.rb @@ -6,12 +6,13 @@ module CsvBuilder attr_reader :rows_written - def initialize(collection, header_to_value_hash, associations_to_preload = []) + def initialize(collection, header_to_value_hash, associations_to_preload = [], replace_newlines: false) @header_to_value_hash = header_to_value_hash @collection = collection @truncated = false @rows_written = 0 @associations_to_preload = associations_to_preload + @replace_newlines = replace_newlines end # Renders the csv to a string @@ -78,13 +79,19 @@ module CsvBuilder def row(object) attributes.map do |attribute| - if attribute.respond_to?(:call) - excel_sanitize(attribute.call(object)) - elsif object.is_a?(Hash) - excel_sanitize(object[attribute]) - else - excel_sanitize(object.public_send(attribute)) # rubocop:disable GitlabSecurity/PublicSend - end + data = if attribute.respond_to?(:call) + attribute.call(object) + elsif object.is_a?(Hash) + object[attribute] + else + object.public_send(attribute) # rubocop:disable GitlabSecurity/PublicSend -- Not user input + end + + next if data.nil? + + data = data.gsub("\n", '\n') if data.is_a?(String) && @replace_newlines + + excel_sanitize(data) end end @@ -104,7 +111,6 @@ module CsvBuilder end def excel_sanitize(line) - return if line.nil? return line unless line.is_a?(String) && line.match?(UNSAFE_EXCEL_PREFIX) ["'", line].join diff --git a/gems/csv_builder/spec/csv_builder/gzip_spec.rb b/gems/csv_builder/spec/csv_builder/gzip_spec.rb index 22462d8dd0a..8b05b0e2337 100644 --- a/gems/csv_builder/spec/csv_builder/gzip_spec.rb +++ b/gems/csv_builder/spec/csv_builder/gzip_spec.rb @@ -7,7 +7,7 @@ RSpec.describe CsvBuilder::Gzip do let(:event_2) { double(title: 'Added sugar', description: 'Just a pinch') } let(:items) { [event_1, event_2] } - subject(:builder) { described_class.new(items, 'Title' => 'title', 'Description' => 'description') } + subject(:builder) { described_class.new(items, { 'Title' => 'title', 'Description' => 'description' }) } describe '#render' do it 'returns yields a tempfile' do diff --git a/gems/csv_builder/spec/csv_builder/stream_spec.rb b/gems/csv_builder/spec/csv_builder/stream_spec.rb index d23e63520af..1866885661e 100644 --- a/gems/csv_builder/spec/csv_builder/stream_spec.rb +++ b/gems/csv_builder/spec/csv_builder/stream_spec.rb @@ -7,7 +7,7 @@ RSpec.describe CsvBuilder::Stream do let(:event_2) { double(title: 'Added sugar', description: 'Just a pinch') } let(:fake_relation) { described_class::FakeRelation.new([event_1, event_2]) } - subject(:builder) { described_class.new(fake_relation, 'Title' => 'title', 'Description' => 'description') } + subject(:builder) { described_class.new(fake_relation, { 'Title' => 'title', 'Description' => 'description' }) } describe '#render' do before do diff --git a/gems/csv_builder/spec/csv_builder_spec.rb b/gems/csv_builder/spec/csv_builder_spec.rb index ccae8365174..94abb0aaa94 100644 --- a/gems/csv_builder/spec/csv_builder_spec.rb +++ b/gems/csv_builder/spec/csv_builder_spec.rb @@ -7,7 +7,7 @@ RSpec.describe CsvBuilder do end let(:subject) do - described_class.new(enumerable, **header_to_value_hash) + described_class.new(enumerable, header_to_value_hash) end shared_examples 'csv builder examples' do @@ -113,6 +113,41 @@ RSpec.describe CsvBuilder do end end + shared_examples 'builder that replaces newlines' do + let(:object) { double(title: "title", description: "Line 1\n\nLine 2") } + let(:header_to_value_hash) { { 'Title' => 'title', 'Description' => 'description' } } + let(:items) { [object] } + + it 'does not replace newlines by default' do + expect(csv_data).to eq("Title,Description\ntitle,\"Line 1\n\nLine 2\"\n") + end + + context 'when replace_newlines is set to true' do + let(:subject) { described_class.new(enumerable, header_to_value_hash, replace_newlines: true) } + + it 'replaces newlines with a literal "\n"' do + expect(csv_data).to eq("Title,Description\ntitle,Line 1\\n\\nLine 2\n") + end + end + + context 'when line is nil' do + let(:object) { double(title: "title", description: nil) } + + it 'gracefully generates CSV' do + expect(csv_data).to eq("Title,Description\ntitle,\n") + end + end + + context 'when data is not a string' do + let(:object) { double(title: "title", created_at: Date.new(2001, 2, 3)) } + let(:header_to_value_hash) { { 'Title' => 'title', 'Created At' => 'created_at' } } + + it 'gracefully generates CSV' do + expect(csv_data).to eq("Title,Created At\ntitle,2001-02-03\n") + end + end + end + context 'when ActiveRecord::Relation like object is given' do let(:object) { double(question: :answer) } let(:enumerable) { described_class::FakeRelation.new(items) } @@ -129,6 +164,7 @@ RSpec.describe CsvBuilder do it_behaves_like 'csv builder examples' it_behaves_like 'excel sanitization' + it_behaves_like 'builder that replaces newlines' it_behaves_like 'csv builder with truncation ability' do let(:big_object) { double(question: 'Long' * 1024) } let(:question_value) { big_object.question } @@ -141,6 +177,7 @@ RSpec.describe CsvBuilder do it_behaves_like 'csv builder examples' it_behaves_like 'excel sanitization' + it_behaves_like 'builder that replaces newlines' it_behaves_like 'csv builder with truncation ability' do let(:big_object) { double(question: 'Long' * 1024) } let(:question_value) { big_object.question } @@ -155,6 +192,7 @@ RSpec.describe CsvBuilder do end it_behaves_like 'csv builder examples' + it_behaves_like 'builder that replaces newlines' it_behaves_like 'excel sanitization' do let(:dangerous_title) { { title: "=cmd|' /C calc'!A0 title", description: "*safe_desc" } } diff --git a/lib/gitlab/ci/templates/Jobs/DAST-Default-Branch-Deploy.gitlab-ci.yml b/lib/gitlab/ci/templates/Jobs/DAST-Default-Branch-Deploy.gitlab-ci.yml index 6c23aa5deb0..d31b4f8eb4d 100644 --- a/lib/gitlab/ci/templates/Jobs/DAST-Default-Branch-Deploy.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Jobs/DAST-Default-Branch-Deploy.gitlab-ci.yml @@ -1,5 +1,5 @@ variables: - DAST_AUTO_DEPLOY_IMAGE_VERSION: 'v2.99.0' + DAST_AUTO_DEPLOY_IMAGE_VERSION: 'v2.100.0' .dast-auto-deploy: image: "${CI_TEMPLATE_REGISTRY_HOST}/gitlab-org/cluster-integration/auto-deploy-image:${DAST_AUTO_DEPLOY_IMAGE_VERSION}" diff --git a/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml b/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml index 921111120ef..b04944183a8 100644 --- a/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml @@ -1,5 +1,5 @@ variables: - AUTO_DEPLOY_IMAGE_VERSION: 'v2.99.0' + AUTO_DEPLOY_IMAGE_VERSION: 'v2.100.0' .auto-deploy: image: "${CI_TEMPLATE_REGISTRY_HOST}/gitlab-org/cluster-integration/auto-deploy-image:${AUTO_DEPLOY_IMAGE_VERSION}" diff --git a/lib/gitlab/ci/templates/Jobs/Deploy.latest.gitlab-ci.yml b/lib/gitlab/ci/templates/Jobs/Deploy.latest.gitlab-ci.yml index 0380ad57919..de3a91bedac 100644 --- a/lib/gitlab/ci/templates/Jobs/Deploy.latest.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Jobs/Deploy.latest.gitlab-ci.yml @@ -1,5 +1,5 @@ variables: - AUTO_DEPLOY_IMAGE_VERSION: 'v2.99.0' + AUTO_DEPLOY_IMAGE_VERSION: 'v2.100.0' .auto-deploy: image: "${CI_TEMPLATE_REGISTRY_HOST}/gitlab-org/cluster-integration/auto-deploy-image:${AUTO_DEPLOY_IMAGE_VERSION}" diff --git a/lib/gitlab/database/partitioning/partition_manager.rb b/lib/gitlab/database/partitioning/partition_manager.rb index 8c892b49d87..45b280b6652 100644 --- a/lib/gitlab/database/partitioning/partition_manager.rb +++ b/lib/gitlab/database/partitioning/partition_manager.rb @@ -13,6 +13,19 @@ module Gitlab MANAGEMENT_LEASE_KEY = 'database_partition_management_%s' RETAIN_DETACHED_PARTITIONS_FOR = 1.week + LOCK_RETRIES_TIMING_CONFIGURATION = [ + [0.1.seconds, 0.05.seconds], + [0.1.seconds, 0.05.seconds], + [0.2.seconds, 0.05.seconds], + [0.3.seconds, 0.10.seconds], + [0.4.seconds, 0.15.seconds], + [0.5.seconds, 2.seconds], + [0.5.seconds, 2.seconds], + [0.5.seconds, 2.seconds], + [0.5.seconds, 2.seconds], + [1.second, 5.seconds] + ].freeze + def initialize(model, connection: nil) @model = model @connection = connection || model.connection @@ -135,6 +148,7 @@ module Gitlab def with_lock_retries(&block) Gitlab::Database::WithLockRetries.new( + timing_configuration: LOCK_RETRIES_TIMING_CONFIGURATION * 2, klass: self.class, logger: Gitlab::AppLogger, connection: connection diff --git a/lib/gitlab/pagination/gitaly_keyset_pager.rb b/lib/gitlab/pagination/gitaly_keyset_pager.rb index f1f3e1a053f..90a315f4e6c 100644 --- a/lib/gitlab/pagination/gitaly_keyset_pager.rb +++ b/lib/gitlab/pagination/gitaly_keyset_pager.rb @@ -70,10 +70,12 @@ module Gitlab finder.execute(gitaly_pagination: true).tap do |records| total = finder.total per_page = params[:per_page].presence || Kaminari.config.default_per_page + total_pages = (total / per_page.to_f).ceil + next_page = total_pages > 1 ? 2 : nil Gitlab::Pagination::OffsetHeaderBuilder.new( - request_context: request_context, per_page: per_page, page: 1, next_page: 2, - total: total, total_pages: (total / per_page) + 1 + request_context: request_context, per_page: per_page, page: 1, next_page: next_page, + total: total, total_pages: total_pages ).execute end end diff --git a/spec/graphql/mutations/groups/update_spec.rb b/spec/graphql/mutations/groups/update_spec.rb index 5227d24af01..10b1052b57a 100644 --- a/spec/graphql/mutations/groups/update_spec.rb +++ b/spec/graphql/mutations/groups/update_spec.rb @@ -2,18 +2,21 @@ require 'spec_helper' -RSpec.describe Mutations::Groups::Update do +RSpec.describe Mutations::Groups::Update, feature_category: :api do + include GraphqlHelpers using RSpec::Parameterized::TableSyntax let_it_be_with_reload(:group) { create(:group) } let_it_be(:user) { create(:user) } let(:params) { { full_path: group.full_path } } + let(:query) { GraphQL::Query.new(empty_schema, document: nil, context: {}, variables: {}) } + let(:context) { GraphQL::Query::Context.new(query: query, values: { current_user: user }) } specify { expect(described_class).to require_graphql_authorizations(:admin_group) } describe '#resolve' do - subject { described_class.new(object: group, context: { current_user: user }, field: nil).resolve(**params) } + subject { described_class.new(object: group, context: context, field: nil).resolve(**params) } shared_examples 'updating the group shared runners setting' do it 'updates the group shared runners setting' do diff --git a/spec/graphql/mutations/incident_management/timeline_event/create_spec.rb b/spec/graphql/mutations/incident_management/timeline_event/create_spec.rb index aab21776a99..a8f752eca98 100644 --- a/spec/graphql/mutations/incident_management/timeline_event/create_spec.rb +++ b/spec/graphql/mutations/incident_management/timeline_event/create_spec.rb @@ -2,7 +2,9 @@ require 'spec_helper' -RSpec.describe Mutations::IncidentManagement::TimelineEvent::Create do +RSpec.describe Mutations::IncidentManagement::TimelineEvent::Create, feature_category: :api do + include GraphqlHelpers + let_it_be(:current_user) { create(:user) } let_it_be(:project) { create(:project) } let_it_be(:incident) { create(:incident, project: project) } @@ -11,6 +13,8 @@ RSpec.describe Mutations::IncidentManagement::TimelineEvent::Create do end let(:args) { { note: 'note', occurred_at: Time.current } } + let(:query) { GraphQL::Query.new(empty_schema, document: nil, context: {}, variables: {}) } + let(:context) { GraphQL::Query::Context.new(query: query, values: { current_user: current_user }) } specify { expect(described_class).to require_graphql_authorizations(:admin_incident_management_timeline_event) } @@ -147,7 +151,7 @@ RSpec.describe Mutations::IncidentManagement::TimelineEvent::Create do private - def mutation_for(project, user) - described_class.new(object: project, context: { current_user: user }, field: nil) + def mutation_for(project, _user) + described_class.new(object: project, context: context, field: nil) end end diff --git a/spec/graphql/mutations/incident_management/timeline_event/destroy_spec.rb b/spec/graphql/mutations/incident_management/timeline_event/destroy_spec.rb index 4dd7b2ccb14..4df08e57af4 100644 --- a/spec/graphql/mutations/incident_management/timeline_event/destroy_spec.rb +++ b/spec/graphql/mutations/incident_management/timeline_event/destroy_spec.rb @@ -2,13 +2,16 @@ require 'spec_helper' -RSpec.describe Mutations::IncidentManagement::TimelineEvent::Destroy do +RSpec.describe Mutations::IncidentManagement::TimelineEvent::Destroy, feature_category: :api do + include GraphqlHelpers let_it_be(:current_user) { create(:user) } let_it_be(:project) { create(:project) } let_it_be(:incident) { create(:incident, project: project) } let(:timeline_event) { create(:incident_management_timeline_event, incident: incident, project: project) } let(:args) { { id: timeline_event.to_global_id } } + let(:query) { GraphQL::Query.new(empty_schema, document: nil, context: {}, variables: {}) } + let(:context) { GraphQL::Query::Context.new(query: query, values: { current_user: current_user }) } specify { expect(described_class).to require_graphql_authorizations(:admin_incident_management_timeline_event) } @@ -60,7 +63,7 @@ RSpec.describe Mutations::IncidentManagement::TimelineEvent::Destroy do private - def mutation_for(project, user) - described_class.new(object: project, context: { current_user: user }, field: nil) + def mutation_for(project, _user) + described_class.new(object: project, context: context, field: nil) end end diff --git a/spec/graphql/mutations/incident_management/timeline_event/promote_from_note_spec.rb b/spec/graphql/mutations/incident_management/timeline_event/promote_from_note_spec.rb index 56514c985ff..e96d41b4212 100644 --- a/spec/graphql/mutations/incident_management/timeline_event/promote_from_note_spec.rb +++ b/spec/graphql/mutations/incident_management/timeline_event/promote_from_note_spec.rb @@ -2,7 +2,8 @@ require 'spec_helper' -RSpec.describe Mutations::IncidentManagement::TimelineEvent::PromoteFromNote do +RSpec.describe Mutations::IncidentManagement::TimelineEvent::PromoteFromNote, feature_category: :api do + include GraphqlHelpers include NotesHelper let_it_be(:current_user) { create(:user) } @@ -15,6 +16,8 @@ RSpec.describe Mutations::IncidentManagement::TimelineEvent::PromoteFromNote do let_it_be(:alert_comment) { create(:note, project: project, noteable: alert) } let(:args) { { note_id: comment.to_global_id.to_s } } + let(:query) { GraphQL::Query.new(empty_schema, document: nil, context: {}, variables: {}) } + let(:context) { GraphQL::Query::Context.new(query: query, values: { current_user: current_user }) } specify { expect(described_class).to require_graphql_authorizations(:admin_incident_management_timeline_event) } @@ -82,7 +85,7 @@ RSpec.describe Mutations::IncidentManagement::TimelineEvent::PromoteFromNote do private - def mutation_for(project, user) - described_class.new(object: project, context: { current_user: user }, field: nil) + def mutation_for(project, _user) + described_class.new(object: project, context: context, field: nil) end end diff --git a/spec/graphql/mutations/incident_management/timeline_event/update_spec.rb b/spec/graphql/mutations/incident_management/timeline_event/update_spec.rb index 9666626f564..8a6f77ce7b9 100644 --- a/spec/graphql/mutations/incident_management/timeline_event/update_spec.rb +++ b/spec/graphql/mutations/incident_management/timeline_event/update_spec.rb @@ -2,7 +2,9 @@ require 'spec_helper' -RSpec.describe Mutations::IncidentManagement::TimelineEvent::Update do +RSpec.describe Mutations::IncidentManagement::TimelineEvent::Update, feature_category: :api do + include GraphqlHelpers + let_it_be(:developer) { create(:user) } let_it_be(:reporter) { create(:user) } let_it_be(:project) { create(:project, developers: developer, reporters: reporter) } @@ -30,6 +32,9 @@ RSpec.describe Mutations::IncidentManagement::TimelineEvent::Update do } end + let(:query) { GraphQL::Query.new(empty_schema, document: nil, context: {}, variables: {}) } + let(:context) { GraphQL::Query::Context.new(query: query, values: { current_user: current_user }) } + let(:note) { 'Updated Note' } let(:timeline_event_id) { GitlabSchema.id_from_object(timeline_event).to_s } let(:occurred_at) { 1.minute.ago } @@ -154,7 +159,7 @@ RSpec.describe Mutations::IncidentManagement::TimelineEvent::Update do private - def mutation_for(user) - described_class.new(object: nil, context: { current_user: user }, field: nil) + def mutation_for(_user) + described_class.new(object: nil, context: context, field: nil) end end diff --git a/spec/graphql/mutations/incident_management/timeline_event_tag/create_spec.rb b/spec/graphql/mutations/incident_management/timeline_event_tag/create_spec.rb index 15216189daf..da49888a191 100644 --- a/spec/graphql/mutations/incident_management/timeline_event_tag/create_spec.rb +++ b/spec/graphql/mutations/incident_management/timeline_event_tag/create_spec.rb @@ -2,11 +2,14 @@ require 'spec_helper' -RSpec.describe Mutations::IncidentManagement::TimelineEventTag::Create do +RSpec.describe Mutations::IncidentManagement::TimelineEventTag::Create, feature_category: :api do + include GraphqlHelpers let_it_be(:current_user) { create(:user) } let_it_be_with_reload(:project) { create(:project, maintainers: current_user) } let(:args) { { name: 'Test tag 1' } } + let(:query) { GraphQL::Query.new(empty_schema, document: nil, context: {}, variables: {}) } + let(:context) { GraphQL::Query::Context.new(query: query, values: { current_user: current_user }) } specify { expect(described_class).to require_graphql_authorizations(:admin_incident_management_timeline_event_tag) } @@ -42,7 +45,7 @@ RSpec.describe Mutations::IncidentManagement::TimelineEventTag::Create do private - def mutation_for(project, user) - described_class.new(object: project, context: { current_user: user }, field: nil) + def mutation_for(project, _user) + described_class.new(object: project, context: context, field: nil) end end diff --git a/spec/graphql/mutations/issues/create_spec.rb b/spec/graphql/mutations/issues/create_spec.rb index 6f2d759d8b9..b350026c231 100644 --- a/spec/graphql/mutations/issues/create_spec.rb +++ b/spec/graphql/mutations/issues/create_spec.rb @@ -2,7 +2,9 @@ require 'spec_helper' -RSpec.describe Mutations::Issues::Create do +RSpec.describe Mutations::Issues::Create, feature_category: :api do + include GraphqlHelpers + let_it_be(:project) { create(:project) } let_it_be(:user) { create(:user) } let_it_be(:assignee1) { create(:user) } @@ -40,7 +42,9 @@ RSpec.describe Mutations::Issues::Create do } end - let(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) } + let(:query) { GraphQL::Query.new(empty_schema, document: nil, context: {}, variables: {}) } + let(:context) { GraphQL::Query::Context.new(query: query, values: { current_user: user }) } + let(:mutation) { described_class.new(object: nil, context: context, field: nil) } let(:mutated_issue) { subject[:issue] } specify { expect(described_class).to require_graphql_authorizations(:create_issue) } diff --git a/spec/graphql/mutations/issues/link_alerts_spec.rb b/spec/graphql/mutations/issues/link_alerts_spec.rb index 38f16a8adac..65d2ecb8a03 100644 --- a/spec/graphql/mutations/issues/link_alerts_spec.rb +++ b/spec/graphql/mutations/issues/link_alerts_spec.rb @@ -3,6 +3,7 @@ require 'spec_helper' RSpec.describe Mutations::Issues::LinkAlerts, feature_category: :incident_management do + include GraphqlHelpers let_it_be(:project) { create(:project) } let_it_be(:guest) { create(:user, guest_of: project) } let_it_be(:developer) { create(:user, developer_of: project) } @@ -10,7 +11,9 @@ RSpec.describe Mutations::Issues::LinkAlerts, feature_category: :incident_manage let_it_be(:alert1) { create(:alert_management_alert, project: project) } let_it_be(:alert2) { create(:alert_management_alert, project: project) } - let(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) } + let(:query) { GraphQL::Query.new(empty_schema, document: nil, context: {}, variables: {}) } + let(:context) { GraphQL::Query::Context.new(query: query, values: { current_user: user }) } + let(:mutation) { described_class.new(object: nil, context: context, field: nil) } specify { expect(described_class).to require_graphql_authorizations(:update_issue, :admin_issue) } diff --git a/spec/graphql/mutations/issues/move_spec.rb b/spec/graphql/mutations/issues/move_spec.rb index c8e9c556a3f..5b4930a3117 100644 --- a/spec/graphql/mutations/issues/move_spec.rb +++ b/spec/graphql/mutations/issues/move_spec.rb @@ -2,12 +2,17 @@ require 'spec_helper' -RSpec.describe Mutations::Issues::Move do +RSpec.describe Mutations::Issues::Move, feature_category: :api do + include GraphqlHelpers + let_it_be(:issue) { create(:issue) } let_it_be(:user) { create(:user) } let_it_be(:target_project) { create(:project) } - subject(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) } + let(:query) { GraphQL::Query.new(empty_schema, document: nil, context: {}, variables: {}) } + let(:context) { GraphQL::Query::Context.new(query: query, values: { current_user: user }) } + + subject(:mutation) { described_class.new(object: nil, context: context, field: nil) } describe '#resolve' do subject(:resolve) { mutation.resolve(project_path: issue.project.full_path, iid: issue.iid, target_project_path: target_project.full_path) } diff --git a/spec/graphql/mutations/issues/set_assignees_spec.rb b/spec/graphql/mutations/issues/set_assignees_spec.rb index 9dc152872a6..6f083cd805b 100644 --- a/spec/graphql/mutations/issues/set_assignees_spec.rb +++ b/spec/graphql/mutations/issues/set_assignees_spec.rb @@ -2,13 +2,17 @@ require 'spec_helper' -RSpec.describe Mutations::Issues::SetAssignees do +RSpec.describe Mutations::Issues::SetAssignees, feature_category: :api do + include GraphqlHelpers context 'when the user does not have permissions' do let_it_be(:issue) { create(:issue) } let_it_be(:user) { create(:user) } let_it_be(:assignee) { create(:user) } - subject(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) } + let(:query) { GraphQL::Query.new(empty_schema, document: nil, context: {}, variables: {}) } + let(:context) { GraphQL::Query::Context.new(query: query, values: { current_user: user }) } + + subject(:mutation) { described_class.new(object: nil, context: context, field: nil) } describe '#resolve' do subject do diff --git a/spec/graphql/mutations/issues/set_confidential_spec.rb b/spec/graphql/mutations/issues/set_confidential_spec.rb index c3269e5c0c0..212a5e20210 100644 --- a/spec/graphql/mutations/issues/set_confidential_spec.rb +++ b/spec/graphql/mutations/issues/set_confidential_spec.rb @@ -2,12 +2,15 @@ require 'spec_helper' -RSpec.describe Mutations::Issues::SetConfidential do +RSpec.describe Mutations::Issues::SetConfidential, feature_category: :api do + include GraphqlHelpers let(:project) { create(:project, :private) } let(:issue) { create(:issue, project: project, assignees: [user]) } let(:user) { create(:user) } + let(:query) { GraphQL::Query.new(empty_schema, document: nil, context: {}, variables: {}) } + let(:context) { GraphQL::Query::Context.new(query: query, values: { current_user: user }) } - subject(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) } + subject(:mutation) { described_class.new(object: nil, context: context, field: nil) } specify { expect(described_class).to require_graphql_authorizations(:update_issue) } diff --git a/spec/graphql/mutations/issues/set_due_date_spec.rb b/spec/graphql/mutations/issues/set_due_date_spec.rb index 83edd670695..54d30e76010 100644 --- a/spec/graphql/mutations/issues/set_due_date_spec.rb +++ b/spec/graphql/mutations/issues/set_due_date_spec.rb @@ -2,12 +2,16 @@ require 'spec_helper' -RSpec.describe Mutations::Issues::SetDueDate do +RSpec.describe Mutations::Issues::SetDueDate, feature_category: :api do + include GraphqlHelpers + let(:issue) { create(:issue, due_date: '2021-05-01') } + let(:query) { GraphQL::Query.new(empty_schema, document: nil, context: {}, variables: {}) } + let(:context) { GraphQL::Query::Context.new(query: query, values: { current_user: user }) } let_it_be(:user) { create(:user) } - subject(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) } + subject(:mutation) { described_class.new(object: nil, context: context, field: nil) } specify { expect(described_class).to require_graphql_authorizations(:update_issue) } diff --git a/spec/graphql/mutations/issues/set_escalation_status_spec.rb b/spec/graphql/mutations/issues/set_escalation_status_spec.rb index f04d396efb8..8039691dd28 100644 --- a/spec/graphql/mutations/issues/set_escalation_status_spec.rb +++ b/spec/graphql/mutations/issues/set_escalation_status_spec.rb @@ -2,14 +2,18 @@ require 'spec_helper' -RSpec.describe Mutations::Issues::SetEscalationStatus do +RSpec.describe Mutations::Issues::SetEscalationStatus, feature_category: :api do + include GraphqlHelpers + let_it_be(:user) { create(:user) } let_it_be(:project) { create(:project) } let_it_be(:issue, reload: true) { create(:incident, project: project) } let_it_be(:escalation_status, reload: true) { create(:incident_management_issuable_escalation_status, issue: issue) } let(:status) { :acknowledged } - let(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) } + let(:query) { GraphQL::Query.new(empty_schema, document: nil, context: {}, variables: {}) } + let(:context) { GraphQL::Query::Context.new(query: query, values: { current_user: user }) } + let(:mutation) { described_class.new(object: nil, context: context, field: nil) } describe '#resolve' do let(:args) { { status: status } } diff --git a/spec/graphql/mutations/issues/set_locked_spec.rb b/spec/graphql/mutations/issues/set_locked_spec.rb index 1a0af0c6c63..6c05088677b 100644 --- a/spec/graphql/mutations/issues/set_locked_spec.rb +++ b/spec/graphql/mutations/issues/set_locked_spec.rb @@ -2,11 +2,15 @@ require 'spec_helper' -RSpec.describe Mutations::Issues::SetLocked do +RSpec.describe Mutations::Issues::SetLocked, feature_category: :api do + include GraphqlHelpers + let_it_be(:issue) { create(:issue) } let_it_be(:user) { create(:user) } + let(:query) { GraphQL::Query.new(empty_schema, document: nil, context: {}, variables: {}) } + let(:context) { GraphQL::Query::Context.new(query: query, values: { current_user: user }) } - subject(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) } + subject(:mutation) { described_class.new(object: nil, context: context, field: nil) } specify { expect(described_class).to require_graphql_authorizations(:update_issue) } diff --git a/spec/graphql/mutations/issues/set_severity_spec.rb b/spec/graphql/mutations/issues/set_severity_spec.rb index f647e2f6cb3..afb14218ea7 100644 --- a/spec/graphql/mutations/issues/set_severity_spec.rb +++ b/spec/graphql/mutations/issues/set_severity_spec.rb @@ -2,13 +2,17 @@ require 'spec_helper' -RSpec.describe Mutations::Issues::SetSeverity do +RSpec.describe Mutations::Issues::SetSeverity, feature_category: :api do + include GraphqlHelpers + let_it_be(:project) { create(:project) } let_it_be(:guest) { create(:user, guest_of: project) } let_it_be(:reporter) { create(:user, reporter_of: project) } let_it_be(:issue) { create(:incident, project: project) } + let(:query) { GraphQL::Query.new(empty_schema, document: nil, context: {}, variables: {}) } + let(:context) { GraphQL::Query::Context.new(query: query, values: { current_user: user }) } - let(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) } + let(:mutation) { described_class.new(object: nil, context: context, field: nil) } specify { expect(described_class).to require_graphql_authorizations(:update_issue, :admin_issue) } diff --git a/spec/graphql/mutations/issues/unlink_alert_spec.rb b/spec/graphql/mutations/issues/unlink_alert_spec.rb index 2ba0ebfe469..102cc0916a7 100644 --- a/spec/graphql/mutations/issues/unlink_alert_spec.rb +++ b/spec/graphql/mutations/issues/unlink_alert_spec.rb @@ -3,6 +3,8 @@ require 'spec_helper' RSpec.describe Mutations::Issues::UnlinkAlert, feature_category: :incident_management do + include GraphqlHelpers + let_it_be(:project) { create(:project) } let_it_be(:another_project) { create(:project) } let_it_be(:guest) { create(:user, guest_of: project) } @@ -11,7 +13,9 @@ RSpec.describe Mutations::Issues::UnlinkAlert, feature_category: :incident_manag let_it_be(:external_alert) { create(:alert_management_alert, project: another_project) } let_it_be(:issue) { create(:incident, project: project, alert_management_alerts: [internal_alert, external_alert]) } - let(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) } + let(:query) { GraphQL::Query.new(empty_schema, document: nil, context: {}, variables: {}) } + let(:context) { GraphQL::Query::Context.new(query: query, values: { current_user: user }) } + let(:mutation) { described_class.new(object: nil, context: context, field: nil) } specify { expect(described_class).to require_graphql_authorizations(:update_issue, :admin_issue) } diff --git a/spec/graphql/mutations/issues/update_spec.rb b/spec/graphql/mutations/issues/update_spec.rb index 25b8c49063b..21af863ce8e 100644 --- a/spec/graphql/mutations/issues/update_spec.rb +++ b/spec/graphql/mutations/issues/update_spec.rb @@ -3,6 +3,8 @@ require 'spec_helper' RSpec.describe Mutations::Issues::Update, feature_category: :team_planning do + include GraphqlHelpers + let_it_be(:project) { create(:project) } let_it_be(:user) { create(:user) } let_it_be(:project_label) { create(:label, project: project) } @@ -20,7 +22,9 @@ RSpec.describe Mutations::Issues::Update, feature_category: :team_planning do } end - let(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) } + let(:query) { GraphQL::Query.new(empty_schema, document: nil, context: {}, variables: {}) } + let(:context) { GraphQL::Query::Context.new(query: query, values: { current_user: user }) } + let(:mutation) { described_class.new(object: nil, context: context, field: nil) } let(:mutated_issue) { subject[:issue] } specify { expect(described_class).to require_graphql_authorizations(:update_issue) } diff --git a/spec/graphql/mutations/labels/create_spec.rb b/spec/graphql/mutations/labels/create_spec.rb index 53a17041125..674c79f8957 100644 --- a/spec/graphql/mutations/labels/create_spec.rb +++ b/spec/graphql/mutations/labels/create_spec.rb @@ -2,7 +2,9 @@ require 'spec_helper' -RSpec.describe Mutations::Labels::Create do +RSpec.describe Mutations::Labels::Create, feature_category: :api do + include GraphqlHelpers + let_it_be(:user) { create(:user) } let(:attributes) do @@ -12,7 +14,9 @@ RSpec.describe Mutations::Labels::Create do } end - let(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) } + let(:query) { GraphQL::Query.new(empty_schema, document: nil, context: {}, variables: {}) } + let(:context) { GraphQL::Query::Context.new(query: query, values: { current_user: user }) } + let(:mutation) { described_class.new(object: nil, context: context, field: nil) } let(:mutated_label) { subject[:label] } shared_examples 'create labels mutation' do diff --git a/spec/graphql/mutations/members/bulk_update_base_spec.rb b/spec/graphql/mutations/members/bulk_update_base_spec.rb index 8c9e20f61e5..17ed2afaa96 100644 --- a/spec/graphql/mutations/members/bulk_update_base_spec.rb +++ b/spec/graphql/mutations/members/bulk_update_base_spec.rb @@ -7,9 +7,11 @@ RSpec.describe Mutations::Members::BulkUpdateBase, feature_category: :groups_and let_it_be(:user) { create(:user) } let_it_be(:group) { create(:group, owners: user) } + let(:query) { GraphQL::Query.new(empty_schema, document: nil, context: {}, variables: {}) } + let(:context) { GraphQL::Query::Context.new(query: query, values: { current_user: user }) } it 'raises a NotImplementedError error if the source_type method is called on the base class' do - mutation = described_class.new(context: { current_user: user }, object: nil, field: nil) + mutation = described_class.new(context: context, object: nil, field: nil) expect { mutation.resolve(group_id: group.to_gid.to_s) }.to raise_error(NotImplementedError) end diff --git a/spec/graphql/mutations/merge_requests/accept_spec.rb b/spec/graphql/mutations/merge_requests/accept_spec.rb index 312aa49824a..d0820fbb412 100644 --- a/spec/graphql/mutations/merge_requests/accept_spec.rb +++ b/spec/graphql/mutations/merge_requests/accept_spec.rb @@ -2,21 +2,16 @@ require 'spec_helper' -RSpec.describe Mutations::MergeRequests::Accept do +RSpec.describe Mutations::MergeRequests::Accept, feature_category: :api do include GraphqlHelpers include AfterNextHelpers - subject(:mutation) { described_class.new(context: context, object: nil, field: nil) } - let_it_be(:user) { create(:user) } - let(:project) { create(:project, :public, :repository) } - let(:context) do - GraphQL::Query::Context.new( - query: query_double(schema: GitlabSchema), - values: { current_user: user } - ) - end + let(:query) { GraphQL::Query.new(empty_schema, document: nil, context: {}, variables: {}) } + let(:context) { GraphQL::Query::Context.new(query: query, values: { current_user: user }) } + + subject(:mutation) { described_class.new(context: context, object: nil, field: nil) } before do project.repository.expire_all_method_caches diff --git a/spec/graphql/mutations/merge_requests/create_spec.rb b/spec/graphql/mutations/merge_requests/create_spec.rb index 6e7a4612510..286b847de76 100644 --- a/spec/graphql/mutations/merge_requests/create_spec.rb +++ b/spec/graphql/mutations/merge_requests/create_spec.rb @@ -2,19 +2,14 @@ require 'spec_helper' -RSpec.describe Mutations::MergeRequests::Create do +RSpec.describe Mutations::MergeRequests::Create, feature_category: :api do include GraphqlHelpers subject(:mutation) { described_class.new(object: nil, context: context, field: nil) } let(:user) { create(:user) } - - let(:context) do - GraphQL::Query::Context.new( - query: query_double(schema: nil), - values: { current_user: user } - ) - end + let(:query) { GraphQL::Query.new(empty_schema, document: nil, context: {}, variables: {}) } + let(:context) { GraphQL::Query::Context.new(query: query, values: { current_user: user }) } describe '#resolve' do subject do diff --git a/spec/graphql/mutations/merge_requests/set_assignees_spec.rb b/spec/graphql/mutations/merge_requests/set_assignees_spec.rb index 42315af75d5..50784bbcc90 100644 --- a/spec/graphql/mutations/merge_requests/set_assignees_spec.rb +++ b/spec/graphql/mutations/merge_requests/set_assignees_spec.rb @@ -2,13 +2,16 @@ require 'spec_helper' -RSpec.describe Mutations::MergeRequests::SetAssignees do +RSpec.describe Mutations::MergeRequests::SetAssignees, feature_category: :api do + include GraphqlHelpers context 'when the user does not have permissions' do let_it_be(:merge_request) { create(:merge_request) } let_it_be(:user) { create(:user) } let_it_be(:assignee) { create(:user) } + let(:query) { GraphQL::Query.new(empty_schema, document: nil, context: {}, variables: {}) } + let(:context) { GraphQL::Query::Context.new(query: query, values: { current_user: user }) } - subject(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) } + subject(:mutation) { described_class.new(object: nil, context: context, field: nil) } describe '#resolve' do subject do diff --git a/spec/graphql/mutations/merge_requests/set_draft_spec.rb b/spec/graphql/mutations/merge_requests/set_draft_spec.rb index 697b2e5b007..777bbc9186a 100644 --- a/spec/graphql/mutations/merge_requests/set_draft_spec.rb +++ b/spec/graphql/mutations/merge_requests/set_draft_spec.rb @@ -2,11 +2,14 @@ require 'spec_helper' -RSpec.describe Mutations::MergeRequests::SetDraft do +RSpec.describe Mutations::MergeRequests::SetDraft, feature_category: :api do + include GraphqlHelpers let_it_be(:merge_request) { create(:merge_request) } let_it_be(:user) { create(:user) } + let(:query) { GraphQL::Query.new(empty_schema, document: nil, context: {}, variables: {}) } + let(:context) { GraphQL::Query::Context.new(query: query, values: { current_user: user }) } - subject(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) } + subject(:mutation) { described_class.new(object: nil, context: context, field: nil) } specify { expect(described_class).to require_graphql_authorizations(:update_merge_request) } diff --git a/spec/graphql/mutations/merge_requests/set_labels_spec.rb b/spec/graphql/mutations/merge_requests/set_labels_spec.rb index 44bd9342b8e..a317445e513 100644 --- a/spec/graphql/mutations/merge_requests/set_labels_spec.rb +++ b/spec/graphql/mutations/merge_requests/set_labels_spec.rb @@ -2,11 +2,15 @@ require 'spec_helper' -RSpec.describe Mutations::MergeRequests::SetLabels do +RSpec.describe Mutations::MergeRequests::SetLabels, feature_category: :api do + include GraphqlHelpers + let(:merge_request) { create(:merge_request) } let(:user) { create(:user) } + let(:query) { GraphQL::Query.new(empty_schema, document: nil, context: {}, variables: {}) } + let(:context) { GraphQL::Query::Context.new(query: query, values: { current_user: user }) } - subject(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) } + subject(:mutation) { described_class.new(object: nil, context: context, field: nil) } specify { expect(described_class).to require_graphql_authorizations(:update_merge_request) } diff --git a/spec/graphql/mutations/merge_requests/set_locked_spec.rb b/spec/graphql/mutations/merge_requests/set_locked_spec.rb index 68bb7aa0aa4..d7810a02989 100644 --- a/spec/graphql/mutations/merge_requests/set_locked_spec.rb +++ b/spec/graphql/mutations/merge_requests/set_locked_spec.rb @@ -2,11 +2,15 @@ require 'spec_helper' -RSpec.describe Mutations::MergeRequests::SetLocked do +RSpec.describe Mutations::MergeRequests::SetLocked, feature_category: :api do + include GraphqlHelpers + let(:merge_request) { create(:merge_request) } let(:user) { create(:user) } + let(:query) { GraphQL::Query.new(empty_schema, document: nil, context: {}, variables: {}) } + let(:context) { GraphQL::Query::Context.new(query: query, values: { current_user: user }) } - subject(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) } + subject(:mutation) { described_class.new(object: nil, context: context, field: nil) } specify { expect(described_class).to require_graphql_authorizations(:update_merge_request) } diff --git a/spec/graphql/mutations/merge_requests/set_milestone_spec.rb b/spec/graphql/mutations/merge_requests/set_milestone_spec.rb index 4de857f43e3..1e23cfbd765 100644 --- a/spec/graphql/mutations/merge_requests/set_milestone_spec.rb +++ b/spec/graphql/mutations/merge_requests/set_milestone_spec.rb @@ -2,11 +2,15 @@ require 'spec_helper' -RSpec.describe Mutations::MergeRequests::SetMilestone do +RSpec.describe Mutations::MergeRequests::SetMilestone, feature_category: :api do + include GraphqlHelpers + let(:user) { create(:user) } let(:project) { create(:project, :private) } let(:merge_request) { create(:merge_request, source_project: project, target_project: project, assignees: [user]) } - let(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) } + let(:query) { GraphQL::Query.new(empty_schema, document: nil, context: {}, variables: {}) } + let(:context) { GraphQL::Query::Context.new(query: query, values: { current_user: user }) } + let(:mutation) { described_class.new(object: nil, context: context, field: nil) } let(:milestone) { create(:milestone, project: project) } subject { mutation.resolve(project_path: project.full_path, iid: merge_request.iid, milestone: milestone) } diff --git a/spec/graphql/mutations/merge_requests/set_reviewers_spec.rb b/spec/graphql/mutations/merge_requests/set_reviewers_spec.rb index 59d3fcbfeda..6a10d50cba6 100644 --- a/spec/graphql/mutations/merge_requests/set_reviewers_spec.rb +++ b/spec/graphql/mutations/merge_requests/set_reviewers_spec.rb @@ -2,13 +2,17 @@ require 'spec_helper' -RSpec.describe Mutations::MergeRequests::SetReviewers do +RSpec.describe Mutations::MergeRequests::SetReviewers, feature_category: :api do + include GraphqlHelpers + let_it_be(:user) { create(:user) } let_it_be(:merge_request, reload: true) { create(:merge_request) } let_it_be(:reviewer) { create(:user) } let_it_be(:reviewer2) { create(:user) } + let(:query) { GraphQL::Query.new(empty_schema, document: nil, context: {}, variables: {}) } + let(:context) { GraphQL::Query::Context.new(query: query, values: { current_user: user }) } - subject(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) } + subject(:mutation) { described_class.new(object: nil, context: context, field: nil) } describe '#resolve' do let(:reviewer_usernames) { [reviewer.username] } diff --git a/spec/graphql/mutations/merge_requests/update_spec.rb b/spec/graphql/mutations/merge_requests/update_spec.rb index c34ec939d12..665e3a82467 100644 --- a/spec/graphql/mutations/merge_requests/update_spec.rb +++ b/spec/graphql/mutations/merge_requests/update_spec.rb @@ -3,10 +3,14 @@ require 'spec_helper' RSpec.describe Mutations::MergeRequests::Update, feature_category: :team_planning do + include GraphqlHelpers + let(:merge_request) { create(:merge_request) } let(:user) { create(:user) } + let(:query) { GraphQL::Query.new(empty_schema, document: nil, context: {}, variables: {}) } + let(:context) { GraphQL::Query::Context.new(query: query, values: { current_user: user }) } - subject(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) } + subject(:mutation) { described_class.new(object: nil, context: context, field: nil) } specify { expect(described_class).to require_graphql_authorizations(:update_merge_request) } diff --git a/spec/lib/gitlab/pagination/gitaly_keyset_pager_spec.rb b/spec/lib/gitlab/pagination/gitaly_keyset_pager_spec.rb index 5dd340f69fd..d059b3b008a 100644 --- a/spec/lib/gitlab/pagination/gitaly_keyset_pager_spec.rb +++ b/spec/lib/gitlab/pagination/gitaly_keyset_pager_spec.rb @@ -70,6 +70,11 @@ RSpec.describe Gitlab::Pagination::GitalyKeysetPager, feature_category: :source_ context 'when first page is requested' do let(:branches) { [branch1, branch2, branch3] } + before do + allow(BranchesFinder).to receive(:===).with(finder).and_return(true) + allow(finder).to receive(:total).and_return(branches.size) + end + it 'keyset pagination is used with offset headers' do allow(request_context).to receive(:request).and_return(fake_request) allow(project.repository).to receive(:branch_count).and_return(branches.size) @@ -85,6 +90,26 @@ RSpec.describe Gitlab::Pagination::GitalyKeysetPager, feature_category: :source_ pager.paginate(finder) end + + context 'when second page does not exist' do + let(:base_query) { { per_page: 3 } } + + it 'does not set an invalid X-Next-Page header' do + allow(request_context).to receive(:request).and_return(fake_request) + allow(project.repository).to receive(:branch_count).and_return(branches.size) + + expect(finder).to receive(:execute).and_return(branches) + expect(request_context).to receive(:header).with('X-Per-Page', '3') + expect(request_context).to receive(:header).with('X-Page', '1') + expect(request_context).to receive(:header).with('X-Next-Page', '') + expect(request_context).to receive(:header).with('X-Prev-Page', '') + expect(request_context).to receive(:header).with('Link', kind_of(String)) + expect(request_context).to receive(:header).with('X-Total', '3') + expect(request_context).to receive(:header).with('X-Total-Pages', '1') + + pager.paginate(finder) + end + end end context 'when second page is requested' do diff --git a/spec/support/shared_examples/graphql/mutations/resolves_subscription_shared_examples.rb b/spec/support/shared_examples/graphql/mutations/resolves_subscription_shared_examples.rb index 678bb908343..cb75d25c607 100644 --- a/spec/support/shared_examples/graphql/mutations/resolves_subscription_shared_examples.rb +++ b/spec/support/shared_examples/graphql/mutations/resolves_subscription_shared_examples.rb @@ -3,7 +3,11 @@ require 'spec_helper' RSpec.shared_examples 'a subscribeable not accessible graphql resource' do - let(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) } + include GraphqlHelpers + + let(:query) { GraphQL::Query.new(empty_schema, document: nil, context: {}, variables: {}) } + let(:context) { GraphQL::Query::Context.new(query: query, values: { current_user: user }) } + let(:mutation) { described_class.new(object: nil, context: context, field: nil) } subject { mutation.resolve(project_path: resource.project.full_path, iid: resource.iid, subscribed_state: true) } @@ -13,8 +17,12 @@ RSpec.shared_examples 'a subscribeable not accessible graphql resource' do end RSpec.shared_examples 'a subscribeable graphql resource' do + include GraphqlHelpers + + let(:query) { GraphQL::Query.new(empty_schema, document: nil, context: {}, variables: {}) } + let(:context) { GraphQL::Query::Context.new(query: query, values: { current_user: user }) } let(:mutated_resource) { subject[resource.class.name.underscore.to_sym] } - let(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) } + let(:mutation) { described_class.new(object: nil, context: context, field: nil) } let(:subscribe) { true } subject { mutation.resolve(project_path: resource.project.full_path, iid: resource.iid, subscribed_state: subscribe) } diff --git a/spec/support/shared_examples/graphql/mutations/set_assignees_shared_examples.rb b/spec/support/shared_examples/graphql/mutations/set_assignees_shared_examples.rb index 3b9dadf2e80..4ab404c7123 100644 --- a/spec/support/shared_examples/graphql/mutations/set_assignees_shared_examples.rb +++ b/spec/support/shared_examples/graphql/mutations/set_assignees_shared_examples.rb @@ -3,9 +3,13 @@ require 'spec_helper' RSpec.shared_examples 'an assignable resource' do - let_it_be(:user) { create(:user) } + include GraphqlHelpers - subject(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) } + let_it_be(:user) { create(:user) } + let(:query) { GraphQL::Query.new(empty_schema, document: nil, context: {}, variables: {}) } + let(:context) { GraphQL::Query::Context.new(query: query, values: { current_user: user }) } + + subject(:mutation) { described_class.new(object: nil, context: context, field: nil) } describe '#resolve' do let_it_be(:assignee) { create(:user) } diff --git a/spec/support/shared_examples/metrics_instrumentation_shared_examples.rb b/spec/support/shared_examples/metrics_instrumentation_shared_examples.rb index 367924ab768..db7bbdb1d61 100644 --- a/spec/support/shared_examples/metrics_instrumentation_shared_examples.rb +++ b/spec/support/shared_examples/metrics_instrumentation_shared_examples.rb @@ -41,6 +41,25 @@ RSpec.shared_examples 'a correct instrumented metric query' do |params| end end +RSpec.shared_examples 'a correct instrumented database query execution value' do |params| + let(:time_frame) { params[:time_frame] } + let(:options) { params[:options] } + let(:metric) { described_class.new(time_frame: time_frame, options: options) } + + around do |example| + freeze_time { example.run } + end + + before do + allow(metric.relation).to receive(:transaction_open?).and_return(false) + end + + it 'returns correct value' do + query_result = metric.relation.connection.execute(metric.instrumentation).to_a.first.each_value.first + expect(query_result).to eq(expected_value) + end +end + RSpec.shared_examples 'a correct instrumented metric value and query' do |params| it_behaves_like 'a correct instrumented metric value', params it_behaves_like 'a correct instrumented metric query', params