{{ $options.i18n.feedbackBannerText }}
+'.html_safe, codeClose: ''.html_safe }
+
+- if @profiles.present?
+ .gl-mt-3
+ - @profiles.each do |path, profiles|
+ .card
+ .card-header
+ %code= path
+ %ul.content-list
+ - profiles.each do |profile|
+ %li
+ = link_to profile.time.to_s(:long) + ' ' + profile.profile_mode.capitalize,
+ admin_requests_profile_path(profile)
+- else
+ %p
+ = _('No profiles found')
diff --git a/app/views/layouts/nav/sidebar/_admin.html.haml b/app/views/layouts/nav/sidebar/_admin.html.haml
index f661d6277d6..f820f911d61 100644
--- a/app/views/layouts/nav/sidebar/_admin.html.haml
+++ b/app/views/layouts/nav/sidebar/_admin.html.haml
@@ -103,6 +103,10 @@
= link_to admin_health_check_path, title: _('Health Check') do
%span
= _('Health Check')
+ = nav_link(controller: :requests_profiles) do
+ = link_to admin_requests_profiles_path, title: _('Requests Profiles') do
+ %span
+ = _('Requests Profiles')
- if Gitlab::CurrentSettings.current_application_settings.grafana_enabled?
= nav_link do
= link_to Gitlab::CurrentSettings.current_application_settings.grafana_url, target: '_blank', title: _('Metrics Dashboard'), rel: 'noopener noreferrer' do
diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml
index 39c69d6cf50..e07a2a6ef1d 100644
--- a/app/workers/all_queues.yml
+++ b/app/workers/all_queues.yml
@@ -597,6 +597,15 @@
:weight: 1
:idempotent:
:tags: []
+- :name: cronjob:requests_profiles
+ :worker_name: RequestsProfilesWorker
+ :feature_category: :source_code_management
+ :has_external_dependencies:
+ :urgency: :low
+ :resource_boundary: :unknown
+ :weight: 1
+ :idempotent:
+ :tags: []
- :name: cronjob:schedule_merge_request_cleanup_refs
:worker_name: ScheduleMergeRequestCleanupRefsWorker
:feature_category: :code_review
diff --git a/app/workers/requests_profiles_worker.rb b/app/workers/requests_profiles_worker.rb
new file mode 100644
index 00000000000..e02b63fb621
--- /dev/null
+++ b/app/workers/requests_profiles_worker.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+class RequestsProfilesWorker # rubocop:disable Scalability/IdempotentWorker
+ include ApplicationWorker
+
+ data_consistency :always
+
+ # rubocop:disable Scalability/CronWorkerContext
+ # This worker does not perform work scoped to a context
+ include CronjobQueue
+ # rubocop:enable Scalability/CronWorkerContext
+
+ feature_category :source_code_management
+
+ def perform
+ Gitlab::RequestProfiler.remove_all_profiles
+ end
+end
diff --git a/config/feature_flags/development/ci_bulk_insert_tags.yml b/config/feature_flags/development/ci_bulk_insert_tags.yml
index 6b8ad4ef39d..52a3e22379c 100644
--- a/config/feature_flags/development/ci_bulk_insert_tags.yml
+++ b/config/feature_flags/development/ci_bulk_insert_tags.yml
@@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/346124
milestone: '14.6'
type: development
group: group::pipeline execution
-default_enabled: false
+default_enabled: true
diff --git a/config/feature_flags/ops/show_gitlab_agent_feedback.yml b/config/feature_flags/ops/show_gitlab_agent_feedback.yml
new file mode 100644
index 00000000000..2e2af65fea8
--- /dev/null
+++ b/config/feature_flags/ops/show_gitlab_agent_feedback.yml
@@ -0,0 +1,8 @@
+---
+name: show_gitlab_agent_feedback
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/78567
+rollout_issue_url:
+milestone: '14.8'
+type: ops
+group: group::configure
+default_enabled: true
diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb
index 4110fa1df0d..8244f570a18 100644
--- a/config/initializers/1_settings.rb
+++ b/config/initializers/1_settings.rb
@@ -479,6 +479,9 @@ Settings.cron_jobs['import_export_project_cleanup_worker']['job_class'] = 'Impor
Settings.cron_jobs['ci_archive_traces_cron_worker'] ||= Settingslogic.new({})
Settings.cron_jobs['ci_archive_traces_cron_worker']['cron'] ||= '17 * * * *'
Settings.cron_jobs['ci_archive_traces_cron_worker']['job_class'] = 'Ci::ArchiveTracesCronWorker'
+Settings.cron_jobs['requests_profiles_worker'] ||= Settingslogic.new({})
+Settings.cron_jobs['requests_profiles_worker']['cron'] ||= '0 0 * * *'
+Settings.cron_jobs['requests_profiles_worker']['job_class'] = 'RequestsProfilesWorker'
Settings.cron_jobs['remove_expired_members_worker'] ||= Settingslogic.new({})
Settings.cron_jobs['remove_expired_members_worker']['cron'] ||= '10 0 * * *'
Settings.cron_jobs['remove_expired_members_worker']['job_class'] = 'RemoveExpiredMembersWorker'
diff --git a/config/initializers/request_profiler.rb b/config/initializers/request_profiler.rb
index deabe2d3caa..2eb9f53d2a3 100644
--- a/config/initializers/request_profiler.rb
+++ b/config/initializers/request_profiler.rb
@@ -1,5 +1,6 @@
# frozen_string_literal: true
Rails.application.configure do |config|
+ config.middleware.use(Gitlab::RequestProfiler::Middleware)
config.middleware.use(Gitlab::Middleware::Speedscope)
end
diff --git a/config/metrics/counts_28d/20210216182125_user_sast_jobs.yml b/config/metrics/counts_28d/20210216182125_user_sast_jobs.yml
deleted file mode 100644
index e8d8b469b2e..00000000000
--- a/config/metrics/counts_28d/20210216182125_user_sast_jobs.yml
+++ /dev/null
@@ -1,23 +0,0 @@
----
-data_category: operational
-key_path: usage_activity_by_stage_monthly.secure.user_sast_jobs
-description: Users who run a SAST job
-product_section: sec
-product_stage: secure
-product_group: group::static analysis
-product_category: static_application_security_testing
-value_type: number
-status: active
-time_frame: 28d
-data_source: database
-distribution:
-- ce
-- ee
-tier:
-- free
-- premium
-- ultimate
-performance_indicator_type:
-- gmau
-- paid_gmau
-milestone: "<13.9"
diff --git a/config/metrics/counts_28d/20210216182127_user_secret_detection_jobs.yml b/config/metrics/counts_28d/20210216182127_user_secret_detection_jobs.yml
deleted file mode 100644
index 67a1c63b528..00000000000
--- a/config/metrics/counts_28d/20210216182127_user_secret_detection_jobs.yml
+++ /dev/null
@@ -1,23 +0,0 @@
----
-data_category: operational
-key_path: usage_activity_by_stage_monthly.secure.user_secret_detection_jobs
-description: Users who run a Secret Detection job
-product_section: sec
-product_stage: secure
-product_group: group::static analysis
-product_category: secret_detection
-value_type: number
-status: active
-time_frame: 28d
-data_source: database
-distribution:
-- ce
-- ee
-tier:
-- free
-- premium
-- ultimate
-performance_indicator_type:
-- gmau
-- paid_gmau
-milestone: "<13.9"
diff --git a/config/metrics/counts_all/20210216182116_user_sast_jobs.yml b/config/metrics/counts_all/20210216182116_user_sast_jobs.yml
deleted file mode 100644
index b4822d0d89b..00000000000
--- a/config/metrics/counts_all/20210216182116_user_sast_jobs.yml
+++ /dev/null
@@ -1,20 +0,0 @@
----
-data_category: operational
-key_path: usage_activity_by_stage.secure.user_sast_jobs
-description: Count of SAST jobs per user
-product_section: sec
-product_stage: secure
-product_group: group::static analysis
-product_category: static_application_security_testing
-value_type: number
-status: active
-time_frame: all
-data_source: database
-distribution:
-- ce
-- ee
-tier:
-- free
-- premium
-- ultimate
-milestone: "<13.9"
diff --git a/config/metrics/counts_all/20210216182118_user_secret_detection_jobs.yml b/config/metrics/counts_all/20210216182118_user_secret_detection_jobs.yml
deleted file mode 100644
index 6dde13f823b..00000000000
--- a/config/metrics/counts_all/20210216182118_user_secret_detection_jobs.yml
+++ /dev/null
@@ -1,20 +0,0 @@
----
-data_category: operational
-key_path: usage_activity_by_stage.secure.user_secret_detection_jobs
-description: Count of Secret Detection Jobs per user
-product_section: sec
-product_stage: secure
-product_group: group::static analysis
-product_category: secret_detection
-value_type: number
-status: active
-time_frame: all
-data_source: database
-distribution:
-- ce
-- ee
-tier:
-- free
-- premium
-- ultimate
-milestone: "<13.9"
diff --git a/config/routes/admin.rb b/config/routes/admin.rb
index da5df8bfdb3..ed1afc9efa3 100644
--- a/config/routes/admin.rb
+++ b/config/routes/admin.rb
@@ -100,6 +100,7 @@ namespace :admin do
resource :background_jobs, controller: 'background_jobs', only: [:show]
resource :system_info, controller: 'system_info', only: [:show]
+ resources :requests_profiles, only: [:index, :show], param: :name, constraints: { name: /.+\.(html|txt)/ }
resources :projects, only: [:index]
diff --git a/data/deprecations/14-8-runner-api-status-filter-does-accept-active-or-paused.yml b/data/deprecations/14-8-runner-api-status-filter-does-accept-active-or-paused.yml
new file mode 100644
index 00000000000..16dd0566a7d
--- /dev/null
+++ b/data/deprecations/14-8-runner-api-status-filter-does-accept-active-or-paused.yml
@@ -0,0 +1,18 @@
+- name: "REST API Runner will not accept `status` filter values of `active` or `paused`"
+ announcement_milestone: "14.8" # The milestone when this feature was first announced as deprecated.
+ announcement_date: "2022-02-22"
+ removal_milestone: "15.0" # the milestone when this feature is planned to be removed
+ removal_date: "2022-05-22" # the date of the milestone release when this feature is planned to be removed
+ breaking_change: true
+ body: | # Do not modify this line, instead modify the lines below.
+ The GitLab Runner REST endpoints will stop accepting `paused` or `active` as a status value in GitLab 15.0.
+
+ A runner's status will only relate to runner contact status, such as: `online`, `offline`.
+ Status values `paused` or `active` will no longer be accepted and will be replaced by the `paused` query parameter.
+
+ When checking for paused runners, API users are advised to specify `paused=true` as the query parameter.
+ When checking for active runners, specify `paused=false`.
+ stage: Verify
+ tiers: [Core, Premium, Ultimate]
+ issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/351109
+ documentation_url: https://docs.gitlab.com/ee/api/runners.html
diff --git a/db/post_migrate/20210415155043_move_container_registry_enabled_to_project_features3.rb b/db/post_migrate/20210415155043_move_container_registry_enabled_to_project_features3.rb
index 6fd8d280c97..f63d7c5138b 100644
--- a/db/post_migrate/20210415155043_move_container_registry_enabled_to_project_features3.rb
+++ b/db/post_migrate/20210415155043_move_container_registry_enabled_to_project_features3.rb
@@ -2,7 +2,6 @@
class MoveContainerRegistryEnabledToProjectFeatures3 < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
- include Gitlab::Database::DynamicModelHelpers
BATCH_SIZE = 21_000
MIGRATION = 'MoveContainerRegistryEnabledToProjectFeature'
diff --git a/db/post_migrate/20210525075724_clean_up_pending_builds_table.rb b/db/post_migrate/20210525075724_clean_up_pending_builds_table.rb
index c380f15188a..59b41dd2008 100644
--- a/db/post_migrate/20210525075724_clean_up_pending_builds_table.rb
+++ b/db/post_migrate/20210525075724_clean_up_pending_builds_table.rb
@@ -10,7 +10,7 @@ class CleanUpPendingBuildsTable < ActiveRecord::Migration[6.0]
def up
return unless Gitlab.dev_or_test_env? || Gitlab.com?
- each_batch_range('ci_pending_builds', of: BATCH_SIZE) do |min, max|
+ each_batch_range('ci_pending_builds', connection: connection, of: BATCH_SIZE) do |min, max|
execute <<~SQL
DELETE FROM ci_pending_builds
USING ci_builds
diff --git a/db/post_migrate/20210610102413_migrate_protected_attribute_to_pending_builds.rb b/db/post_migrate/20210610102413_migrate_protected_attribute_to_pending_builds.rb
index f47ff244d7a..47a6e39e87a 100644
--- a/db/post_migrate/20210610102413_migrate_protected_attribute_to_pending_builds.rb
+++ b/db/post_migrate/20210610102413_migrate_protected_attribute_to_pending_builds.rb
@@ -8,7 +8,7 @@ class MigrateProtectedAttributeToPendingBuilds < ActiveRecord::Migration[6.1]
def up
return unless Gitlab.dev_or_test_env? || Gitlab.com?
- each_batch_range('ci_pending_builds', of: 1000) do |min, max|
+ each_batch_range('ci_pending_builds', connection: connection, of: 1000) do |min, max|
execute <<~SQL
UPDATE ci_pending_builds
SET protected = true
diff --git a/doc/administration/postgresql/replication_and_failover.md b/doc/administration/postgresql/replication_and_failover.md
index 5777f35bfcf..8c7151606a5 100644
--- a/doc/administration/postgresql/replication_and_failover.md
+++ b/doc/administration/postgresql/replication_and_failover.md
@@ -1029,7 +1029,7 @@ Considering these, you should carefully plan your PostgreSQL upgrade:
```
NOTE:
- `gitlab-ctl pg-upgrade` tries to detect the role of the node. If for any reason the auto-detection does not work or you believe it did not detect the role correctly, you can use the `--leader` or `--replica` arguments to manually override it.
+ On a Geo secondary site, the Patroni leader node is called `standby leader`.
1. Stop Patroni **only on replicas**.
@@ -1049,6 +1049,11 @@ Considering these, you should carefully plan your PostgreSQL upgrade:
sudo gitlab-ctl pg-upgrade -V 12
```
+ NOTE:
+ `gitlab-ctl pg-upgrade` tries to detect the role of the node. If for any reason the auto-detection
+ does not work or you believe it did not detect the role correctly, you can use the `--leader` or
+ `--replica` arguments to manually override it.
+
1. Check the status of the leader and cluster. You can proceed only if you have a healthy leader:
```shell
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index 76c853ea1f8..6a876db1165 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -1018,6 +1018,30 @@ Input type: `CommitCreateInput`
| `content` | [`[String!]`](#string) | Contents of the commit. |
| `errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
+### `Mutation.configureContainerScanning`
+
+Configure Container Scanning for a project by enabling Container Scanning in a new or modified
+`.gitlab-ci.yml` file in a new branch. The new branch and a URL to
+create a merge request are part of the response.
+
+Input type: `ConfigureContainerScanningInput`
+
+#### Arguments
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| `clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
+| `projectPath` | [`ID!`](#id) | Full path of the project. |
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| `branch` | [`String`](#string) | Branch that has the new/modified `.gitlab-ci.yml` file. |
+| `clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
+| `errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
+| `successPath` | [`String`](#string) | Redirect path to use when the response is successful. |
+
### `Mutation.configureDependencyScanning`
Configure Dependency Scanning for a project by enabling Dependency Scanning in a new or modified
@@ -17834,6 +17858,7 @@ Name of the feature that the callout is for.
| `SECURITY_CONFIGURATION_DEVOPS_ALERT` | Callout feature name for security_configuration_devops_alert. |
| `SECURITY_CONFIGURATION_UPGRADE_BANNER` | Callout feature name for security_configuration_upgrade_banner. |
| `SECURITY_NEWSLETTER_CALLOUT` | Callout feature name for security_newsletter_callout. |
+| `SECURITY_TRAINING_FEATURE_PROMOTION` | Callout feature name for security_training_feature_promotion. |
| `SUGGEST_PIPELINE` | Callout feature name for suggest_pipeline. |
| `SUGGEST_POPOVER_DISMISSED` | Callout feature name for suggest_popover_dismissed. |
| `TABS_POSITION_HIGHLIGHT` | Callout feature name for tabs_position_highlight. |
diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md
index 56682f0f107..b91b0714274 100644
--- a/doc/api/merge_requests.md
+++ b/doc/api/merge_requests.md
@@ -44,7 +44,7 @@ placed on database-backed diffs. [Limits inherent to Gitaly](../development/diff
still apply.
- [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/349031) in GitLab 14.7,
-field `merge_user` can be either user who merged this merge request,
+field `merge_user` can be either user who merged this merge request,
user who set it to merge when pipeline succeeds or `null`.
Field `merged_by` (user who merged this merge request or `null`) has been deprecated.
@@ -1116,7 +1116,7 @@ POST /projects/:id/merge_requests
| `milestone_id` | integer | no | The global ID of a milestone. |
| `remove_source_branch` | boolean | no | Flag indicating if a merge request should remove the source branch when merging. |
| `allow_collaboration` | boolean | no | Allow commits from members who can merge to the target branch. |
-| `allow_maintainer_to_push` | boolean | no | [Deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/22665), see `allow_collaboration`. |
+| `allow_maintainer_to_push` | boolean | no | Alias of `allow_collaboration`. |
| `squash` | boolean | no | Squash commits into a single commit when merging. |
```json
@@ -1278,7 +1278,7 @@ PUT /projects/:id/merge_requests/:merge_request_iid
| `squash` | boolean | no | Squash commits into a single commit when merging. |
| `discussion_locked` | boolean | no | Flag indicating if the merge request's discussion is locked. If the discussion is locked only project members can add, edit or resolve comments. |
| `allow_collaboration` | boolean | no | Allow commits from members who can merge to the target branch. |
-| `allow_maintainer_to_push` | boolean | no | [Deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/22665), see `allow_collaboration`. |
+| `allow_maintainer_to_push` | boolean | no | Alias of `allow_collaboration`. |
Must include at least one non-required attribute from above.
diff --git a/doc/api/runners.md b/doc/api/runners.md
index 07914ec53a3..3cccb86194a 100644
--- a/doc/api/runners.md
+++ b/doc/api/runners.md
@@ -39,21 +39,27 @@ Get a list of specific runners available to the user.
GET /runners
GET /runners?scope=active
GET /runners?type=project_type
-GET /runners?status=active
+GET /runners?status=online
+GET /runners?paused=true
GET /runners?tag_list=tag1,tag2
```
-| Attribute | Type | Required | Description |
-|-------------|----------------|----------|---------------------|
-| `scope` | string | no | Deprecated: Use `type` or `status` instead. The scope of specific runners to show, one of: `active`, `paused`, `online`, `offline`; showing all runners if none provided |
-| `type` | string | no | The type of runners to show, one of: `instance_type`, `group_type`, `project_type` |
-| `status` | string | no | The status of runners to show, one of: `active`, `paused`, `online`, `offline` |
-| `tag_list` | string array | no | List of the runner's tags |
+| Attribute | Type | Required | Description |
+|------------|--------------|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `scope` | string | no | Deprecated: Use `type` or `status` instead. The scope of specific runners to show, one of: `active`, `paused`, `online` and `offline`; showing all runners if none provided |
+| `type` | string | no | The type of runners to show, one of: `instance_type`, `group_type`, `project_type` |
+| `status` | string | no | The status of runners to show, one of: `online` and `offline`. `active` and `paused` are also possible values which were deprecated in GitLab 14.8 and will be removed in GitLab 15.0 |
+| `paused` | boolean | no | Whether to include only runners that are accepting or ignoring new jobs |
+| `tag_list` | string array | no | List of the runner's tags |
```shell
curl --header "PRIVATE-TOKEN: paragraph.
' + end + + it 'renders the data' do + subject + + expect(response).to have_gitlab_http_status(:ok) + expect(response.body).to eq(sample_data) + end + end + + context 'when loading TXT profile' do + let(:basename) { "profile_#{Time.current.to_i}_memory.txt" } + + let(:sample_data) do + <<~TXT + Total allocated: 112096396 bytes (1080431 objects) + Total retained: 10312598 bytes (53567 objects) + TXT + end + + it 'renders the data' do + subject + + expect(response).to have_gitlab_http_status(:ok) + expect(response.body).to eq(sample_data) + end + end + + context 'when loading PDF profile' do + let(:basename) { "profile_#{Time.current.to_i}_anything.pdf" } + + let(:sample_data) { 'mocked pdf content' } + + it 'fails to render the data' do + expect { subject }.to raise_error(ActionController::UrlGenerationError, /No route matches.*unmatched constraints:/) + end + end + end +end diff --git a/spec/controllers/projects/commits_controller_spec.rb b/spec/controllers/projects/commits_controller_spec.rb index fd840fafa61..d84717f1124 100644 --- a/spec/controllers/projects/commits_controller_spec.rb +++ b/spec/controllers/projects/commits_controller_spec.rb @@ -88,6 +88,26 @@ RSpec.describe Projects::CommitsController do expect(response).to be_successful end + + context 'when limit is a hash' do + it 'uses the default limit' do + expect_any_instance_of(Repository).to receive(:commits).with( + "master", + path: "README.md", + limit: described_class::COMMITS_DEFAULT_LIMIT, + offset: 0 + ).and_call_original + + get(:show, params: { + namespace_id: project.namespace, + project_id: project, + id: id, + limit: { 'broken' => 'value' } + }) + + expect(response).to be_successful + end + end end context "when the ref name ends in .atom" do diff --git a/spec/controllers/projects/settings/repository_controller_spec.rb b/spec/controllers/projects/settings/repository_controller_spec.rb index 2bb93990c58..22287fea82c 100644 --- a/spec/controllers/projects/settings/repository_controller_spec.rb +++ b/spec/controllers/projects/settings/repository_controller_spec.rb @@ -33,6 +33,20 @@ RSpec.describe Projects::Settings::RepositoryController do expect(response).to redirect_to project_settings_repository_path(project) end + + context 'when project cleanup returns an error', :aggregate_failures do + it 'shows an error' do + expect(Projects::CleanupService) + .to receive(:enqueue) + .with(project, user, anything) + .and_return(status: :error, message: 'error message') + + put :cleanup, params: { namespace_id: project.namespace, project_id: project, project: { bfg_object_map: object_map } } + + expect(controller).to set_flash[:alert].to('error message') + expect(response).to redirect_to project_settings_repository_path(project) + end + end end describe 'POST create_deploy_token' do diff --git a/spec/features/admin/admin_requests_profiles_spec.rb b/spec/features/admin/admin_requests_profiles_spec.rb new file mode 100644 index 00000000000..e92528d431d --- /dev/null +++ b/spec/features/admin/admin_requests_profiles_spec.rb @@ -0,0 +1,136 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Admin::RequestsProfilesController' do + let(:tmpdir) { Dir.mktmpdir('profiler-test') } + + before do + stub_const('Gitlab::RequestProfiler::PROFILES_DIR', tmpdir) + admin = create(:admin) + sign_in(admin) + gitlab_enable_admin_mode_sign_in(admin) + end + + after do + FileUtils.rm_rf(tmpdir) + end + + describe 'GET /admin/requests_profiles' do + it 'shows the current profile token' do + allow(Rails).to receive(:cache).and_return(ActiveSupport::Cache::MemoryStore.new) + + visit admin_requests_profiles_path + + expect(page).to have_content("X-Profile-Token: #{Gitlab::RequestProfiler.profile_token}") + end + + context 'when having multiple profiles' do + let(:time1) { 1.hour.ago } + let(:time2) { 2.hours.ago } + + let(:profiles) do + [ + { + request_path: '/gitlab-org/gitlab-foss', + name: "|gitlab-org|gitlab-foss_#{time1.to_i}_execution.html", + created: time1, + profile_mode: 'Execution' + }, + { + request_path: '/gitlab-org/gitlab-foss', + name: "|gitlab-org|gitlab-foss_#{time2.to_i}_execution.html", + created: time2, + profile_mode: 'Execution' + }, + { + request_path: '/gitlab-org/gitlab-foss', + name: "|gitlab-org|gitlab-foss_#{time1.to_i}_memory.html", + created: time1, + profile_mode: 'Memory' + }, + { + request_path: '/gitlab-org/gitlab-foss', + name: "|gitlab-org|gitlab-foss_#{time2.to_i}_memory.html", + created: time2, + profile_mode: 'Memory' + }, + { + request_path: '/gitlab-org/infrastructure', + name: "|gitlab-org|infrastructure_#{time1.to_i}_execution.html", + created: time1, + profile_mode: 'Execution' + }, + { + request_path: '/gitlab-org/infrastructure', + name: "|gitlab-org|infrastructure_#{time2.to_i}_memory.html", + created: time2, + profile_mode: 'Memory' + }, + { + request_path: '/gitlab-org/infrastructure', + name: "|gitlab-org|infrastructure_#{time2.to_i}.html", + created: time2, + profile_mode: 'Unknown' + } + ] + end + + before do + profiles.each do |profile| + FileUtils.touch(File.join(Gitlab::RequestProfiler::PROFILES_DIR, profile[:name])) + end + end + + it 'lists all available profiles' do + visit admin_requests_profiles_path + + profiles.each do |profile| + within('.card', text: profile[:request_path]) do + expect(page).to have_selector( + "a[href='#{admin_requests_profile_path(profile[:name])}']", + text: "#{profile[:created].to_s(:long)} #{profile[:profile_mode]}") + end + end + end + end + end + + describe 'GET /admin/requests_profiles/:profile' do + context 'when a profile exists' do + before do + File.write("#{Gitlab::RequestProfiler::PROFILES_DIR}/#{profile}", content) + end + + context 'when is valid call stack profile' do + let(:content) { 'This is a call stack request profile' } + let(:profile) { "|gitlab-org|gitlab-ce_#{Time.now.to_i}_execution.html" } + + it 'displays the content' do + visit admin_requests_profile_path(profile) + + expect(page).to have_content(content) + end + end + + context 'when is valid memory profile' do + let(:content) { 'This is a memory request profile' } + let(:profile) { "|gitlab-org|gitlab-ce_#{Time.now.to_i}_memory.txt" } + + it 'displays the content' do + visit admin_requests_profile_path(profile) + + expect(page).to have_content(content) + end + end + end + + context 'when a profile does not exist' do + it 'shows an error message' do + visit admin_requests_profile_path('|non|existent_12345.html') + + expect(page).to have_content('Profile not found') + end + end + end +end diff --git a/spec/features/groups/members/manage_groups_spec.rb b/spec/features/groups/members/manage_groups_spec.rb index 2beecda23b5..61c6709f9cc 100644 --- a/spec/features/groups/members/manage_groups_spec.rb +++ b/spec/features/groups/members/manage_groups_spec.rb @@ -156,6 +156,26 @@ RSpec.describe 'Groups > Members > Manage groups', :js do group_outside_hierarchy.add_owner(user) end + context 'when the invite members group modal is enabled' do + it 'does not show self or ancestors', :aggregate_failures do + group_sibbling = create(:group, parent: group) + group_sibbling.add_owner(user) + + visit group_group_members_path(group_within_hierarchy) + + click_on 'Invite a group' + click_on 'Select a group' + wait_for_requests + + page.within('[data-testid="group-select-dropdown"]') do + expect(page).to have_selector("[entity-id='#{group_outside_hierarchy.id}']") + expect(page).to have_selector("[entity-id='#{group_sibbling.id}']") + expect(page).not_to have_selector("[entity-id='#{group.id}']") + expect(page).not_to have_selector("[entity-id='#{group_within_hierarchy.id}']") + end + end + end + context 'when sharing with groups outside the hierarchy is enabled' do context 'when the invite members group modal is disabled' do before do diff --git a/spec/features/projects/members/invite_group_spec.rb b/spec/features/projects/members/invite_group_spec.rb index 08ec2f54fba..066e0b0d20f 100644 --- a/spec/features/projects/members/invite_group_spec.rb +++ b/spec/features/projects/members/invite_group_spec.rb @@ -8,7 +8,7 @@ RSpec.describe 'Project > Members > Invite group', :js do include Spec::Support::Helpers::Features::MembersHelpers include Spec::Support::Helpers::Features::InviteMembersModalHelper - let(:maintainer) { create(:user) } + let_it_be(:maintainer) { create(:user) } using RSpec::Parameterized::TableSyntax @@ -190,17 +190,26 @@ RSpec.describe 'Project > Members > Invite group', :js do end describe 'the groups dropdown' do - context 'with multiple groups to choose from' do - let(:project) { create(:project) } + let_it_be(:parent_group) { create(:group, :public) } + let_it_be(:project_group) { create(:group, :public, parent: parent_group) } + let_it_be(:public_sub_subgroup) { create(:group, :public, parent: project_group) } + let_it_be(:public_sibbling_group) { create(:group, :public, parent: parent_group) } + let_it_be(:private_sibbling_group) { create(:group, :private, parent: parent_group) } + let_it_be(:private_membership_group) { create(:group, :private) } + let_it_be(:public_membership_group) { create(:group, :public) } + let_it_be(:project) { create(:project, group: project_group) } - it 'includes multiple groups' do + before do + private_membership_group.add_guest(maintainer) + public_membership_group.add_maintainer(maintainer) + + sign_in(maintainer) + end + + context 'for a project in a nested group' do + it 'does not show the groups inherited from projects' do project.add_maintainer(maintainer) - sign_in(maintainer) - - group1 = create(:group) - group1.add_owner(maintainer) - group2 = create(:group) - group2.add_owner(maintainer) + public_sibbling_group.add_maintainer(maintainer) visit project_project_members_path(project) @@ -208,57 +217,36 @@ RSpec.describe 'Project > Members > Invite group', :js do click_on 'Select a group' wait_for_requests - expect(page).to have_button(group1.name) - expect(page).to have_button(group2.name) - end - end + page.within('[data-testid="group-select-dropdown"]') do + expect_to_have_group(public_membership_group) + expect_to_have_group(public_sibbling_group) + expect_to_have_group(private_membership_group) - context 'for a project in a nested group' do - let!(:parent_group) { create(:group, :public) } - let!(:public_subgroup) { create(:group, :public, parent: parent_group) } - let!(:public_sub_subgroup) { create(:group, :public, parent: public_subgroup) } - let!(:private_subgroup) { create(:group, :private, parent: parent_group) } - let!(:project) { create(:project, :public, namespace: public_subgroup) } - - let!(:membership_group) { create(:group, :public) } - - before do - project.add_maintainer(maintainer) - membership_group.add_guest(maintainer) - - sign_in(maintainer) - end - - context 'when invite_members_group_modal feature enabled' do - it 'does not show the groups inherited from projects' do - visit project_project_members_path(project) - - click_on 'Invite a group' - click_on 'Select a group' - wait_for_requests - - expect(page).to have_button(membership_group.name) - expect(page).not_to have_button(parent_group.name) - expect(page).not_to have_button(public_subgroup.name) - expect(page).not_to have_button(public_sub_subgroup.name) - expect(page).not_to have_button(private_subgroup.name) + expect_not_to_have_group(public_sub_subgroup) + expect_not_to_have_group(private_sibbling_group) + expect_not_to_have_group(parent_group) + expect_not_to_have_group(project_group) end + end - # This behavior should be changed to exclude the ancestor and project - # group from the options once issue is fixed for the modal: - # https://gitlab.com/gitlab-org/gitlab/-/issues/329835 - it 'does show ancestors and the project group' do - parent_group.add_maintainer(maintainer) + it 'does not show the ancestors or project group', :aggregate_failures do + parent_group.add_maintainer(maintainer) - visit project_project_members_path(project) + visit project_project_members_path(project) - click_on 'Invite a group' - click_on 'Select a group' - wait_for_requests + click_on 'Invite a group' + click_on 'Select a group' + wait_for_requests - expect(page).to have_button(membership_group.name) - expect(page).to have_button(parent_group.name) - expect(page).to have_button(public_subgroup.name) + page.within('[data-testid="group-select-dropdown"]') do + expect_to_have_group(public_membership_group) + expect_to_have_group(public_sibbling_group) + expect_to_have_group(private_membership_group) + expect_to_have_group(public_sub_subgroup) + expect_to_have_group(private_sibbling_group) + + expect_not_to_have_group(parent_group) + expect_not_to_have_group(project_group) end end @@ -269,21 +257,26 @@ RSpec.describe 'Project > Members > Invite group', :js do stub_feature_flags(invite_members_group_modal: false) end - it 'does not show the groups inherited from projects' do + it 'does not show the groups inherited from projects', :aggregate_failures do + project.add_maintainer(maintainer) + public_sibbling_group.add_maintainer(maintainer) + visit project_project_members_path(project) click_on 'Invite group' click_on 'Search for a group' wait_for_requests - expect(group_invite_dropdown).to have_text(membership_group.name) - expect(group_invite_dropdown).not_to have_text(parent_group.name) - expect(group_invite_dropdown).not_to have_text(public_subgroup.name) - expect(group_invite_dropdown).not_to have_text(public_sub_subgroup.name) - expect(group_invite_dropdown).not_to have_text(private_subgroup.name) + expect(group_invite_dropdown).to have_text(public_membership_group.full_path) + expect(group_invite_dropdown).to have_text(public_sibbling_group.full_path) + expect(group_invite_dropdown).to have_text(private_membership_group.full_path) + expect(group_invite_dropdown).not_to have_text(public_sub_subgroup.full_path) + expect(group_invite_dropdown).not_to have_text(private_sibbling_group.full_path) + expect(group_invite_dropdown).not_to have_text(parent_group.full_path, exact: true) + expect(group_invite_dropdown).not_to have_text(project_group.full_path, exact: true) end - it 'does not show ancestors and the project group' do + it 'does not show the ancestors or project group', :aggregate_failures do parent_group.add_maintainer(maintainer) visit project_project_members_path(project) @@ -292,11 +285,23 @@ RSpec.describe 'Project > Members > Invite group', :js do click_on 'Search for a group' wait_for_requests - expect(group_invite_dropdown).to have_text(membership_group.name) - expect(group_invite_dropdown).not_to have_text(parent_group.name, exact: true) - expect(group_invite_dropdown).not_to have_text(public_subgroup.name, exact: true) + expect(group_invite_dropdown).to have_text(public_membership_group.full_path) + expect(group_invite_dropdown).to have_text(public_sub_subgroup.full_path) + expect(group_invite_dropdown).to have_text(public_sibbling_group.full_path) + expect(group_invite_dropdown).to have_text(private_sibbling_group.full_path) + expect(group_invite_dropdown).to have_text(private_membership_group.full_path) + expect(group_invite_dropdown).not_to have_text(parent_group.full_path, exact: true) + expect(group_invite_dropdown).not_to have_text(project_group.full_path, exact: true) end end + + def expect_to_have_group(group) + expect(page).to have_selector("[entity-id='#{group.id}']") + end + + def expect_not_to_have_group(group) + expect(page).not_to have_selector("[entity-id='#{group.id}']") + end end end end diff --git a/spec/frontend/clusters_list/components/agents_spec.js b/spec/frontend/clusters_list/components/agents_spec.js index 5d7a6a251d2..c8b86fb49c1 100644 --- a/spec/frontend/clusters_list/components/agents_spec.js +++ b/spec/frontend/clusters_list/components/agents_spec.js @@ -1,13 +1,18 @@ -import { GlAlert, GlKeysetPagination, GlLoadingIcon } from '@gitlab/ui'; +import { GlAlert, GlKeysetPagination, GlLoadingIcon, GlBanner } from '@gitlab/ui'; import { createLocalVue, shallowMount } from '@vue/test-utils'; import VueApollo from 'vue-apollo'; import { nextTick } from 'vue'; import AgentEmptyState from '~/clusters_list/components/agent_empty_state.vue'; import AgentTable from '~/clusters_list/components/agent_table.vue'; import Agents from '~/clusters_list/components/agents.vue'; -import { ACTIVE_CONNECTION_TIME } from '~/clusters_list/constants'; +import { + ACTIVE_CONNECTION_TIME, + AGENT_FEEDBACK_KEY, + AGENT_FEEDBACK_ISSUE, +} from '~/clusters_list/constants'; import getAgentsQuery from '~/clusters_list/graphql/queries/get_agents.query.graphql'; import createMockApollo from 'helpers/mock_apollo_helper'; +import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue'; const localVue = createLocalVue(); localVue.use(VueApollo); @@ -24,6 +29,7 @@ describe('Agents', () => { const createWrapper = async ({ props = {}, + glFeatures = {}, agents = [], pageInfo = null, trees = [], @@ -51,20 +57,29 @@ describe('Agents', () => { ...defaultProps, ...props, }, - provide: provideData, + provide: { + ...provideData, + glFeatures, + }, + stubs: { + GlBanner, + LocalStorageSync, + }, }); await nextTick(); }; - const findAgentTable = () => wrapper.find(AgentTable); - const findEmptyState = () => wrapper.find(AgentEmptyState); - const findPaginationButtons = () => wrapper.find(GlKeysetPagination); + const findAgentTable = () => wrapper.findComponent(AgentTable); + const findEmptyState = () => wrapper.findComponent(AgentEmptyState); + const findPaginationButtons = () => wrapper.findComponent(GlKeysetPagination); + const findAlert = () => wrapper.findComponent(GlAlert); + const findBanner = () => wrapper.findComponent(GlBanner); afterEach(() => { - if (wrapper) { - wrapper.destroy(); - } + wrapper.destroy(); + + localStorage.removeItem(AGENT_FEEDBACK_KEY); }); describe('when there is a list of agents', () => { @@ -150,6 +165,49 @@ describe('Agents', () => { expect(wrapper.emitted().onAgentsLoad).toEqual([[count]]); }); + describe.each` + featureFlagEnabled | localStorageItemExists | bannerShown + ${true} | ${false} | ${true} + ${true} | ${true} | ${false} + ${false} | ${true} | ${false} + ${false} | ${false} | ${false} + `( + 'when the feature flag enabled is $featureFlagEnabled and dismissed localStorage item exists is $localStorageItemExists', + ({ featureFlagEnabled, localStorageItemExists, bannerShown }) => { + const glFeatures = { + showGitlabAgentFeedback: featureFlagEnabled, + }; + beforeEach(() => { + if (localStorageItemExists) { + localStorage.setItem(AGENT_FEEDBACK_KEY, true); + } + + return createWrapper({ glFeatures, agents, count, trees }); + }); + + it(`should ${bannerShown ? 'show' : 'hide'} the feedback banner`, () => { + expect(findBanner().exists()).toBe(bannerShown); + }); + }, + ); + + describe('when the agent feedback banner is present', () => { + const glFeatures = { + showGitlabAgentFeedback: true, + }; + beforeEach(() => { + return createWrapper({ glFeatures, agents, count, trees }); + }); + + it('should render the correct title', () => { + expect(findBanner().props('title')).toBe('Tell us what you think'); + }); + + it('should render the correct issue link', () => { + expect(findBanner().props('buttonLink')).toBe(AGENT_FEEDBACK_ISSUE); + }); + }); + describe('when the agent has recently connected tokens', () => { it('should set agent status to active', () => { expect(findAgentTable().props('agents')).toMatchObject(expectedAgentsList); @@ -223,6 +281,10 @@ describe('Agents', () => { expect(findAgentTable().exists()).toBe(false); expect(findEmptyState().exists()).toBe(true); }); + + it('should not show agent feedback alert', () => { + expect(findAlert().exists()).toBe(false); + }); }); describe('when agents query has errored', () => { @@ -231,7 +293,7 @@ describe('Agents', () => { }); it('displays an alert message', () => { - expect(wrapper.find(GlAlert).exists()).toBe(true); + expect(findAlert().text()).toBe('An error occurred while loading your Agents'); }); }); diff --git a/spec/frontend/invite_members/components/group_select_spec.js b/spec/frontend/invite_members/components/group_select_spec.js index 6face6a785c..192f3fdd381 100644 --- a/spec/frontend/invite_members/components/group_select_spec.js +++ b/spec/frontend/invite_members/components/group_select_spec.js @@ -5,19 +5,20 @@ import * as groupsApi from '~/api/groups_api'; import GroupSelect from '~/invite_members/components/group_select.vue'; const accessLevels = { Guest: 10, Reporter: 20, Developer: 30, Maintainer: 40, Owner: 50 }; - -const createComponent = () => { - return mount(GroupSelect, { - propsData: { - accessLevels, - }, - }); -}; - const group1 = { id: 1, full_name: 'Group One', avatar_url: 'test' }; const group2 = { id: 2, full_name: 'Group Two', avatar_url: 'test' }; const allGroups = [group1, group2]; +const createComponent = (props = {}) => { + return mount(GroupSelect, { + propsData: { + invalidGroups: [], + accessLevels, + ...props, + }, + }); +}; + describe('GroupSelect', () => { let wrapper; @@ -90,6 +91,20 @@ describe('GroupSelect', () => { size: '32', }); }); + + describe('when filtering out the group from results', () => { + beforeEach(() => { + wrapper = createComponent({ invalidGroups: [group1.id] }); + }); + + it('does not find an invalid group', () => { + expect(findAvatarByLabel(group1.full_name)).toBe(undefined); + }); + + it('finds a group that is valid', () => { + expect(findAvatarByLabel(group2.full_name).exists()).toBe(true); + }); + }); }); describe('when group is selected from the dropdown', () => { diff --git a/spec/frontend/invite_members/components/invite_members_modal_spec.js b/spec/frontend/invite_members/components/invite_members_modal_spec.js index 8b02d916045..72db6904c31 100644 --- a/spec/frontend/invite_members/components/invite_members_modal_spec.js +++ b/spec/frontend/invite_members/components/invite_members_modal_spec.js @@ -46,6 +46,7 @@ jest.mock('~/lib/utils/url_utility', () => ({ const id = '1'; const name = 'test name'; const isProject = false; +const invalidGroups = []; const inviteeType = 'members'; const accessLevels = { Guest: 10, Reporter: 20, Developer: 30, Maintainer: 40, Owner: 50 }; const defaultAccessLevel = 10; @@ -93,6 +94,7 @@ const createComponent = (data = {}, props = {}) => { tasksToBeDoneOptions, projects, helpLink, + invalidGroups, ...props, }, data() { diff --git a/spec/frontend/pages/projects/shared/permissions/components/settings_panel_spec.js b/spec/frontend/pages/projects/shared/permissions/components/settings_panel_spec.js index 8a9bb025d55..305dce51971 100644 --- a/spec/frontend/pages/projects/shared/permissions/components/settings_panel_spec.js +++ b/spec/frontend/pages/projects/shared/permissions/components/settings_panel_spec.js @@ -244,7 +244,7 @@ describe('Settings Panel', () => { wrapper = mountComponent({ currentSettings: { visibilityLevel: visibilityOptions.PUBLIC } }); expect(findRepositoryFeatureProjectRow().props('helpText')).toBe( - 'View and edit files in this project. Non-project members will only have read access.', + 'View and edit files in this project. Non-project members have only read access.', ); }); }); diff --git a/spec/helpers/invite_members_helper_spec.rb b/spec/helpers/invite_members_helper_spec.rb index d8a97b93bc9..c032c7875a9 100644 --- a/spec/helpers/invite_members_helper_spec.rb +++ b/spec/helpers/invite_members_helper_spec.rb @@ -20,7 +20,8 @@ RSpec.describe InviteMembersHelper do attributes = { id: project.id, name: project.name, - default_access_level: Gitlab::Access::GUEST + default_access_level: Gitlab::Access::GUEST, + invalid_groups: project.related_group_ids } expect(helper.common_invite_modal_dataset(project)).to include(attributes) @@ -155,4 +156,28 @@ RSpec.describe InviteMembersHelper do end end end + + describe '#group_select_data' do + let_it_be(:group) { create(:group) } + + context 'when sharing with groups outside the hierarchy is disabled' do + before do + group.namespace_settings.update!(prevent_sharing_groups_outside_hierarchy: true) + end + + it 'provides the correct attributes' do + expect(helper.group_select_data(group)).to eq({ groups_filter: 'descendant_groups', parent_id: group.id }) + end + end + + context 'when sharing with groups outside the hierarchy is enabled' do + before do + group.namespace_settings.update!(prevent_sharing_groups_outside_hierarchy: false) + end + + it 'returns an empty hash' do + expect(helper.group_select_data(project.group)).to eq({}) + end + end + end end diff --git a/spec/lib/backup/manager_spec.rb b/spec/lib/backup/manager_spec.rb index 31cc3012eb1..9c186205067 100644 --- a/spec/lib/backup/manager_spec.rb +++ b/spec/lib/backup/manager_spec.rb @@ -409,7 +409,7 @@ RSpec.describe Backup::Manager do # the Fog mock only knows about directories we create explicitly connection = ::Fog::Storage.new(Gitlab.config.backup.upload.connection.symbolize_keys) - connection.directories.create(key: Gitlab.config.backup.upload.remote_directory) + connection.directories.create(key: Gitlab.config.backup.upload.remote_directory) # rubocop:disable Rails/SaveBang end context 'target path' do @@ -455,7 +455,7 @@ RSpec.describe Backup::Manager do } ) - connection.directories.create(key: Gitlab.config.backup.upload.remote_directory) + connection.directories.create(key: Gitlab.config.backup.upload.remote_directory) # rubocop:disable Rails/SaveBang end context 'with SSE-S3 without using storage_options' do @@ -521,7 +521,7 @@ RSpec.describe Backup::Manager do ) connection = ::Fog::Storage.new(Gitlab.config.backup.upload.connection.symbolize_keys) - connection.directories.create(key: Gitlab.config.backup.upload.remote_directory) + connection.directories.create(key: Gitlab.config.backup.upload.remote_directory) # rubocop:disable Rails/SaveBang end it 'does not attempt to set ACL' do diff --git a/spec/lib/gitlab/analytics/cycle_analytics/records_fetcher_spec.rb b/spec/lib/gitlab/analytics/cycle_analytics/records_fetcher_spec.rb index 4fe55ba0c0c..dc46dade87e 100644 --- a/spec/lib/gitlab/analytics/cycle_analytics/records_fetcher_spec.rb +++ b/spec/lib/gitlab/analytics/cycle_analytics/records_fetcher_spec.rb @@ -43,8 +43,8 @@ RSpec.describe Gitlab::Analytics::CycleAnalytics::RecordsFetcher do end before do - issue1.metrics.update(first_added_to_board_at: 3.days.ago, first_mentioned_in_commit_at: 2.days.ago) - issue2.metrics.update(first_added_to_board_at: 3.days.ago, first_mentioned_in_commit_at: 2.days.ago) + issue1.metrics.update!(first_added_to_board_at: 3.days.ago, first_mentioned_in_commit_at: 2.days.ago) + issue2.metrics.update!(first_added_to_board_at: 3.days.ago, first_mentioned_in_commit_at: 2.days.ago) end context 'when records are loaded by guest' do @@ -73,8 +73,8 @@ RSpec.describe Gitlab::Analytics::CycleAnalytics::RecordsFetcher do end before do - mr1.metrics.update(merged_at: 3.days.ago) - mr2.metrics.update(merged_at: 3.days.ago) + mr1.metrics.update!(merged_at: 3.days.ago) + mr2.metrics.update!(merged_at: 3.days.ago) end include_context 'when records are loaded by maintainer' @@ -95,9 +95,9 @@ RSpec.describe Gitlab::Analytics::CycleAnalytics::RecordsFetcher do end before(:all) do - issue1.metrics.update(first_added_to_board_at: 3.days.ago, first_mentioned_in_commit_at: 2.days.ago) - issue2.metrics.update(first_added_to_board_at: 3.days.ago, first_mentioned_in_commit_at: 2.days.ago) - issue3.metrics.update(first_added_to_board_at: 3.days.ago, first_mentioned_in_commit_at: 2.days.ago) + issue1.metrics.update!(first_added_to_board_at: 3.days.ago, first_mentioned_in_commit_at: 2.days.ago) + issue2.metrics.update!(first_added_to_board_at: 3.days.ago, first_mentioned_in_commit_at: 2.days.ago) + issue3.metrics.update!(first_added_to_board_at: 3.days.ago, first_mentioned_in_commit_at: 2.days.ago) end before do diff --git a/spec/lib/gitlab/auth/ldap/user_spec.rb b/spec/lib/gitlab/auth/ldap/user_spec.rb index e910ac09448..da0bb5fe675 100644 --- a/spec/lib/gitlab/auth/ldap/user_spec.rb +++ b/spec/lib/gitlab/auth/ldap/user_spec.rb @@ -53,12 +53,12 @@ RSpec.describe Gitlab::Auth::Ldap::User do it "finds the user if already existing" do create(:omniauth_user, extern_uid: 'uid=john smith,ou=people,dc=example,dc=com', provider: 'ldapmain') - expect { ldap_user.save }.not_to change { User.count } + expect { ldap_user.save }.not_to change { User.count } # rubocop:disable Rails/SaveBang end it "connects to existing non-ldap user if the email matches" do existing_user = create(:omniauth_user, email: 'john@example.com', provider: "twitter") - expect { ldap_user.save }.not_to change { User.count } + expect { ldap_user.save }.not_to change { User.count } # rubocop:disable Rails/SaveBang existing_user.reload expect(existing_user.ldap_identity.extern_uid).to eql 'uid=john smith,ou=people,dc=example,dc=com' @@ -67,7 +67,7 @@ RSpec.describe Gitlab::Auth::Ldap::User do it 'connects to existing ldap user if the extern_uid changes' do existing_user = create(:omniauth_user, email: 'john@example.com', extern_uid: 'old-uid', provider: 'ldapmain') - expect { ldap_user.save }.not_to change { User.count } + expect { ldap_user.save }.not_to change { User.count } # rubocop:disable Rails/SaveBang existing_user.reload expect(existing_user.ldap_identity.extern_uid).to eql 'uid=john smith,ou=people,dc=example,dc=com' @@ -77,7 +77,7 @@ RSpec.describe Gitlab::Auth::Ldap::User do it 'connects to existing ldap user if the extern_uid changes and email address has upper case characters' do existing_user = create(:omniauth_user, email: 'john@example.com', extern_uid: 'old-uid', provider: 'ldapmain') - expect { ldap_user_upper_case.save }.not_to change { User.count } + expect { ldap_user_upper_case.save }.not_to change { User.count } # rubocop:disable Rails/SaveBang existing_user.reload expect(existing_user.ldap_identity.extern_uid).to eql 'uid=john smith,ou=people,dc=example,dc=com' @@ -89,7 +89,7 @@ RSpec.describe Gitlab::Auth::Ldap::User do existing_user = create(:omniauth_user, email: 'john@example.com', provider: 'twitter') expect(existing_user.identities.count).to be(1) - ldap_user.save + ldap_user.save # rubocop:disable Rails/SaveBang expect(ldap_user.gl_user.identities.count).to be(2) # Expect that find_by provider only returns a single instance of an identity and not an Enumerable @@ -98,7 +98,7 @@ RSpec.describe Gitlab::Auth::Ldap::User do end it "creates a new user if not found" do - expect { ldap_user.save }.to change { User.count }.by(1) + expect { ldap_user.save }.to change { User.count }.by(1) # rubocop:disable Rails/SaveBang end context 'when signup is disabled' do @@ -107,7 +107,7 @@ RSpec.describe Gitlab::Auth::Ldap::User do end it 'creates the user' do - ldap_user.save + ldap_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_persisted end @@ -119,7 +119,7 @@ RSpec.describe Gitlab::Auth::Ldap::User do end it 'creates and confirms the user anyway' do - ldap_user.save + ldap_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_persisted expect(gl_user).to be_confirmed @@ -132,7 +132,7 @@ RSpec.describe Gitlab::Auth::Ldap::User do end it 'creates the user' do - ldap_user.save + ldap_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_persisted end @@ -189,7 +189,7 @@ RSpec.describe Gitlab::Auth::Ldap::User do end it do - ldap_user.save + ldap_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_valid expect(gl_user).not_to be_blocked end @@ -201,7 +201,7 @@ RSpec.describe Gitlab::Auth::Ldap::User do end it do - ldap_user.save + ldap_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_valid expect(gl_user).to be_blocked end @@ -210,7 +210,7 @@ RSpec.describe Gitlab::Auth::Ldap::User do context 'sign-in' do before do - ldap_user.save + ldap_user.save # rubocop:disable Rails/SaveBang ldap_user.gl_user.activate end @@ -220,7 +220,7 @@ RSpec.describe Gitlab::Auth::Ldap::User do end it do - ldap_user.save + ldap_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_valid expect(gl_user).not_to be_blocked end @@ -232,7 +232,7 @@ RSpec.describe Gitlab::Auth::Ldap::User do end it do - ldap_user.save + ldap_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_valid expect(gl_user).not_to be_blocked end diff --git a/spec/lib/gitlab/auth/o_auth/user_spec.rb b/spec/lib/gitlab/auth/o_auth/user_spec.rb index 7a8e6e77d52..8d36507ec7a 100644 --- a/spec/lib/gitlab/auth/o_auth/user_spec.rb +++ b/spec/lib/gitlab/auth/o_auth/user_spec.rb @@ -67,7 +67,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do create(:omniauth_user, extern_uid: 'my-uid', provider: provider) stub_omniauth_config(allow_single_sign_on: [provider], external_providers: [provider]) - oauth_user.save + oauth_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_valid expect(gl_user.external).to be_falsey @@ -83,7 +83,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do it 'creates the user' do stub_omniauth_config(allow_single_sign_on: [provider]) - oauth_user.save + oauth_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_persisted end @@ -97,7 +97,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do it 'creates and confirms the user anyway' do stub_omniauth_config(allow_single_sign_on: [provider]) - oauth_user.save + oauth_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_persisted expect(gl_user).to be_confirmed @@ -112,7 +112,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do it 'creates the user' do stub_omniauth_config(allow_single_sign_on: [provider]) - oauth_user.save + oauth_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_persisted end @@ -121,7 +121,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do it 'marks user as having password_automatically_set' do stub_omniauth_config(allow_single_sign_on: [provider], external_providers: [provider]) - oauth_user.save + oauth_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_persisted expect(gl_user).to be_password_automatically_set @@ -131,7 +131,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do context 'provider is marked as external' do it 'marks user as external' do stub_omniauth_config(allow_single_sign_on: [provider], external_providers: [provider]) - oauth_user.save + oauth_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_valid expect(gl_user.external).to be_truthy end @@ -141,7 +141,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do it 'does not mark external user as internal' do create(:omniauth_user, extern_uid: 'my-uid', provider: provider, external: true) stub_omniauth_config(allow_single_sign_on: [provider], external_providers: ['facebook']) - oauth_user.save + oauth_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_valid expect(gl_user.external).to be_truthy end @@ -151,9 +151,9 @@ RSpec.describe Gitlab::Auth::OAuth::User do context 'when adding a new OAuth identity' do it 'does not promote an external user to internal' do user = create(:user, email: 'john@mail.com', external: true) - user.identities.create(provider: provider, extern_uid: uid) + user.identities.create!(provider: provider, extern_uid: uid) - oauth_user.save + oauth_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_valid expect(gl_user.external).to be_truthy end @@ -166,7 +166,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do end it "creates a user from Omniauth" do - oauth_user.save + oauth_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_valid identity = gl_user.identities.first @@ -181,7 +181,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do end it "creates a user from Omniauth" do - oauth_user.save + oauth_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_valid identity = gl_user.identities.first @@ -196,7 +196,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do end it 'throws an error' do - expect { oauth_user.save }.to raise_error StandardError + expect { oauth_user.save }.to raise_error StandardError # rubocop:disable Rails/SaveBang end end @@ -206,7 +206,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do end it 'throws an error' do - expect { oauth_user.save }.to raise_error StandardError + expect { oauth_user.save }.to raise_error StandardError # rubocop:disable Rails/SaveBang end end end @@ -228,7 +228,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do let!(:existing_user) { create(:user, email: 'john@mail.com', username: 'john') } it "adds the OmniAuth identity to the GitLab user account" do - oauth_user.save + oauth_user.save # rubocop:disable Rails/SaveBang expect(gl_user).not_to be_valid end @@ -248,7 +248,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do let!(:existing_user) { create(:user, email: 'john@mail.com', username: 'john') } it "adds the OmniAuth identity to the GitLab user account" do - oauth_user.save + oauth_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_valid expect(gl_user.username).to eql 'john' @@ -277,7 +277,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do let!(:existing_user) { create(:user, email: 'john@mail.com', username: 'john') } it "adds the OmniAuth identity to the GitLab user account" do - oauth_user.save + oauth_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_valid expect(gl_user.username).to eql 'john' @@ -337,7 +337,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do before do allow(Gitlab::Auth::Ldap::Person).to receive(:find_by_uid).and_return(ldap_user) - oauth_user.save + oauth_user.save # rubocop:disable Rails/SaveBang end it "creates a user with dual LDAP and omniauth identities" do @@ -376,7 +376,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do allow(Gitlab::Auth::Ldap::Person).to receive(:find_by_email).with(uid, any_args).and_return(nil) allow(Gitlab::Auth::Ldap::Person).to receive(:find_by_email).with(info_hash[:email], any_args).and_return(ldap_user) - oauth_user.save + oauth_user.save # rubocop:disable Rails/SaveBang end it 'creates the LDAP identity' do @@ -392,7 +392,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do it "adds the omniauth identity to the LDAP account" do allow(Gitlab::Auth::Ldap::Person).to receive(:find_by_uid).and_return(ldap_user) - oauth_user.save + oauth_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_valid expect(gl_user.username).to eql 'john' @@ -414,7 +414,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do allow(Gitlab::Auth::Ldap::Person).to receive(:find_by_uid).and_return(nil) allow(Gitlab::Auth::Ldap::Person).to receive(:find_by_email).and_return(ldap_user) - oauth_user.save + oauth_user.save # rubocop:disable Rails/SaveBang identities_as_hash = gl_user.identities.map { |id| { provider: id.provider, extern_uid: id.extern_uid } } expect(identities_as_hash).to match_array(result_identities(dn, uid)) @@ -426,7 +426,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do allow(Gitlab::Auth::Ldap::Person).to receive(:find_by_email).and_return(nil) allow(Gitlab::Auth::Ldap::Person).to receive(:find_by_dn).and_return(ldap_user) - oauth_user.save + oauth_user.save # rubocop:disable Rails/SaveBang identities_as_hash = gl_user.identities.map { |id| { provider: id.provider, extern_uid: id.extern_uid } } expect(identities_as_hash).to match_array(result_identities(dn, uid)) @@ -447,7 +447,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do end it 'does not save the identity' do - oauth_user.save + oauth_user.save # rubocop:disable Rails/SaveBang identities_as_hash = gl_user.identities.map { |id| { provider: id.provider, extern_uid: id.extern_uid } } expect(identities_as_hash).to match_array([{ provider: 'twitter', extern_uid: uid }]) @@ -467,7 +467,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do it 'creates a user favoring the LDAP username and strips email domain' do allow(Gitlab::Auth::Ldap::Person).to receive(:find_by_uid).and_return(ldap_user) - oauth_user.save + oauth_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_valid expect(gl_user.username).to eql 'johndoe' @@ -510,7 +510,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do before do allow(Gitlab::Auth::Ldap::Person).to receive(:find_by_uid).and_return(ldap_user) - oauth_user.save + oauth_user.save # rubocop:disable Rails/SaveBang end it "creates a user with dual LDAP and omniauth identities" do @@ -549,7 +549,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do it "adds the omniauth identity to the LDAP account" do allow(Gitlab::Auth::Ldap::Person).to receive(:find_by_uid).and_return(ldap_user) - oauth_user.save + oauth_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_valid expect(gl_user.username).to eql 'john' @@ -584,7 +584,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do end it do - oauth_user.save + oauth_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_valid expect(gl_user).not_to be_blocked end @@ -596,7 +596,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do end it do - oauth_user.save + oauth_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_valid expect(gl_user).to be_blocked end @@ -622,7 +622,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do end it do - oauth_user.save + oauth_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_valid expect(gl_user).not_to be_blocked end @@ -636,7 +636,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do end it do - oauth_user.save + oauth_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_valid expect(gl_user).to be_blocked end @@ -654,7 +654,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do end it do - oauth_user.save + oauth_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_valid expect(gl_user).not_to be_blocked end @@ -668,7 +668,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do end it do - oauth_user.save + oauth_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_valid expect(gl_user).not_to be_blocked end @@ -678,7 +678,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do context 'sign-in' do before do - oauth_user.save + oauth_user.save # rubocop:disable Rails/SaveBang oauth_user.gl_user.activate end @@ -688,7 +688,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do end it do - oauth_user.save + oauth_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_valid expect(gl_user).not_to be_blocked end @@ -700,7 +700,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do end it do - oauth_user.save + oauth_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_valid expect(gl_user).not_to be_blocked end @@ -714,7 +714,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do end it do - oauth_user.save + oauth_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_valid expect(gl_user).not_to be_blocked end @@ -728,7 +728,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do end it do - oauth_user.save + oauth_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_valid expect(gl_user).not_to be_blocked end @@ -791,7 +791,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do context 'when collision with existing user' do it 'generates the username with a counter' do - oauth_user.save + oauth_user.save # rubocop:disable Rails/SaveBang oauth_user2 = described_class.new(OmniAuth::AuthHash.new(uid: 'my-uid2', provider: provider, info: { nickname: 'johngitlab-ETC@othermail.com', email: 'john@othermail.com' })) expect(oauth_user2.gl_user.username).to eq('johngitlab-ETC1') diff --git a/spec/lib/gitlab/auth/saml/user_spec.rb b/spec/lib/gitlab/auth/saml/user_spec.rb index fd48492f18d..796512bc52b 100644 --- a/spec/lib/gitlab/auth/saml/user_spec.rb +++ b/spec/lib/gitlab/auth/saml/user_spec.rb @@ -36,7 +36,7 @@ RSpec.describe Gitlab::Auth::Saml::User do context 'and should bind with SAML' do it 'adds the SAML identity to the existing user' do - saml_user.save + saml_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_valid expect(gl_user).to eq existing_user identity = gl_user.identities.first @@ -49,7 +49,7 @@ RSpec.describe Gitlab::Auth::Saml::User do context 'are defined' do it 'marks the user as external' do stub_saml_group_config(%w(Freelancers)) - saml_user.save + saml_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_valid expect(gl_user.external).to be_truthy end @@ -61,7 +61,7 @@ RSpec.describe Gitlab::Auth::Saml::User do context 'are defined but the user does not belong there' do it 'does not mark the user as external' do - saml_user.save + saml_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_valid expect(gl_user.external).to be_falsey end @@ -70,7 +70,7 @@ RSpec.describe Gitlab::Auth::Saml::User do context 'user was external, now should not be' do it 'makes user internal' do existing_user.update_attribute('external', true) - saml_user.save + saml_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_valid expect(gl_user.external).to be_falsey end @@ -86,7 +86,7 @@ RSpec.describe Gitlab::Auth::Saml::User do end it 'creates a user from SAML' do - saml_user.save + saml_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_valid identity = gl_user.identities.first @@ -101,7 +101,7 @@ RSpec.describe Gitlab::Auth::Saml::User do end it 'does not throw an error' do - expect { saml_user.save }.not_to raise_error + expect { saml_user.save }.not_to raise_error # rubocop:disable Rails/SaveBang end end @@ -111,7 +111,7 @@ RSpec.describe Gitlab::Auth::Saml::User do end it 'throws an error' do - expect { saml_user.save }.to raise_error StandardError + expect { saml_user.save }.to raise_error StandardError # rubocop:disable Rails/SaveBang end end end @@ -120,7 +120,7 @@ RSpec.describe Gitlab::Auth::Saml::User do context 'are defined' do it 'marks the user as external' do stub_saml_group_config(%w(Freelancers)) - saml_user.save + saml_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_valid expect(gl_user.external).to be_truthy end @@ -129,7 +129,7 @@ RSpec.describe Gitlab::Auth::Saml::User do context 'are defined but the user does not belong there' do it 'does not mark the user as external' do stub_saml_group_config(%w(Interns)) - saml_user.save + saml_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_valid expect(gl_user.external).to be_falsey end @@ -170,7 +170,7 @@ RSpec.describe Gitlab::Auth::Saml::User do context 'and no account for the LDAP user' do it 'creates a user with dual LDAP and SAML identities' do - saml_user.save + saml_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_valid expect(gl_user.username).to eql uid @@ -230,7 +230,7 @@ RSpec.describe Gitlab::Auth::Saml::User do { provider: id.provider, extern_uid: id.extern_uid } end - saml_user.save + saml_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_valid expect(gl_user.username).to eql 'john' @@ -259,7 +259,7 @@ RSpec.describe Gitlab::Auth::Saml::User do end it 'adds the omniauth identity to the LDAP account' do - saml_user.save + saml_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_valid expect(gl_user.username).to eql 'john' @@ -271,9 +271,9 @@ RSpec.describe Gitlab::Auth::Saml::User do end it 'saves successfully on subsequent tries, when both identities are present' do - saml_user.save + saml_user.save # rubocop:disable Rails/SaveBang local_saml_user = described_class.new(auth_hash) - local_saml_user.save + local_saml_user.save # rubocop:disable Rails/SaveBang expect(local_saml_user.gl_user).to be_valid expect(local_saml_user.gl_user).to be_persisted @@ -289,7 +289,7 @@ RSpec.describe Gitlab::Auth::Saml::User do local_hash = OmniAuth::AuthHash.new(uid: dn, provider: provider, info: info_hash) local_saml_user = described_class.new(local_hash) - local_saml_user.save + local_saml_user.save # rubocop:disable Rails/SaveBang local_gl_user = local_saml_user.gl_user expect(local_gl_user).to be_valid @@ -309,7 +309,7 @@ RSpec.describe Gitlab::Auth::Saml::User do end it 'creates the user' do - saml_user.save + saml_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_persisted end @@ -321,7 +321,7 @@ RSpec.describe Gitlab::Auth::Saml::User do end it 'creates and confirms the user anyway' do - saml_user.save + saml_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_persisted expect(gl_user).to be_confirmed @@ -334,7 +334,7 @@ RSpec.describe Gitlab::Auth::Saml::User do end it 'creates the user' do - saml_user.save + saml_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_persisted end @@ -353,7 +353,7 @@ RSpec.describe Gitlab::Auth::Saml::User do end it 'does not block the user' do - saml_user.save + saml_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_valid expect(gl_user).not_to be_blocked end @@ -365,7 +365,7 @@ RSpec.describe Gitlab::Auth::Saml::User do end it 'blocks user' do - saml_user.save + saml_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_valid expect(gl_user).to be_blocked end @@ -374,7 +374,7 @@ RSpec.describe Gitlab::Auth::Saml::User do context 'sign-in' do before do - saml_user.save + saml_user.save # rubocop:disable Rails/SaveBang saml_user.gl_user.activate end @@ -384,7 +384,7 @@ RSpec.describe Gitlab::Auth::Saml::User do end it do - saml_user.save + saml_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_valid expect(gl_user).not_to be_blocked end @@ -396,7 +396,7 @@ RSpec.describe Gitlab::Auth::Saml::User do end it do - saml_user.save + saml_user.save # rubocop:disable Rails/SaveBang expect(gl_user).to be_valid expect(gl_user).not_to be_blocked end diff --git a/spec/lib/gitlab/auth_spec.rb b/spec/lib/gitlab/auth_spec.rb index 1541334192e..fc93910344c 100644 --- a/spec/lib/gitlab/auth_spec.rb +++ b/spec/lib/gitlab/auth_spec.rb @@ -165,27 +165,27 @@ RSpec.describe Gitlab::Auth, :use_clean_rails_memory_store_caching do end it 'recognises user token' do - build.update(user: create(:user)) + build.update!(user: create(:user)) expect(subject).to have_attributes(actor: build.user, project: build.project, type: :build, authentication_abilities: described_class.build_authentication_abilities) end it 'recognises project level bot access token' do - build.update(user: create(:user, :project_bot)) + build.update!(user: create(:user, :project_bot)) project.add_maintainer(build.user) expect(subject).to have_attributes(actor: build.user, project: build.project, type: :build, authentication_abilities: described_class.build_authentication_abilities) end it 'recognises group level bot access token' do - build.update(user: create(:user, :project_bot)) + build.update!(user: create(:user, :project_bot)) group.add_maintainer(build.user) expect(subject).to have_attributes(actor: build.user, project: build.project, type: :build, authentication_abilities: described_class.build_authentication_abilities) end it 'fails with blocked user token' do - build.update(user: create(:user, :blocked)) + build.update!(user: create(:user, :blocked)) expect(subject).to have_attributes(auth_failure) end @@ -213,7 +213,7 @@ RSpec.describe Gitlab::Auth, :use_clean_rails_memory_store_caching do it 'recognizes other ci services' do project.create_drone_ci_integration(active: true) - project.drone_ci_integration.update(token: 'token') + project.drone_ci_integration.update!(token: 'token', drone_url: generate(:url)) expect(gl_auth.find_for_git_client('drone-ci-token', 'token', project: project, ip: 'ip')).to have_attributes(actor: nil, project: project, type: :ci, authentication_abilities: described_class.build_authentication_abilities) end @@ -326,7 +326,7 @@ RSpec.describe Gitlab::Auth, :use_clean_rails_memory_store_caching do context 'orphaned token' do before do - user.destroy + user.destroy! end it_behaves_like 'an oauth failure' @@ -903,7 +903,7 @@ RSpec.describe Gitlab::Auth, :use_clean_rails_memory_store_caching do it 'resets failed_attempts when true and password is correct' do user.failed_attempts = 2 - user.save + user.save! expect do gl_auth.find_with_user_password(username, password, increment_failed_attempts: true) @@ -932,7 +932,7 @@ RSpec.describe Gitlab::Auth, :use_clean_rails_memory_store_caching do it 'does not reset failed_attempts when true and password is correct' do user.failed_attempts = 2 - user.save + user.save! expect do gl_auth.find_with_user_password(username, password, increment_failed_attempts: true) diff --git a/spec/lib/gitlab/authorized_keys_spec.rb b/spec/lib/gitlab/authorized_keys_spec.rb index 1053ae2e325..073cee96ede 100644 --- a/spec/lib/gitlab/authorized_keys_spec.rb +++ b/spec/lib/gitlab/authorized_keys_spec.rb @@ -38,7 +38,7 @@ RSpec.describe Gitlab::AuthorizedKeys do end describe '#create' do - subject { authorized_keys.create } + subject { authorized_keys.create } # rubocop:disable Rails/SaveBang context 'authorized_keys file exists' do before do diff --git a/spec/lib/gitlab/backtrace_cleaner_spec.rb b/spec/lib/gitlab/backtrace_cleaner_spec.rb index cdde5a02d3b..e46a90e8606 100644 --- a/spec/lib/gitlab/backtrace_cleaner_spec.rb +++ b/spec/lib/gitlab/backtrace_cleaner_spec.rb @@ -25,6 +25,7 @@ RSpec.describe Gitlab::BacktraceCleaner do "app/models/repository.rb:113:in `commit'", "lib/gitlab/i18n.rb:50:in `with_locale'", "lib/gitlab/middleware/multipart.rb:95:in `call'", + "lib/gitlab/request_profiler/middleware.rb:14:in `call'", "ee/lib/gitlab/database/load_balancing/rack_middleware.rb:37:in `call'", "ee/lib/gitlab/jira/middleware.rb:15:in `call'" ] diff --git a/spec/lib/gitlab/bitbucket_server_import/importer_spec.rb b/spec/lib/gitlab/bitbucket_server_import/importer_spec.rb index 0380ddd9a2e..d2abdb740f8 100644 --- a/spec/lib/gitlab/bitbucket_server_import/importer_spec.rb +++ b/spec/lib/gitlab/bitbucket_server_import/importer_spec.rb @@ -22,8 +22,8 @@ RSpec.describe Gitlab::BitbucketServerImport::Importer do data: { project_key: project_key, repo_slug: repo_slug }, credentials: { base_uri: import_url, user: bitbucket_user, password: password } ) - data.save - project.save + data.save! + project.save! end describe '#import_repository' do diff --git a/spec/lib/gitlab/database/dynamic_model_helpers_spec.rb b/spec/lib/gitlab/database/dynamic_model_helpers_spec.rb index 0844616ee1c..31486240bfa 100644 --- a/spec/lib/gitlab/database/dynamic_model_helpers_spec.rb +++ b/spec/lib/gitlab/database/dynamic_model_helpers_spec.rb @@ -4,10 +4,11 @@ require 'spec_helper' RSpec.describe Gitlab::Database::DynamicModelHelpers do let(:including_class) { Class.new.include(described_class) } - let(:table_name) { 'projects' } + let(:table_name) { Project.table_name } + let(:connection) { Project.connection } describe '#define_batchable_model' do - subject { including_class.new.define_batchable_model(table_name) } + subject { including_class.new.define_batchable_model(table_name, connection: connection) } it 'is an ActiveRecord model' do expect(subject.ancestors).to include(ActiveRecord::Base) @@ -40,7 +41,7 @@ RSpec.describe Gitlab::Database::DynamicModelHelpers do it 'iterates table in batches' do each_batch_size = ->(&block) do - subject.each_batch(table_name, of: 1) do |batch| + subject.each_batch(table_name, connection: connection, of: 1) do |batch| block.call(batch.size) end end @@ -56,7 +57,7 @@ RSpec.describe Gitlab::Database::DynamicModelHelpers do end it 'raises an error' do - expect { subject.each_batch(table_name, of: 1) { |batch| batch.size } } + expect { subject.each_batch(table_name, connection: connection, of: 1) { |batch| batch.size } } .to raise_error(RuntimeError, /each_batch should not run inside a transaction/) end end @@ -74,7 +75,7 @@ RSpec.describe Gitlab::Database::DynamicModelHelpers do end it 'iterates table in batch ranges' do - expect { |b| subject.each_batch_range(table_name, of: 1, &b) } + expect { |b| subject.each_batch_range(table_name, connection: connection, of: 1, &b) } .to yield_successive_args( [first_project.id, first_project.id], [second_project.id, second_project.id] @@ -82,13 +83,13 @@ RSpec.describe Gitlab::Database::DynamicModelHelpers do end it 'yields only one batch if bigger than the table size' do - expect { |b| subject.each_batch_range(table_name, of: 2, &b) } + expect { |b| subject.each_batch_range(table_name, connection: connection, of: 2, &b) } .to yield_successive_args([first_project.id, second_project.id]) end it 'makes it possible to apply a scope' do each_batch_limited = ->(&b) do - subject.each_batch_range(table_name, scope: ->(table) { table.limit(1) }, of: 1, &b) + subject.each_batch_range(table_name, connection: connection, scope: ->(table) { table.limit(1) }, of: 1, &b) end expect { |b| each_batch_limited.call(&b) } @@ -102,7 +103,7 @@ RSpec.describe Gitlab::Database::DynamicModelHelpers do end it 'raises an error' do - expect { subject.each_batch_range(table_name, of: 1) { 1 } } + expect { subject.each_batch_range(table_name, connection: connection, of: 1) { 1 } } .to raise_error(RuntimeError, /each_batch should not run inside a transaction/) end end diff --git a/spec/lib/gitlab/database/migration_helpers_spec.rb b/spec/lib/gitlab/database/migration_helpers_spec.rb index d6f0d7bffe8..d71a4f81901 100644 --- a/spec/lib/gitlab/database/migration_helpers_spec.rb +++ b/spec/lib/gitlab/database/migration_helpers_spec.rb @@ -14,6 +14,54 @@ RSpec.describe Gitlab::Database::MigrationHelpers do allow(model).to receive(:puts) end + describe 'overridden dynamic model helpers' do + let(:test_table) { '__test_batching_table' } + + before do + model.connection.execute(<<~SQL) + CREATE TABLE #{test_table} ( + id integer NOT NULL PRIMARY KEY, + name text NOT NULL + ); + + INSERT INTO #{test_table} (id, name) + VALUES (1, 'bob'), (2, 'mary'), (3, 'amy'); + SQL + end + + describe '#define_batchable_model' do + it 'defines a batchable model with the migration connection' do + expect(model.define_batchable_model(test_table).count).to eq(3) + end + end + + describe '#each_batch' do + before do + allow(model).to receive(:transaction_open?).and_return(false) + end + + it 'calls each_batch with the migration connection' do + each_batch_name = ->(&block) do + model.each_batch(test_table, of: 2) do |batch| + block.call(batch.pluck(:name)) + end + end + + expect { |b| each_batch_name.call(&b) }.to yield_successive_args(%w[bob mary], %w[amy]) + end + end + + describe '#each_batch_range' do + before do + allow(model).to receive(:transaction_open?).and_return(false) + end + + it 'calls each_batch with the migration connection' do + expect { |b| model.each_batch_range(test_table, of: 2, &b) }.to yield_successive_args([1, 2], [3, 3]) + end + end + end + describe '#remove_timestamps' do it 'can remove the default timestamps' do Gitlab::Database::MigrationHelpers::DEFAULT_TIMESTAMP_COLUMNS.each do |column_name| diff --git a/spec/lib/gitlab/database_importers/self_monitoring/project/create_service_spec.rb b/spec/lib/gitlab/database_importers/self_monitoring/project/create_service_spec.rb index f5ea660ee1e..6601b6658d5 100644 --- a/spec/lib/gitlab/database_importers/self_monitoring/project/create_service_spec.rb +++ b/spec/lib/gitlab/database_importers/self_monitoring/project/create_service_spec.rb @@ -60,7 +60,7 @@ RSpec.describe Gitlab::DatabaseImporters::SelfMonitoring::Project::CreateService before do stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false') - application_setting.update(allow_local_requests_from_web_hooks_and_services: true) + application_setting.update!(allow_local_requests_from_web_hooks_and_services: true) end shared_examples 'has prometheus integration' do |server_address| @@ -181,7 +181,7 @@ RSpec.describe Gitlab::DatabaseImporters::SelfMonitoring::Project::CreateService let(:existing_project) { create(:project, namespace: existing_group) } before do - application_setting.update(instance_administrators_group_id: existing_group.id, + application_setting.update!(instance_administrators_group_id: existing_group.id, self_monitoring_project_id: existing_project.id) end @@ -195,7 +195,7 @@ RSpec.describe Gitlab::DatabaseImporters::SelfMonitoring::Project::CreateService context 'when local requests from hooks and integrations are not allowed' do before do - application_setting.update(allow_local_requests_from_web_hooks_and_services: false) + application_setting.update!(allow_local_requests_from_web_hooks_and_services: false) end it_behaves_like 'has prometheus integration', 'http://localhost:9090' diff --git a/spec/lib/gitlab/request_profiler/profile_spec.rb b/spec/lib/gitlab/request_profiler/profile_spec.rb new file mode 100644 index 00000000000..30e23a99b22 --- /dev/null +++ b/spec/lib/gitlab/request_profiler/profile_spec.rb @@ -0,0 +1,61 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::RequestProfiler::Profile do + let(:profile) { described_class.new(filename) } + + describe '.new' do + context 'using old filename' do + let(:filename) { '|api|v4|version.txt_1562854738.html' } + + it 'returns valid data' do + expect(profile).to be_valid + expect(profile.request_path).to eq('/api/v4/version.txt') + expect(profile.time).to eq(Time.at(1562854738).utc) + expect(profile.type).to eq('html') + end + end + + context 'using new filename' do + let(:filename) { '|api|v4|version.txt_1563547949_execution.html' } + + it 'returns valid data' do + expect(profile).to be_valid + expect(profile.request_path).to eq('/api/v4/version.txt') + expect(profile.profile_mode).to eq('execution') + expect(profile.time).to eq(Time.at(1563547949).utc) + expect(profile.type).to eq('html') + end + end + end + + describe '#content_type' do + context 'when using html file' do + let(:filename) { '|api|v4|version.txt_1562854738_memory.html' } + + it 'returns valid data' do + expect(profile).to be_valid + expect(profile.content_type).to eq('text/html') + end + end + + context 'when using text file' do + let(:filename) { '|api|v4|version.txt_1562854738_memory.txt' } + + it 'returns valid data' do + expect(profile).to be_valid + expect(profile.content_type).to eq('text/plain') + end + end + + context 'when file is unknown' do + let(:filename) { '|api|v4|version.txt_1562854738_memory.xxx' } + + it 'returns valid data' do + expect(profile).not_to be_valid + expect(profile.content_type).to be_nil + end + end + end +end diff --git a/spec/lib/gitlab/request_profiler_spec.rb b/spec/lib/gitlab/request_profiler_spec.rb new file mode 100644 index 00000000000..4d3b361efcb --- /dev/null +++ b/spec/lib/gitlab/request_profiler_spec.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::RequestProfiler do + describe '.profile_token' do + it 'returns a token' do + expect(described_class.profile_token).to be_present + end + + it 'caches the token' do + expect(Rails.cache).to receive(:fetch).with('profile-token') + + described_class.profile_token + end + end + + context 'with temporary PROFILES_DIR' do + let(:tmpdir) { Dir.mktmpdir('profiler-test') } + let(:profile_name) { '|api|v4|version.txt_1562854738_memory.html' } + let(:profile_path) { File.join(tmpdir, profile_name) } + + before do + stub_const('Gitlab::RequestProfiler::PROFILES_DIR', tmpdir) + FileUtils.touch(profile_path) + end + + after do + FileUtils.rm_rf(tmpdir) + end + + describe '.remove_all_profiles' do + it 'removes Gitlab::RequestProfiler::PROFILES_DIR directory' do + described_class.remove_all_profiles + + expect(Dir.exist?(tmpdir)).to be false + end + end + + describe '.all' do + subject { described_class.all } + + it 'returns all profiles' do + expect(subject.map(&:name)).to contain_exactly(profile_name) + end + end + + describe '.find' do + subject { described_class.find(profile_name) } + + it 'returns all profiles' do + expect(subject.name).to eq(profile_name) + end + end + end +end diff --git a/spec/lib/security/ci_configuration/container_scanning_build_action_spec.rb b/spec/lib/security/ci_configuration/container_scanning_build_action_spec.rb new file mode 100644 index 00000000000..38066e41c53 --- /dev/null +++ b/spec/lib/security/ci_configuration/container_scanning_build_action_spec.rb @@ -0,0 +1,191 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Security::CiConfiguration::ContainerScanningBuildAction do + subject(:result) { described_class.new(auto_devops_enabled, gitlab_ci_content).generate } + + let(:params) { {} } + + context 'with existing .gitlab-ci.yml' do + let(:auto_devops_enabled) { false } + + context 'container_scanning has not been included' do + let(:expected_yml) do + <<-CI_YML.strip_heredoc + # You can override the included template(s) by including variable overrides + # SAST customization: https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings + # Secret Detection customization: https://docs.gitlab.com/ee/user/application_security/secret_detection/#customizing-settings + # Dependency Scanning customization: https://docs.gitlab.com/ee/user/application_security/dependency_scanning/#customizing-the-dependency-scanning-settings + # Container Scanning customization: https://docs.gitlab.com/ee/user/application_security/container_scanning/#customizing-the-container-scanning-settings + # Note that environment variables can be set in several places + # See https://docs.gitlab.com/ee/ci/variables/#cicd-variable-precedence + + # container_scanning: + # variables: + # DOCKER_IMAGE: ... + # DOCKER_USER: ... + # DOCKER_PASSWORD: ... + stages: + - test + - security + variables: + RANDOM: make sure this persists + include: + - template: existing.yml + - template: Security/Container-Scanning.gitlab-ci.yml + CI_YML + end + + context 'template includes are an array' do + let(:gitlab_ci_content) do + { "stages" => %w(test security), + "variables" => { "RANDOM" => "make sure this persists" }, + "include" => [{ "template" => "existing.yml" }] } + end + + it 'generates the correct YML' do + expect(result[:action]).to eq('update') + expect(result[:content]).to eq(expected_yml) + end + end + + context 'template include is not an array' do + let(:gitlab_ci_content) do + { "stages" => %w(test security), + "variables" => { "RANDOM" => "make sure this persists" }, + "include" => { "template" => "existing.yml" } } + end + + it 'generates the correct YML' do + expect(result[:action]).to eq('update') + expect(result[:content]).to eq(expected_yml) + end + end + end + + context 'container_scanning has been included' do + let(:expected_yml) do + <<-CI_YML.strip_heredoc + # You can override the included template(s) by including variable overrides + # SAST customization: https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings + # Secret Detection customization: https://docs.gitlab.com/ee/user/application_security/secret_detection/#customizing-settings + # Dependency Scanning customization: https://docs.gitlab.com/ee/user/application_security/dependency_scanning/#customizing-the-dependency-scanning-settings + # Container Scanning customization: https://docs.gitlab.com/ee/user/application_security/container_scanning/#customizing-the-container-scanning-settings + # Note that environment variables can be set in several places + # See https://docs.gitlab.com/ee/ci/variables/#cicd-variable-precedence + + # container_scanning: + # variables: + # DOCKER_IMAGE: ... + # DOCKER_USER: ... + # DOCKER_PASSWORD: ... + stages: + - test + variables: + RANDOM: make sure this persists + include: + - template: Security/Container-Scanning.gitlab-ci.yml + CI_YML + end + + context 'container_scanning template include are an array' do + let(:gitlab_ci_content) do + { "stages" => %w(test), + "variables" => { "RANDOM" => "make sure this persists" }, + "include" => [{ "template" => "Security/Container-Scanning.gitlab-ci.yml" }] } + end + + it 'generates the correct YML' do + expect(result[:action]).to eq('update') + expect(result[:content]).to eq(expected_yml) + end + end + + context 'container_scanning template include is not an array' do + let(:gitlab_ci_content) do + { "stages" => %w(test), + "variables" => { "RANDOM" => "make sure this persists" }, + "include" => { "template" => "Security/Container-Scanning.gitlab-ci.yml" } } + end + + it 'generates the correct YML' do + expect(result[:action]).to eq('update') + expect(result[:content]).to eq(expected_yml) + end + end + end + end + + context 'with no .gitlab-ci.yml' do + let(:gitlab_ci_content) { nil } + + context 'autodevops disabled' do + let(:auto_devops_enabled) { false } + let(:expected_yml) do + <<-CI_YML.strip_heredoc + # You can override the included template(s) by including variable overrides + # SAST customization: https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings + # Secret Detection customization: https://docs.gitlab.com/ee/user/application_security/secret_detection/#customizing-settings + # Dependency Scanning customization: https://docs.gitlab.com/ee/user/application_security/dependency_scanning/#customizing-the-dependency-scanning-settings + # Container Scanning customization: https://docs.gitlab.com/ee/user/application_security/container_scanning/#customizing-the-container-scanning-settings + # Note that environment variables can be set in several places + # See https://docs.gitlab.com/ee/ci/variables/#cicd-variable-precedence + + # container_scanning: + # variables: + # DOCKER_IMAGE: ... + # DOCKER_USER: ... + # DOCKER_PASSWORD: ... + include: + - template: Security/Container-Scanning.gitlab-ci.yml + CI_YML + end + + it 'generates the correct YML' do + expect(result[:action]).to eq('create') + expect(result[:content]).to eq(expected_yml) + end + end + + context 'with autodevops enabled' do + let(:auto_devops_enabled) { true } + let(:expected_yml) do + <<-CI_YML.strip_heredoc + # You can override the included template(s) by including variable overrides + # SAST customization: https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings + # Secret Detection customization: https://docs.gitlab.com/ee/user/application_security/secret_detection/#customizing-settings + # Dependency Scanning customization: https://docs.gitlab.com/ee/user/application_security/dependency_scanning/#customizing-the-dependency-scanning-settings + # Container Scanning customization: https://docs.gitlab.com/ee/user/application_security/container_scanning/#customizing-the-container-scanning-settings + # Note that environment variables can be set in several places + # See https://docs.gitlab.com/ee/ci/variables/#cicd-variable-precedence + + # container_scanning: + # variables: + # DOCKER_IMAGE: ... + # DOCKER_USER: ... + # DOCKER_PASSWORD: ... + include: + - template: Auto-DevOps.gitlab-ci.yml + CI_YML + end + + before do + allow_next_instance_of(described_class) do |secret_detection_build_actions| + allow(secret_detection_build_actions).to receive(:auto_devops_stages).and_return(fast_auto_devops_stages) + end + end + + it 'generates the correct YML' do + expect(result[:action]).to eq('create') + expect(result[:content]).to eq(expected_yml) + end + end + end + + # stubbing this method allows this spec file to use fast_spec_helper + def fast_auto_devops_stages + auto_devops_template = YAML.safe_load( File.read('lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml') ) + auto_devops_template['stages'] + end +end diff --git a/spec/lib/security/ci_configuration/sast_build_action_spec.rb b/spec/lib/security/ci_configuration/sast_build_action_spec.rb index d93175249f5..6f702e51b73 100644 --- a/spec/lib/security/ci_configuration/sast_build_action_spec.rb +++ b/spec/lib/security/ci_configuration/sast_build_action_spec.rb @@ -324,6 +324,7 @@ RSpec.describe Security::CiConfiguration::SastBuildAction do # SAST customization: https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings # Secret Detection customization: https://docs.gitlab.com/ee/user/application_security/secret_detection/#customizing-settings # Dependency Scanning customization: https://docs.gitlab.com/ee/user/application_security/dependency_scanning/#customizing-the-dependency-scanning-settings + # Container Scanning customization: https://docs.gitlab.com/ee/user/application_security/container_scanning/#customizing-the-container-scanning-settings # Note that environment variables can be set in several places # See https://docs.gitlab.com/ee/ci/variables/#cicd-variable-precedence stages: @@ -344,6 +345,7 @@ RSpec.describe Security::CiConfiguration::SastBuildAction do # SAST customization: https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings # Secret Detection customization: https://docs.gitlab.com/ee/user/application_security/secret_detection/#customizing-settings # Dependency Scanning customization: https://docs.gitlab.com/ee/user/application_security/dependency_scanning/#customizing-the-dependency-scanning-settings + # Container Scanning customization: https://docs.gitlab.com/ee/user/application_security/container_scanning/#customizing-the-container-scanning-settings # Note that environment variables can be set in several places # See https://docs.gitlab.com/ee/ci/variables/#cicd-variable-precedence stages: @@ -361,6 +363,7 @@ RSpec.describe Security::CiConfiguration::SastBuildAction do # SAST customization: https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings # Secret Detection customization: https://docs.gitlab.com/ee/user/application_security/secret_detection/#customizing-settings # Dependency Scanning customization: https://docs.gitlab.com/ee/user/application_security/dependency_scanning/#customizing-the-dependency-scanning-settings + # Container Scanning customization: https://docs.gitlab.com/ee/user/application_security/container_scanning/#customizing-the-container-scanning-settings # Note that environment variables can be set in several places # See https://docs.gitlab.com/ee/ci/variables/#cicd-variable-precedence stages: @@ -384,6 +387,7 @@ RSpec.describe Security::CiConfiguration::SastBuildAction do # SAST customization: https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings # Secret Detection customization: https://docs.gitlab.com/ee/user/application_security/secret_detection/#customizing-settings # Dependency Scanning customization: https://docs.gitlab.com/ee/user/application_security/dependency_scanning/#customizing-the-dependency-scanning-settings + # Container Scanning customization: https://docs.gitlab.com/ee/user/application_security/container_scanning/#customizing-the-container-scanning-settings # Note that environment variables can be set in several places # See https://docs.gitlab.com/ee/ci/variables/#cicd-variable-precedence stages: @@ -420,6 +424,7 @@ RSpec.describe Security::CiConfiguration::SastBuildAction do # SAST customization: https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings # Secret Detection customization: https://docs.gitlab.com/ee/user/application_security/secret_detection/#customizing-settings # Dependency Scanning customization: https://docs.gitlab.com/ee/user/application_security/dependency_scanning/#customizing-the-dependency-scanning-settings + # Container Scanning customization: https://docs.gitlab.com/ee/user/application_security/container_scanning/#customizing-the-container-scanning-settings # Note that environment variables can be set in several places # See https://docs.gitlab.com/ee/ci/variables/#cicd-variable-precedence stages: @@ -445,6 +450,7 @@ RSpec.describe Security::CiConfiguration::SastBuildAction do # SAST customization: https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings # Secret Detection customization: https://docs.gitlab.com/ee/user/application_security/secret_detection/#customizing-settings # Dependency Scanning customization: https://docs.gitlab.com/ee/user/application_security/dependency_scanning/#customizing-the-dependency-scanning-settings + # Container Scanning customization: https://docs.gitlab.com/ee/user/application_security/container_scanning/#customizing-the-container-scanning-settings # Note that environment variables can be set in several places # See https://docs.gitlab.com/ee/ci/variables/#cicd-variable-precedence stages: @@ -468,6 +474,7 @@ RSpec.describe Security::CiConfiguration::SastBuildAction do # SAST customization: https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings # Secret Detection customization: https://docs.gitlab.com/ee/user/application_security/secret_detection/#customizing-settings # Dependency Scanning customization: https://docs.gitlab.com/ee/user/application_security/dependency_scanning/#customizing-the-dependency-scanning-settings + # Container Scanning customization: https://docs.gitlab.com/ee/user/application_security/container_scanning/#customizing-the-container-scanning-settings # Note that environment variables can be set in several places # See https://docs.gitlab.com/ee/ci/variables/#cicd-variable-precedence stages: @@ -492,6 +499,7 @@ RSpec.describe Security::CiConfiguration::SastBuildAction do # SAST customization: https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings # Secret Detection customization: https://docs.gitlab.com/ee/user/application_security/secret_detection/#customizing-settings # Dependency Scanning customization: https://docs.gitlab.com/ee/user/application_security/dependency_scanning/#customizing-the-dependency-scanning-settings + # Container Scanning customization: https://docs.gitlab.com/ee/user/application_security/container_scanning/#customizing-the-container-scanning-settings # Note that environment variables can be set in several places # See https://docs.gitlab.com/ee/ci/variables/#cicd-variable-precedence stages: @@ -516,6 +524,7 @@ RSpec.describe Security::CiConfiguration::SastBuildAction do # SAST customization: https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings # Secret Detection customization: https://docs.gitlab.com/ee/user/application_security/secret_detection/#customizing-settings # Dependency Scanning customization: https://docs.gitlab.com/ee/user/application_security/dependency_scanning/#customizing-the-dependency-scanning-settings + # Container Scanning customization: https://docs.gitlab.com/ee/user/application_security/container_scanning/#customizing-the-container-scanning-settings # Note that environment variables can be set in several places # See https://docs.gitlab.com/ee/ci/variables/#cicd-variable-precedence stages: diff --git a/spec/lib/security/ci_configuration/sast_iac_build_action_spec.rb b/spec/lib/security/ci_configuration/sast_iac_build_action_spec.rb index ecd1602dd9e..4c459058368 100644 --- a/spec/lib/security/ci_configuration/sast_iac_build_action_spec.rb +++ b/spec/lib/security/ci_configuration/sast_iac_build_action_spec.rb @@ -17,6 +17,7 @@ RSpec.describe Security::CiConfiguration::SastIacBuildAction do # SAST customization: https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings # Secret Detection customization: https://docs.gitlab.com/ee/user/application_security/secret_detection/#customizing-settings # Dependency Scanning customization: https://docs.gitlab.com/ee/user/application_security/dependency_scanning/#customizing-the-dependency-scanning-settings + # Container Scanning customization: https://docs.gitlab.com/ee/user/application_security/container_scanning/#customizing-the-container-scanning-settings # Note that environment variables can be set in several places # See https://docs.gitlab.com/ee/ci/variables/#cicd-variable-precedence stages: @@ -64,6 +65,7 @@ RSpec.describe Security::CiConfiguration::SastIacBuildAction do # SAST customization: https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings # Secret Detection customization: https://docs.gitlab.com/ee/user/application_security/secret_detection/#customizing-settings # Dependency Scanning customization: https://docs.gitlab.com/ee/user/application_security/dependency_scanning/#customizing-the-dependency-scanning-settings + # Container Scanning customization: https://docs.gitlab.com/ee/user/application_security/container_scanning/#customizing-the-container-scanning-settings # Note that environment variables can be set in several places # See https://docs.gitlab.com/ee/ci/variables/#cicd-variable-precedence stages: @@ -114,6 +116,7 @@ RSpec.describe Security::CiConfiguration::SastIacBuildAction do # SAST customization: https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings # Secret Detection customization: https://docs.gitlab.com/ee/user/application_security/secret_detection/#customizing-settings # Dependency Scanning customization: https://docs.gitlab.com/ee/user/application_security/dependency_scanning/#customizing-the-dependency-scanning-settings + # Container Scanning customization: https://docs.gitlab.com/ee/user/application_security/container_scanning/#customizing-the-container-scanning-settings # Note that environment variables can be set in several places # See https://docs.gitlab.com/ee/ci/variables/#cicd-variable-precedence include: @@ -135,6 +138,7 @@ RSpec.describe Security::CiConfiguration::SastIacBuildAction do # SAST customization: https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings # Secret Detection customization: https://docs.gitlab.com/ee/user/application_security/secret_detection/#customizing-settings # Dependency Scanning customization: https://docs.gitlab.com/ee/user/application_security/dependency_scanning/#customizing-the-dependency-scanning-settings + # Container Scanning customization: https://docs.gitlab.com/ee/user/application_security/container_scanning/#customizing-the-container-scanning-settings # Note that environment variables can be set in several places # See https://docs.gitlab.com/ee/ci/variables/#cicd-variable-precedence include: diff --git a/spec/lib/security/ci_configuration/secret_detection_build_action_spec.rb b/spec/lib/security/ci_configuration/secret_detection_build_action_spec.rb index 146c60ffb6e..4d9860ca4a5 100644 --- a/spec/lib/security/ci_configuration/secret_detection_build_action_spec.rb +++ b/spec/lib/security/ci_configuration/secret_detection_build_action_spec.rb @@ -17,6 +17,7 @@ RSpec.describe Security::CiConfiguration::SecretDetectionBuildAction do # SAST customization: https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings # Secret Detection customization: https://docs.gitlab.com/ee/user/application_security/secret_detection/#customizing-settings # Dependency Scanning customization: https://docs.gitlab.com/ee/user/application_security/dependency_scanning/#customizing-the-dependency-scanning-settings + # Container Scanning customization: https://docs.gitlab.com/ee/user/application_security/container_scanning/#customizing-the-container-scanning-settings # Note that environment variables can be set in several places # See https://docs.gitlab.com/ee/ci/variables/#cicd-variable-precedence stages: @@ -64,6 +65,7 @@ RSpec.describe Security::CiConfiguration::SecretDetectionBuildAction do # SAST customization: https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings # Secret Detection customization: https://docs.gitlab.com/ee/user/application_security/secret_detection/#customizing-settings # Dependency Scanning customization: https://docs.gitlab.com/ee/user/application_security/dependency_scanning/#customizing-the-dependency-scanning-settings + # Container Scanning customization: https://docs.gitlab.com/ee/user/application_security/container_scanning/#customizing-the-container-scanning-settings # Note that environment variables can be set in several places # See https://docs.gitlab.com/ee/ci/variables/#cicd-variable-precedence stages: @@ -114,6 +116,7 @@ RSpec.describe Security::CiConfiguration::SecretDetectionBuildAction do # SAST customization: https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings # Secret Detection customization: https://docs.gitlab.com/ee/user/application_security/secret_detection/#customizing-settings # Dependency Scanning customization: https://docs.gitlab.com/ee/user/application_security/dependency_scanning/#customizing-the-dependency-scanning-settings + # Container Scanning customization: https://docs.gitlab.com/ee/user/application_security/container_scanning/#customizing-the-container-scanning-settings # Note that environment variables can be set in several places # See https://docs.gitlab.com/ee/ci/variables/#cicd-variable-precedence include: @@ -135,6 +138,7 @@ RSpec.describe Security::CiConfiguration::SecretDetectionBuildAction do # SAST customization: https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings # Secret Detection customization: https://docs.gitlab.com/ee/user/application_security/secret_detection/#customizing-settings # Dependency Scanning customization: https://docs.gitlab.com/ee/user/application_security/dependency_scanning/#customizing-the-dependency-scanning-settings + # Container Scanning customization: https://docs.gitlab.com/ee/user/application_security/container_scanning/#customizing-the-container-scanning-settings # Note that environment variables can be set in several places # See https://docs.gitlab.com/ee/ci/variables/#cicd-variable-precedence include: diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index d78d0a56f10..35d0566d9b4 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -7118,6 +7118,29 @@ RSpec.describe Project, factory_default: :keep do it { is_expected.to be true } end + describe '#related_group_ids' do + let_it_be(:group) { create(:group) } + let_it_be(:sub_group) { create(:group, parent: group) } + + context 'when associated with a namespace' do + let(:project) { create(:project, namespace: create(:namespace)) } + let!(:linked_group) { create(:project_group_link, project: project).group } + + it 'only includes linked groups' do + expect(project.related_group_ids).to contain_exactly(linked_group.id) + end + end + + context 'when associated with a group' do + let(:project) { create(:project, group: sub_group) } + let!(:linked_group) { create(:project_group_link, project: project).group } + + it 'includes self, ancestors and linked groups' do + expect(project.related_group_ids).to contain_exactly(group.id, sub_group.id, linked_group.id) + end + end + end + describe '#package_already_taken?' do let_it_be(:namespace) { create(:namespace, path: 'test') } let_it_be(:project) { create(:project, :public, namespace: namespace) } diff --git a/spec/requests/api/ci/runners_spec.rb b/spec/requests/api/ci/runners_spec.rb index 7cf436969db..7ba67f7f29b 100644 --- a/spec/requests/api/ci/runners_spec.rb +++ b/spec/requests/api/ci/runners_spec.rb @@ -86,14 +86,24 @@ RSpec.describe API::Ci::Runners do expect(response).to have_gitlab_http_status(:bad_request) end - it 'filters runners by status' do - create(:ci_runner, :project, :inactive, description: 'Inactive project runner', projects: [project]) + context 'with an inactive runner' do + let_it_be(:runner) { create(:ci_runner, :project, :inactive, description: 'Inactive project runner', projects: [project]) } - get api('/runners?status=paused', user) + it 'filters runners by paused state' do + get api('/runners?paused=true', user) - expect(json_response).to match_array [ - a_hash_including('description' => 'Inactive project runner') - ] + expect(json_response).to match_array [ + a_hash_including('description' => 'Inactive project runner') + ] + end + + it 'filters runners by status' do + get api('/runners?status=paused', user) + + expect(json_response).to match_array [ + a_hash_including('description' => 'Inactive project runner') + ] + end end it 'does not filter by invalid status' do @@ -199,14 +209,24 @@ RSpec.describe API::Ci::Runners do expect(response).to have_gitlab_http_status(:bad_request) end - it 'filters runners by status' do - create(:ci_runner, :project, :inactive, description: 'Inactive project runner', projects: [project]) + context 'with an inactive runner' do + let_it_be(:runner) { create(:ci_runner, :project, :inactive, description: 'Inactive project runner', projects: [project]) } - get api('/runners/all?status=paused', admin) + it 'filters runners by status' do + get api('/runners/all?paused=true', admin) - expect(json_response).to match_array [ - a_hash_including('description' => 'Inactive project runner') - ] + expect(json_response).to match_array [ + a_hash_including('description' => 'Inactive project runner') + ] + end + + it 'filters runners by status' do + get api('/runners/all?status=paused', admin) + + expect(json_response).to match_array [ + a_hash_including('description' => 'Inactive project runner') + ] + end end it 'does not filter by invalid status' do @@ -956,14 +976,24 @@ RSpec.describe API::Ci::Runners do expect(response).to have_gitlab_http_status(:bad_request) end - it 'filters runners by status' do - create(:ci_runner, :project, :inactive, description: 'Inactive project runner', projects: [project]) + context 'with an inactive runner' do + let_it_be(:runner) { create(:ci_runner, :project, :inactive, description: 'Inactive project runner', projects: [project]) } - get api("/projects/#{project.id}/runners?status=paused", user) + it 'filters runners by status' do + get api("/projects/#{project.id}/runners?paused=true", user) - expect(json_response).to match_array [ - a_hash_including('description' => 'Inactive project runner') - ] + expect(json_response).to match_array [ + a_hash_including('description' => 'Inactive project runner') + ] + end + + it 'filters runners by status' do + get api("/projects/#{project.id}/runners?status=paused", user) + + expect(json_response).to match_array [ + a_hash_including('description' => 'Inactive project runner') + ] + end end it 'does not filter by invalid status' do @@ -1022,21 +1052,31 @@ RSpec.describe API::Ci::Runners do end end - context 'filter runners by status' do - it 'returns runners by valid status' do - create(:ci_runner, :group, :inactive, description: 'Inactive group runner', groups: [group]) + context 'with an inactive runner' do + let_it_be(:runner) { create(:ci_runner, :group, :inactive, description: 'Inactive group runner', groups: [group]) } - get api("/groups/#{group.id}/runners?status=paused", user) + it 'returns runners by paused state' do + get api("/groups/#{group.id}/runners?paused=true", user) expect(json_response).to match_array([ a_hash_including('description' => 'Inactive group runner') ]) end - it 'does not filter by invalid status' do - get api("/groups/#{group.id}/runners?status=bogus", user) + context 'filter runners by status' do + it 'returns runners by valid status' do + get api("/groups/#{group.id}/runners?status=paused", user) - expect(response).to have_gitlab_http_status(:bad_request) + expect(json_response).to match_array([ + a_hash_including('description' => 'Inactive group runner') + ]) + end + + it 'does not filter by invalid status' do + get api("/groups/#{group.id}/runners?status=bogus", user) + + expect(response).to have_gitlab_http_status(:bad_request) + end end end diff --git a/spec/requests/request_profiler_spec.rb b/spec/requests/request_profiler_spec.rb new file mode 100644 index 00000000000..72689595480 --- /dev/null +++ b/spec/requests/request_profiler_spec.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Request Profiler' do + let(:user) { create(:user) } + + shared_examples 'profiling a request' do |profile_type, extension| + before do + allow(Rails).to receive(:cache).and_return(ActiveSupport::Cache::MemoryStore.new) + allow(RubyProf::Profile).to receive(:profile) do |&blk| + blk.call + RubyProf::Profile.new + end + allow(MemoryProfiler).to receive(:report) do |&blk| + blk.call + MemoryProfiler.start + MemoryProfiler.stop + end + end + + it 'creates a profile of the request' do + project = create(:project, namespace: user.namespace) + time = Time.now + path = "/#{project.full_path}" + + travel_to(time) do + get path, params: {}, headers: { 'X-Profile-Token' => Gitlab::RequestProfiler.profile_token, 'X-Profile-Mode' => profile_type } + end + + profile_type = 'execution' if profile_type.nil? + profile_path = "#{Gitlab.config.shared.path}/tmp/requests_profiles/#{path.tr('/', '|')}_#{time.to_i}_#{profile_type}.#{extension}" + expect(File.exist?(profile_path)).to be true + end + + after do + Gitlab::RequestProfiler.remove_all_profiles + end + end + + context "when user is logged-in" do + before do + login_as(user) + end + + include_examples 'profiling a request', 'execution', 'html' + include_examples 'profiling a request', nil, 'html' + include_examples 'profiling a request', 'memory', 'txt' + end + + context "when user is not logged-in" do + include_examples 'profiling a request', 'execution', 'html' + include_examples 'profiling a request', nil, 'html' + include_examples 'profiling a request', 'memory', 'txt' + end +end diff --git a/spec/services/security/ci_configuration/container_scanning_create_service_spec.rb b/spec/services/security/ci_configuration/container_scanning_create_service_spec.rb new file mode 100644 index 00000000000..df76750efc8 --- /dev/null +++ b/spec/services/security/ci_configuration/container_scanning_create_service_spec.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Security::CiConfiguration::ContainerScanningCreateService, :snowplow do + subject(:result) { described_class.new(project, user).execute } + + let(:branch_name) { 'set-container-scanning-config-1' } + + let(:snowplow_event) do + { + category: 'Security::CiConfiguration::ContainerScanningCreateService', + action: 'create', + label: '' + } + end + + include_examples 'services security ci configuration create service', true +end