Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-10-05 15:10:06 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-10-05 15:10:06 +0300
commitc8d44b1e3bd9dd04e5a3724fafd702932d1752be (patch)
treedc69c26c21d9f08e16c89af6e487e1106bda2ba7
parentae566b89cc7aa7f45ade0f91603b9f7d80c49528 (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--app/assets/javascripts/batch_comments/components/preview_dropdown.vue6
-rw-r--r--app/assets/javascripts/boards/components/board_list_header.vue10
-rw-r--r--app/assets/javascripts/boards/graphql/cache_updates.js4
-rw-r--r--app/assets/javascripts/boards/stores/actions.js6
-rw-r--r--app/helpers/safe_format_helper.rb4
-rw-r--r--app/helpers/todos_helper.rb4
-rw-r--r--app/models/bulk_import.rb4
-rw-r--r--app/models/integrations/bamboo.rb4
-rw-r--r--app/policies/project_policy.rb2
-rw-r--r--app/services/chat_names/find_user_service.rb7
-rw-r--r--app/services/users/signup_service.rb34
-rw-r--r--app/views/dashboard/todos/index.html.haml37
-rw-r--r--app/views/layouts/nav/_top_bar.html.haml2
-rw-r--r--app/workers/bulk_import_worker.rb2
-rw-r--r--doc/architecture/blueprints/cells/index.md6
-rw-r--r--doc/security/token_overview.md10
-rw-r--r--doc/security/two_factor_authentication.md2
-rw-r--r--doc/user/group/saml_sso/troubleshooting.md8
-rw-r--r--lib/api/helpers/packages/maven.rb2
-rw-r--r--locale/gitlab.pot7
-rw-r--r--qa/qa/mobile/page/sub_menus/common.rb2
-rw-r--r--qa/qa/page/project/sub_menus/common.rb2
-rw-r--r--spec/factories/bulk_import.rb4
-rw-r--r--spec/features/dashboard/todos/todos_spec.rb20
-rw-r--r--spec/frontend/batch_comments/components/preview_dropdown_spec.js8
-rw-r--r--spec/frontend/boards/mock_data.js2
-rw-r--r--spec/lib/gitlab/ci/config/interpolation/inputs_spec.rb422
-rw-r--r--spec/models/bulk_import_spec.rb8
-rw-r--r--spec/models/chat_name_spec.rb2
-rw-r--r--spec/models/integrations/bamboo_spec.rb6
-rw-r--r--spec/policies/project_policy_spec.rb1
-rw-r--r--spec/requests/api/maven_packages_spec.rb30
-rw-r--r--spec/services/chat_names/find_user_service_spec.rb26
-rw-r--r--spec/services/users/signup_service_spec.rb64
-rw-r--r--spec/workers/bulk_import_worker_spec.rb10
35 files changed, 458 insertions, 310 deletions
diff --git a/app/assets/javascripts/batch_comments/components/preview_dropdown.vue b/app/assets/javascripts/batch_comments/components/preview_dropdown.vue
index 42fc85cc5fb..3768e0bacfd 100644
--- a/app/assets/javascripts/batch_comments/components/preview_dropdown.vue
+++ b/app/assets/javascripts/batch_comments/components/preview_dropdown.vue
@@ -31,14 +31,14 @@ export default {
},
},
methods: {
- ...mapActions('diffs', ['setCurrentFileHash']),
+ ...mapActions('diffs', ['goToFile']),
...mapActions('batchComments', ['scrollToDraft']),
isOnLatestDiff(draft) {
return draft.position?.head_sha === this.getNoteableData.diff_head_sha;
},
async onClickDraft(draft) {
- if (this.viewDiffsFileByFile && draft.file_hash) {
- await this.setCurrentFileHash(draft.file_hash);
+ if (this.viewDiffsFileByFile) {
+ await this.goToFile({ path: draft.file_path });
}
if (draft.position && !this.isOnLatestDiff(draft)) {
diff --git a/app/assets/javascripts/boards/components/board_list_header.vue b/app/assets/javascripts/boards/components/board_list_header.vue
index 42c30dc8245..3dd7f7b70f5 100644
--- a/app/assets/javascripts/boards/components/board_list_header.vue
+++ b/app/assets/javascripts/boards/components/board_list_header.vue
@@ -201,8 +201,8 @@ export default {
})
);
},
- totalWeight() {
- return this.boardList?.totalWeight;
+ totalIssueWeight() {
+ return this.boardList?.totalIssueWeight;
},
canShowTotalWeight() {
return this.weightFeatureAvailable && !this.isLoading;
@@ -473,8 +473,8 @@ export default {
<div v-else>• {{ itemsTooltipLabel }}</div>
<div v-if="weightFeatureAvailable && !isLoading">
•
- <gl-sprintf :message="__('%{totalWeight} total weight')">
- <template #totalWeight>{{ totalWeight }}</template>
+ <gl-sprintf :message="__('%{totalIssueWeight} total weight')">
+ <template #totalIssueWeight>{{ totalIssueWeight }}</template>
</gl-sprintf>
</div>
</gl-tooltip>
@@ -507,7 +507,7 @@ export default {
<gl-tooltip :target="() => $refs.weightTooltip" :title="weightCountToolTip" />
<span ref="weightTooltip" class="gl-display-inline-flex gl-ml-3" data-testid="weight">
<gl-icon class="gl-mr-2" name="weight" :size="14" />
- {{ totalWeight }}
+ {{ totalIssueWeight }}
</span>
</template>
<!-- EE end -->
diff --git a/app/assets/javascripts/boards/graphql/cache_updates.js b/app/assets/javascripts/boards/graphql/cache_updates.js
index 3551c3ed982..0cd43cb9cee 100644
--- a/app/assets/javascripts/boards/graphql/cache_updates.js
+++ b/app/assets/javascripts/boards/graphql/cache_updates.js
@@ -68,7 +68,7 @@ export function updateIssueCountAndWeight({
boardList: {
...boardList,
issuesCount: boardList.issuesCount - 1,
- totalWeight: boardList.totalWeight - issue.weight,
+ totalIssueWeight: boardList.totalIssueWeight - issue.weight,
},
}),
);
@@ -83,7 +83,7 @@ export function updateIssueCountAndWeight({
boardList: {
...boardList,
issuesCount: boardList.issuesCount + 1,
- totalWeight: boardList.totalWeight + issue.weight,
+ totalIssueWeight: boardList.totalIssueWeight + issue.weight,
},
}),
);
diff --git a/app/assets/javascripts/boards/stores/actions.js b/app/assets/javascripts/boards/stores/actions.js
index e044283534a..3e7d7a7a8d3 100644
--- a/app/assets/javascripts/boards/stores/actions.js
+++ b/app/assets/javascripts/boards/stores/actions.js
@@ -621,7 +621,7 @@ export default {
__typename: 'BoardList',
id: fromList.boardList.id,
issuesCount: fromList.boardList.issuesCount - 1,
- totalWeight: fromList.boardList.totalWeight - Number(weight),
+ totalIssueWeight: fromList.boardList.totalIssueWeight - Number(weight),
},
};
@@ -645,7 +645,7 @@ export default {
__typename: 'BoardList',
id: toList.boardList.id,
issuesCount: toList.boardList.issuesCount + 1,
- totalWeight: toList.boardList.totalWeight + Number(weight),
+ totalIssueWeight: toList.boardList.totalIssueWeight + Number(weight),
},
};
@@ -731,7 +731,7 @@ export default {
__typename: 'BoardList',
id: fromList.boardList.id,
issuesCount: fromList.boardList.issuesCount + 1,
- totalWeight: fromList.boardList.totalWeight,
+ totalIssueWeight: fromList.boardList.totalIssueWeight,
},
};
diff --git a/app/helpers/safe_format_helper.rb b/app/helpers/safe_format_helper.rb
index 71bfc9ecb40..9f8c5082c26 100644
--- a/app/helpers/safe_format_helper.rb
+++ b/app/helpers/safe_format_helper.rb
@@ -25,8 +25,8 @@ module SafeFormatHelper
# Use `Kernel.format` to avoid conflicts with ViewComponent's `format`.
Kernel.format(
- html_escape_once(format),
- args.transform_values { |value| html_escape(value) }
+ ERB::Util.html_escape_once(format),
+ args.transform_values { |value| ERB::Util.html_escape(value) }
).html_safe
end
diff --git a/app/helpers/todos_helper.rb b/app/helpers/todos_helper.rb
index d4604124b77..d053aeb7bfe 100644
--- a/app/helpers/todos_helper.rb
+++ b/app/helpers/todos_helper.rb
@@ -166,6 +166,10 @@ module TodosHelper
todos_filter_params.values.none?
end
+ def todos_has_filtered_results?
+ params[:group_id] || params[:project_id] || params[:author_id] || params[:type] || params[:action_id]
+ end
+
def no_todos_messages
[
s_('Todos|Good job! Looks like you don\'t have anything left on your To-Do List'),
diff --git a/app/models/bulk_import.rb b/app/models/bulk_import.rb
index fde528e3fa0..a7ace7429d7 100644
--- a/app/models/bulk_import.rb
+++ b/app/models/bulk_import.rb
@@ -76,4 +76,8 @@ class BulkImport < ApplicationRecord
def supports_batched_export?
source_version_info >= self.class.min_gl_version_for_migration_in_batches
end
+
+ def completed?
+ finished? || failed? || timeout?
+ end
end
diff --git a/app/models/integrations/bamboo.rb b/app/models/integrations/bamboo.rb
index 0b8432136dd..4b98014e0cc 100644
--- a/app/models/integrations/bamboo.rb
+++ b/app/models/integrations/bamboo.rb
@@ -76,6 +76,10 @@ module Integrations
{ build_page: read_build_page(response), commit_status: read_commit_status(response) }
end
+ def avatar_url
+ ActionController::Base.helpers.image_path('illustrations/third-party-logos/integrations-logos/atlassian-bamboo.svg')
+ end
+
private
def get_build_result(response)
diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb
index a3a0ab70a43..20f88577d67 100644
--- a/app/policies/project_policy.rb
+++ b/app/policies/project_policy.rb
@@ -642,7 +642,6 @@ class ProjectPolicy < BasePolicy
prevent(*create_read_update_admin_destroy(:build))
prevent(*create_read_update_admin_destroy(:pipeline_schedule))
prevent(*create_read_update_admin_destroy(:environment))
- prevent(*create_read_update_admin_destroy(:cluster))
prevent(*create_read_update_admin_destroy(:deployment))
end
@@ -666,6 +665,7 @@ class ProjectPolicy < BasePolicy
prevent :read_pipeline_schedule
prevent(*create_read_update_admin_destroy(:feature_flag))
prevent(:admin_feature_flags_user_lists)
+ prevent(*create_read_update_admin_destroy(:cluster))
end
rule { container_registry_disabled }.policy do
diff --git a/app/services/chat_names/find_user_service.rb b/app/services/chat_names/find_user_service.rb
index 3b204d51bab..a7dc6a47a6b 100644
--- a/app/services/chat_names/find_user_service.rb
+++ b/app/services/chat_names/find_user_service.rb
@@ -11,7 +11,7 @@ module ChatNames
chat_name = find_chat_name
return unless chat_name
- chat_name.update_last_used_at
+ record_chat_activity(chat_name)
chat_name
end
@@ -27,5 +27,10 @@ module ChatNames
)
end
# rubocop: enable CodeReuse/ActiveRecord
+
+ def record_chat_activity(chat_name)
+ chat_name.update_last_used_at
+ Users::ActivityService.new(author: chat_name.user).execute
+ end
end
end
diff --git a/app/services/users/signup_service.rb b/app/services/users/signup_service.rb
deleted file mode 100644
index 810c29bfb1e..00000000000
--- a/app/services/users/signup_service.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-# frozen_string_literal: true
-
-module Users
- class SignupService < BaseService
- def initialize(current_user, params = {})
- @user = current_user
- @params = params.dup
- end
-
- def execute
- assign_attributes
- inject_validators
-
- if @user.save
- ServiceResponse.success
- else
- ServiceResponse.error(message: @user.errors.full_messages.join('. '))
- end
- end
-
- private
-
- def assign_attributes
- @user.assign_attributes(params) unless params.empty?
- end
-
- def inject_validators
- class << @user
- validates :role, presence: true
- validates :setup_for_company, inclusion: { in: [true, false], message: :blank }
- end
- end
- end
-end
diff --git a/app/views/dashboard/todos/index.html.haml b/app/views/dashboard/todos/index.html.haml
index 5084adadfcb..ab97507b3c8 100644
--- a/app/views/dashboard/todos/index.html.haml
+++ b/app/views/dashboard/todos/index.html.haml
@@ -6,8 +6,8 @@
- add_page_specific_style 'page_bundles/todos'
- add_page_specific_style 'page_bundles/issuable'
- filter_by_done = params[:state] == 'done'
-- open_todo_count = !filter_by_done ? @allowed_todos.count : todos_pending_count
-- done_todo_count = filter_by_done ? @allowed_todos.count : todos_done_count
+- open_todo_count = todos_has_filtered_results? && !filter_by_done ? @allowed_todos.count : todos_pending_count
+- done_todo_count = todos_has_filtered_results? && filter_by_done ? @allowed_todos.count : todos_done_count
.page-title-holder.d-flex.align-items-center
%h1.page-title.gl-font-size-h-display= _("To-Do List")
@@ -83,31 +83,38 @@
%ul.content-list.todos-list
= render @allowed_todos
= paginate @todos, theme: "gitlab"
- .js-nothing-here-container.empty-state.hidden
+ .js-nothing-here-container.gl-empty-state.gl-text-center.hidden
.svg-content.svg-150
= image_tag 'illustrations/empty-todos-all-done-md.svg'
.text-content.gl-text-center
- %h4
+ %h1.gl-font-size-h-display.gl-line-height-36.gl-mt-0
= s_("Todos|You're all done!")
- elsif current_user.todos.any?
- .col.todos-all-done.empty-state
+ .col.todos-all-done.gl-empty-state.gl-text-center
.svg-content.svg-150
- = image_tag 'illustrations/empty-todos-all-done-md.svg'
- .text-content.gl-text-center
- - if todos_filter_empty?
- %h4
+ = image_tag (!todos_filter_empty? && !todos_has_filtered_results?) ? 'illustrations/empty-todos-all-done-md.svg' : 'illustrations/empty-todos-md.svg'
+ .text-content.gl-text-center.gl-m-auto{ class: "gl-max-w-88!" }
+ %h1.gl-font-size-h-display.gl-line-height-36.gl-mt-0
+ - if todos_filter_empty?
= no_todos_messages.sample
+ - elsif todos_has_filtered_results?
+ = _("Sorry, your filter produced no results")
+ - else
+ = s_("Todos|Nothing is on your to-do list. Nice work!")
+
+ - if todos_filter_empty?
%p
= (s_("Todos|Are you looking for things to do? Take a look at %{strongStart}%{openIssuesLinkStart}open issues%{openIssuesLinkEnd}%{strongEnd}, contribute to %{strongStart}%{mergeRequestLinkStart}a merge request%{mergeRequestLinkEnd}%{mergeRequestLinkEnd}%{strongEnd}, or mention someone in a comment to automatically assign them a new to-do item.") % { strongStart: '<strong>', strongEnd: '</strong>', openIssuesLinkStart: "<a href=\"#{issues_dashboard_path}\">", openIssuesLinkEnd: '</a>', mergeRequestLinkStart: "<a href=\"#{merge_requests_dashboard_path}\">", mergeRequestLinkEnd: '</a>' }).html_safe
- - else
- %h4
- = s_("Todos|Nothing is on your to-do list. Nice work!")
+ - elsif todos_has_filtered_results?
+ %p
+ = link_to s_("Todos|Do you want to remove the filters?"), todos_filter_path(without: [:project_id, :author_id, :type, :action_id])
+
- else
- .col.empty-state
+ .col.gl-empty-state.gl-text-center
.svg-content.svg-150
= image_tag 'illustrations/empty-todos-md.svg'
- .text-content.gl-text-center
- %h4
+ .text-content.gl-text-center.gl-m-auto{ class: "gl-max-w-88!" }
+ %h1.gl-font-size-h-display.gl-line-height-36.gl-mt-0
= s_("Todos|Your To-Do List shows what to work on next")
%p
= (s_("Todos|When an issue or merge request is assigned to you, or when you receive a %{strongStart}@mention%{strongEnd} in a comment, this automatically triggers a new item in your To-Do List.") % { strongStart: '<strong>', strongEnd: '</strong>' }).html_safe
diff --git a/app/views/layouts/nav/_top_bar.html.haml b/app/views/layouts/nav/_top_bar.html.haml
index 59bfd8dd0dd..ef783b688e0 100644
--- a/app/views/layouts/nav/_top_bar.html.haml
+++ b/app/views/layouts/nav/_top_bar.html.haml
@@ -10,6 +10,6 @@
- if show_super_sidebar?
= render Pajamas::ButtonComponent.new(icon: 'sidebar', category: :tertiary, button_options: { class: 'js-super-sidebar-toggle-expand super-sidebar-toggle gl-ml-n3', aria: { controls: 'super-sidebar', expanded: 'false', label: _('Primary navigation sidebar') } })
- elsif defined?(@left_sidebar)
- = render Pajamas::ButtonComponent.new(icon: 'sidebar', category: :tertiary, button_options: { class: 'toggle-mobile-nav gl-ml-n3', data: { testid: 'toggle_mobile_nav_button' }, aria: { label: _('Open sidebar') } })
+ = render Pajamas::ButtonComponent.new(icon: 'sidebar', category: :tertiary, button_options: { class: 'toggle-mobile-nav gl-ml-n3', data: { testid: 'toggle-mobile-nav-button' }, aria: { label: _('Open sidebar') } })
= render "layouts/nav/breadcrumbs/breadcrumbs"
= render "layouts/nav/ask_duo_button"
diff --git a/app/workers/bulk_import_worker.rb b/app/workers/bulk_import_worker.rb
index 83b881ee525..c337824d6d9 100644
--- a/app/workers/bulk_import_worker.rb
+++ b/app/workers/bulk_import_worker.rb
@@ -14,7 +14,7 @@ class BulkImportWorker # rubocop:disable Scalability/IdempotentWorker
@bulk_import = BulkImport.find_by_id(bulk_import_id)
return unless @bulk_import
- return if @bulk_import.finished? || @bulk_import.failed?
+ return if @bulk_import.completed?
return @bulk_import.fail_op! if all_entities_failed?
return @bulk_import.finish! if all_entities_processed? && @bulk_import.started?
return re_enqueue if max_batch_size_exceeded? # Do not start more jobs if max allowed are already running
diff --git a/doc/architecture/blueprints/cells/index.md b/doc/architecture/blueprints/cells/index.md
index b8eea16e059..bac41a4d903 100644
--- a/doc/architecture/blueprints/cells/index.md
+++ b/doc/architecture/blueprints/cells/index.md
@@ -93,11 +93,11 @@ The first 2-3 quarters are required to define a general split of data and build
The Admin Area section for the most part is shared across a cluster.
-1. **User accounts are shared across cluster.**
+1. **User accounts are shared across cluster.** ✓
The purpose is to make `users` cluster-wide.
-1. **User can create Group.**
+1. **User can create Group.** ✓ ([demo](https://www.youtube.com/watch?v=LUyV0ncfdRs))
The purpose is to perform a targeted decomposition of `users` and `namespaces`, because `namespaces` will be stored locally in the Cell.
@@ -323,6 +323,7 @@ Below is a list of known affected features with preliminary proposed solutions.
- [Cells: Admin Area](impacted_features/admin-area.md)
- [Cells: Backups](impacted_features/backups.md)
+- [Cells: CI/CD Catalog](impacted_features/ci-cd-catalog.md)
- [Cells: CI Runners](impacted_features/ci-runners.md)
- [Cells: Container Registry](impacted_features/container-registry.md)
- [Cells: Contributions: Forks](impacted_features/contributions-forks.md)
@@ -344,7 +345,6 @@ Below is a list of known affected features with preliminary proposed solutions.
The following list of impacted features only represents placeholders that still require work to estimate the impact of Cells and develop solution proposals.
- [Cells: Agent for Kubernetes](impacted_features/agent-for-kubernetes.md)
-- [Cells: CI/CD Catalog](impacted_features/ci-cd-catalog.md)
- [Cells: Data pipeline ingestion](impacted_features/data-pipeline-ingestion.md)
- [Cells: GitLab Pages](impacted_features/gitlab-pages.md)
- [Cells: Personal Access Tokens](impacted_features/personal-access-tokens.md)
diff --git a/doc/security/token_overview.md b/doc/security/token_overview.md
index 637a0ffebcd..0a28f37b99b 100644
--- a/doc/security/token_overview.md
+++ b/doc/security/token_overview.md
@@ -93,9 +93,9 @@ Project maintainers and owners can add or enable a deploy key for a project repo
## Runner authentication tokens
-In GitLab 16.0 and later, you can use a runner authentication token to register
-runners instead of a runner registration token. Runner registration tokens have
-been [deprecated](../update/deprecations.md#registration-tokens-and-server-side-runner-arguments-in-gitlab-runner-register-command).
+In GitLab 16.0 and later, to register a runner, you can use a runner authentication token
+instead of a runner registration token. Runner registration tokens have
+been [deprecated](../ci/runners/new_creation_workflow.md).
After you create a runner and its configuration, you receive a runner authentication token
that you use to register the runner. The runner authentication token is stored locally in the
@@ -117,7 +117,7 @@ for the following executors only have access to the job token and not the runner
- SSH
Malicious access to a runner's file system may expose the `config.toml` file and the
-runner authentication token. The attacker could use the runner authentication
+runner authentication token. The attacker could use the runner authentication token
to [clone the runner](https://docs.gitlab.com/runner/security/#cloning-a-runner).
You can use the `runners` API to
@@ -126,7 +126,7 @@ programmatically [rotate or revoke a runner authentication token](../api/runners
## Runner registration tokens (deprecated)
WARNING:
-The ability to pass a runner registration token has been [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/380872) and is
+The ability to pass a runner registration token has been [deprecated](../ci/runners/new_creation_workflow.md) and is
planned for removal in GitLab 18.0, along with support for certain configuration arguments. This change is a breaking change. GitLab has implemented a new
[GitLab Runner token architecture](../ci/runners/new_creation_workflow.md), which introduces
a new method for registering runners and eliminates the
diff --git a/doc/security/two_factor_authentication.md b/doc/security/two_factor_authentication.md
index 0c569e9843d..281b8238d75 100644
--- a/doc/security/two_factor_authentication.md
+++ b/doc/security/two_factor_authentication.md
@@ -71,6 +71,8 @@ To enforce 2FA only for certain groups:
and projects, the shortest grace period is used.
1. Select **Save changes**.
+Enforcement affects all [direct and inherited members](../user/project/members/index.md#membership-types) in the group.
+
Access tokens are not required to provide a second factor for authentication because
they are API-based. Tokens generated before 2FA is enforced remain valid.
diff --git a/doc/user/group/saml_sso/troubleshooting.md b/doc/user/group/saml_sso/troubleshooting.md
index cf9b9f5d4eb..b6b2da8e44c 100644
--- a/doc/user/group/saml_sso/troubleshooting.md
+++ b/doc/user/group/saml_sso/troubleshooting.md
@@ -259,6 +259,14 @@ If you receive a `404` during setup when using "verify configuration", make sure
If a user is trying to sign in for the first time and the GitLab single sign-on URL has not [been configured](index.md#set-up-your-identity-provider), they may see a 404.
As outlined in the [user access section](index.md#link-saml-to-your-existing-gitlabcom-account), a group Owner needs to provide the URL to users.
+If the top-level group has [restricted membership by email domain](../access_and_permissions.md#restrict-group-access-by-domain), and a user with an email domain that is not allowed tries to sign in with SSO, that user might receive a 404. Users might have multiple accounts, and their SAML identity might be linked to their personal account which has an email address that is different than the company domain. To check this, verify the following:
+
+- That the top-level group has restricted membership by email domain.
+- That, in [Audit Events](../../../administration/audit_events.md) for the top-level group:
+ - You can see **Signed in with GROUP_SAML authentication** action for that user.
+ - That the user's username is the same as the username you configured for SAML SSO, by selecting the **Author** name.
+ - If the username is different to the username you configured for SAML SSO, ask the user to [unlink the SAML identity](index.md#unlink-accounts) from their personal account.
+
If all users are receiving a `404` after signing in to the identity provider (IdP):
- Verify the `assertion_consumer_service_url`:
diff --git a/lib/api/helpers/packages/maven.rb b/lib/api/helpers/packages/maven.rb
index 71d1ba486ed..04470bc6dad 100644
--- a/lib/api/helpers/packages/maven.rb
+++ b/lib/api/helpers/packages/maven.rb
@@ -9,10 +9,12 @@ module API
params :path_and_file_name do
requires :path,
type: String,
+ file_path: true,
desc: 'Package path',
documentation: { example: 'foo/bar/mypkg/1.0-SNAPSHOT' }
requires :file_name,
type: String,
+ file_path: true,
desc: 'Package file name',
documentation: { example: 'mypkg-1.0-SNAPSHOT.jar' }
end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 3e207cbb53a..d4e5fe8d58a 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -1268,10 +1268,10 @@ msgstr ""
msgid "%{totalCpu} (%{freeSpacePercentage}%{percentSymbol} free)"
msgstr ""
-msgid "%{totalMemory} (%{freeSpacePercentage}%{percentSymbol} free)"
+msgid "%{totalIssueWeight} total weight"
msgstr ""
-msgid "%{totalWeight} total weight"
+msgid "%{totalMemory} (%{freeSpacePercentage}%{percentSymbol} free)"
msgstr ""
msgid "%{total_warnings} warning(s) found:"
@@ -49497,6 +49497,9 @@ msgstr ""
msgid "Todos|Design"
msgstr ""
+msgid "Todos|Do you want to remove the filters?"
+msgstr ""
+
msgid "Todos|Due %{due_date}"
msgstr ""
diff --git a/qa/qa/mobile/page/sub_menus/common.rb b/qa/qa/mobile/page/sub_menus/common.rb
index aaa2de270b0..ef2815ca777 100644
--- a/qa/qa/mobile/page/sub_menus/common.rb
+++ b/qa/qa/mobile/page/sub_menus/common.rb
@@ -8,7 +8,7 @@ module QA
def open_mobile_nav_sidebar
unless has_css?('.sidebar-expanded-mobile')
Support::Retrier.retry_until do
- click_element(:toggle_mobile_nav_button)
+ click_element('toggle-mobile-nav-button')
has_css?('.sidebar-expanded-mobile')
end
end
diff --git a/qa/qa/page/project/sub_menus/common.rb b/qa/qa/page/project/sub_menus/common.rb
index 563bc9257c5..0619afc313c 100644
--- a/qa/qa/page/project/sub_menus/common.rb
+++ b/qa/qa/page/project/sub_menus/common.rb
@@ -14,7 +14,7 @@ module QA
include QA::Page::SubMenus::Common
view 'app/views/layouts/nav/_top_bar.html.haml' do
- element :toggle_mobile_nav_button
+ element 'toggle-mobile-nav-button'
end
end
end
diff --git a/spec/factories/bulk_import.rb b/spec/factories/bulk_import.rb
index 54d05264269..097bec43543 100644
--- a/spec/factories/bulk_import.rb
+++ b/spec/factories/bulk_import.rb
@@ -22,5 +22,9 @@ FactoryBot.define do
trait :failed do
status { -1 }
end
+
+ trait :timeout do
+ status { 3 }
+ end
end
end
diff --git a/spec/features/dashboard/todos/todos_spec.rb b/spec/features/dashboard/todos/todos_spec.rb
index 5642d083673..ade7da0cb49 100644
--- a/spec/features/dashboard/todos/todos_spec.rb
+++ b/spec/features/dashboard/todos/todos_spec.rb
@@ -7,6 +7,7 @@ RSpec.describe 'Dashboard Todos', feature_category: :team_planning do
let_it_be(:user) { create(:user, :no_super_sidebar, username: 'john') }
let_it_be(:user2) { create(:user, :no_super_sidebar, username: 'diane') }
+ let_it_be(:user3) { create(:user) }
let_it_be(:author) { create(:user, :no_super_sidebar) }
let_it_be(:project) { create(:project, :public) }
let_it_be(:issue) { create(:issue, project: project, due_date: Date.today, title: "Fix bug") }
@@ -424,6 +425,25 @@ RSpec.describe 'Dashboard Todos', feature_category: :team_planning do
wait_for_requests
end
end
+
+ describe 'shows a count of todos' do
+ before do
+ allow(Todo).to receive(:default_per_page).and_return(1)
+ create_list(:todo, 2, :mentioned, user: user3, project: project, target: issue, author: author, state: :pending)
+ create_list(:todo, 2, :mentioned, user: user3, project: project, target: issue, author: author, state: :done)
+ sign_in(user3)
+ end
+
+ it 'displays a count of all pending todos' do
+ visit dashboard_todos_path
+ expect(find('.js-todos-pending')).to have_content('2')
+ end
+
+ it 'displays a count of all done todos' do
+ visit dashboard_todos_path(state: 'done')
+ expect(find('.js-todos-done')).to have_content('2')
+ end
+ end
end
context 'User has a Build Failed todo' do
diff --git a/spec/frontend/batch_comments/components/preview_dropdown_spec.js b/spec/frontend/batch_comments/components/preview_dropdown_spec.js
index 608e9c82961..c0ad40b75ad 100644
--- a/spec/frontend/batch_comments/components/preview_dropdown_spec.js
+++ b/spec/frontend/batch_comments/components/preview_dropdown_spec.js
@@ -16,7 +16,7 @@ Vue.use(Vuex);
let wrapper;
-const setCurrentFileHash = jest.fn();
+const goToFile = jest.fn();
const scrollToDraft = jest.fn();
const findPreviewItem = () => wrapper.findComponent(PreviewItem);
@@ -27,7 +27,7 @@ function factory({ viewDiffsFileByFile = false, draftsCount = 1, sortedDrafts =
diffs: {
namespaced: true,
actions: {
- setCurrentFileHash,
+ goToFile,
},
state: {
viewDiffsFileByFile,
@@ -59,12 +59,12 @@ describe('Batch comments preview dropdown', () => {
it('toggles active file when viewDiffsFileByFile is true', async () => {
factory({
viewDiffsFileByFile: true,
- sortedDrafts: [{ id: 1, file_hash: 'hash' }],
+ sortedDrafts: [{ id: 1, file_hash: 'hash', file_path: 'foo' }],
});
findPreviewItem().trigger('click');
await nextTick();
- expect(setCurrentFileHash).toHaveBeenCalledWith(expect.anything(), 'hash');
+ expect(goToFile).toHaveBeenCalledWith(expect.anything(), { path: 'foo' });
await nextTick();
expect(scrollToDraft).toHaveBeenCalledWith(
diff --git a/spec/frontend/boards/mock_data.js b/spec/frontend/boards/mock_data.js
index dfcdb4c05d0..2b4c694bee9 100644
--- a/spec/frontend/boards/mock_data.js
+++ b/spec/frontend/boards/mock_data.js
@@ -973,7 +973,7 @@ export const boardListQueryResponse = ({
boardList: {
__typename: 'BoardList',
id: listId,
- totalWeight: 5,
+ totalIssueWeight: '5',
issuesCount,
},
},
diff --git a/spec/lib/gitlab/ci/config/interpolation/inputs_spec.rb b/spec/lib/gitlab/ci/config/interpolation/inputs_spec.rb
index c7ec0a34119..79a26f5a5ef 100644
--- a/spec/lib/gitlab/ci/config/interpolation/inputs_spec.rb
+++ b/spec/lib/gitlab/ci/config/interpolation/inputs_spec.rb
@@ -7,155 +7,291 @@ RSpec.describe Gitlab::Ci::Config::Interpolation::Inputs, feature_category: :pip
let(:specs) { { foo: { default: 'bar' } } }
let(:args) { {} }
- context 'when inputs are valid' do
- where(:specs, :args, :merged) do
- [
- [
- { foo: { default: 'bar' } }, {},
- { foo: 'bar' }
- ],
- [
- { foo: { default: 'bar' } }, { foo: 'test' },
- { foo: 'test' }
- ],
- [
- { foo: nil }, { foo: 'bar' },
- { foo: 'bar' }
- ],
- [
- { foo: { type: 'string' } }, { foo: 'bar' },
- { foo: 'bar' }
- ],
- [
- { foo: { type: 'string', default: 'bar' } }, { foo: 'test' },
- { foo: 'test' }
- ],
- [
- { foo: { type: 'string', default: 'bar' } }, {},
- { foo: 'bar' }
- ],
- [
- { foo: { default: 'bar' }, baz: nil }, { baz: 'test' },
- { foo: 'bar', baz: 'test' }
- ],
- [
- { number_input: { type: 'number' } },
- { number_input: 8 },
- { number_input: 8 }
- ],
- [
- { default_number_input: { default: 9, type: 'number' } },
- {},
- { default_number_input: 9 }
- ],
- [
- { true_input: { type: 'boolean' }, false_input: { type: 'boolean' } },
- { true_input: true, false_input: false },
- { true_input: true, false_input: false }
- ],
- [
- { default_boolean_input: { default: true, type: 'boolean' } },
- {},
- { default_boolean_input: true }
- ],
- [
- { test_input: { regex: '^input_value$' } },
- { test_input: 'input_value' },
- { test_input: 'input_value' }
- ],
- [
- { test_input: { regex: '^input_value$', default: 'input_value' } },
- {},
- { test_input: 'input_value' }
- ]
- ]
- end
-
- with_them do
- it 'contains the merged inputs' do
+ context 'when given unrecognized inputs' do
+ let(:specs) { { foo: nil } }
+ let(:args) { { foo: 'bar', test: 'bar' } }
+
+ it 'is invalid' do
+ expect(inputs).not_to be_valid
+ expect(inputs.errors).to contain_exactly('unknown input arguments: test')
+ end
+ end
+
+ context 'when given unrecognized configuration keywords' do
+ let(:specs) { { foo: 123 } }
+ let(:args) { {} }
+
+ it 'is invalid' do
+ expect(inputs).not_to be_valid
+ expect(inputs.errors).to contain_exactly(
+ 'unknown input specification for `foo` (valid types: boolean, number, string)'
+ )
+ end
+ end
+
+ context 'when the inputs have multiple errors' do
+ let(:specs) { { foo: nil } }
+ let(:args) { { test: 'bar', gitlab: '1' } }
+
+ it 'reports all of them' do
+ expect(inputs).not_to be_valid
+ expect(inputs.errors).to contain_exactly(
+ 'unknown input arguments: test, gitlab',
+ '`foo` input: required value has not been provided'
+ )
+ end
+ end
+
+ describe 'required inputs' do
+ let(:specs) { { foo: nil } }
+
+ context 'when a value is given' do
+ let(:args) { { foo: 'bar' } }
+
+ it 'is valid' do
+ expect(inputs).to be_valid
+ expect(inputs.to_hash).to eq(foo: 'bar')
+ end
+ end
+
+ context 'when no value is given' do
+ let(:args) { {} }
+
+ it 'is invalid' do
+ expect(inputs).not_to be_valid
+ expect(inputs.errors).to contain_exactly('`foo` input: required value has not been provided')
+ end
+ end
+ end
+
+ describe 'inputs with a default value' do
+ let(:specs) { { foo: { default: 'bar' } } }
+
+ context 'when a value is given' do
+ let(:args) { { foo: 'test' } }
+
+ it 'uses the given value' do
expect(inputs).to be_valid
- expect(inputs.to_hash).to eq(merged)
+ expect(inputs.to_hash).to eq(foo: 'test')
+ end
+ end
+
+ context 'when no value is given' do
+ let(:args) { {} }
+
+ it 'uses the default value' do
+ expect(inputs).to be_valid
+ expect(inputs.to_hash).to eq(foo: 'bar')
+ end
+ end
+ end
+
+ describe 'inputs with type validation' do
+ describe 'string validation' do
+ let(:specs) { { a_input: nil, b_input: { default: 'test' }, c_input: { default: 123 } } }
+ let(:args) { { a_input: 123, b_input: 123, c_input: 'test' } }
+
+ it 'is the default type' do
+ expect(inputs).not_to be_valid
+ expect(inputs.errors).to contain_exactly(
+ '`a_input` input: provided value is not a string',
+ '`b_input` input: provided value is not a string',
+ '`c_input` input: default value is not a string'
+ )
+ end
+
+ context 'when the value is a string' do
+ let(:specs) { { foo: { type: 'string' } } }
+ let(:args) { { foo: 'bar' } }
+
+ it 'is valid' do
+ expect(inputs).to be_valid
+ expect(inputs.to_hash).to eq(foo: 'bar')
+ end
+ end
+
+ context 'when the default is a string' do
+ let(:specs) { { foo: { type: 'string', default: 'bar' } } }
+ let(:args) { {} }
+
+ it 'is valid' do
+ expect(inputs).to be_valid
+ expect(inputs.to_hash).to eq(foo: 'bar')
+ end
+ end
+
+ context 'when the value is not a string' do
+ let(:specs) { { foo: { type: 'string' } } }
+ let(:args) { { foo: 123 } }
+
+ it 'is invalid' do
+ expect(inputs).not_to be_valid
+ expect(inputs.errors).to contain_exactly('`foo` input: provided value is not a string')
+ end
+ end
+
+ context 'when the default is not a string' do
+ let(:specs) { { foo: { default: 123, type: 'string' } } }
+ let(:args) { {} }
+
+ it 'is invalid' do
+ expect(inputs).not_to be_valid
+ expect(inputs.errors).to contain_exactly('`foo` input: default value is not a string')
+ end
+ end
+ end
+
+ describe 'number validation' do
+ let(:specs) { { integer: { type: 'number' }, float: { type: 'number' } } }
+
+ context 'when the value is a float or integer' do
+ let(:args) { { integer: 6, float: 6.6 } }
+
+ it 'is valid' do
+ expect(inputs).to be_valid
+ expect(inputs.to_hash).to eq(integer: 6, float: 6.6)
+ end
+ end
+
+ context 'when the default is a float or integer' do
+ let(:specs) { { integer: { default: 6, type: 'number' }, float: { default: 6.6, type: 'number' } } }
+
+ it 'is valid' do
+ expect(inputs).to be_valid
+ expect(inputs.to_hash).to eq(integer: 6, float: 6.6)
+ end
+ end
+
+ context 'when the value is not a number' do
+ let(:specs) { { number_input: { type: 'number' } } }
+ let(:args) { { number_input: 'NaN' } }
+
+ it 'is invalid' do
+ expect(inputs).not_to be_valid
+ expect(inputs.errors).to contain_exactly('`number_input` input: provided value is not a number')
+ end
+ end
+
+ context 'when the default is not a number' do
+ let(:specs) { { number_input: { default: 'NaN', type: 'number' } } }
+ let(:args) { {} }
+
+ it 'is invalid' do
+ expect(inputs).not_to be_valid
+ expect(inputs.errors).to contain_exactly('`number_input` input: default value is not a number')
+ end
+ end
+ end
+
+ describe 'boolean validation' do
+ context 'when the value is true or false' do
+ let(:specs) { { truthy: { type: 'boolean' }, falsey: { type: 'boolean' } } }
+ let(:args) { { truthy: true, falsey: false } }
+
+ it 'is valid' do
+ expect(inputs).to be_valid
+ expect(inputs.to_hash).to eq(truthy: true, falsey: false)
+ end
+ end
+
+ context 'when the default is true or false' do
+ let(:specs) { { truthy: { default: true, type: 'boolean' }, falsey: { default: false, type: 'boolean' } } }
+ let(:args) { {} }
+
+ it 'is valid' do
+ expect(inputs).to be_valid
+ expect(inputs.to_hash).to eq(truthy: true, falsey: false)
+ end
+ end
+
+ context 'when the value is not a boolean' do
+ let(:specs) { { boolean_input: { type: 'boolean' } } }
+ let(:args) { { boolean_input: 'string' } }
+
+ it 'is invalid' do
+ expect(inputs).not_to be_valid
+ expect(inputs.errors).to contain_exactly('`boolean_input` input: provided value is not a boolean')
+ end
+ end
+
+ context 'when the default is not a boolean' do
+ let(:specs) { { boolean_input: { default: 'string', type: 'boolean' } } }
+ let(:args) { {} }
+
+ it 'is invalid' do
+ expect(inputs).not_to be_valid
+ expect(inputs.errors).to contain_exactly('`boolean_input` input: default value is not a boolean')
+ end
+ end
+ end
+
+ context 'when given an unknown type' do
+ let(:specs) { { unknown: { type: 'datetime' } } }
+ let(:args) { { unknown: '2023-10-31' } }
+
+ it 'is invalid' do
+ expect(inputs).not_to be_valid
+ expect(inputs.errors).to contain_exactly(
+ 'unknown input specification for `unknown` (valid types: boolean, number, string)'
+ )
end
end
end
- context 'when inputs are invalid' do
- where(:specs, :args, :errors) do
- [
- [
- { foo: nil }, { foo: 'bar', test: 'bar' },
- ['unknown input arguments: test']
- ],
- [
- { foo: nil }, { test: 'bar', gitlab: '1' },
- ['unknown input arguments: test, gitlab', '`foo` input: required value has not been provided']
- ],
- [
- { foo: 123 }, {},
- ['unknown input specification for `foo` (valid types: boolean, number, string)']
- ],
- [
- { a: nil, foo: 123 }, { a: '123' },
- ['unknown input specification for `foo` (valid types: boolean, number, string)']
- ],
- [
- { foo: nil }, {},
- ['`foo` input: required value has not been provided']
- ],
- [
- { foo: { default: 123 } }, { foo: 'test' },
- ['`foo` input: default value is not a string']
- ],
- [
- { foo: { default: 'test' } }, { foo: 123 },
- ['`foo` input: provided value is not a string']
- ],
- [
- { foo: nil }, { foo: 123 },
- ['`foo` input: provided value is not a string']
- ],
- [
- { number_input: { type: 'number' } },
- { number_input: 'NaN' },
- ['`number_input` input: provided value is not a number']
- ],
- [
- { default_number_input: { default: 'NaN', type: 'number' } },
- {},
- ['`default_number_input` input: default value is not a number']
- ],
- [
- { boolean_input: { type: 'boolean' } },
- { boolean_input: 'string' },
- ['`boolean_input` input: provided value is not a boolean']
- ],
- [
- { default_boolean_input: { default: 'string', type: 'boolean' } },
- {},
- ['`default_boolean_input` input: default value is not a boolean']
- ],
- [
- { test_input: { regex: '^input_value$' } },
- { test_input: 'input' },
- ['`test_input` input: provided value does not match required RegEx pattern']
- ],
- [
- { test_input: { regex: '^input_value$', default: 'default' } },
- {},
- ['`test_input` input: default value does not match required RegEx pattern']
- ],
- [
- { test_input: { regex: '^input_value$', type: 'number' } },
- { test_input: 999 },
- ['`test_input` input: RegEx validation can only be used with string inputs']
- ]
- ]
- end
-
- with_them do
- it 'contains the merged inputs', :aggregate_failures do
+ describe 'inputs with RegEx validation' do
+ context 'when given a value that matches the pattern' do
+ let(:specs) { { test_input: { regex: '^input_value$' } } }
+ let(:args) { { test_input: 'input_value' } }
+
+ it 'is valid' do
+ expect(inputs).to be_valid
+ expect(inputs.to_hash).to eq(test_input: 'input_value')
+ end
+ end
+
+ context 'when given a default that matches the pattern' do
+ let(:specs) { { test_input: { default: 'input_value', regex: '^input_value$' } } }
+ let(:args) { {} }
+
+ it 'is valid' do
+ expect(inputs).to be_valid
+ expect(inputs.to_hash).to eq(test_input: 'input_value')
+ end
+ end
+
+ context 'when given a value that does not match the pattern' do
+ let(:specs) { { test_input: { regex: '^input_value$' } } }
+ let(:args) { { test_input: 'input' } }
+
+ it 'is invalid' do
+ expect(inputs).not_to be_valid
+ expect(inputs.errors).to contain_exactly(
+ '`test_input` input: provided value does not match required RegEx pattern'
+ )
+ end
+ end
+
+ context 'when given a default that does not match the pattern' do
+ let(:specs) { { test_input: { default: 'input', regex: '^input_value$' } } }
+ let(:args) { {} }
+
+ it 'is invalid' do
+ expect(inputs).not_to be_valid
+ expect(inputs.errors).to contain_exactly(
+ '`test_input` input: default value does not match required RegEx pattern'
+ )
+ end
+ end
+
+ context 'when used with any type other than `string`' do
+ let(:specs) { { test_input: { regex: '^input_value$', type: 'number' } } }
+ let(:args) { { test_input: 999 } }
+
+ it 'is invalid' do
expect(inputs).not_to be_valid
- expect(inputs.errors).to contain_exactly(*errors)
+ expect(inputs.errors).to contain_exactly(
+ '`test_input` input: RegEx validation can only be used with string inputs'
+ )
end
end
end
diff --git a/spec/models/bulk_import_spec.rb b/spec/models/bulk_import_spec.rb
index a50fc6eaba4..ff24f57f7c4 100644
--- a/spec/models/bulk_import_spec.rb
+++ b/spec/models/bulk_import_spec.rb
@@ -40,6 +40,14 @@ RSpec.describe BulkImport, type: :model, feature_category: :importers do
it { expect(described_class.min_gl_version_for_project_migration.to_s).to eq('14.4.0') }
end
+ describe '#completed?' do
+ it { expect(described_class.new(status: -1)).to be_completed }
+ it { expect(described_class.new(status: 0)).not_to be_completed }
+ it { expect(described_class.new(status: 1)).not_to be_completed }
+ it { expect(described_class.new(status: 2)).to be_completed }
+ it { expect(described_class.new(status: 3)).to be_completed }
+ end
+
describe '#source_version_info' do
it 'returns source_version as Gitlab::VersionInfo' do
bulk_import = build(:bulk_import, source_version: '9.13.2')
diff --git a/spec/models/chat_name_spec.rb b/spec/models/chat_name_spec.rb
index 5271e52f429..dbe013f3872 100644
--- a/spec/models/chat_name_spec.rb
+++ b/spec/models/chat_name_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe ChatName, feature_category: :integrations do
- let_it_be(:chat_name) { create(:chat_name) }
+ let_it_be_with_reload(:chat_name) { create(:chat_name) }
subject { chat_name }
diff --git a/spec/models/integrations/bamboo_spec.rb b/spec/models/integrations/bamboo_spec.rb
index 3b459ab9d5b..50e131a0845 100644
--- a/spec/models/integrations/bamboo_spec.rb
+++ b/spec/models/integrations/bamboo_spec.rb
@@ -205,6 +205,12 @@ RSpec.describe Integrations::Bamboo, :use_clean_rails_memory_store_caching, feat
end
end
+ describe '#avatar_url' do
+ it 'returns the avatar image path' do
+ expect(subject.avatar_url).to eq(ActionController::Base.helpers.image_path('illustrations/third-party-logos/integrations-logos/atlassian-bamboo.svg'))
+ end
+ end
+
def stub_update_and_build_request(status: 200, body: nil)
bamboo_full_url = 'http://gitlab.com/bamboo/updateAndBuild.action?buildKey=foo&os_authType=basic'
diff --git a/spec/policies/project_policy_spec.rb b/spec/policies/project_policy_spec.rb
index e7c2dcc4158..3de006d8c9b 100644
--- a/spec/policies/project_policy_spec.rb
+++ b/spec/policies/project_policy_spec.rb
@@ -288,7 +288,6 @@ RSpec.describe ProjectPolicy, feature_category: :system_access do
:create_build, :read_build, :update_build, :admin_build, :destroy_build,
:create_pipeline_schedule, :read_pipeline_schedule_variables, :update_pipeline_schedule, :admin_pipeline_schedule, :destroy_pipeline_schedule,
:create_environment, :read_environment, :update_environment, :admin_environment, :destroy_environment,
- :create_cluster, :read_cluster, :update_cluster, :admin_cluster, :destroy_cluster,
:create_deployment, :read_deployment, :update_deployment, :admin_deployment, :destroy_deployment
]
diff --git a/spec/requests/api/maven_packages_spec.rb b/spec/requests/api/maven_packages_spec.rb
index 4e746802500..1f841eefff2 100644
--- a/spec/requests/api/maven_packages_spec.rb
+++ b/spec/requests/api/maven_packages_spec.rb
@@ -377,6 +377,20 @@ RSpec.describe API::MavenPackages, feature_category: :package_registry do
end
end
+ shared_examples 'rejecting request with invalid params' do
+ context 'with invalid maven path' do
+ subject { download_file(file_name: package_file.file_name, path: 'foo/bar/%0d%0ahttp:/%2fexample.com') }
+
+ it_behaves_like 'returning response status with error', status: :bad_request, error: 'path should be a valid file path'
+ end
+
+ context 'with invalid file name' do
+ subject { download_file(file_name: '%0d%0ahttp:/%2fexample.com') }
+
+ it_behaves_like 'returning response status with error', status: :bad_request, error: 'file_name should be a valid file path'
+ end
+ end
+
describe 'GET /api/v4/packages/maven/*path/:file_name' do
context 'a public project' do
let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, property: 'i_package_maven_user' } }
@@ -403,6 +417,8 @@ RSpec.describe API::MavenPackages, feature_category: :package_registry do
it_behaves_like 'returning response status', :forbidden
end
+ it_behaves_like 'rejecting request with invalid params'
+
it 'returns not found when a package is not found' do
finder = double('finder', execute: nil)
expect(::Packages::Maven::PackageFinder).to receive(:new).and_return(finder)
@@ -444,6 +460,8 @@ RSpec.describe API::MavenPackages, feature_category: :package_registry do
end
end
+ it_behaves_like 'rejecting request with invalid params'
+
it_behaves_like 'handling groups, subgroups and user namespaces for', 'getting a file', visibilities: { public: :redirect, internal: :not_found }
end
@@ -501,6 +519,8 @@ RSpec.describe API::MavenPackages, feature_category: :package_registry do
end
end
+ it_behaves_like 'rejecting request with invalid params'
+
it_behaves_like 'handling groups, subgroups and user namespaces for', 'getting a file', visibilities: { public: :redirect, internal: :not_found, private: :not_found }
end
@@ -566,6 +586,8 @@ RSpec.describe API::MavenPackages, feature_category: :package_registry do
end
end
+ it_behaves_like 'rejecting request with invalid params'
+
it_behaves_like 'handling groups and subgroups for', 'getting a file for a group'
end
@@ -597,6 +619,8 @@ RSpec.describe API::MavenPackages, feature_category: :package_registry do
end
end
+ it_behaves_like 'rejecting request with invalid params'
+
it_behaves_like 'handling groups and subgroups for', 'getting a file for a group', visibilities: { internal: :unauthorized, public: :redirect }
end
@@ -634,6 +658,8 @@ RSpec.describe API::MavenPackages, feature_category: :package_registry do
it_behaves_like 'returning response status', :redirect
end
+ it_behaves_like 'rejecting request with invalid params'
+
context 'with group deploy token' do
subject { download_file_with_token(file_name: package_file.file_name, request_headers: group_deploy_token_headers) }
@@ -786,6 +812,8 @@ RSpec.describe API::MavenPackages, feature_category: :package_registry do
it_behaves_like 'returning response status', :redirect
end
+
+ it_behaves_like 'rejecting request with invalid params'
end
context 'private project' do
@@ -830,6 +858,8 @@ RSpec.describe API::MavenPackages, feature_category: :package_registry do
it_behaves_like 'returning response status', :redirect
end
+
+ it_behaves_like 'rejecting request with invalid params'
end
it_behaves_like 'forwarding package requests'
diff --git a/spec/services/chat_names/find_user_service_spec.rb b/spec/services/chat_names/find_user_service_spec.rb
index 14bece4efb4..94a56553983 100644
--- a/spec/services/chat_names/find_user_service_spec.rb
+++ b/spec/services/chat_names/find_user_service_spec.rb
@@ -8,7 +8,7 @@ RSpec.describe ChatNames::FindUserService, :clean_gitlab_redis_shared_state, fea
context 'find user mapping' do
let_it_be(:user) { create(:user) }
- let_it_be(:chat_name) { create(:chat_name, user: user) }
+ let(:chat_name) { create(:chat_name, user: user) }
let(:team_id) { chat_name.team_id }
let(:user_id) { chat_name.chat_id }
@@ -19,26 +19,20 @@ RSpec.describe ChatNames::FindUserService, :clean_gitlab_redis_shared_state, fea
end
it 'updates the last used timestamp if one is not already set' do
- expect(chat_name.last_used_at).to be_nil
-
- subject
-
- expect(chat_name.reload.last_used_at).to be_present
+ expect { subject }.to change { chat_name.reload.last_used_at }.from(nil)
end
it 'only updates an existing timestamp once within a certain time frame' do
- chat_name = create(:chat_name, user: user)
- service = described_class.new(team_id, user_id)
-
- expect(chat_name.last_used_at).to be_nil
-
- service.execute
-
- time = chat_name.reload.last_used_at
+ expect { described_class.new(team_id, user_id).execute }.to change { chat_name.reload.last_used_at }.from(nil)
+ expect { described_class.new(team_id, user_id).execute }.not_to change { chat_name.reload.last_used_at }
+ end
- service.execute
+ it 'records activity for the related user' do
+ expect_next_instance_of(Users::ActivityService, author: user) do |activity_service|
+ expect(activity_service).to receive(:execute)
+ end
- expect(chat_name.reload.last_used_at).to eq(time)
+ subject
end
end
diff --git a/spec/services/users/signup_service_spec.rb b/spec/services/users/signup_service_spec.rb
deleted file mode 100644
index e35cb03fd1c..00000000000
--- a/spec/services/users/signup_service_spec.rb
+++ /dev/null
@@ -1,64 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Users::SignupService, feature_category: :system_access do
- let_it_be(:user) { create(:user, setup_for_company: true) }
-
- describe '#execute' do
- context 'when updating name' do
- it 'updates the name attribute' do
- result = update_user(user, name: 'New Name')
-
- expect(result.success?).to be(true)
- expect(user.reload.name).to eq('New Name')
- end
-
- it 'returns an error result when name is missing' do
- result = update_user(user, name: '')
-
- expect(user.reload.name).not_to be_blank
- expect(result.success?).to be(false)
- expect(result.message).to include("Name can't be blank")
- end
- end
-
- context 'when updating role' do
- it 'updates the role attribute' do
- result = update_user(user, role: 'development_team_lead')
-
- expect(result.success?).to be(true)
- expect(user.reload.role).to eq('development_team_lead')
- end
-
- it 'returns an error result when role is missing' do
- result = update_user(user, role: '')
-
- expect(user.reload.role).not_to be_blank
- expect(result.success?).to be(false)
- expect(result.message).to eq("Role can't be blank")
- end
- end
-
- context 'when updating setup_for_company' do
- it 'updates the setup_for_company attribute' do
- result = update_user(user, setup_for_company: 'false')
-
- expect(result.success?).to be(true)
- expect(user.reload.setup_for_company).to be(false)
- end
-
- it 'returns an error result when setup_for_company is missing' do
- result = update_user(user, setup_for_company: '')
-
- expect(user.reload.setup_for_company).not_to be_blank
- expect(result.success?).to be(false)
- expect(result.message).to eq("Setup for company can't be blank")
- end
- end
-
- def update_user(user, opts)
- described_class.new(user, opts).execute
- end
- end
-end
diff --git a/spec/workers/bulk_import_worker_spec.rb b/spec/workers/bulk_import_worker_spec.rb
index c96e5ace124..092eb7c5264 100644
--- a/spec/workers/bulk_import_worker_spec.rb
+++ b/spec/workers/bulk_import_worker_spec.rb
@@ -32,6 +32,16 @@ RSpec.describe BulkImportWorker, feature_category: :importers do
end
end
+ context 'when bulk import is timeout' do
+ it 'does nothing' do
+ bulk_import = create(:bulk_import, :timeout)
+
+ expect(described_class).not_to receive(:perform_in)
+
+ subject.perform(bulk_import.id)
+ end
+ end
+
context 'when all entities are processed' do
it 'marks bulk import as finished' do
bulk_import = create(:bulk_import, :started)