Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2024-05-02 12:10:28 +00:00
parent 66db8363ac
commit 80aae84337
44 changed files with 425 additions and 450 deletions

View File

@ -213,7 +213,6 @@ variables:
RSPEC_CHANGED_FILES_PATH: rspec/changed_files.txt
RSPEC_FAIL_FAST_THRESHOLD: 20
RSPEC_FAST_QUARANTINE_PATH: rspec/fast_quarantine-gitlab.txt
RSPEC_FOSS_IMPACT_PIPELINE_TEMPLATE_YML: .gitlab/ci/rails/rspec-foss-impact.gitlab-ci.yml.erb
RSPEC_LAST_RUN_RESULTS_FILE: rspec/rspec_last_run_results.txt
RSPEC_MATCHING_JS_FILES_PATH: rspec/js_matching_files.txt
RSPEC_MATCHING_TESTS_EE_PATH: rspec/matching_tests-ee.txt

View File

@ -95,6 +95,10 @@ start-as-if-foss:
ENABLE_GRAPHQL_SCHEMA_DUMP: $ENABLE_GRAPHQL_SCHEMA_DUMP
ENABLE_JEST: $ENABLE_JEST
ENABLE_JEST_INTEGRATION: $ENABLE_JEST_INTEGRATION
ENABLE_RSPEC_PREDICTIVE_PIPELINE_GENERATE: $ENABLE_RSPEC_PREDICTIVE_PIPELINE_GENERATE
ENABLE_RSPEC_PREDICTIVE_TRIGGER: $ENABLE_RSPEC_PREDICTIVE_TRIGGER
ENABLE_RSPEC_PREDICTIVE_TRIGGER_SINGLE_DB: $ENABLE_RSPEC_PREDICTIVE_TRIGGER_SINGLE_DB
ENABLE_RSPEC_PREDICTIVE_TRIGGER_SINGLE_DB_CI_CONNECTION: $ENABLE_RSPEC_PREDICTIVE_TRIGGER_SINGLE_DB_CI_CONNECTION
ENABLE_RUBOCOP: $ENABLE_RUBOCOP
ENABLE_QA_INTERNAL: $ENABLE_QA_INTERNAL
ENABLE_QA_SELECTORS: $ENABLE_QA_SELECTORS

View File

@ -1290,44 +1290,6 @@ rspec-ee fail-fast:
variables:
MATCHING_TESTS_PATH: "${RSPEC_MATCHING_TESTS_EE_PATH}"
rspec-foss-impact:pipeline-generate:
extends:
- .rails:rules:rspec-foss-impact
stage: prepare
needs: ["detect-tests", "retrieve-tests-metadata"]
script:
- scripts/generate_rspec_pipeline.rb -f "${RSPEC_MATCHING_TESTS_FOSS_PATH}" -t "${RSPEC_FOSS_IMPACT_PIPELINE_TEMPLATE_YML}" -k "${KNAPSACK_RSPEC_SUITE_REPORT_PATH}"
- cat "${RSPEC_FOSS_IMPACT_PIPELINE_TEMPLATE_YML}.yml"
artifacts:
expire_in: 1 day
paths:
- "${RSPEC_FOSS_IMPACT_PIPELINE_TEMPLATE_YML}.yml"
rspec-foss-impact:trigger:
extends:
- .rails:rules:rspec-foss-impact
stage: test
needs:
- job: "setup-test-env"
artifacts: false
- job: "retrieve-tests-metadata"
artifacts: false
- job: "compile-test-assets as-if-foss"
artifacts: false
- job: "rspec-foss-impact:pipeline-generate"
artifacts: true
variables:
PARENT_PIPELINE_ID: $CI_PIPELINE_ID
GIT_STRATEGY: $GIT_STRATEGY
trigger:
strategy: depend
forward:
yaml_variables: true
pipeline_variables: true
include:
- artifact: "${RSPEC_FOSS_IMPACT_PIPELINE_TEMPLATE_YML}.yml"
job: rspec-foss-impact:pipeline-generate
fail-pipeline-early:
extends:
- .rails:rules:fail-pipeline-early

View File

@ -1,89 +0,0 @@
# RSpec FOSS impact pipeline loaded dynamically by script: scripts/generate_rspec_pipeline.rb
include:
- local: .gitlab/ci/rails/shared.gitlab-ci.yml
default:
image: $DEFAULT_CI_IMAGE
tags:
- gitlab-org
# Default job timeout set to 90m https://gitlab.com/gitlab-com/gl-infra/infrastructure/-/issues/10520
timeout: 90m
interruptible: true
stages:
- test
dont-interrupt-me:
extends: .rules:dont-interrupt
stage: .pre
interruptible: false
script:
- echo "This jobs makes sure this pipeline won't be interrupted! See https://docs.gitlab.com/ee/ci/yaml/#interruptible."
.base-rspec-foss-impact:
extends: .rspec-base-pg14-as-if-foss
needs:
<% if repo_from_artifacts %>
- pipeline: $PARENT_PIPELINE_ID
job: clone-gitlab-repo
<% end %>
- pipeline: $PARENT_PIPELINE_ID
job: detect-tests
- pipeline: $PARENT_PIPELINE_ID
job: setup-test-env
- pipeline: $PARENT_PIPELINE_ID
job: retrieve-tests-metadata
- pipeline: $PARENT_PIPELINE_ID
job: compile-test-assets as-if-foss
rules:
- when: always
variables:
RSPEC_TESTS_FILTER_FILE: "${RSPEC_MATCHING_TESTS_FOSS_PATH}"
RSPEC_TESTS_MAPPING_ENABLED: "true"
script:
- !reference [.base-script, script]
- rspec_parallelized_job "--fail-fast=${RSPEC_FAIL_FAST_THRESHOLD} --tag ~quarantine --tag ~level:background_migration --tag ~zoekt --tag ~click_house"
<% if rspec_files_per_test_level[:migration][:files].size > 0 %>
rspec migration foss-impact:
extends: .base-rspec-foss-impact
<% if rspec_files_per_test_level[:migration][:parallelization] > 1 %>
parallel: <%= rspec_files_per_test_level[:migration][:parallelization] %>
<% end %>
script:
- !reference [.base-script, script]
- rspec_parallelized_job "--fail-fast=${RSPEC_FAIL_FAST_THRESHOLD} --tag ~quarantine --tag ~zoekt --tag ~click_house"
<% end %>
<% if rspec_files_per_test_level[:background_migration][:files].size > 0 %>
rspec background_migration foss-impact:
extends: .base-rspec-foss-impact
<% if rspec_files_per_test_level[:background_migration][:parallelization] > 1 %>
parallel: <%= rspec_files_per_test_level[:background_migration][:parallelization] %>
<% end %>
<% end %>
<% if rspec_files_per_test_level[:unit][:files].size > 0 %>
rspec unit foss-impact:
extends: .base-rspec-foss-impact
<% if rspec_files_per_test_level[:unit][:parallelization] > 1 %>
parallel: <%= rspec_files_per_test_level[:unit][:parallelization] %>
<% end %>
<% end %>
<% if rspec_files_per_test_level[:integration][:files].size > 0 %>
rspec integration foss-impact:
extends: .base-rspec-foss-impact
<% if rspec_files_per_test_level[:integration][:parallelization] > 1 %>
parallel: <%= rspec_files_per_test_level[:integration][:parallelization] %>
<% end %>
<% end %>
<% if rspec_files_per_test_level[:system][:files].size > 0 %>
rspec system foss-impact:
extends: .base-rspec-foss-impact
<% if rspec_files_per_test_level[:system][:parallelization] > 1 %>
parallel: <%= rspec_files_per_test_level[:system][:parallelization] %>
<% end %>
<% end %>

View File

@ -2056,6 +2056,7 @@
changes: *code-backstage-patterns
- <<: *if-merge-request-labels-pipeline-expedite
when: never
- if: '$ENABLE_RSPEC_PREDICTIVE_PIPELINE_GENERATE == "true"'
- <<: *if-merge-request
changes: *db-patterns
- <<: *if-merge-request
@ -2085,6 +2086,7 @@
changes: *code-backstage-patterns
- <<: *if-merge-request-labels-pipeline-expedite
when: never
- if: '$ENABLE_RSPEC_PREDICTIVE_TRIGGER == "true"'
- <<: *if-merge-request-labels-run-all-rspec
when: never
- <<: *if-merge-request
@ -2112,6 +2114,7 @@
when: never
- <<: *if-merge-request-labels-pipeline-expedite
when: never
- if: '$ENABLE_RSPEC_PREDICTIVE_TRIGGER_SINGLE_DB == "true"'
- <<: *if-merge-request-approved
when: never
- <<: *if-merge-request
@ -2130,6 +2133,7 @@
when: never
- <<: *if-merge-request-labels-pipeline-expedite
when: never
- if: '$ENABLE_RSPEC_PREDICTIVE_TRIGGER_SINGLE_DB_CI_CONNECTION == "true"'
- <<: *if-merge-request-approved
when: never
- <<: *if-merge-request
@ -2371,19 +2375,6 @@
rules:
- !reference [".rails:rules:previous-failed-tests-default-rules", rules]
.rails:rules:rspec-foss-impact:
rules:
- <<: *if-not-ee
when: never
- <<: *if-merge-request-labels-as-if-foss
when: never
- <<: *if-merge-request-labels-pipeline-expedite
when: never
- <<: *if-security-merge-request
changes: *code-backstage-patterns
- <<: *if-dot-com-gitlab-org-merge-request
changes: *code-backstage-patterns
.rails:rules:rspec fail-fast:
rules:
- <<: *if-not-ee

View File

@ -108,7 +108,7 @@ rubocop:
script:
- |
# For non-merge request, or when RUN_ALL_RUBOCOP is 'true', run all RuboCop rules
if [ -z "${CI_MERGE_REQUEST_IID}" ] || [ "${RUN_ALL_RUBOCOP}" == "true" ]; then
if [ -z "${CI_MERGE_REQUEST_IID:-$FIND_CHANGES_MERGE_REQUEST_IID}" ] || [ "${RUN_ALL_RUBOCOP}" == "true" ]; then
# Silence cop offenses for rules with "grace period".
# We won't notify Slack if offenses were silenced to avoid frequent messages.
# Job `update-static-analysis-cache` takes care of Slack notifications every 2 hours.

View File

@ -345,22 +345,6 @@ Layout/ArgumentAlignment:
- 'app/graphql/types/release_type.rb'
- 'app/graphql/types/repository/blob_type.rb'
- 'app/graphql/types/repository_type.rb'
- 'app/graphql/types/resolvable_interface.rb'
- 'app/graphql/types/saved_reply_type.rb'
- 'app/graphql/types/security/report_type_enum.rb'
- 'app/graphql/types/sha_format_enum.rb'
- 'app/graphql/types/snippet_type.rb'
- 'app/graphql/types/snippets/blob_action_input_type.rb'
- 'app/graphql/types/snippets/blob_connection_type.rb'
- 'app/graphql/types/snippets/blob_type.rb'
- 'app/graphql/types/sort_enum.rb'
- 'app/graphql/types/task_completion_status.rb'
- 'app/graphql/types/terraform/state_type.rb'
- 'app/graphql/types/terraform/state_version_type.rb'
- 'app/graphql/types/time_tracking/timelog_category_type.rb'
- 'app/graphql/types/time_tracking/timelog_connection_type.rb'
- 'app/graphql/types/timelog_type.rb'
- 'app/graphql/types/todo_type.rb'
- 'app/graphql/types/tree/blob_type.rb'
- 'app/graphql/types/tree/entry_type.rb'
- 'app/graphql/types/tree/submodule_type.rb'

View File

@ -121,8 +121,11 @@ function handleAttributeFilter(filterValue, filterOperator, searchParams, namePa
function addDateRangeFilterToQueryParams(dateRangeFilter, params) {
if (!dateRangeFilter || !params) return;
const { value, endDate, startDate } = dateRangeFilter;
if (value === CUSTOM_DATE_RANGE_OPTION) {
const { value, endDate, startDate, timestamp } = dateRangeFilter;
if (timestamp) {
params.append('start_time', timestamp);
params.append('end_time', timestamp);
} else if (value === CUSTOM_DATE_RANGE_OPTION) {
if (isValidDate(startDate) && isValidDate(endDate)) {
params.append('start_time', startDate.toISOString());
params.append('end_time', endDate.toISOString());

View File

@ -49,5 +49,6 @@ export const CUSTOM_DATE_RANGE_OPTION = 'custom';
export const DATE_RANGE_QUERY_KEY = 'date_range';
export const DATE_RANGE_START_QUERY_KEY = 'date_start';
export const DATE_RANGE_END_QUERY_KEY = 'date_end';
export const TIMESTAMP_QUERY_KEY = 'timestamp';
export const FILTERED_SEARCH_TERM_QUERY_KEY = 'search';

View File

@ -5,6 +5,7 @@ import {
DATE_RANGE_QUERY_KEY,
DATE_RANGE_START_QUERY_KEY,
DATE_RANGE_END_QUERY_KEY,
TIMESTAMP_QUERY_KEY,
} from './constants';
/**
@ -83,8 +84,18 @@ export function queryToDateFilterObj(queryObj) {
[DATE_RANGE_QUERY_KEY]: dateRangeValue,
[DATE_RANGE_START_QUERY_KEY]: dateRangeStart,
[DATE_RANGE_END_QUERY_KEY]: dateRangeEnd,
[TIMESTAMP_QUERY_KEY]: timestamp,
} = queryObj;
if (timestamp) {
return {
value: CUSTOM_DATE_RANGE_OPTION,
startDate: new Date(timestamp),
endDate: new Date(timestamp),
timestamp,
};
}
return validatedDateRangeQuery(dateRangeValue, dateRangeStart, dateRangeEnd);
}
@ -109,5 +120,6 @@ export function dateFilterObjToQuery(dateFilter = {}) {
[DATE_RANGE_START_QUERY_KEY]: undefined,
[DATE_RANGE_END_QUERY_KEY]: undefined,
}),
[TIMESTAMP_QUERY_KEY]: dateFilter.timestamp,
};
}

View File

@ -7,8 +7,8 @@ module Types
include Types::BaseInterface
field :resolved_by, Types::UserType,
null: true,
description: 'User who resolved the object.'
null: true,
description: 'User who resolved the object.'
def resolved_by
return unless object.resolved_by_id
@ -17,12 +17,12 @@ module Types
end
field :resolved, GraphQL::Types::Boolean, null: false,
description: 'Indicates if the object is resolved.',
method: :resolved?
description: 'Indicates if the object is resolved.',
method: :resolved?
field :resolvable, GraphQL::Types::Boolean, null: false,
description: 'Indicates if the object can be resolved.',
method: :resolvable?
description: 'Indicates if the object can be resolved.',
method: :resolvable?
field :resolved_at, Types::TimeType, null: true,
description: 'Timestamp of when the object was resolved.'
description: 'Timestamp of when the object was resolved.'
end
end

View File

@ -7,11 +7,11 @@ module Types
authorize :read_saved_replies
field :content, GraphQL::Types::String,
null: false,
description: 'Content of the saved reply.'
null: false,
description: 'Content of the saved reply.'
field :name, GraphQL::Types::String,
null: false,
description: 'Name of the saved reply.'
null: false,
description: 'Name of the saved reply.'
end
end

View File

@ -7,8 +7,8 @@ module Types
::Security::SecurityJobsFinder.allowed_job_types.each do |report_type|
value report_type.upcase,
value: report_type,
description: "#{report_type.upcase.to_s.tr('_', ' ')} scan report"
value: report_type,
description: "#{report_type.upcase.to_s.tr('_', ' ')} scan report"
end
end
end

View File

@ -12,8 +12,8 @@ module Types
FORMATS_DESCRIPTION.each do |format, description|
value format.to_s.upcase,
description: description,
value: format.to_s
description: description,
value: format.to_s
end
end
end

View File

@ -14,73 +14,73 @@ module Types
expose_permissions Types::PermissionTypes::Snippet
field :id, Types::GlobalIDType[::Snippet],
description: 'ID of the snippet.',
null: false
description: 'ID of the snippet.',
null: false
field :title, GraphQL::Types::String,
description: 'Title of the snippet.',
null: false
description: 'Title of the snippet.',
null: false
field :project, Types::ProjectType,
description: 'Project the snippet is associated with.',
null: true,
authorize: :read_project
description: 'Project the snippet is associated with.',
null: true,
authorize: :read_project
# Author can be nil in some scenarios. For example,
# when the admin setting restricted visibility
# level is set to public
field :author, Types::UserType,
description: 'Owner of the snippet.',
null: true
description: 'Owner of the snippet.',
null: true
field :file_name, GraphQL::Types::String,
description: 'File Name of the snippet.',
null: true
description: 'File Name of the snippet.',
null: true
field :description, GraphQL::Types::String,
description: 'Description of the snippet.',
null: true
description: 'Description of the snippet.',
null: true
field :visibility_level, Types::VisibilityLevelsEnum,
description: 'Visibility Level of the snippet.',
null: false
description: 'Visibility Level of the snippet.',
null: false
field :hidden, GraphQL::Types::Boolean,
description: 'Indicates the snippet is hidden because the author has been banned.',
null: false,
method: :hidden_due_to_author_ban?
description: 'Indicates the snippet is hidden because the author has been banned.',
null: false,
method: :hidden_due_to_author_ban?
field :created_at, Types::TimeType,
description: 'Timestamp this snippet was created.',
null: false
description: 'Timestamp this snippet was created.',
null: false
field :updated_at, Types::TimeType,
description: 'Timestamp this snippet was updated.',
null: false
description: 'Timestamp this snippet was updated.',
null: false
field :web_url, type: GraphQL::Types::String,
description: 'Web URL of the snippet.',
null: false
description: 'Web URL of the snippet.',
null: false
field :raw_url, type: GraphQL::Types::String,
description: 'Raw URL of the snippet.',
null: false
description: 'Raw URL of the snippet.',
null: false
field :blobs, type: Types::Snippets::BlobType.connection_type,
description: 'Snippet blobs.',
calls_gitaly: true,
null: true,
resolver: Resolvers::Snippets::BlobsResolver
description: 'Snippet blobs.',
calls_gitaly: true,
null: true,
resolver: Resolvers::Snippets::BlobsResolver
field :ssh_url_to_repo, type: GraphQL::Types::String,
description: 'SSH URL to the snippet repository.',
calls_gitaly: true,
null: true
description: 'SSH URL to the snippet repository.',
calls_gitaly: true,
null: true
field :http_url_to_repo, type: GraphQL::Types::String,
description: 'HTTP URL to the snippet repository.',
calls_gitaly: true,
null: true
description: 'HTTP URL to the snippet repository.',
calls_gitaly: true,
null: true
markdown_field :description_html, null: true, method: :description

View File

@ -7,20 +7,20 @@ module Types
description 'Represents an action to perform over a snippet file'
argument :action, Types::Snippets::BlobActionEnum,
description: 'Type of input action.',
required: true
description: 'Type of input action.',
required: true
argument :previous_path, GraphQL::Types::String,
description: 'Previous path of the snippet file.',
required: false
description: 'Previous path of the snippet file.',
required: false
argument :file_path, GraphQL::Types::String,
description: 'Path of the snippet file.',
required: true
description: 'Path of the snippet file.',
required: true
argument :content, GraphQL::Types::String,
description: 'Snippet file content.',
required: false
description: 'Snippet file content.',
required: false
end
end
end

View File

@ -5,10 +5,10 @@ module Types
# rubocop: disable Graphql/AuthorizeTypes
class BlobConnectionType < GraphQL::Types::Relay::BaseConnection
field :has_unretrievable_blobs,
GraphQL::Types::Boolean,
null: false,
description: 'Indicates if the snippet has unretrievable blobs.',
resolver_method: :unretrievable_blobs?
GraphQL::Types::Boolean,
null: false,
description: 'Indicates if the snippet has unretrievable blobs.',
resolver_method: :unretrievable_blobs?
def unretrievable_blobs?
!!context[:unretrievable_blobs?]

View File

@ -11,58 +11,58 @@ module Types
connection_type_class Types::Snippets::BlobConnectionType
field :rich_data, GraphQL::Types::String,
description: 'Blob highlighted data.',
null: true
description: 'Blob highlighted data.',
null: true
field :plain_data, GraphQL::Types::String,
description: 'Blob plain highlighted data.',
null: true
description: 'Blob plain highlighted data.',
null: true
field :raw_plain_data, GraphQL::Types::String,
description: 'Raw content of the blob, if the blob is text data.',
null: true
description: 'Raw content of the blob, if the blob is text data.',
null: true
field :raw_path, GraphQL::Types::String,
description: 'Blob raw content endpoint path.',
null: false
description: 'Blob raw content endpoint path.',
null: false
field :size, GraphQL::Types::Int,
description: 'Blob size.',
null: false
description: 'Blob size.',
null: false
field :binary, GraphQL::Types::Boolean,
description: 'Shows whether the blob is binary.',
method: :binary?,
null: false
description: 'Shows whether the blob is binary.',
method: :binary?,
null: false
field :name, GraphQL::Types::String,
description: 'Blob name.',
null: true
description: 'Blob name.',
null: true
field :path, GraphQL::Types::String,
description: 'Blob path.',
null: true
description: 'Blob path.',
null: true
field :simple_viewer, type: Types::Snippets::BlobViewerType,
description: 'Blob content simple viewer.',
null: false
description: 'Blob content simple viewer.',
null: false
field :rich_viewer, type: Types::Snippets::BlobViewerType,
description: 'Blob content rich viewer.',
null: true
description: 'Blob content rich viewer.',
null: true
field :mode, type: GraphQL::Types::String,
description: 'Blob mode.',
null: true
description: 'Blob mode.',
null: true
field :external_storage, type: GraphQL::Types::String,
description: 'Blob external storage.',
null: true
description: 'Blob external storage.',
null: true
field :rendered_as_text, type: GraphQL::Types::Boolean,
description: 'Shows whether the blob is rendered as text.',
method: :rendered_as_text?,
null: false
description: 'Shows whether the blob is rendered as text.',
method: :rendered_as_text?,
null: false
end
# rubocop: enable Graphql/AuthorizeTypes
end

View File

@ -8,33 +8,33 @@ module Types
# Deprecated, as we prefer uppercase enums
# https://gitlab.com/groups/gitlab-org/-/epics/1838
value 'updated_desc', 'Updated at descending order.',
value: :updated_desc,
deprecated: {
reason: :renamed,
replacement: 'UPDATED_DESC',
milestone: '13.5'
}
value: :updated_desc,
deprecated: {
reason: :renamed,
replacement: 'UPDATED_DESC',
milestone: '13.5'
}
value 'updated_asc', 'Updated at ascending order.',
value: :updated_asc,
deprecated: {
reason: :renamed,
replacement: 'UPDATED_ASC',
milestone: '13.5'
}
value: :updated_asc,
deprecated: {
reason: :renamed,
replacement: 'UPDATED_ASC',
milestone: '13.5'
}
value 'created_desc', 'Created at descending order.',
value: :created_desc,
deprecated: {
reason: :renamed,
replacement: 'CREATED_DESC',
milestone: '13.5'
}
value: :created_desc,
deprecated: {
reason: :renamed,
replacement: 'CREATED_DESC',
milestone: '13.5'
}
value 'created_asc', 'Created at ascending order.',
value: :created_asc,
deprecated: {
reason: :renamed,
replacement: 'CREATED_ASC',
milestone: '13.5'
}
value: :created_asc,
deprecated: {
reason: :renamed,
replacement: 'CREATED_ASC',
milestone: '13.5'
}
value 'UPDATED_DESC', 'Updated at descending order.', value: :updated_desc
value 'UPDATED_ASC', 'Updated at ascending order.', value: :updated_asc

View File

@ -9,9 +9,9 @@ module Types
description 'Completion status of tasks'
field :completed_count, GraphQL::Types::Int, null: false,
description: 'Number of completed tasks.'
description: 'Number of completed tasks.'
field :count, GraphQL::Types::Int, null: false,
description: 'Number of total tasks.'
description: 'Number of total tasks.'
end
# rubocop: enable Graphql/AuthorizeTypes
end

View File

@ -10,37 +10,37 @@ module Types
connection_type_class Types::CountableConnectionType
field :id, GraphQL::Types::ID,
null: false,
description: 'ID of the Terraform state.'
null: false,
description: 'ID of the Terraform state.'
field :name, GraphQL::Types::String,
null: false,
description: 'Name of the Terraform state.'
null: false,
description: 'Name of the Terraform state.'
field :locked_by_user, Types::UserType,
null: true,
description: 'User currently holding a lock on the Terraform state.'
null: true,
description: 'User currently holding a lock on the Terraform state.'
field :locked_at, Types::TimeType,
null: true,
description: 'Timestamp the Terraform state was locked.'
null: true,
description: 'Timestamp the Terraform state was locked.'
field :latest_version, Types::Terraform::StateVersionType,
complexity: 3,
null: true,
description: 'Latest version of the Terraform state.'
complexity: 3,
null: true,
description: 'Latest version of the Terraform state.'
field :created_at, Types::TimeType,
null: false,
description: 'Timestamp the Terraform state was created.'
null: false,
description: 'Timestamp the Terraform state was created.'
field :updated_at, Types::TimeType,
null: false,
description: 'Timestamp the Terraform state was updated.'
null: false,
description: 'Timestamp the Terraform state was updated.'
field :deleted_at, Types::TimeType,
null: true,
description: 'Timestamp the Terraform state was deleted.'
null: true,
description: 'Timestamp the Terraform state was deleted.'
def locked_by_user
Gitlab::Graphql::Loaders::BatchModelLoader.new(User, object.locked_by_user_id).find

View File

@ -10,34 +10,34 @@ module Types
authorize :read_terraform_state
field :id, GraphQL::Types::ID,
null: false,
description: 'ID of the Terraform state version.'
null: false,
description: 'ID of the Terraform state version.'
field :created_by_user, Types::UserType,
null: true,
description: 'User that created this version.'
null: true,
description: 'User that created this version.'
field :download_path, GraphQL::Types::String,
null: true,
description: "URL for downloading the version's JSON file."
null: true,
description: "URL for downloading the version's JSON file."
field :job, Types::Ci::JobType,
null: true,
description: 'Job that created this version.',
authorize: :read_commit_status
null: true,
description: 'Job that created this version.',
authorize: :read_commit_status
field :serial, GraphQL::Types::Int,
null: true,
description: 'Serial number of the version.',
method: :version
null: true,
description: 'Serial number of the version.',
method: :version
field :created_at, Types::TimeType,
null: false,
description: 'Timestamp the version was created.'
null: false,
description: 'Timestamp the version was created.'
field :updated_at, Types::TimeType,
null: false,
description: 'Timestamp the version was updated.'
null: false,
description: 'Timestamp the version was updated.'
def created_by_user
Gitlab::Graphql::Loaders::BatchModelLoader.new(User, object.created_by_user_id).find

View File

@ -8,44 +8,44 @@ module Types
authorize :read_timelog_category
field :id,
GraphQL::Types::ID,
null: false,
description: 'Internal ID of the timelog category.'
GraphQL::Types::ID,
null: false,
description: 'Internal ID of the timelog category.'
field :name,
GraphQL::Types::String,
null: false,
description: 'Name of the category.'
GraphQL::Types::String,
null: false,
description: 'Name of the category.'
field :description,
GraphQL::Types::String,
null: true,
description: 'Description of the category.'
GraphQL::Types::String,
null: true,
description: 'Description of the category.'
field :color,
Types::ColorType,
null: true,
description: 'Color assigned to the category.'
Types::ColorType,
null: true,
description: 'Color assigned to the category.'
field :billable,
GraphQL::Types::Boolean,
null: true,
description: 'Whether the category is billable or not.'
GraphQL::Types::Boolean,
null: true,
description: 'Whether the category is billable or not.'
field :billing_rate,
GraphQL::Types::Float,
null: true,
description: 'Billing rate for the category.'
GraphQL::Types::Float,
null: true,
description: 'Billing rate for the category.'
field :created_at,
Types::TimeType,
null: false,
description: 'When the category was created.'
Types::TimeType,
null: false,
description: 'When the category was created.'
field :updated_at,
Types::TimeType,
null: false,
description: 'When the category was last updated.'
Types::TimeType,
null: false,
description: 'When the category was last updated.'
end
end
end

View File

@ -5,9 +5,9 @@ module Types
# rubocop: disable Graphql/AuthorizeTypes
class TimelogConnectionType < CountableConnectionType
field :total_spent_time,
GraphQL::Types::BigInt,
null: false,
description: 'Total time spent in seconds.'
GraphQL::Types::BigInt,
null: false,
description: 'Total time spent in seconds.'
def total_spent_time
# rubocop: disable CodeReuse/ActiveRecord

View File

@ -11,47 +11,47 @@ module Types
expose_permissions Types::PermissionTypes::Timelog
field :id,
GraphQL::Types::ID,
null: false,
description: 'Internal ID of the timelog.'
GraphQL::Types::ID,
null: false,
description: 'Internal ID of the timelog.'
field :spent_at,
Types::TimeType,
null: true,
description: 'Timestamp of when the time tracked was spent at.'
Types::TimeType,
null: true,
description: 'Timestamp of when the time tracked was spent at.'
field :time_spent,
GraphQL::Types::Int,
null: false,
description: 'Time spent displayed in seconds.'
GraphQL::Types::Int,
null: false,
description: 'Time spent displayed in seconds.'
field :user,
Types::UserType,
null: false,
description: 'User that logged the time.'
Types::UserType,
null: false,
description: 'User that logged the time.'
field :issue,
Types::IssueType,
null: true,
description: 'Issue that logged time was added to.'
Types::IssueType,
null: true,
description: 'Issue that logged time was added to.'
field :merge_request,
Types::MergeRequestType,
null: true,
description: 'Merge request that logged time was added to.'
Types::MergeRequestType,
null: true,
description: 'Merge request that logged time was added to.'
field :note,
Types::Notes::NoteType,
null: true,
description: 'Note where the quick action was executed to add the logged time.'
Types::Notes::NoteType,
null: true,
description: 'Note where the quick action was executed to add the logged time.'
field :summary, GraphQL::Types::String,
null: true,
description: 'Summary of how the time was spent.'
null: true,
description: 'Summary of how the time was spent.'
field :project, Types::ProjectType,
null: false,
description: 'Target project of the timelog merge request or issue.'
null: false,
description: 'Target project of the timelog merge request or issue.'
def user
Gitlab::Graphql::Loaders::BatchModelLoader.new(User, object.user_id).find

View File

@ -10,50 +10,50 @@ module Types
authorize :read_todo
field :id, GraphQL::Types::ID,
description: 'ID of the to-do item.',
null: false
description: 'ID of the to-do item.',
null: false
field :project, Types::ProjectType,
description: 'Project this to-do item is associated with.',
null: true
description: 'Project this to-do item is associated with.',
null: true
field :group, 'Types::GroupType',
description: 'Group this to-do item is associated with.',
null: true
description: 'Group this to-do item is associated with.',
null: true
field :author, Types::UserType,
description: 'Author of this to-do item.',
null: false
description: 'Author of this to-do item.',
null: false
field :action, Types::TodoActionEnum,
description: 'Action of the to-do item.',
null: false
description: 'Action of the to-do item.',
null: false
field :target, Types::TodoableInterface,
description: 'Target of the to-do item.',
calls_gitaly: true,
null: false
description: 'Target of the to-do item.',
calls_gitaly: true,
null: false
field :target_type, Types::TodoTargetEnum,
description: 'Target type of the to-do item.',
null: false
description: 'Target type of the to-do item.',
null: false
field :body, GraphQL::Types::String,
description: 'Body of the to-do item.',
null: false,
calls_gitaly: true # TODO This is only true when `target_type` is `Commit`. See https://gitlab.com/gitlab-org/gitlab/issues/34757#note_234752665
description: 'Body of the to-do item.',
null: false,
calls_gitaly: true # TODO This is only true when `target_type` is `Commit`. See https://gitlab.com/gitlab-org/gitlab/issues/34757#note_234752665
field :state, Types::TodoStateEnum,
description: 'State of the to-do item.',
null: false
description: 'State of the to-do item.',
null: false
field :created_at, Types::TimeType,
description: 'Timestamp this to-do item was created.',
null: false
description: 'Timestamp this to-do item was created.',
null: false
field :note, Types::Notes::NoteType,
description: 'Note which created this to-do item.',
null: true
description: 'Note which created this to-do item.',
null: true
def project
Gitlab::Graphql::Loaders::BatchModelLoader.new(Project, object.project_id).find

View File

@ -32,10 +32,12 @@ module Users
today = Date.today
return if user.last_activity_on == today
lease = Gitlab::ExclusiveLease.new("activity_service:#{user.id}", timeout: LEASE_TIMEOUT)
# Skip transaction checks for exclusive lease as it is breaking system specs.
# See issue: https://gitlab.com/gitlab-org/gitlab/-/issues/441536
return unless Gitlab::ExclusiveLease.skipping_transaction_check { lease.try_obtain }
if Feature.disabled?(:do_not_use_exclusive_lease_for_user_activity_service)
lease = Gitlab::ExclusiveLease.new("activity_service:#{user.id}", timeout: LEASE_TIMEOUT)
# Skip transaction checks for exclusive lease as it is breaking system specs.
# See issue: https://gitlab.com/gitlab-org/gitlab/-/issues/441536
return unless Gitlab::ExclusiveLease.skipping_transaction_check { lease.try_obtain }
end
user.update_attribute(:last_activity_on, today)

View File

@ -0,0 +1,9 @@
---
name: do_not_use_exclusive_lease_for_user_activity_service
feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/441536
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/151342
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/458719
milestone: '17.0'
group: group::tenant scale
type: gitlab_com_derisk
default_enabled: false

View File

@ -19,7 +19,7 @@ This could be a sign that you're testing an EE-specific behavior in a FOSS test.
Please make sure the spec files pass in AS-IF-FOSS mode either:
1. Locally with `FOSS_ONLY=1 bin/rspec -- %<spec_files>s`.
1. In the MR pipeline by verifying that the `rspec foss-impact` job has passed.
1. In the MR pipeline by verifying that the `rspec:predictive:trigger` job has passed in the as-if-foss cross project downstream pipeline.
1. In the MR pipelines by setting the ~"pipeline:run-as-if-foss" label on the MR (you can do it with the `/label ~"pipeline:run-as-if-foss"` quick action) and start a new MR pipeline.
MSG

View File

@ -9,19 +9,8 @@ class MigrateZoektSettingsInApplicationSettings < Gitlab::Database::Migration[2.
end
def up
return unless Gitlab.ee? # zoekt_settings available only in EE version
ApplicationSetting.reset_column_information
application_setting = ApplicationSetting.last
return if application_setting.nil? || application_setting.zoekt_settings.any?
zoekt_settings = {
zoekt_indexing_enabled: Feature.enabled?(:index_code_with_zoekt),
zoekt_indexing_paused: Feature.enabled?(:zoekt_pause_indexing, type: :ops),
zoekt_search_enabled: Feature.enabled?(:search_code_with_zoekt)
}
application_setting.update!(zoekt_settings: zoekt_settings)
# no-op this was just a data migration which is already done in 16.11. The plan is to remove the feature-flags used
# in this migration. So better to disable this migration in 17.0 to avoid any migration issues.
end
def down

View File

@ -75,10 +75,14 @@ Before you use a new Elasticsearch cluster in production, see the
> - Support for Elasticsearch 6.8 [removed](https://gitlab.com/gitlab-org/gitlab/-/issues/350275) in GitLab 15.0.
You don't have to change the GitLab configuration when you upgrade Elasticsearch.
When you upgrade Elasticsearch, you do not have to change the GitLab configuration.
You should pause indexing during an Elasticsearch upgrade so changes can still be tracked.
When the Elasticsearch cluster is fully upgraded and active, [resume indexing](#unpause-indexing).
During an Elasticsearch upgrade, you must:
- Pause indexing so changes can still be tracked.
- Disable advanced search so searches do not fail with an `HTTP 500` error.
When the Elasticsearch cluster is fully upgraded and active, [resume indexing](#unpause-indexing) and enable advanced search.
When you upgrade to GitLab 15.0 and later, you must use Elasticsearch 7.x and later.

View File

@ -6,6 +6,7 @@ require 'gitlab' unless Object.const_defined?(:Gitlab)
require 'set' # rubocop:disable Lint/RedundantRequireStatement -- Ruby 3.1 and earlier needs this. Drop this line after Ruby 3.2+ is only supported.
class GenerateAsIfFossEnv
# rubocop:disable Style/WordArray -- Probably a bug? It already is
FOSS_JOBS = Set.new(%w[
build-assets-image
build-qa-image
@ -16,11 +17,16 @@ class GenerateAsIfFossEnv
eslint
generate-apollo-graphql-schema
graphql-schema-dump
rspec-predictive:pipeline-generate
rspec:predictive:trigger
rspec:predictive:trigger\ single-db
rspec:predictive:trigger\ single-db-ci-connection
rubocop
qa:internal
qa:selectors
static-analysis
]).freeze
# rubocop:enable Style/WordArray
def initialize
@client = Gitlab.client(
@ -62,8 +68,10 @@ class GenerateAsIfFossEnv
end
def each_job
client.pipeline_jobs(ENV['CI_PROJECT_ID'], ENV['CI_PIPELINE_ID']).auto_paginate do |job|
yield(job)
%i[pipeline_jobs pipeline_bridges].each do |kind|
client.public_send(kind, ENV['CI_PROJECT_ID'], ENV['CI_PIPELINE_ID']).auto_paginate do |job| # rubocop:disable GitlabSecurity/PublicSend -- We're sending with static values, no concerns
yield(job)
end
end
end

View File

@ -102,6 +102,9 @@ RSpec.describe '.gitlab/ci/rules.gitlab-ci.yml', feature_category: :tooling do
if base['when'] == 'never'
expect(derived).to eq(base.except('when'))
elsif base['if'].start_with?('$ENABLE_')
derived_if = base['if'].sub('RSPEC', 'RSPEC_PREDICTIVE_TRIGGER')
expect(derived).to eq(base.merge('if' => derived_if))
elsif base['when'].nil?
expect(derived).to eq(base.merge('when' => 'never'))
end
@ -119,7 +122,13 @@ RSpec.describe '.gitlab/ci/rules.gitlab-ci.yml', feature_category: :tooling do
it_behaves_like 'predictive is inverse of non-predictive' do
let(:base_rules) { config.dig('.rails:rules:ee-and-foss-default-rules', 'rules') }
let(:derived_rules) { config.dig('.rails:rules:rspec-predictive', 'rules') }
let(:derived_rules) do
config.dig('.rails:rules:rspec-predictive', 'rules').reject do |rule|
# This happens in each specific rspec,
# which is outside of .rails:rules:ee-and-foss-default-rules
rule['if'].start_with?('$ENABLE_')
end
end
it 'contains an additional allow rule about code-backstage-patterns not present in the base' do
expected_rule = {
@ -135,24 +144,14 @@ RSpec.describe '.gitlab/ci/rules.gitlab-ci.yml', feature_category: :tooling do
describe '.rails:rules:single-db' do
it_behaves_like 'predictive is inverse of non-predictive' do
let(:base_rules) do
config.dig('.rails:rules:single-db', 'rules').reject do |rule|
rule['if'].start_with?('$ENABLE_') # We don't handle this yet
end
end
let(:base_rules) { config.dig('.rails:rules:single-db', 'rules') }
let(:derived_rules) { config.dig('.rails:rules:rspec-predictive:single-db', 'rules') }
end
end
describe '.rails:rules:single-db-ci-connection' do
it_behaves_like 'predictive is inverse of non-predictive' do
let(:base_rules) do
config.dig('.rails:rules:single-db-ci-connection', 'rules').reject do |rule|
rule['if'].start_with?('$ENABLE_') # We don't handle this yet
end
end
let(:base_rules) { config.dig('.rails:rules:single-db-ci-connection', 'rules') }
let(:derived_rules) { config.dig('.rails:rules:rspec-predictive:single-db-ci-connection', 'rules') }
end
end

View File

@ -140,6 +140,10 @@ FactoryBot.define do
avatar { fixture_file_upload('spec/fixtures/dk.png') }
end
trait :with_last_activity_on_today do
last_activity_on { Date.today }
end
trait :with_sign_ins do
sign_in_count { 3 }
current_sign_in_at { FFaker::Time.between(10.days.ago, 1.day.ago) }

View File

@ -1171,6 +1171,22 @@ describe('buildClient', () => {
'start_time=2020-07-05T00:00:00.000Z&end_time=2020-07-06T00:00:00.000Z',
);
});
it('handles exact timestamps', async () => {
await client.fetchLogs({
filters: {
dateRange: {
timestamp: '2024-02-19T16:10:15.4433398Z',
endDate: new Date('2024-02-19'),
startDate: new Date('2024-02-19'),
value: 'custom',
},
},
});
expect(getQueryParam()).toContain(
'start_time=2024-02-19T16:10:15.4433398Z&end_time=2024-02-19T16:10:15.4433398Z',
);
});
});
describe('attributes filters', () => {

View File

@ -4,6 +4,7 @@ import {
DATE_RANGE_QUERY_KEY,
DATE_RANGE_START_QUERY_KEY,
DATE_RANGE_END_QUERY_KEY,
TIMESTAMP_QUERY_KEY,
} from '~/observability/constants';
describe('periodToDate', () => {
@ -74,6 +75,15 @@ describe('queryToDateFilterObj', () => {
};
expect(queryToDateFilterObj(query)).toEqual({ value: '1h' });
});
it('returns a date range object from a nano timestamp', () => {
expect(queryToDateFilterObj({ timestamp: '2024-02-19T16:10:15.4433398Z' })).toEqual({
value: CUSTOM_DATE_RANGE_OPTION,
startDate: new Date('2024-02-19T16:10:15.443Z'),
endDate: new Date('2024-02-19T16:10:15.443Z'),
timestamp: '2024-02-19T16:10:15.4433398Z',
});
});
});
describe('dateFilterObjToQuery', () => {
@ -99,6 +109,11 @@ describe('dateFilterObjToQuery', () => {
[DATE_RANGE_END_QUERY_KEY]: '2020-01-02T00:00:00.000Z',
});
});
it('converts a filter with timestamp', () => {
expect(dateFilterObjToQuery({ timestamp: '2024-02-19T16:10:15.4433398Z' })).toEqual({
[TIMESTAMP_QUERY_KEY]: '2024-02-19T16:10:15.4433398Z',
});
});
it('returns empty object if filter is empty', () => {
expect(dateFilterObjToQuery({})).toEqual({});

View File

@ -4,9 +4,11 @@ require 'spec_helper'
RSpec.describe 'getting a detailed sentry error', feature_category: :error_tracking do
include GraphqlHelpers
let_it_be(:project) { create(:project, :repository) }
# user should have `last_on_activity` set to today,
# so that `Users::ActivityService` does not register any more updates.
let_it_be(:current_user) { create(:user, :with_last_activity_on_today) }
let_it_be(:project) { create(:project, :repository, namespace: create(:namespace, owner: current_user)) }
let_it_be(:project_setting) { create(:project_error_tracking_setting, project: project) }
let_it_be(:current_user) { project.first_owner }
let_it_be(:sentry_detailed_error) { build(:error_tracking_sentry_detailed_error) }
let(:sentry_gid) { sentry_detailed_error.to_global_id.to_s }

View File

@ -178,7 +178,7 @@ RSpec.describe API::UsageData, feature_category: :service_ping do
context 'with unknown event' do
it 'returns status ok' do
expect(Gitlab::Redis::HLL).not_to receive(:add)
expect(Gitlab::Redis::HLL).not_to receive(:add).with(hash_including(key: unknown_event))
post api(endpoint, user), params: { event: unknown_event }

View File

@ -1608,6 +1608,10 @@ RSpec.describe API::Users, :aggregate_failures, feature_category: :user_profile
end
context 'updating password' do
# user should have `last_on_activity` set to today,
# so that `Users::ActivityService` does not register any more updates.
let_it_be(:admin) { create(:admin, :with_last_activity_on_today) }
def update_password(user, admin, password = User.random_password)
put api("/users/#{user.id}", admin, admin_mode: true), params: { password: password }
end
@ -1617,7 +1621,7 @@ RSpec.describe API::Users, :aggregate_failures, feature_category: :user_profile
update_password(admin, admin)
expect(response).to have_gitlab_http_status(:ok)
expect(user.reload.password_expired?).to eq(false)
expect(admin.reload.password_expired?).to eq(false)
end
it 'does not enqueue the `admin changed your password` email' do

View File

@ -3,9 +3,11 @@
require 'spec_helper'
RSpec.describe Projects::MergeRequestsController, feature_category: :source_code_management do
let_it_be(:merge_request) { create(:merge_request) }
let_it_be(:project) { merge_request.project }
let_it_be(:user) { merge_request.author }
# user should have `last_on_activity` set to today,
# so that `Users::ActivityService` does not register any more updates.
let_it_be(:user) { create(:user, :with_last_activity_on_today, :with_namespace) }
let_it_be(:project) { create(:project, :repository, namespace: user.namespace) }
let_it_be(:merge_request) { create(:merge_request, source_project: project, author: user) }
describe 'GET #show' do
let_it_be(:group) { create(:group) }

View File

@ -3,7 +3,10 @@
require 'spec_helper'
RSpec.describe 'merge requests actions', feature_category: :source_code_management do
let_it_be(:project) { create(:project, :repository) }
# user should have `last_on_activity` set to today,
# so that `Users::ActivityService` does not register any more updates.
let_it_be(:user) { create(:user, :with_last_activity_on_today) }
let_it_be(:project) { create(:project, :repository, namespace: create(:namespace, owner: user)) }
let(:merge_request) do
create(
@ -15,7 +18,6 @@ RSpec.describe 'merge requests actions', feature_category: :source_code_manageme
)
end
let(:user) { project.first_owner }
let(:user2) { create(:user) }
before do

View File

@ -53,15 +53,32 @@ RSpec.describe GenerateAsIfFossEnv, feature_category: :tooling do
]
end
before do
messages = receive_message_chain(:client, :pipeline_jobs, :auto_paginate)
yield_jobs = jobs.inject(messages) do |stub, job|
stub.and_yield(double(name: job)) # rubocop:disable RSpec/VerifiedDoubles -- As explained at the top of this file, we do not load the Gitlab client
end
allow(Gitlab).to yield_jobs
let(:bridges) do
[
'rspec-predictive:pipeline-generate',
'rspec:predictive:trigger',
'rspec:predictive:trigger single-db',
'rspec:predictive:trigger single-db-ci-connection'
]
end
# rubocop:disable RSpec/VerifiedDoubles -- As explained at the top of this file, we do not load the Gitlab client
before do
client = double
allow(Gitlab).to receive(:client).and_return(client)
allow(client).to yield_jobs(:pipeline_jobs, jobs)
allow(client).to yield_jobs(:pipeline_bridges, bridges)
end
def yield_jobs(api_method, jobs)
messages = receive_message_chain(api_method, :auto_paginate)
jobs.inject(messages) do |stub, job_name|
stub.and_yield(double(name: job_name))
end
end
# rubocop:enable RSpec/VerifiedDoubles
end
describe '#variables' do
@ -100,7 +117,11 @@ RSpec.describe GenerateAsIfFossEnv, feature_category: :tooling do
ENABLE_RUBOCOP: 'true',
ENABLE_QA_INTERNAL: 'true',
ENABLE_QA_SELECTORS: 'true',
ENABLE_STATIC_ANALYSIS: 'true'
ENABLE_STATIC_ANALYSIS: 'true',
ENABLE_RSPEC_PREDICTIVE_PIPELINE_GENERATE: 'true',
ENABLE_RSPEC_PREDICTIVE_TRIGGER: 'true',
ENABLE_RSPEC_PREDICTIVE_TRIGGER_SINGLE_DB: 'true',
ENABLE_RSPEC_PREDICTIVE_TRIGGER_SINGLE_DB_CI_CONNECTION: 'true'
})
end
end
@ -142,6 +163,10 @@ RSpec.describe GenerateAsIfFossEnv, feature_category: :tooling do
ENABLE_QA_INTERNAL=true
ENABLE_QA_SELECTORS=true
ENABLE_STATIC_ANALYSIS=true
ENABLE_RSPEC_PREDICTIVE_PIPELINE_GENERATE=true
ENABLE_RSPEC_PREDICTIVE_TRIGGER=true
ENABLE_RSPEC_PREDICTIVE_TRIGGER_SINGLE_DB=true
ENABLE_RSPEC_PREDICTIVE_TRIGGER_SINGLE_DB_CI_CONNECTION=true
ENV
end
end

View File

@ -643,7 +643,11 @@ RSpec.describe QuickActions::InterpretService, feature_category: :team_planning
it 'returns the confidential message' do
_, _, message = service.execute(content, issuable)
issuable_type = issuable.to_ability_name.humanize(capitalize: false)
issuable_type = if issuable.to_ability_name == "work_item"
'item'
else
issuable.to_ability_name.humanize(capitalize: false)
end
expect(message).to eq("Made this #{issuable_type} confidential.")
end

View File

@ -41,10 +41,25 @@ RSpec.describe Users::ActivityService, feature_category: :user_profile do
.to(Date.today)
end
it 'tries to obtain ExclusiveLease' do
expect(Gitlab::ExclusiveLease).to receive(:new).with("activity_service:#{user.id}", anything).and_call_original
context 'for ExclusiveLease' do
context 'when the feature flag `do_not_use_exclusive_lease_for_user_activity_service` is turned off' do
before do
stub_feature_flags(do_not_use_exclusive_lease_for_user_activity_service: false)
end
subject.execute
it 'tries to obtain ExclusiveLease' do
expect(Gitlab::ExclusiveLease).to receive(:new).with("activity_service:#{user.id}", anything)
.and_call_original
subject.execute
end
end
it 'does not try to obtain ExclusiveLease' do
expect(Gitlab::ExclusiveLease).not_to receive(:new).with("activity_service:#{user.id}", anything)
subject.execute
end
end
it 'tracks RedisHLL event' do
@ -108,14 +123,22 @@ RSpec.describe Users::ActivityService, feature_category: :user_profile do
it_behaves_like 'does not update last_activity_on'
end
context 'when a lease could not be obtained' do
let(:last_activity_on) { nil }
context 'for ExclusiveLease' do
context 'when the feature flag `do_not_use_exclusive_lease_for_user_activity_service` is turned off' do
before do
stub_feature_flags(do_not_use_exclusive_lease_for_user_activity_service: false)
end
before do
stub_exclusive_lease_taken("activity_service:#{user.id}", timeout: 1.minute.to_i)
context 'when a lease could not be obtained' do
let(:last_activity_on) { nil }
before do
stub_exclusive_lease_taken("activity_service:#{user.id}", timeout: 1.minute.to_i)
end
it_behaves_like 'does not update last_activity_on'
end
end
it_behaves_like 'does not update last_activity_on'
end
end