summaryrefslogtreecommitdiff
path: root/spec/lib/gitlab
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-05-19 07:33:21 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2022-05-19 07:33:21 +0000
commit36a59d088eca61b834191dacea009677a96c052f (patch)
treee4f33972dab5d8ef79e3944a9f403035fceea43f /spec/lib/gitlab
parenta1761f15ec2cae7c7f7bbda39a75494add0dfd6f (diff)
downloadgitlab-ce-36a59d088eca61b834191dacea009677a96c052f.tar.gz
Add latest changes from gitlab-org/gitlab@15-0-stable-eev15.0.0-rc42
Diffstat (limited to 'spec/lib/gitlab')
-rw-r--r--spec/lib/gitlab/application_rate_limiter_spec.rb14
-rw-r--r--spec/lib/gitlab/asciidoc_spec.rb12
-rw-r--r--spec/lib/gitlab/audit/deploy_token_author_spec.rb17
-rw-r--r--spec/lib/gitlab/audit/null_author_spec.rb9
-rw-r--r--spec/lib/gitlab/auth/ldap/adapter_spec.rb10
-rw-r--r--spec/lib/gitlab/auth/otp/strategies/forti_authenticator/manual_otp_spec.rb (renamed from spec/lib/gitlab/auth/otp/strategies/forti_authenticator_spec.rb)2
-rw-r--r--spec/lib/gitlab/auth/otp/strategies/forti_authenticator/push_otp_spec.rb65
-rw-r--r--spec/lib/gitlab/auth/saml/config_spec.rb19
-rw-r--r--spec/lib/gitlab/background_migration/backfill_artifact_expiry_date_spec.rb82
-rw-r--r--spec/lib/gitlab/background_migration/backfill_draft_status_on_merge_requests_spec.rb2
-rw-r--r--spec/lib/gitlab/background_migration/backfill_draft_status_on_merge_requests_with_corrected_regex_spec.rb75
-rw-r--r--spec/lib/gitlab/background_migration/backfill_group_features_spec.rb12
-rw-r--r--spec/lib/gitlab/background_migration/backfill_integrations_enable_ssl_verification_spec.rb73
-rw-r--r--spec/lib/gitlab/background_migration/backfill_integrations_type_new_spec.rb13
-rw-r--r--spec/lib/gitlab/background_migration/backfill_note_discussion_id_spec.rb29
-rw-r--r--spec/lib/gitlab/background_migration/backfill_project_settings_spec.rb41
-rw-r--r--spec/lib/gitlab/background_migration/backfill_topics_title_spec.rb21
-rw-r--r--spec/lib/gitlab/background_migration/batched_migration_job_spec.rb96
-rw-r--r--spec/lib/gitlab/background_migration/copy_column_using_background_migration_job_spec.rb169
-rw-r--r--spec/lib/gitlab/background_migration/expire_o_auth_tokens_spec.rb35
-rw-r--r--spec/lib/gitlab/background_migration/extract_project_topics_into_separate_table_spec.rb2
-rw-r--r--spec/lib/gitlab/background_migration/job_coordinator_spec.rb2
-rw-r--r--spec/lib/gitlab/background_migration/merge_topics_with_same_name_spec.rb25
-rw-r--r--spec/lib/gitlab/background_migration/nullify_orphan_runner_id_on_ci_builds_spec.rb8
-rw-r--r--spec/lib/gitlab/background_migration/reset_duplicate_ci_runners_token_encrypted_values_on_projects_spec.rb39
-rw-r--r--spec/lib/gitlab/background_migration/reset_duplicate_ci_runners_token_values_on_projects_spec.rb39
-rw-r--r--spec/lib/gitlab/background_migration/reset_too_many_tags_skipped_registry_imports_spec.rb85
-rw-r--r--spec/lib/gitlab/backtrace_cleaner_spec.rb1
-rw-r--r--spec/lib/gitlab/checks/branch_check_spec.rb6
-rw-r--r--spec/lib/gitlab/checks/changes_access_spec.rb39
-rw-r--r--spec/lib/gitlab/checks/single_change_access_spec.rb15
-rw-r--r--spec/lib/gitlab/ci/ansi2json_spec.rb11
-rw-r--r--spec/lib/gitlab/ci/build/rules/rule/clause/if_spec.rb74
-rw-r--r--spec/lib/gitlab/ci/build/rules_spec.rb13
-rw-r--r--spec/lib/gitlab/ci/config/entry/environment_spec.rb26
-rw-r--r--spec/lib/gitlab/ci/config/entry/job_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/config/entry/reports_spec.rb14
-rw-r--r--spec/lib/gitlab/ci/config/entry/root_spec.rb37
-rw-r--r--spec/lib/gitlab/ci/config/external/file/local_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/config/external/file/project_spec.rb4
-rw-r--r--spec/lib/gitlab/ci/config/external/file/remote_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/config/external/file/template_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/config/external/processor_spec.rb72
-rw-r--r--spec/lib/gitlab/ci/config_spec.rb12
-rw-r--r--spec/lib/gitlab/ci/lint_spec.rb20
-rw-r--r--spec/lib/gitlab/ci/parsers/security/common_spec.rb29
-rw-r--r--spec/lib/gitlab/ci/parsers/security/validators/schema_validator_spec.rb278
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/cancel_pending_pipelines_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/limit/deployments_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/pipeline/expression/lexeme/matches_spec.rb28
-rw-r--r--spec/lib/gitlab/ci/pipeline/expression/lexeme/not_matches_spec.rb28
-rw-r--r--spec/lib/gitlab/ci/pipeline/expression/lexeme/pattern_spec.rb49
-rw-r--r--spec/lib/gitlab/ci/pipeline/expression/statement_spec.rb45
-rw-r--r--spec/lib/gitlab/ci/pipeline/seed/deployment_spec.rb33
-rw-r--r--spec/lib/gitlab/ci/reports/security/scanner_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/runner_upgrade_check_spec.rb4
-rw-r--r--spec/lib/gitlab/ci/templates/Jobs/sast_iac_gitlab_ci_yaml_spec.rb4
-rw-r--r--spec/lib/gitlab/ci/templates/Jobs/sast_iac_latest_gitlab_ci_yaml_spec.rb65
-rw-r--r--spec/lib/gitlab/ci/templates/MATLAB_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/templates/Terraform/base_gitlab_ci_yaml_spec.rb1
-rw-r--r--spec/lib/gitlab/ci/templates/managed_cluster_applications_gitlab_ci_yaml_spec.rb40
-rw-r--r--spec/lib/gitlab/ci/templates/templates_spec.rb3
-rw-r--r--spec/lib/gitlab/ci/templates/terraform_gitlab_ci_yaml_spec.rb5
-rw-r--r--spec/lib/gitlab/ci/templates/terraform_latest_gitlab_ci_yaml_spec.rb4
-rw-r--r--spec/lib/gitlab/ci/variables/builder_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/yaml_processor/result_spec.rb18
-rw-r--r--spec/lib/gitlab/ci/yaml_processor_spec.rb10
-rw-r--r--spec/lib/gitlab/color_spec.rb42
-rw-r--r--spec/lib/gitlab/content_security_policy/config_loader_spec.rb20
-rw-r--r--spec/lib/gitlab/data_builder/issuable_spec.rb (renamed from spec/lib/gitlab/hook_data/issuable_builder_spec.rb)2
-rw-r--r--spec/lib/gitlab/database/background_migration/batched_migration_spec.rb9
-rw-r--r--spec/lib/gitlab/database/background_migration/batched_migration_wrapper_spec.rb96
-rw-r--r--spec/lib/gitlab/database/migration_helpers/restrict_gitlab_schema_spec.rb47
-rw-r--r--spec/lib/gitlab/database/migration_helpers_spec.rb133
-rw-r--r--spec/lib/gitlab/database/migrations/background_migration_helpers_spec.rb141
-rw-r--r--spec/lib/gitlab/database/migrations/base_background_runner_spec.rb23
-rw-r--r--spec/lib/gitlab/database/migrations/batched_background_migration_helpers_spec.rb41
-rw-r--r--spec/lib/gitlab/database/migrations/observers/query_statistics_spec.rb1
-rw-r--r--spec/lib/gitlab/database/migrations/reestablished_connection_stack_spec.rb57
-rw-r--r--spec/lib/gitlab/database/migrations/runner_spec.rb33
-rw-r--r--spec/lib/gitlab/database/migrations/test_background_runner_spec.rb37
-rw-r--r--spec/lib/gitlab/database/migrations/test_batched_background_runner_spec.rb87
-rw-r--r--spec/lib/gitlab/database/partitioning_migration_helpers/index_helpers_spec.rb10
-rw-r--r--spec/lib/gitlab/database/query_analyzer_spec.rb34
-rw-r--r--spec/lib/gitlab/database/query_analyzers/gitlab_schemas_metrics_spec.rb2
-rw-r--r--spec/lib/gitlab/database/query_analyzers/gitlab_schemas_validate_connection_spec.rb70
-rw-r--r--spec/lib/gitlab/database/query_analyzers/restrict_allowed_schemas_spec.rb2
-rw-r--r--spec/lib/gitlab/database/shared_model_spec.rb2
-rw-r--r--spec/lib/gitlab/database_importers/work_items/base_type_importer_spec.rb2
-rw-r--r--spec/lib/gitlab/database_spec.rb34
-rw-r--r--spec/lib/gitlab/diff/file_spec.rb16
-rw-r--r--spec/lib/gitlab/diff/rendered/notebook/diff_file_spec.rb22
-rw-r--r--spec/lib/gitlab/doctor/secrets_spec.rb34
-rw-r--r--spec/lib/gitlab/email/handler/create_note_handler_spec.rb4
-rw-r--r--spec/lib/gitlab/email/message/build_ios_app_guide_spec.rb23
-rw-r--r--spec/lib/gitlab/email/message/in_product_marketing/base_spec.rb2
-rw-r--r--spec/lib/gitlab/email/message/in_product_marketing/helper_spec.rb79
-rw-r--r--spec/lib/gitlab/experiment/rollout/feature_spec.rb3
-rw-r--r--spec/lib/gitlab/experimentation/controller_concern_spec.rb4
-rw-r--r--spec/lib/gitlab/git/diff_spec.rb8
-rw-r--r--spec/lib/gitlab/git_access_spec.rb9
-rw-r--r--spec/lib/gitlab/gitaly_client/commit_service_spec.rb129
-rw-r--r--spec/lib/gitlab/github_import/importer/diff_note_importer_spec.rb12
-rw-r--r--spec/lib/gitlab/github_import/importer/pull_requests_reviews_importer_spec.rb4
-rw-r--r--spec/lib/gitlab/github_import/importer/single_endpoint_diff_notes_importer_spec.rb4
-rw-r--r--spec/lib/gitlab/github_import/importer/single_endpoint_issue_notes_importer_spec.rb4
-rw-r--r--spec/lib/gitlab/github_import/importer/single_endpoint_merge_request_notes_importer_spec.rb4
-rw-r--r--spec/lib/gitlab/github_import/milestone_finder_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/parallel_scheduling_spec.rb71
-rw-r--r--spec/lib/gitlab/gon_helper_spec.rb1
-rw-r--r--spec/lib/gitlab/graphql/find_argument_in_parent_spec.rb45
-rw-r--r--spec/lib/gitlab/graphql/pagination/keyset/connection_generic_keyset_spec.rb4
-rw-r--r--spec/lib/gitlab/graphql/pagination/keyset/connection_spec.rb4
-rw-r--r--spec/lib/gitlab/graphql/queries_spec.rb10
-rw-r--r--spec/lib/gitlab/health_checks/middleware_spec.rb (renamed from spec/lib/gitlab/metrics/exporter/health_checks_middleware_spec.rb)8
-rw-r--r--spec/lib/gitlab/health_checks/server_spec.rb64
-rw-r--r--spec/lib/gitlab/http_spec.rb4
-rw-r--r--spec/lib/gitlab/import/import_failure_service_spec.rb40
-rw-r--r--spec/lib/gitlab/import_export/all_models.yml1
-rw-r--r--spec/lib/gitlab/import_export/group/relation_factory_spec.rb15
-rw-r--r--spec/lib/gitlab/import_export/project/tree_restorer_spec.rb4
-rw-r--r--spec/lib/gitlab/inactive_projects_deletion_warning_tracker_spec.rb67
-rw-r--r--spec/lib/gitlab/instrumentation/rate_limiting_gates_spec.rb39
-rw-r--r--spec/lib/gitlab/instrumentation_helper_spec.rb21
-rw-r--r--spec/lib/gitlab/jira/middleware_spec.rb4
-rw-r--r--spec/lib/gitlab/json_cache_spec.rb4
-rw-r--r--spec/lib/gitlab/json_spec.rb2
-rw-r--r--spec/lib/gitlab/kubernetes/cilium_network_policy_spec.rb274
-rw-r--r--spec/lib/gitlab/kubernetes/kube_client_spec.rb64
-rw-r--r--spec/lib/gitlab/kubernetes/network_policy_spec.rb235
-rw-r--r--spec/lib/gitlab/legacy_github_import/importer_spec.rb3
-rw-r--r--spec/lib/gitlab/lograge/custom_options_spec.rb12
-rw-r--r--spec/lib/gitlab/markdown_cache/active_record/extension_spec.rb4
-rw-r--r--spec/lib/gitlab/metrics/dashboard/importers/prometheus_metrics_spec.rb44
-rw-r--r--spec/lib/gitlab/metrics/exporter/base_exporter_spec.rb2
-rw-r--r--spec/lib/gitlab/metrics/methods_spec.rb2
-rw-r--r--spec/lib/gitlab/metrics/rails_slis_spec.rb14
-rw-r--r--spec/lib/gitlab/metrics/sli_spec.rb183
-rw-r--r--spec/lib/gitlab/metrics/subscribers/active_record_spec.rb122
-rw-r--r--spec/lib/gitlab/metrics/subscribers/rack_attack_spec.rb55
-rw-r--r--spec/lib/gitlab/patch/database_config_spec.rb59
-rw-r--r--spec/lib/gitlab/path_regex_spec.rb2
-rw-r--r--spec/lib/gitlab/popen_spec.rb2
-rw-r--r--spec/lib/gitlab/process_supervisor_spec.rb76
-rw-r--r--spec/lib/gitlab/query_limiting/transaction_spec.rb15
-rw-r--r--spec/lib/gitlab/request_profiler/profile_spec.rb61
-rw-r--r--spec/lib/gitlab/request_profiler_spec.rb56
-rw-r--r--spec/lib/gitlab/saas_spec.rb6
-rw-r--r--spec/lib/gitlab/safe_request_purger_spec.rb73
-rw-r--r--spec/lib/gitlab/setup_helper/praefect_spec.rb79
-rw-r--r--spec/lib/gitlab/sidekiq_config_spec.rb40
-rw-r--r--spec/lib/gitlab/sidekiq_death_handler_spec.rb8
-rw-r--r--spec/lib/gitlab/sidekiq_logging/structured_logger_spec.rb3
-rw-r--r--spec/lib/gitlab/sidekiq_middleware/server_metrics_spec.rb16
-rw-r--r--spec/lib/gitlab/subscription_portal_spec.rb1
-rw-r--r--spec/lib/gitlab/template/gitlab_ci_yml_template_spec.rb49
-rw-r--r--spec/lib/gitlab/tracking/event_definition_spec.rb2
-rw-r--r--spec/lib/gitlab/url_builder_spec.rb16
-rw-r--r--spec/lib/gitlab/usage/metric_definition_spec.rb22
-rw-r--r--spec/lib/gitlab/usage/metric_spec.rb27
-rw-r--r--spec/lib/gitlab/usage/metrics/instrumentations/collected_data_categories_metric_spec.rb2
-rw-r--r--spec/lib/gitlab/usage/metrics/instrumentations/count_bulk_imports_entities_metric_spec.rb106
-rw-r--r--spec/lib/gitlab/usage/metrics/instrumentations/count_imported_projects_metric_spec.rb70
-rw-r--r--spec/lib/gitlab/usage/metrics/instrumentations/database_metric_spec.rb40
-rw-r--r--spec/lib/gitlab/usage/metrics/instrumentations/redis_hll_metric_spec.rb24
-rw-r--r--spec/lib/gitlab/usage/metrics/instrumentations/redis_metric_spec.rb24
-rw-r--r--spec/lib/gitlab/usage/metrics/query_spec.rb32
-rw-r--r--spec/lib/gitlab/usage/service_ping/legacy_metric_timing_decorator_spec.rb35
-rw-r--r--spec/lib/gitlab/usage/service_ping_report_spec.rb64
-rw-r--r--spec/lib/gitlab/usage_counters/pod_logs_spec.rb7
-rw-r--r--spec/lib/gitlab/usage_data_counters/editor_unique_counter_spec.rb3
-rw-r--r--spec/lib/gitlab/usage_data_counters/ipynb_diff_activity_counter_spec.rb107
-rw-r--r--spec/lib/gitlab/usage_data_queries_spec.rb6
-rw-r--r--spec/lib/gitlab/usage_data_spec.rb29
-rw-r--r--spec/lib/gitlab/user_access_spec.rb11
-rw-r--r--spec/lib/gitlab/utils/usage_data_spec.rb65
-rw-r--r--spec/lib/gitlab/utils_spec.rb8
-rw-r--r--spec/lib/gitlab/workhorse_spec.rb52
-rw-r--r--spec/lib/gitlab/zentao/client_spec.rb32
179 files changed, 4130 insertions, 1912 deletions
diff --git a/spec/lib/gitlab/application_rate_limiter_spec.rb b/spec/lib/gitlab/application_rate_limiter_spec.rb
index 20c89eab5f5..efe78cd3a35 100644
--- a/spec/lib/gitlab/application_rate_limiter_spec.rb
+++ b/spec/lib/gitlab/application_rate_limiter_spec.rb
@@ -56,6 +56,20 @@ RSpec.describe Gitlab::ApplicationRateLimiter, :clean_gitlab_redis_rate_limiting
end
end
+ context 'when the key is valid' do
+ it 'records the checked key in request storage', :request_store do
+ subject.throttled?(:test_action, scope: [user])
+
+ expect(::Gitlab::Instrumentation::RateLimitingGates.payload)
+ .to eq(::Gitlab::Instrumentation::RateLimitingGates::GATES => [:test_action])
+
+ subject.throttled?(:another_action, scope: [user], peek: true)
+
+ expect(::Gitlab::Instrumentation::RateLimitingGates.payload)
+ .to eq(::Gitlab::Instrumentation::RateLimitingGates::GATES => [:test_action, :another_action])
+ end
+ end
+
shared_examples 'throttles based on key and scope' do
let(:start_time) { Time.current.beginning_of_hour }
diff --git a/spec/lib/gitlab/asciidoc_spec.rb b/spec/lib/gitlab/asciidoc_spec.rb
index 44bbbe49cd3..d86191ca0c2 100644
--- a/spec/lib/gitlab/asciidoc_spec.rb
+++ b/spec/lib/gitlab/asciidoc_spec.rb
@@ -79,7 +79,7 @@ module Gitlab
},
'image with onerror' => {
input: 'image:https://localhost.com/image.png[Alt text" onerror="alert(7)]',
- output: "<div>\n<p><span><a class=\"no-attachment-icon\" href=\"https://localhost.com/image.png\" target=\"_blank\" rel=\"noopener noreferrer\"><img src=\"data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" alt='Alt text\" onerror=\"alert(7)' class=\"lazy\" data-src=\"https://localhost.com/image.png\"></a></span></p>\n</div>"
+ output: "<div>\n<p><span><a class=\"no-attachment-icon\" href=\"https://localhost.com/image.png\" target=\"_blank\" rel=\"noopener noreferrer\"><img src=\"data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" alt='Alt text\" onerror=\"alert(7)' decoding=\"async\" class=\"lazy\" data-src=\"https://localhost.com/image.png\"></a></span></p>\n</div>"
}
}
@@ -112,13 +112,13 @@ module Gitlab
context "images" do
it "does lazy load and link image" do
input = 'image:https://localhost.com/image.png[]'
- output = "<div>\n<p><span><a class=\"no-attachment-icon\" href=\"https://localhost.com/image.png\" target=\"_blank\" rel=\"noopener noreferrer\"><img src=\"data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" alt=\"image\" class=\"lazy\" data-src=\"https://localhost.com/image.png\"></a></span></p>\n</div>"
+ output = "<div>\n<p><span><a class=\"no-attachment-icon\" href=\"https://localhost.com/image.png\" target=\"_blank\" rel=\"noopener noreferrer\"><img src=\"data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" alt=\"image\" decoding=\"async\" class=\"lazy\" data-src=\"https://localhost.com/image.png\"></a></span></p>\n</div>"
expect(render(input, context)).to include(output)
end
it "does not automatically link image if link is explicitly defined" do
input = 'image:https://localhost.com/image.png[link=https://gitlab.com]'
- output = "<div>\n<p><span><a href=\"https://gitlab.com\" rel=\"nofollow noreferrer noopener\" target=\"_blank\"><img src=\"data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" alt=\"image\" class=\"lazy\" data-src=\"https://localhost.com/image.png\"></a></span></p>\n</div>"
+ output = "<div>\n<p><span><a href=\"https://gitlab.com\" rel=\"nofollow noreferrer noopener\" target=\"_blank\"><img src=\"data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" alt=\"image\" decoding=\"async\" class=\"lazy\" data-src=\"https://localhost.com/image.png\"></a></span></p>\n</div>"
expect(render(input, context)).to include(output)
end
end
@@ -524,7 +524,7 @@ module Gitlab
output = <<~HTML
<div>
<div>
- <a class="no-attachment-icon" href="https://kroki.io/graphviz/svg/eNpLyUwvSizIUHBXqOZSUPBIzcnJ17ULzy_KSeGqBQCEzQka" target="_blank" rel="noopener noreferrer"><img src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="Diagram" class="lazy" data-src="https://kroki.io/graphviz/svg/eNpLyUwvSizIUHBXqOZSUPBIzcnJ17ULzy_KSeGqBQCEzQka"></a>
+ <a class="no-attachment-icon" href="https://kroki.io/graphviz/svg/eNpLyUwvSizIUHBXqOZSUPBIzcnJ17ULzy_KSeGqBQCEzQka" target="_blank" rel="noopener noreferrer"><img src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="Diagram" decoding="async" class="lazy" data-src="https://kroki.io/graphviz/svg/eNpLyUwvSizIUHBXqOZSUPBIzcnJ17ULzy_KSeGqBQCEzQka"></a>
</div>
</div>
HTML
@@ -578,7 +578,7 @@ module Gitlab
output = <<~HTML
<div>
<div>
- <a class=\"no-attachment-icon\" href=\"https://kroki.io/plantuml/png/eNpLzkksLlZwyslPzg4oyk9OLS7OL-LiQuUr2NTo6ipUJ-eX5pWkFlllF-VnZ-oW5CTmlZTm5uhm5iXnlKak1gIABQEb8A==\" target=\"_blank\" rel=\"noopener noreferrer\"><img src=\"data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" alt=\"Diagram\" class=\"lazy\" data-src=\"https://kroki.io/plantuml/png/eNpLzkksLlZwyslPzg4oyk9OLS7OL-LiQuUr2NTo6ipUJ-eX5pWkFlllF-VnZ-oW5CTmlZTm5uhm5iXnlKak1gIABQEb8A==\"></a>
+ <a class=\"no-attachment-icon\" href=\"https://kroki.io/plantuml/png/eNpLzkksLlZwyslPzg4oyk9OLS7OL-LiQuUr2NTo6ipUJ-eX5pWkFlllF-VnZ-oW5CTmlZTm5uhm5iXnlKak1gIABQEb8A==\" target=\"_blank\" rel=\"noopener noreferrer\"><img src=\"data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" alt=\"Diagram\" decoding=\"async\" class=\"lazy\" data-src=\"https://kroki.io/plantuml/png/eNpLzkksLlZwyslPzg4oyk9OLS7OL-LiQuUr2NTo6ipUJ-eX5pWkFlllF-VnZ-oW5CTmlZTm5uhm5iXnlKak1gIABQEb8A==\"></a>
</div>
</div>
HTML
@@ -625,7 +625,7 @@ module Gitlab
output = <<~HTML
<div>
<div>
- <a class="no-attachment-icon" href="https://kroki.io/blockdiag/svg/eNpdzDEKQjEQhOHeU4zpPYFoYesRxGJ9bwghMSsbUYJ4d10UCZbDfPynolOek0Q8FsDeNCestoisNLmy-Qg7R3Blcm5hPcr0ITdaB6X15fv-_YdJixo2CNHI2lmK3sPRA__RwV5SzV80ZAegJjXSyfMFptc71w==" target="_blank" rel="noopener noreferrer"><img src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="Diagram" class="lazy" data-src="https://kroki.io/blockdiag/svg/eNpdzDEKQjEQhOHeU4zpPYFoYesRxGJ9bwghMSsbUYJ4d10UCZbDfPynolOek0Q8FsDeNCestoisNLmy-Qg7R3Blcm5hPcr0ITdaB6X15fv-_YdJixo2CNHI2lmK3sPRA__RwV5SzV80ZAegJjXSyfMFptc71w=="></a>
+ <a class="no-attachment-icon" href="https://kroki.io/blockdiag/svg/eNpdzDEKQjEQhOHeU4zpPYFoYesRxGJ9bwghMSsbUYJ4d10UCZbDfPynolOek0Q8FsDeNCestoisNLmy-Qg7R3Blcm5hPcr0ITdaB6X15fv-_YdJixo2CNHI2lmK3sPRA__RwV5SzV80ZAegJjXSyfMFptc71w==" target="_blank" rel="noopener noreferrer"><img src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="Diagram" decoding="async" class="lazy" data-src="https://kroki.io/blockdiag/svg/eNpdzDEKQjEQhOHeU4zpPYFoYesRxGJ9bwghMSsbUYJ4d10UCZbDfPynolOek0Q8FsDeNCestoisNLmy-Qg7R3Blcm5hPcr0ITdaB6X15fv-_YdJixo2CNHI2lmK3sPRA__RwV5SzV80ZAegJjXSyfMFptc71w=="></a>
</div>
</div>
HTML
diff --git a/spec/lib/gitlab/audit/deploy_token_author_spec.rb b/spec/lib/gitlab/audit/deploy_token_author_spec.rb
new file mode 100644
index 00000000000..449b7456a80
--- /dev/null
+++ b/spec/lib/gitlab/audit/deploy_token_author_spec.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Audit::DeployTokenAuthor do
+ describe '#initialize' do
+ it 'sets correct attributes' do
+ expect(described_class.new(name: 'Lorem deploy token'))
+ .to have_attributes(id: -2, name: 'Lorem deploy token')
+ end
+
+ it 'sets default name when it is not provided' do
+ expect(described_class.new)
+ .to have_attributes(id: -2, name: 'Deploy Token')
+ end
+ end
+end
diff --git a/spec/lib/gitlab/audit/null_author_spec.rb b/spec/lib/gitlab/audit/null_author_spec.rb
index 7203a0cd816..2045139a5f7 100644
--- a/spec/lib/gitlab/audit/null_author_spec.rb
+++ b/spec/lib/gitlab/audit/null_author_spec.rb
@@ -48,6 +48,15 @@ RSpec.describe Gitlab::Audit::NullAuthor do
expect(subject.for(-1, audit_event)).to be_a(Gitlab::Audit::CiRunnerTokenAuthor)
expect(subject.for(-1, audit_event)).to have_attributes(id: -1, name: 'Authentication token: cde456')
end
+
+ it 'returns DeployTokenAuthor when id equals -2', :aggregate_failures do
+ allow(audit_event).to receive(:[]).with(:author_name).and_return('Test deploy token')
+ allow(audit_event).to receive(:details).and_return({})
+ allow(audit_event).to receive(:target_type)
+
+ expect(subject.for(-2, audit_event)).to be_a(Gitlab::Audit::DeployTokenAuthor)
+ expect(subject.for(-2, audit_event)).to have_attributes(id: -2, name: 'Test deploy token')
+ end
end
describe '#current_sign_in_ip' do
diff --git a/spec/lib/gitlab/auth/ldap/adapter_spec.rb b/spec/lib/gitlab/auth/ldap/adapter_spec.rb
index b7b12e49a8e..3791b7a07dd 100644
--- a/spec/lib/gitlab/auth/ldap/adapter_spec.rb
+++ b/spec/lib/gitlab/auth/ldap/adapter_spec.rb
@@ -26,10 +26,12 @@ RSpec.describe Gitlab::Auth::Ldap::Adapter do
it 'searches with the proper options when searching by dn' do
expect(adapter).to receive(:ldap_search).with(
- base: 'uid=johndoe,ou=users,dc=example,dc=com',
- scope: Net::LDAP::SearchScope_BaseObject,
- attributes: ldap_attributes,
- filter: nil
+ {
+ base: 'uid=johndoe,ou=users,dc=example,dc=com',
+ scope: Net::LDAP::SearchScope_BaseObject,
+ attributes: ldap_attributes,
+ filter: nil
+ }
).and_return({})
adapter.users('dn', 'uid=johndoe,ou=users,dc=example,dc=com')
diff --git a/spec/lib/gitlab/auth/otp/strategies/forti_authenticator_spec.rb b/spec/lib/gitlab/auth/otp/strategies/forti_authenticator/manual_otp_spec.rb
index dc20df98185..f08c787382e 100644
--- a/spec/lib/gitlab/auth/otp/strategies/forti_authenticator_spec.rb
+++ b/spec/lib/gitlab/auth/otp/strategies/forti_authenticator/manual_otp_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Gitlab::Auth::Otp::Strategies::FortiAuthenticator do
+RSpec.describe Gitlab::Auth::Otp::Strategies::FortiAuthenticator::ManualOtp do
let_it_be(:user) { create(:user) }
let(:otp_code) { 42 }
diff --git a/spec/lib/gitlab/auth/otp/strategies/forti_authenticator/push_otp_spec.rb b/spec/lib/gitlab/auth/otp/strategies/forti_authenticator/push_otp_spec.rb
new file mode 100644
index 00000000000..231bd3f48f1
--- /dev/null
+++ b/spec/lib/gitlab/auth/otp/strategies/forti_authenticator/push_otp_spec.rb
@@ -0,0 +1,65 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Auth::Otp::Strategies::FortiAuthenticator::PushOtp do
+ let_it_be(:user) { create(:user) }
+
+ let(:host) { 'forti_authenticator.example.com' }
+ let(:port) { '444' }
+ let(:api_username) { 'janedoe' }
+ let(:api_token) { 's3cr3t' }
+
+ let(:forti_authenticator_auth_url) { "https://#{host}:#{port}/api/v1/pushauth/" }
+ let(:response_status) { 200 }
+
+ subject(:validate) { described_class.new(user).validate }
+
+ before do
+ stub_feature_flags(forti_authenticator: user)
+
+ stub_forti_authenticator_config(
+ enabled: true,
+ host: host,
+ port: port,
+ username: api_username,
+ access_token: api_token
+ )
+
+ request_body = { username: user.username }
+
+ stub_request(:post, forti_authenticator_auth_url)
+ .with(body: JSON(request_body),
+ headers: { 'Content-Type': 'application/json' },
+ basic_auth: [api_username, api_token])
+ .to_return(status: response_status, body: '')
+ end
+
+ context 'successful validation' do
+ it 'returns success' do
+ expect(validate[:status]).to eq(:success)
+ end
+ end
+
+ context 'unsuccessful validation' do
+ let(:response_status) { 401 }
+
+ it 'returns error' do
+ expect(validate[:status]).to eq(:error)
+ end
+ end
+
+ context 'unexpected error' do
+ it 'returns error' do
+ error_message = 'boom!'
+ stub_request(:post, forti_authenticator_auth_url).to_raise(StandardError.new(error_message))
+
+ expect(validate[:status]).to eq(:error)
+ expect(validate[:message]).to eq(error_message)
+ end
+ end
+
+ def stub_forti_authenticator_config(forti_authenticator_settings)
+ allow(::Gitlab.config.forti_authenticator).to(receive_messages(forti_authenticator_settings))
+ end
+end
diff --git a/spec/lib/gitlab/auth/saml/config_spec.rb b/spec/lib/gitlab/auth/saml/config_spec.rb
new file mode 100644
index 00000000000..12f5da48873
--- /dev/null
+++ b/spec/lib/gitlab/auth/saml/config_spec.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Auth::Saml::Config do
+ describe '.enabled?' do
+ subject { described_class.enabled? }
+
+ it { is_expected.to eq(false) }
+
+ context 'when SAML is enabled' do
+ before do
+ allow(Gitlab::Auth::OAuth::Provider).to receive(:providers).and_return([:saml])
+ end
+
+ it { is_expected.to eq(true) }
+ end
+ end
+end
diff --git a/spec/lib/gitlab/background_migration/backfill_artifact_expiry_date_spec.rb b/spec/lib/gitlab/background_migration/backfill_artifact_expiry_date_spec.rb
deleted file mode 100644
index f5d2224747a..00000000000
--- a/spec/lib/gitlab/background_migration/backfill_artifact_expiry_date_spec.rb
+++ /dev/null
@@ -1,82 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Gitlab::BackgroundMigration::BackfillArtifactExpiryDate, :migration, schema: 20210301200959 do
- subject(:perform) { migration.perform(1, 99) }
-
- let(:migration) { described_class.new }
- let(:artifact_outside_id_range) { create_artifact!(id: 100, created_at: 1.year.ago, expire_at: nil) }
- let(:artifact_outside_date_range) { create_artifact!(id: 40, created_at: Time.current, expire_at: nil) }
- let(:old_artifact) { create_artifact!(id: 10, created_at: 16.months.ago, expire_at: nil) }
- let(:recent_artifact) { create_artifact!(id: 20, created_at: 1.year.ago, expire_at: nil) }
- let(:artifact_with_expiry) { create_artifact!(id: 30, created_at: 1.year.ago, expire_at: Time.current + 1.day) }
-
- before do
- table(:namespaces).create!(id: 1, name: 'the-namespace', path: 'the-path')
- table(:projects).create!(id: 1, name: 'the-project', namespace_id: 1)
- table(:ci_builds).create!(id: 1, allow_failure: false)
- end
-
- context 'when current date is before the 22nd' do
- before do
- travel_to(Time.zone.local(2020, 1, 1, 0, 0, 0))
- end
-
- it 'backfills the expiry date for old artifacts' do
- expect(old_artifact.reload.expire_at).to eq(nil)
-
- perform
-
- expect(old_artifact.reload.expire_at).to be_within(1.minute).of(Time.zone.local(2020, 4, 22, 0, 0, 0))
- end
-
- it 'backfills the expiry date for recent artifacts' do
- expect(recent_artifact.reload.expire_at).to eq(nil)
-
- perform
-
- expect(recent_artifact.reload.expire_at).to be_within(1.minute).of(Time.zone.local(2021, 1, 22, 0, 0, 0))
- end
- end
-
- context 'when current date is after the 22nd' do
- before do
- travel_to(Time.zone.local(2020, 1, 23, 0, 0, 0))
- end
-
- it 'backfills the expiry date for old artifacts' do
- expect(old_artifact.reload.expire_at).to eq(nil)
-
- perform
-
- expect(old_artifact.reload.expire_at).to be_within(1.minute).of(Time.zone.local(2020, 5, 22, 0, 0, 0))
- end
-
- it 'backfills the expiry date for recent artifacts' do
- expect(recent_artifact.reload.expire_at).to eq(nil)
-
- perform
-
- expect(recent_artifact.reload.expire_at).to be_within(1.minute).of(Time.zone.local(2021, 2, 22, 0, 0, 0))
- end
- end
-
- it 'does not touch artifacts with expiry date' do
- expect { perform }.not_to change { artifact_with_expiry.reload.expire_at }
- end
-
- it 'does not touch artifacts outside id range' do
- expect { perform }.not_to change { artifact_outside_id_range.reload.expire_at }
- end
-
- it 'does not touch artifacts outside date range' do
- expect { perform }.not_to change { artifact_outside_date_range.reload.expire_at }
- end
-
- private
-
- def create_artifact!(**args)
- table(:ci_job_artifacts).create!(**args, project_id: 1, job_id: 1, file_type: 1)
- end
-end
diff --git a/spec/lib/gitlab/background_migration/backfill_draft_status_on_merge_requests_spec.rb b/spec/lib/gitlab/background_migration/backfill_draft_status_on_merge_requests_spec.rb
index 1158eedfe7c..84611c88806 100644
--- a/spec/lib/gitlab/background_migration/backfill_draft_status_on_merge_requests_spec.rb
+++ b/spec/lib/gitlab/background_migration/backfill_draft_status_on_merge_requests_spec.rb
@@ -37,7 +37,7 @@ RSpec.describe Gitlab::BackgroundMigration::BackfillDraftStatusOnMergeRequests,
end
end
- it "updates all open draft merge request's draft field to true" do
+ it "updates all eligible draft merge request's draft field to true" do
mr_count = merge_requests.all.count
expect { subject.perform(mr_ids.first, mr_ids.last) }
diff --git a/spec/lib/gitlab/background_migration/backfill_draft_status_on_merge_requests_with_corrected_regex_spec.rb b/spec/lib/gitlab/background_migration/backfill_draft_status_on_merge_requests_with_corrected_regex_spec.rb
new file mode 100644
index 00000000000..e6e10977143
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/backfill_draft_status_on_merge_requests_with_corrected_regex_spec.rb
@@ -0,0 +1,75 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::BackgroundMigration::BackfillDraftStatusOnMergeRequestsWithCorrectedRegex,
+ :migration, schema: 20220326161803 do
+ let(:namespaces) { table(:namespaces) }
+ let(:projects) { table(:projects) }
+ let(:merge_requests) { table(:merge_requests) }
+
+ let(:group) { namespaces.create!(name: 'gitlab', path: 'gitlab') }
+ let(:project) { projects.create!(namespace_id: group.id) }
+
+ let(:draft_prefixes) { ["[Draft]", "(Draft)", "Draft:", "Draft", "[WIP]", "WIP:", "WIP"] }
+
+ def create_merge_request(params)
+ common_params = {
+ target_project_id: project.id,
+ target_branch: 'feature1',
+ source_branch: 'master'
+ }
+
+ merge_requests.create!(common_params.merge(params))
+ end
+
+ context "for MRs with #draft? == true titles but draft attribute false" do
+ let(:mr_ids) { merge_requests.all.collect(&:id) }
+
+ before do
+ draft_prefixes.each do |prefix|
+ (1..4).each do |n|
+ create_merge_request(
+ title: "#{prefix} This is a title",
+ draft: false,
+ state_id: n
+ )
+
+ create_merge_request(
+ title: "This is a title with the #{prefix} in a weird spot",
+ draft: false,
+ state_id: n
+ )
+ end
+ end
+ end
+
+ it "updates all eligible draft merge request's draft field to true" do
+ mr_count = merge_requests.all.count
+
+ expect { subject.perform(mr_ids.first, mr_ids.last) }
+ .to change { MergeRequest.where(draft: false).count }
+ .from(mr_count).to(mr_count - draft_prefixes.length)
+ end
+
+ it "marks successful slices as completed" do
+ expect(subject).to receive(:mark_job_as_succeeded).with(mr_ids.first, mr_ids.last)
+
+ subject.perform(mr_ids.first, mr_ids.last)
+ end
+
+ it_behaves_like 'marks background migration job records' do
+ let!(:non_eligible_mrs) do
+ Array.new(2) do
+ create_merge_request(
+ title: "Not a d-r-a-f-t 1",
+ draft: false,
+ state_id: 1
+ )
+ end
+ end
+
+ let(:arguments) { [non_eligible_mrs.first.id, non_eligible_mrs.last.id] }
+ end
+ end
+end
diff --git a/spec/lib/gitlab/background_migration/backfill_group_features_spec.rb b/spec/lib/gitlab/background_migration/backfill_group_features_spec.rb
index 4705f0d0ab9..d84bc479554 100644
--- a/spec/lib/gitlab/background_migration/backfill_group_features_spec.rb
+++ b/spec/lib/gitlab/background_migration/backfill_group_features_spec.rb
@@ -6,7 +6,15 @@ RSpec.describe Gitlab::BackgroundMigration::BackfillGroupFeatures, :migration, s
let(:group_features) { table(:group_features) }
let(:namespaces) { table(:namespaces) }
- subject { described_class.new(connection: ActiveRecord::Base.connection) }
+ subject do
+ described_class.new(start_id: 1,
+ end_id: 4,
+ batch_table: :namespaces,
+ batch_column: :id,
+ sub_batch_size: 10,
+ pause_ms: 0,
+ connection: ActiveRecord::Base.connection)
+ end
describe '#perform' do
it 'creates settings for all group namespaces in range' do
@@ -19,7 +27,7 @@ RSpec.describe Gitlab::BackgroundMigration::BackfillGroupFeatures, :migration, s
group_features.create!(id: 1, group_id: 4)
expect(group_features.count).to eq 1
- expect { subject.perform(1, 4, :namespaces, :id, 10, 0, 4) }.to change { group_features.count }.by(2)
+ expect { subject.perform(4) }.to change { group_features.count }.by(2)
expect(group_features.count).to eq 3
expect(group_features.all.pluck(:group_id)).to contain_exactly(1, 3, 4)
diff --git a/spec/lib/gitlab/background_migration/backfill_integrations_enable_ssl_verification_spec.rb b/spec/lib/gitlab/background_migration/backfill_integrations_enable_ssl_verification_spec.rb
new file mode 100644
index 00000000000..b3825a7c4ea
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/backfill_integrations_enable_ssl_verification_spec.rb
@@ -0,0 +1,73 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::BackgroundMigration::BackfillIntegrationsEnableSslVerification, schema: 20220425121410 do
+ let(:migration) { described_class.new }
+ let(:integrations) { described_class::Integration }
+
+ before do
+ integrations.create!(id: 1, type_new: 'Integrations::Bamboo') # unaffected integration
+ integrations.create!(id: 2, type_new: 'Integrations::DroneCi') # no properties
+ integrations.create!(id: 3, type_new: 'Integrations::DroneCi',
+ properties: {}) # no URL
+ integrations.create!(id: 4, type_new: 'Integrations::DroneCi',
+ properties: { 'drone_url' => '' }) # blank URL
+ integrations.create!(id: 5, type_new: 'Integrations::DroneCi',
+ properties: { 'drone_url' => 'https://example.com:foo' }) # invalid URL
+ integrations.create!(id: 6, type_new: 'Integrations::DroneCi',
+ properties: { 'drone_url' => 'https://example.com' }) # unknown URL
+ integrations.create!(id: 7, type_new: 'Integrations::DroneCi',
+ properties: { 'drone_url' => 'http://cloud.drone.io' }) # no HTTPS
+ integrations.create!(id: 8, type_new: 'Integrations::DroneCi',
+ properties: { 'drone_url' => 'https://cloud.drone.io' }) # known URL
+ integrations.create!(id: 9, type_new: 'Integrations::Teamcity',
+ properties: { 'teamcity_url' => 'https://example.com' }) # unknown URL
+ integrations.create!(id: 10, type_new: 'Integrations::Teamcity',
+ properties: { 'teamcity_url' => 'https://foo.bar.teamcity.com' }) # unknown URL
+ integrations.create!(id: 11, type_new: 'Integrations::Teamcity',
+ properties: { 'teamcity_url' => 'https://teamcity.com' }) # unknown URL
+ integrations.create!(id: 12, type_new: 'Integrations::Teamcity',
+ properties: { 'teamcity_url' => 'https://customer.teamcity.com' }) # known URL
+ end
+
+ def properties(id)
+ integrations.find(id).properties
+ end
+
+ it 'enables SSL verification for known-good hostnames', :aggregate_failures do
+ migration.perform(1, 12)
+
+ # Bamboo
+ expect(properties(1)).to be_nil
+
+ # DroneCi
+ expect(properties(2)).to be_nil
+ expect(properties(3)).not_to include('enable_ssl_verification')
+ expect(properties(4)).not_to include('enable_ssl_verification')
+ expect(properties(5)).not_to include('enable_ssl_verification')
+ expect(properties(6)).not_to include('enable_ssl_verification')
+ expect(properties(7)).not_to include('enable_ssl_verification')
+ expect(properties(8)).to include('enable_ssl_verification' => true)
+
+ # Teamcity
+ expect(properties(9)).not_to include('enable_ssl_verification')
+ expect(properties(10)).not_to include('enable_ssl_verification')
+ expect(properties(11)).not_to include('enable_ssl_verification')
+ expect(properties(12)).to include('enable_ssl_verification' => true)
+ end
+
+ it 'only updates records within the given ID range', :aggregate_failures do
+ migration.perform(1, 8)
+
+ expect(properties(8)).to include('enable_ssl_verification' => true)
+ expect(properties(12)).not_to include('enable_ssl_verification')
+ end
+
+ it 'marks the job as succeeded' do
+ expect(Gitlab::Database::BackgroundMigrationJob).to receive(:mark_all_as_succeeded)
+ .with('BackfillIntegrationsEnableSslVerification', [1, 10])
+
+ migration.perform(1, 10)
+ end
+end
diff --git a/spec/lib/gitlab/background_migration/backfill_integrations_type_new_spec.rb b/spec/lib/gitlab/background_migration/backfill_integrations_type_new_spec.rb
index 8f765a7a536..d8a7ec775dd 100644
--- a/spec/lib/gitlab/background_migration/backfill_integrations_type_new_spec.rb
+++ b/spec/lib/gitlab/background_migration/backfill_integrations_type_new_spec.rb
@@ -2,10 +2,19 @@
require 'spec_helper'
-RSpec.describe Gitlab::BackgroundMigration::BackfillIntegrationsTypeNew do
+RSpec.describe Gitlab::BackgroundMigration::BackfillIntegrationsTypeNew, :migration, schema: 20220212120735 do
let(:migration) { described_class.new }
let(:integrations) { table(:integrations) }
- let(:namespaced_integrations) { Gitlab::Integrations::StiType.namespaced_integrations }
+
+ let(:namespaced_integrations) do
+ Set.new(%w[
+ Asana Assembla Bamboo Bugzilla Buildkite Campfire Confluence CustomIssueTracker Datadog
+ Discord DroneCi EmailsOnPush Ewm ExternalWiki Flowdock HangoutsChat Harbor Irker Jenkins Jira Mattermost
+ MattermostSlashCommands MicrosoftTeams MockCi MockMonitoring Packagist PipelinesEmail Pivotaltracker
+ Prometheus Pushover Redmine Shimo Slack SlackSlashCommands Teamcity UnifyCircuit WebexTeams Youtrack Zentao
+ Github GitlabSlackApplication
+ ]).freeze
+ end
before do
integrations.connection.execute 'ALTER TABLE integrations DISABLE TRIGGER "trigger_type_new_on_insert"'
diff --git a/spec/lib/gitlab/background_migration/backfill_note_discussion_id_spec.rb b/spec/lib/gitlab/background_migration/backfill_note_discussion_id_spec.rb
new file mode 100644
index 00000000000..dcb4ede36af
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/backfill_note_discussion_id_spec.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::BackgroundMigration::BackfillNoteDiscussionId do
+ let(:migration) { described_class.new }
+ let(:notes_table) { table(:notes) }
+ let(:existing_discussion_id) { Digest::SHA1.hexdigest('test') }
+
+ before do
+ notes_table.create!(id: 1, noteable_type: 'Issue', noteable_id: 2, discussion_id: existing_discussion_id)
+ notes_table.create!(id: 2, noteable_type: 'Issue', noteable_id: 1, discussion_id: nil)
+ notes_table.create!(id: 3, noteable_type: 'MergeRequest', noteable_id: 1, discussion_id: nil)
+ notes_table.create!(id: 4, noteable_type: 'Commit', commit_id: RepoHelpers.sample_commit.id, discussion_id: nil)
+ notes_table.create!(id: 5, noteable_type: 'Issue', noteable_id: 2, discussion_id: nil)
+ notes_table.create!(id: 6, noteable_type: 'MergeRequest', noteable_id: 2, discussion_id: nil)
+ end
+
+ it 'updates records in the specified batch', :aggregate_failures do
+ migration.perform(1, 5)
+
+ expect(notes_table.where(discussion_id: nil).count).to eq(1)
+
+ expect(notes_table.find(1).discussion_id).to eq(existing_discussion_id)
+ notes_table.where(id: 2..5).each do |n|
+ expect(n.discussion_id).to match(/\A[0-9a-f]{40}\z/)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/background_migration/backfill_project_settings_spec.rb b/spec/lib/gitlab/background_migration/backfill_project_settings_spec.rb
new file mode 100644
index 00000000000..525c236b644
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/backfill_project_settings_spec.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::BackgroundMigration::BackfillProjectSettings, :migration, schema: 20220324165436 do
+ let(:migration) { described_class.new }
+ let(:namespaces_table) { table(:namespaces) }
+ let(:projects_table) { table(:projects) }
+ let(:project_settings_table) { table(:project_settings) }
+
+ let(:table_name) { 'projects' }
+ let(:batch_column) { :id }
+ let(:sub_batch_size) { 2 }
+ let(:pause_ms) { 0 }
+
+ subject(:perform_migration) { migration.perform(1, 30, table_name, batch_column, sub_batch_size, pause_ms) }
+
+ before do
+ namespaces_table.create!(id: 1, name: 'namespace', path: 'namespace-path', type: 'Group')
+ projects_table.create!(id: 11, name: 'group-project-1', path: 'group-project-path-1', namespace_id: 1)
+ projects_table.create!(id: 12, name: 'group-project-2', path: 'group-project-path-2', namespace_id: 1)
+ project_settings_table.create!(project_id: 11)
+
+ namespaces_table.create!(id: 2, name: 'namespace', path: 'namespace-path', type: 'User')
+ projects_table.create!(id: 21, name: 'user-project-1', path: 'user--project-path-1', namespace_id: 2)
+ projects_table.create!(id: 22, name: 'user-project-2', path: 'user-project-path-2', namespace_id: 2)
+ project_settings_table.create!(project_id: 21)
+ end
+
+ it 'backfills project settings when it does not exist', :aggregate_failures do
+ expect(project_settings_table.count).to eq 2
+
+ queries = ActiveRecord::QueryRecorder.new do
+ perform_migration
+ end
+
+ expect(queries.count).to eq(5)
+
+ expect(project_settings_table.count).to eq 4
+ end
+end
diff --git a/spec/lib/gitlab/background_migration/backfill_topics_title_spec.rb b/spec/lib/gitlab/background_migration/backfill_topics_title_spec.rb
new file mode 100644
index 00000000000..3c46456eed0
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/backfill_topics_title_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::BackgroundMigration::BackfillTopicsTitle, schema: 20220331133802 do
+ it 'correctly backfills the title of the topics' do
+ topics = table(:topics)
+
+ topic_1 = topics.create!(name: 'topic1')
+ topic_2 = topics.create!(name: 'topic2', title: 'Topic 2')
+ topic_3 = topics.create!(name: 'topic3')
+ topic_4 = topics.create!(name: 'topic4')
+
+ subject.perform(topic_1.id, topic_3.id)
+
+ expect(topic_1.reload.title).to eq('topic1')
+ expect(topic_2.reload.title).to eq('Topic 2')
+ expect(topic_3.reload.title).to eq('topic3')
+ expect(topic_4.reload.title).to be_nil
+ end
+end
diff --git a/spec/lib/gitlab/background_migration/batched_migration_job_spec.rb b/spec/lib/gitlab/background_migration/batched_migration_job_spec.rb
new file mode 100644
index 00000000000..f8b3a8681f0
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/batched_migration_job_spec.rb
@@ -0,0 +1,96 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::BackgroundMigration::BatchedMigrationJob do
+ describe '#perform' do
+ let(:connection) { Gitlab::Database.database_base_models[:main].connection }
+
+ let(:job_class) { Class.new(described_class) }
+
+ let(:job_instance) do
+ job_class.new(start_id: 1, end_id: 10,
+ batch_table: '_test_table',
+ batch_column: 'id',
+ sub_batch_size: 2,
+ pause_ms: 1000,
+ connection: connection)
+ end
+
+ subject(:perform_job) { job_instance.perform }
+
+ it 'raises an error if not overridden' do
+ expect { perform_job }.to raise_error(NotImplementedError, /must implement perform/)
+ end
+
+ context 'when the subclass uses sub-batching' do
+ let(:job_class) do
+ Class.new(described_class) do
+ def perform(*job_arguments)
+ each_sub_batch(
+ operation_name: :update,
+ batching_arguments: { order_hint: :updated_at },
+ batching_scope: -> (relation) { relation.where.not(bar: nil) }
+ ) do |sub_batch|
+ sub_batch.update_all('to_column = from_column')
+ end
+ end
+ end
+ end
+
+ let(:test_table) { table(:_test_table) }
+
+ before do
+ allow(job_instance).to receive(:sleep)
+
+ connection.create_table :_test_table do |t|
+ t.timestamps_with_timezone null: false
+ t.integer :from_column, null: false
+ t.text :bar
+ t.integer :to_column
+ end
+
+ test_table.create!(id: 1, from_column: 5, bar: 'value')
+ test_table.create!(id: 2, from_column: 10, bar: 'value')
+ test_table.create!(id: 3, from_column: 15)
+ test_table.create!(id: 4, from_column: 20, bar: 'value')
+ end
+
+ after do
+ connection.drop_table(:_test_table)
+ end
+
+ it 'calls the operation for each sub-batch' do
+ expect { perform_job }.to change { test_table.where(to_column: nil).count }.from(4).to(1)
+
+ expect(test_table.order(:id).pluck(:to_column)).to contain_exactly(5, 10, nil, 20)
+ end
+
+ it 'instruments the batch operation' do
+ expect(job_instance.batch_metrics.affected_rows).to be_empty
+
+ expect(job_instance.batch_metrics).to receive(:instrument_operation).with(:update).twice.and_call_original
+
+ perform_job
+
+ expect(job_instance.batch_metrics.affected_rows[:update]).to contain_exactly(2, 1)
+ end
+
+ it 'pauses after each sub-batch' do
+ expect(job_instance).to receive(:sleep).with(1.0).twice
+
+ perform_job
+ end
+
+ context 'when batching_arguments are given' do
+ it 'forwards them for batching' do
+ expect(job_instance).to receive(:parent_batch_relation).and_return(test_table)
+
+ expect(test_table).to receive(:each_batch).with(column: 'id', of: 2, order_hint: :updated_at)
+
+ perform_job
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/background_migration/copy_column_using_background_migration_job_spec.rb b/spec/lib/gitlab/background_migration/copy_column_using_background_migration_job_spec.rb
index 90d9bbb42c3..78bd1afd8d2 100644
--- a/spec/lib/gitlab/background_migration/copy_column_using_background_migration_job_spec.rb
+++ b/spec/lib/gitlab/background_migration/copy_column_using_background_migration_job_spec.rb
@@ -3,123 +3,134 @@
require 'spec_helper'
RSpec.describe Gitlab::BackgroundMigration::CopyColumnUsingBackgroundMigrationJob do
- let(:table_name) { :_test_copy_primary_key_test }
- let(:test_table) { table(table_name) }
- let(:sub_batch_size) { 1000 }
- let(:pause_ms) { 0 }
- let(:connection) { ApplicationRecord.connection }
-
- let(:helpers) do
- ActiveRecord::Migration.new.extend(Gitlab::Database::MigrationHelpers)
- end
-
- before do
- connection.execute(<<~SQL)
- CREATE TABLE #{table_name}
- (
- id integer NOT NULL,
- name character varying,
- fk integer NOT NULL,
- #{helpers.convert_to_bigint_column(:id)} bigint DEFAULT 0 NOT NULL,
- #{helpers.convert_to_bigint_column(:fk)} bigint DEFAULT 0 NOT NULL,
- name_convert_to_text text DEFAULT 'no name'
- );
- SQL
-
- # Insert some data, it doesn't make a difference
- test_table.create!(id: 11, name: 'test1', fk: 1)
- test_table.create!(id: 12, name: 'test2', fk: 2)
- test_table.create!(id: 15, name: nil, fk: 3)
- test_table.create!(id: 19, name: 'test4', fk: 4)
- end
+ it { expect(described_class).to be < Gitlab::BackgroundMigration::BatchedMigrationJob }
- after do
- # Make sure that the temp table we created is dropped (it is not removed by the database_cleaner)
- connection.execute(<<~SQL)
- DROP TABLE IF EXISTS #{table_name};
- SQL
- end
+ describe '#perform' do
+ let(:table_name) { :_test_copy_primary_key_test }
+ let(:test_table) { table(table_name) }
+ let(:sub_batch_size) { 1000 }
+ let(:pause_ms) { 0 }
+ let(:connection) { ApplicationRecord.connection }
+
+ let(:helpers) do
+ ActiveRecord::Migration.new.extend(Gitlab::Database::MigrationHelpers)
+ end
- subject(:copy_columns) { described_class.new(connection: connection) }
+ let(:copy_job) do
+ described_class.new(start_id: 12,
+ end_id: 20,
+ batch_table: table_name,
+ batch_column: 'id',
+ sub_batch_size: sub_batch_size,
+ pause_ms: pause_ms,
+ connection: connection)
+ end
- it { expect(described_class).to be < Gitlab::BackgroundMigration::BaseJob }
+ before do
+ connection.execute(<<~SQL)
+ CREATE TABLE #{table_name}
+ (
+ id integer NOT NULL,
+ name character varying,
+ fk integer NOT NULL,
+ #{helpers.convert_to_bigint_column(:id)} bigint DEFAULT 0 NOT NULL,
+ #{helpers.convert_to_bigint_column(:fk)} bigint DEFAULT 0 NOT NULL,
+ name_convert_to_text text DEFAULT 'no name'
+ );
+ SQL
+
+ # Insert some data, it doesn't make a difference
+ test_table.create!(id: 11, name: 'test1', fk: 1)
+ test_table.create!(id: 12, name: 'test2', fk: 2)
+ test_table.create!(id: 15, name: nil, fk: 3)
+ test_table.create!(id: 19, name: 'test4', fk: 4)
+ end
- describe '#perform' do
- let(:migration_class) { described_class.name }
+ after do
+ # Make sure that the temp table we created is dropped (it is not removed by the database_cleaner)
+ connection.execute(<<~SQL)
+ DROP TABLE IF EXISTS #{table_name};
+ SQL
+ end
it 'copies all primary keys in range' do
temporary_column = helpers.convert_to_bigint_column(:id)
- copy_columns.perform(12, 15, table_name, 'id', sub_batch_size, pause_ms, 'id', temporary_column)
- expect(test_table.where("id = #{temporary_column}").pluck(:id)).to contain_exactly(12, 15)
- expect(test_table.where(temporary_column => 0).pluck(:id)).to contain_exactly(11, 19)
- expect(test_table.all.count).to eq(4)
+ copy_job.perform('id', temporary_column)
+
+ expect(test_table.count).to eq(4)
+ expect(test_table.where("id = #{temporary_column}").pluck(:id)).to contain_exactly(12, 15, 19)
+ expect(test_table.where(temporary_column => 0).pluck(:id)).to contain_exactly(11)
end
it 'copies all foreign keys in range' do
temporary_column = helpers.convert_to_bigint_column(:fk)
- copy_columns.perform(10, 14, table_name, 'id', sub_batch_size, pause_ms, 'fk', temporary_column)
- expect(test_table.where("fk = #{temporary_column}").pluck(:id)).to contain_exactly(11, 12)
- expect(test_table.where(temporary_column => 0).pluck(:id)).to contain_exactly(15, 19)
- expect(test_table.all.count).to eq(4)
+ copy_job.perform('fk', temporary_column)
+
+ expect(test_table.count).to eq(4)
+ expect(test_table.where("fk = #{temporary_column}").pluck(:id)).to contain_exactly(12, 15, 19)
+ expect(test_table.where(temporary_column => 0).pluck(:id)).to contain_exactly(11)
end
it 'copies columns with NULLs' do
- expect(test_table.where("name_convert_to_text = 'no name'").count).to eq(4)
+ expect { copy_job.perform('name', 'name_convert_to_text') }
+ .to change { test_table.where("name_convert_to_text = 'no name'").count }.from(4).to(1)
- copy_columns.perform(10, 20, table_name, 'id', sub_batch_size, pause_ms, 'name', 'name_convert_to_text')
-
- expect(test_table.where('name = name_convert_to_text').pluck(:id)).to contain_exactly(11, 12, 19)
+ expect(test_table.where('name = name_convert_to_text').pluck(:id)).to contain_exactly(12, 19)
expect(test_table.where('name is NULL and name_convert_to_text is NULL').pluck(:id)).to contain_exactly(15)
- expect(test_table.where("name_convert_to_text = 'no name'").count).to eq(0)
end
- it 'copies multiple columns when given' do
- columns_to_copy_from = %w[id fk]
- id_tmp_column = helpers.convert_to_bigint_column('id')
- fk_tmp_column = helpers.convert_to_bigint_column('fk')
- columns_to_copy_to = [id_tmp_column, fk_tmp_column]
+ context 'when multiple columns are given' do
+ let(:id_tmp_column) { helpers.convert_to_bigint_column('id') }
+ let(:fk_tmp_column) { helpers.convert_to_bigint_column('fk') }
+ let(:columns_to_copy_from) { %w[id fk] }
+ let(:columns_to_copy_to) { [id_tmp_column, fk_tmp_column] }
- subject.perform(10, 15, table_name, 'id', sub_batch_size, pause_ms, columns_to_copy_from, columns_to_copy_to)
+ it 'copies all values in the range' do
+ copy_job.perform(columns_to_copy_from, columns_to_copy_to)
- expect(test_table.where("id = #{id_tmp_column} AND fk = #{fk_tmp_column}").pluck(:id)).to contain_exactly(11, 12, 15)
- expect(test_table.where(id_tmp_column => 0).where(fk_tmp_column => 0).pluck(:id)).to contain_exactly(19)
- expect(test_table.all.count).to eq(4)
- end
+ expect(test_table.count).to eq(4)
+ expect(test_table.where("id = #{id_tmp_column} AND fk = #{fk_tmp_column}").pluck(:id)).to contain_exactly(12, 15, 19)
+ expect(test_table.where(id_tmp_column => 0).where(fk_tmp_column => 0).pluck(:id)).to contain_exactly(11)
+ end
- it 'raises error when number of source and target columns does not match' do
- columns_to_copy_from = %w[id fk]
- columns_to_copy_to = [helpers.convert_to_bigint_column(:id)]
+ context 'when the number of source and target columns does not match' do
+ let(:columns_to_copy_to) { [id_tmp_column] }
- expect do
- subject.perform(10, 15, table_name, 'id', sub_batch_size, pause_ms, columns_to_copy_from, columns_to_copy_to)
- end.to raise_error(ArgumentError, 'number of source and destination columns must match')
+ it 'raises an error' do
+ expect do
+ copy_job.perform(columns_to_copy_from, columns_to_copy_to)
+ end.to raise_error(ArgumentError, 'number of source and destination columns must match')
+ end
+ end
end
it 'tracks timings of queries' do
- expect(copy_columns.batch_metrics.timings).to be_empty
+ expect(copy_job.batch_metrics.timings).to be_empty
- copy_columns.perform(10, 20, table_name, 'id', sub_batch_size, pause_ms, 'name', 'name_convert_to_text')
+ copy_job.perform('name', 'name_convert_to_text')
- expect(copy_columns.batch_metrics.timings[:update_all]).not_to be_empty
+ expect(copy_job.batch_metrics.timings[:update_all]).not_to be_empty
end
context 'pause interval between sub-batches' do
- it 'sleeps for the specified time between sub-batches' do
- sub_batch_size = 2
+ let(:pause_ms) { 5 }
- expect(copy_columns).to receive(:sleep).with(0.005)
+ it 'sleeps for the specified time between sub-batches' do
+ expect(copy_job).to receive(:sleep).with(0.005)
- copy_columns.perform(10, 12, table_name, 'id', sub_batch_size, 5, 'name', 'name_convert_to_text')
+ copy_job.perform('name', 'name_convert_to_text')
end
- it 'treats negative values as 0' do
- sub_batch_size = 2
+ context 'when pause_ms value is negative' do
+ let(:pause_ms) { -5 }
- expect(copy_columns).to receive(:sleep).with(0)
+ it 'treats it as a 0' do
+ expect(copy_job).to receive(:sleep).with(0)
- copy_columns.perform(10, 12, table_name, 'id', sub_batch_size, -5, 'name', 'name_convert_to_text')
+ copy_job.perform('name', 'name_convert_to_text')
+ end
end
end
end
diff --git a/spec/lib/gitlab/background_migration/expire_o_auth_tokens_spec.rb b/spec/lib/gitlab/background_migration/expire_o_auth_tokens_spec.rb
new file mode 100644
index 00000000000..cffcda0a2ca
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/expire_o_auth_tokens_spec.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::BackgroundMigration::ExpireOAuthTokens, :migration, schema: 20220428133724 do
+ let(:migration) { described_class.new }
+ let(:oauth_access_tokens_table) { table(:oauth_access_tokens) }
+
+ let(:table_name) { 'oauth_access_tokens' }
+
+ subject(:perform_migration) do
+ described_class.new(start_id: 1,
+ end_id: 30,
+ batch_table: :oauth_access_tokens,
+ batch_column: :id,
+ sub_batch_size: 2,
+ pause_ms: 0,
+ connection: ActiveRecord::Base.connection)
+ .perform
+ end
+
+ before do
+ oauth_access_tokens_table.create!(id: 1, token: 's3cr3t-1', expires_in: nil)
+ oauth_access_tokens_table.create!(id: 2, token: 's3cr3t-2', expires_in: 42)
+ oauth_access_tokens_table.create!(id: 3, token: 's3cr3t-3', expires_in: nil)
+ end
+
+ it 'adds expiry to oauth tokens', :aggregate_failures do
+ expect(ActiveRecord::QueryRecorder.new { perform_migration }.count).to eq(3)
+
+ expect(oauth_access_tokens_table.find(1).expires_in).to eq(7_200)
+ expect(oauth_access_tokens_table.find(2).expires_in).to eq(42)
+ expect(oauth_access_tokens_table.find(3).expires_in).to eq(7_200)
+ end
+end
diff --git a/spec/lib/gitlab/background_migration/extract_project_topics_into_separate_table_spec.rb b/spec/lib/gitlab/background_migration/extract_project_topics_into_separate_table_spec.rb
index a111007a984..65d55f85a98 100644
--- a/spec/lib/gitlab/background_migration/extract_project_topics_into_separate_table_spec.rb
+++ b/spec/lib/gitlab/background_migration/extract_project_topics_into_separate_table_spec.rb
@@ -29,7 +29,7 @@ RSpec.describe Gitlab::BackgroundMigration::ExtractProjectTopicsIntoSeparateTabl
# Tagging records
expect { tagging_1.reload }.to raise_error(ActiveRecord::RecordNotFound)
expect { tagging_2.reload }.to raise_error(ActiveRecord::RecordNotFound)
- expect { other_tagging.reload }.not_to raise_error(ActiveRecord::RecordNotFound)
+ expect { other_tagging.reload }.not_to raise_error
expect { tagging_3.reload }.to raise_error(ActiveRecord::RecordNotFound)
expect { tagging_4.reload }.to raise_error(ActiveRecord::RecordNotFound)
expect { tagging_5.reload }.to raise_error(ActiveRecord::RecordNotFound)
diff --git a/spec/lib/gitlab/background_migration/job_coordinator_spec.rb b/spec/lib/gitlab/background_migration/job_coordinator_spec.rb
index c1351481505..95847c67d94 100644
--- a/spec/lib/gitlab/background_migration/job_coordinator_spec.rb
+++ b/spec/lib/gitlab/background_migration/job_coordinator_spec.rb
@@ -18,7 +18,7 @@ RSpec.describe Gitlab::BackgroundMigration::JobCoordinator do
it 'raises an error' do
expect do
described_class.for_tracking_database('notvalid')
- end.to raise_error(ArgumentError, /tracking_database must be one of/)
+ end.to raise_error(ArgumentError, /must be one of/)
end
end
end
diff --git a/spec/lib/gitlab/background_migration/merge_topics_with_same_name_spec.rb b/spec/lib/gitlab/background_migration/merge_topics_with_same_name_spec.rb
index 254b4fea698..2c2c048992f 100644
--- a/spec/lib/gitlab/background_migration/merge_topics_with_same_name_spec.rb
+++ b/spec/lib/gitlab/background_migration/merge_topics_with_same_name_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Gitlab::BackgroundMigration::MergeTopicsWithSameName, schema: 20220223124428 do
+RSpec.describe Gitlab::BackgroundMigration::MergeTopicsWithSameName, schema: 20220331133802 do
def set_avatar(topic_id, avatar)
topic = ::Projects::Topic.find(topic_id)
topic.avatar = avatar
@@ -16,49 +16,62 @@ RSpec.describe Gitlab::BackgroundMigration::MergeTopicsWithSameName, schema: 202
topics = table(:topics)
project_topics = table(:project_topics)
- group = namespaces.create!(name: 'group', path: 'group')
- project_1 = projects.create!(namespace_id: group.id, visibility_level: 20)
- project_2 = projects.create!(namespace_id: group.id, visibility_level: 10)
- project_3 = projects.create!(namespace_id: group.id, visibility_level: 0)
+ group_1 = namespaces.create!(name: 'space1', type: 'Group', path: 'space1')
+ group_2 = namespaces.create!(name: 'space2', type: 'Group', path: 'space2')
+ group_3 = namespaces.create!(name: 'space3', type: 'Group', path: 'space3')
+ proj_space_1 = namespaces.create!(name: 'proj1', path: 'proj1', type: 'Project', parent_id: group_1.id)
+ proj_space_2 = namespaces.create!(name: 'proj2', path: 'proj2', type: 'Project', parent_id: group_2.id)
+ proj_space_3 = namespaces.create!(name: 'proj3', path: 'proj3', type: 'Project', parent_id: group_3.id)
+ project_1 = projects.create!(namespace_id: group_1.id, project_namespace_id: proj_space_1.id, visibility_level: 20)
+ project_2 = projects.create!(namespace_id: group_2.id, project_namespace_id: proj_space_2.id, visibility_level: 10)
+ project_3 = projects.create!(namespace_id: group_3.id, project_namespace_id: proj_space_3.id, visibility_level: 0)
topic_1_keep = topics.create!(
name: 'topic1',
+ title: 'Topic 1',
description: 'description 1 to keep',
total_projects_count: 2,
non_private_projects_count: 2
)
topic_1_remove = topics.create!(
name: 'TOPIC1',
+ title: 'Topic 1',
description: 'description 1 to remove',
total_projects_count: 2,
non_private_projects_count: 1
)
topic_2_remove = topics.create!(
name: 'topic2',
+ title: 'Topic 2',
total_projects_count: 0
)
topic_2_keep = topics.create!(
name: 'TOPIC2',
+ title: 'Topic 2',
description: 'description 2 to keep',
total_projects_count: 1
)
topic_3_remove_1 = topics.create!(
name: 'topic3',
+ title: 'Topic 3',
total_projects_count: 2,
non_private_projects_count: 1
)
topic_3_keep = topics.create!(
name: 'Topic3',
+ title: 'Topic 3',
total_projects_count: 2,
non_private_projects_count: 2
)
topic_3_remove_2 = topics.create!(
name: 'TOPIC3',
+ title: 'Topic 3',
description: 'description 3 to keep',
total_projects_count: 2,
non_private_projects_count: 1
)
topic_4_keep = topics.create!(
- name: 'topic4'
+ name: 'topic4',
+ title: 'Topic 4'
)
project_topics_1 = []
diff --git a/spec/lib/gitlab/background_migration/nullify_orphan_runner_id_on_ci_builds_spec.rb b/spec/lib/gitlab/background_migration/nullify_orphan_runner_id_on_ci_builds_spec.rb
index 90dd3e14606..e38edfc3643 100644
--- a/spec/lib/gitlab/background_migration/nullify_orphan_runner_id_on_ci_builds_spec.rb
+++ b/spec/lib/gitlab/background_migration/nullify_orphan_runner_id_on_ci_builds_spec.rb
@@ -5,9 +5,9 @@ require 'spec_helper'
RSpec.describe Gitlab::BackgroundMigration::NullifyOrphanRunnerIdOnCiBuilds, :migration, schema: 20220223112304 do
let(:namespaces) { table(:namespaces) }
let(:projects) { table(:projects) }
- let(:ci_runners) { table(:ci_runners) }
- let(:ci_pipelines) { table(:ci_pipelines) }
- let(:ci_builds) { table(:ci_builds) }
+ let(:ci_runners) { table(:ci_runners, database: :ci) }
+ let(:ci_pipelines) { table(:ci_pipelines, database: :ci) }
+ let(:ci_builds) { table(:ci_builds, database: :ci) }
subject { described_class.new }
@@ -26,9 +26,9 @@ RSpec.describe Gitlab::BackgroundMigration::NullifyOrphanRunnerIdOnCiBuilds, :mi
describe '#perform' do
let(:namespace) { namespaces.create!(name: 'test', path: 'test', type: 'Group') }
let(:project) { projects.create!(namespace_id: namespace.id, name: 'test') }
- let(:pipeline) { ci_pipelines.create!(project_id: project.id, ref: 'master', sha: 'adf43c3a', status: 'success') }
it 'nullifies runner_id for orphan ci_builds in range' do
+ pipeline = ci_pipelines.create!(project_id: project.id, ref: 'master', sha: 'adf43c3a', status: 'success')
ci_runners.create!(id: 2, runner_type: 'project_type')
ci_builds.create!(id: 5, type: 'Ci::Build', commit_id: pipeline.id, runner_id: 2)
diff --git a/spec/lib/gitlab/background_migration/reset_duplicate_ci_runners_token_encrypted_values_on_projects_spec.rb b/spec/lib/gitlab/background_migration/reset_duplicate_ci_runners_token_encrypted_values_on_projects_spec.rb
index d02f7245c15..71020746fa7 100644
--- a/spec/lib/gitlab/background_migration/reset_duplicate_ci_runners_token_encrypted_values_on_projects_spec.rb
+++ b/spec/lib/gitlab/background_migration/reset_duplicate_ci_runners_token_encrypted_values_on_projects_spec.rb
@@ -6,32 +6,47 @@ RSpec.describe Gitlab::BackgroundMigration::ResetDuplicateCiRunnersTokenEncrypte
let(:namespaces) { table(:namespaces) }
let(:projects) { table(:projects) }
- let(:perform) { described_class.new.perform(1, 4) }
+ subject(:background_migration) { described_class.new }
before do
namespaces.create!(id: 123, name: 'sample', path: 'sample')
projects.create!(id: 1, namespace_id: 123, runners_token_encrypted: 'duplicate')
projects.create!(id: 2, namespace_id: 123, runners_token_encrypted: 'a-runners-token')
- projects.create!(id: 3, namespace_id: 123, runners_token_encrypted: 'duplicate')
+ projects.create!(id: 3, namespace_id: 123, runners_token_encrypted: 'duplicate-2')
projects.create!(id: 4, namespace_id: 123, runners_token_encrypted: nil)
projects.create!(id: 5, namespace_id: 123, runners_token_encrypted: 'duplicate-2')
- projects.create!(id: 6, namespace_id: 123, runners_token_encrypted: 'duplicate-2')
+ projects.create!(id: 6, namespace_id: 123, runners_token_encrypted: 'duplicate')
+ projects.create!(id: 7, namespace_id: 123, runners_token_encrypted: 'another-runners-token')
+ projects.create!(id: 8, namespace_id: 123, runners_token_encrypted: 'another-runners-token')
end
describe '#up' do
- before do
- stub_const("#{described_class}::SUB_BATCH_SIZE", 2)
- end
-
it 'nullifies duplicate tokens', :aggregate_failures do
- perform
+ background_migration.perform(1, 2)
+ background_migration.perform(3, 4)
- expect(projects.count).to eq(6)
+ expect(projects.count).to eq(8)
expect(projects.all.pluck(:id, :runners_token_encrypted).to_h).to eq(
- { 1 => nil, 2 => 'a-runners-token', 3 => nil, 4 => nil, 5 => 'duplicate-2', 6 => 'duplicate-2' }
- )
- expect(projects.pluck(:runners_token_encrypted).uniq).to match_array [nil, 'a-runners-token', 'duplicate-2']
+ {
+ 1 => nil,
+ 2 => 'a-runners-token',
+ 3 => nil,
+ 4 => nil,
+ 5 => 'duplicate-2',
+ 6 => 'duplicate',
+ 7 => 'another-runners-token',
+ 8 => 'another-runners-token'
+ })
+ expect(projects.pluck(:runners_token_encrypted).uniq).to match_array [
+ nil, 'a-runners-token', 'duplicate', 'duplicate-2', 'another-runners-token'
+ ]
+ end
+
+ it 'does not touch projects outside id range' do
+ expect do
+ background_migration.perform(1, 2)
+ end.not_to change { projects.where(id: [3..8]).each(&:reload).map(&:updated_at) }
end
end
end
diff --git a/spec/lib/gitlab/background_migration/reset_duplicate_ci_runners_token_values_on_projects_spec.rb b/spec/lib/gitlab/background_migration/reset_duplicate_ci_runners_token_values_on_projects_spec.rb
index fd61047d851..7d3df69bee2 100644
--- a/spec/lib/gitlab/background_migration/reset_duplicate_ci_runners_token_values_on_projects_spec.rb
+++ b/spec/lib/gitlab/background_migration/reset_duplicate_ci_runners_token_values_on_projects_spec.rb
@@ -6,32 +6,47 @@ RSpec.describe Gitlab::BackgroundMigration::ResetDuplicateCiRunnersTokenValuesOn
let(:namespaces) { table(:namespaces) }
let(:projects) { table(:projects) }
- let(:perform) { described_class.new.perform(1, 4) }
+ subject(:background_migration) { described_class.new }
before do
namespaces.create!(id: 123, name: 'sample', path: 'sample')
projects.create!(id: 1, namespace_id: 123, runners_token: 'duplicate')
projects.create!(id: 2, namespace_id: 123, runners_token: 'a-runners-token')
- projects.create!(id: 3, namespace_id: 123, runners_token: 'duplicate')
+ projects.create!(id: 3, namespace_id: 123, runners_token: 'duplicate-2')
projects.create!(id: 4, namespace_id: 123, runners_token: nil)
projects.create!(id: 5, namespace_id: 123, runners_token: 'duplicate-2')
- projects.create!(id: 6, namespace_id: 123, runners_token: 'duplicate-2')
+ projects.create!(id: 6, namespace_id: 123, runners_token: 'duplicate')
+ projects.create!(id: 7, namespace_id: 123, runners_token: 'another-runners-token')
+ projects.create!(id: 8, namespace_id: 123, runners_token: 'another-runners-token')
end
describe '#up' do
- before do
- stub_const("#{described_class}::SUB_BATCH_SIZE", 2)
- end
-
it 'nullifies duplicate tokens', :aggregate_failures do
- perform
+ background_migration.perform(1, 2)
+ background_migration.perform(3, 4)
- expect(projects.count).to eq(6)
+ expect(projects.count).to eq(8)
expect(projects.all.pluck(:id, :runners_token).to_h).to eq(
- { 1 => nil, 2 => 'a-runners-token', 3 => nil, 4 => nil, 5 => 'duplicate-2', 6 => 'duplicate-2' }
- )
- expect(projects.pluck(:runners_token).uniq).to match_array [nil, 'a-runners-token', 'duplicate-2']
+ {
+ 1 => nil,
+ 2 => 'a-runners-token',
+ 3 => nil,
+ 4 => nil,
+ 5 => 'duplicate-2',
+ 6 => 'duplicate',
+ 7 => 'another-runners-token',
+ 8 => 'another-runners-token'
+ })
+ expect(projects.pluck(:runners_token).uniq).to match_array [
+ nil, 'a-runners-token', 'duplicate', 'duplicate-2', 'another-runners-token'
+ ]
+ end
+
+ it 'does not touch projects outside id range' do
+ expect do
+ background_migration.perform(1, 2)
+ end.not_to change { projects.where(id: [3..8]).each(&:reload).map(&:updated_at) }
end
end
end
diff --git a/spec/lib/gitlab/background_migration/reset_too_many_tags_skipped_registry_imports_spec.rb b/spec/lib/gitlab/background_migration/reset_too_many_tags_skipped_registry_imports_spec.rb
new file mode 100644
index 00000000000..3f59b0a24a3
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/reset_too_many_tags_skipped_registry_imports_spec.rb
@@ -0,0 +1,85 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::BackgroundMigration::ResetTooManyTagsSkippedRegistryImports, :migration,
+ :aggregate_failures,
+ schema: 20220502173045 do
+ let(:namespaces) { table(:namespaces) }
+ let(:projects) { table(:projects) }
+ let(:container_repositories) { table(:container_repositories) }
+
+ subject(:background_migration) { described_class.new }
+
+ let!(:namespace) { namespaces.create!(id: 1, path: 'foo', name: 'foo') }
+ let!(:project) { projects.create!(id: 1, project_namespace_id: 1, namespace_id: 1, path: 'bar', name: 'bar') }
+
+ let!(:container_repository1) do
+ container_repositories.create!(id: 1,
+ project_id: 1,
+ name: 'a',
+ migration_state: 'import_skipped',
+ migration_skipped_at: Time.zone.now,
+ migration_skipped_reason: 2,
+ migration_pre_import_started_at: Time.zone.now,
+ migration_pre_import_done_at: Time.zone.now,
+ migration_import_started_at: Time.zone.now,
+ migration_import_done_at: Time.zone.now,
+ migration_aborted_at: Time.zone.now,
+ migration_retries_count: 2,
+ migration_aborted_in_state: 'importing')
+ end
+
+ let!(:container_repository2) do
+ container_repositories.create!(id: 2,
+ project_id: 1,
+ name: 'b',
+ migration_state: 'import_skipped',
+ migration_skipped_at: Time.zone.now,
+ migration_skipped_reason: 2)
+ end
+
+ let!(:container_repository3) do
+ container_repositories.create!(id: 3,
+ project_id: 1,
+ name: 'c',
+ migration_state: 'import_skipped',
+ migration_skipped_at: Time.zone.now,
+ migration_skipped_reason: 1)
+ end
+
+ # This is an unlikely state, but included here to test the edge case
+ let!(:container_repository4) do
+ container_repositories.create!(id: 4,
+ project_id: 1,
+ name: 'd',
+ migration_state: 'default',
+ migration_skipped_reason: 2)
+ end
+
+ describe '#up' do
+ it 'resets only qualified container repositories', :aggregate_failures do
+ background_migration.perform(1, 4)
+
+ expect(container_repository1.reload.migration_state).to eq('default')
+ expect(container_repository1.migration_skipped_reason).to eq(nil)
+ expect(container_repository1.migration_pre_import_started_at).to eq(nil)
+ expect(container_repository1.migration_pre_import_done_at).to eq(nil)
+ expect(container_repository1.migration_import_started_at).to eq(nil)
+ expect(container_repository1.migration_import_done_at).to eq(nil)
+ expect(container_repository1.migration_aborted_at).to eq(nil)
+ expect(container_repository1.migration_skipped_at).to eq(nil)
+ expect(container_repository1.migration_retries_count).to eq(0)
+ expect(container_repository1.migration_aborted_in_state).to eq(nil)
+
+ expect(container_repository2.reload.migration_state).to eq('default')
+ expect(container_repository2.migration_skipped_reason).to eq(nil)
+
+ expect(container_repository3.reload.migration_state).to eq('import_skipped')
+ expect(container_repository3.migration_skipped_reason).to eq(1)
+
+ expect(container_repository4.reload.migration_state).to eq('default')
+ expect(container_repository4.migration_skipped_reason).to eq(2)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/backtrace_cleaner_spec.rb b/spec/lib/gitlab/backtrace_cleaner_spec.rb
index e46a90e8606..cdde5a02d3b 100644
--- a/spec/lib/gitlab/backtrace_cleaner_spec.rb
+++ b/spec/lib/gitlab/backtrace_cleaner_spec.rb
@@ -25,7 +25,6 @@ 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/checks/branch_check_spec.rb b/spec/lib/gitlab/checks/branch_check_spec.rb
index c06d26d1441..d6280d3c28c 100644
--- a/spec/lib/gitlab/checks/branch_check_spec.rb
+++ b/spec/lib/gitlab/checks/branch_check_spec.rb
@@ -103,7 +103,7 @@ RSpec.describe Gitlab::Checks::BranchCheck do
it 'prevents force push' do
expect(Gitlab::Checks::ForcePush).to receive(:force_push?).and_return(true)
- expect { subject.validate! }.to raise_error
+ expect { subject.validate! }.to raise_error(Gitlab::GitAccess::ForbiddenError)
end
end
end
@@ -126,7 +126,7 @@ RSpec.describe Gitlab::Checks::BranchCheck do
it 'prevents force push' do
expect(Gitlab::Checks::ForcePush).to receive(:force_push?).and_return(true)
- expect { subject.validate! }.to raise_error
+ expect { subject.validate! }.to raise_error(Gitlab::GitAccess::ForbiddenError)
end
end
@@ -141,7 +141,7 @@ RSpec.describe Gitlab::Checks::BranchCheck do
it 'prevents force push' do
expect(Gitlab::Checks::ForcePush).to receive(:force_push?).and_return(true)
- expect { subject.validate! }.to raise_error
+ expect { subject.validate! }.to raise_error(Gitlab::GitAccess::ForbiddenError)
end
end
end
diff --git a/spec/lib/gitlab/checks/changes_access_spec.rb b/spec/lib/gitlab/checks/changes_access_spec.rb
index 1cb4edd7337..41ec11c1055 100644
--- a/spec/lib/gitlab/checks/changes_access_spec.rb
+++ b/spec/lib/gitlab/checks/changes_access_spec.rb
@@ -49,10 +49,17 @@ RSpec.describe Gitlab::Checks::ChangesAccess do
context 'when changes contain empty revisions' do
let(:expected_commit) { instance_double(Commit) }
+ let(:expected_allow_quarantine) { allow_quarantine }
shared_examples 'returns only commits with non empty revisions' do
+ before do
+ stub_feature_flags(filter_quarantined_commits: filter_quarantined_commits)
+ end
+
specify do
- expect(project.repository).to receive(:new_commits).with([newrev], { allow_quarantine: allow_quarantine }) { [expected_commit] }
+ expect(project.repository)
+ .to receive(:new_commits)
+ .with([newrev], allow_quarantine: expected_allow_quarantine) { [expected_commit] }
expect(subject.commits).to match_array([expected_commit])
end
end
@@ -60,13 +67,37 @@ RSpec.describe Gitlab::Checks::ChangesAccess do
it_behaves_like 'returns only commits with non empty revisions' do
let(:changes) { [{ oldrev: oldrev, newrev: newrev }, { newrev: '' }, { newrev: Gitlab::Git::BLANK_SHA }] }
let(:allow_quarantine) { true }
+ let(:filter_quarantined_commits) { true }
end
context 'without oldrev' do
- it_behaves_like 'returns only commits with non empty revisions' do
- let(:changes) { [{ newrev: newrev }, { newrev: '' }, { newrev: Gitlab::Git::BLANK_SHA }] }
- # The quarantine directory should not be used because we're lacking oldrev.
+ let(:changes) { [{ newrev: newrev }, { newrev: '' }, { newrev: Gitlab::Git::BLANK_SHA }] }
+
+ context 'with disallowed quarantine' do
+ # The quarantine directory should not be used because we're lacking
+ # oldrev, and we're not filtering commits.
let(:allow_quarantine) { false }
+ let(:filter_quarantined_commits) { false }
+
+ it_behaves_like 'returns only commits with non empty revisions'
+ end
+
+ context 'with allowed quarantine and :filter_quarantined_commits disabled' do
+ # When we allow usage of the quarantine but have no oldrev and we're
+ # not filtering commits then results returned by the quarantine aren't
+ # accurate. We thus mustn't try using it.
+ let(:allow_quarantine) { true }
+ let(:filter_quarantined_commits) { false }
+ let(:expected_allow_quarantine) { false }
+
+ it_behaves_like 'returns only commits with non empty revisions'
+ end
+
+ context 'with allowed quarantine and :filter_quarantined_commits enabled' do
+ let(:allow_quarantine) { true }
+ let(:filter_quarantined_commits) { true }
+
+ it_behaves_like 'returns only commits with non empty revisions'
end
end
end
diff --git a/spec/lib/gitlab/checks/single_change_access_spec.rb b/spec/lib/gitlab/checks/single_change_access_spec.rb
index e81e4951539..1b34e58797e 100644
--- a/spec/lib/gitlab/checks/single_change_access_spec.rb
+++ b/spec/lib/gitlab/checks/single_change_access_spec.rb
@@ -96,13 +96,26 @@ RSpec.describe Gitlab::Checks::SingleChangeAccess do
let(:provided_commits) { nil }
before do
+ stub_feature_flags(filter_quarantined_commits: filter_quarantined_commits)
+
expect(project.repository)
.to receive(:new_commits)
+ .with(newrev, allow_quarantine: filter_quarantined_commits)
.once
.and_return(expected_commits)
end
- it_behaves_like '#commits'
+ context 'with :filter_quarantined_commits disabled' do
+ let(:filter_quarantined_commits) { false }
+
+ it_behaves_like '#commits'
+ end
+
+ context 'with :filter_quarantined_commits enabled' do
+ let(:filter_quarantined_commits) { true }
+
+ it_behaves_like '#commits'
+ end
end
end
end
diff --git a/spec/lib/gitlab/ci/ansi2json_spec.rb b/spec/lib/gitlab/ci/ansi2json_spec.rb
index c9c0d1a744e..f9d23ff97bc 100644
--- a/spec/lib/gitlab/ci/ansi2json_spec.rb
+++ b/spec/lib/gitlab/ci/ansi2json_spec.rb
@@ -27,6 +27,17 @@ RSpec.describe Gitlab::Ci::Ansi2json do
])
end
+ it 'ignores empty newlines' do
+ expect(convert_json("Hello\n\nworld")).to eq([
+ { offset: 0, content: [{ text: 'Hello' }] },
+ { offset: 7, content: [{ text: 'world' }] }
+ ])
+ expect(convert_json("Hello\r\n\r\nworld")).to eq([
+ { offset: 0, content: [{ text: 'Hello' }] },
+ { offset: 9, content: [{ text: 'world' }] }
+ ])
+ end
+
it 'replace the current line when encountering \r' do
expect(convert_json("Hello\rworld")).to eq([
{ offset: 0, content: [{ text: 'world' }] }
diff --git a/spec/lib/gitlab/ci/build/rules/rule/clause/if_spec.rb b/spec/lib/gitlab/ci/build/rules/rule/clause/if_spec.rb
new file mode 100644
index 00000000000..81bce989833
--- /dev/null
+++ b/spec/lib/gitlab/ci/build/rules/rule/clause/if_spec.rb
@@ -0,0 +1,74 @@
+# frozen_string_literal: true
+
+require 'fast_spec_helper'
+require 'support/helpers/stubbed_feature'
+require 'support/helpers/stub_feature_flags'
+
+RSpec.describe Gitlab::Ci::Build::Rules::Rule::Clause::If do
+ include StubFeatureFlags
+
+ subject(:if_clause) { described_class.new(expression) }
+
+ describe '#satisfied_by?' do
+ let(:context_class) { Gitlab::Ci::Build::Context::Base }
+ let(:rules_context) { instance_double(context_class, variables_hash: {}) }
+
+ subject(:satisfied_by?) { if_clause.satisfied_by?(nil, rules_context) }
+
+ context 'when expression is a basic string comparison' do
+ context 'when comparison is true' do
+ let(:expression) { '"value" == "value"' }
+
+ it { is_expected.to eq(true) }
+ end
+
+ context 'when comparison is false' do
+ let(:expression) { '"value" == "other"' }
+
+ it { is_expected.to eq(false) }
+ end
+ end
+
+ context 'when expression is a regexp' do
+ context 'when comparison is true' do
+ let(:expression) { '"abcde" =~ /^ab.*/' }
+
+ it { is_expected.to eq(true) }
+ end
+
+ context 'when comparison is false' do
+ let(:expression) { '"abcde" =~ /^af.*/' }
+
+ it { is_expected.to eq(false) }
+ end
+
+ context 'when both side of the expression are variables' do
+ let(:expression) { '$teststring =~ $pattern' }
+
+ context 'when comparison is true' do
+ let(:rules_context) do
+ instance_double(context_class, variables_hash: { 'teststring' => 'abcde', 'pattern' => '/^ab.*/' })
+ end
+
+ it { is_expected.to eq(true) }
+
+ context 'when the FF ci_fix_rules_if_comparison_with_regexp_variable is disabled' do
+ before do
+ stub_feature_flags(ci_fix_rules_if_comparison_with_regexp_variable: false)
+ end
+
+ it { is_expected.to eq(false) }
+ end
+ end
+
+ context 'when comparison is false' do
+ let(:rules_context) do
+ instance_double(context_class, variables_hash: { 'teststring' => 'abcde', 'pattern' => '/^af.*/' })
+ end
+
+ it { is_expected.to eq(false) }
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/build/rules_spec.rb b/spec/lib/gitlab/ci/build/rules_spec.rb
index 37bfdca4d1d..e82dcd0254d 100644
--- a/spec/lib/gitlab/ci/build/rules_spec.rb
+++ b/spec/lib/gitlab/ci/build/rules_spec.rb
@@ -188,6 +188,19 @@ RSpec.describe Gitlab::Ci::Build::Rules do
it { is_expected.to eq(described_class::Result.new('on_success', nil, nil, { MY_VAR: 'my var' })) }
end
end
+
+ context 'with a regexp variable matching rule' do
+ let(:rule_list) { [{ if: '"abcde" =~ $pattern' }] }
+
+ before do
+ allow(ci_build).to receive(:scoped_variables).and_return(
+ Gitlab::Ci::Variables::Collection.new
+ .append(key: 'pattern', value: '/^ab.*/', public: true)
+ )
+ end
+
+ it { is_expected.to eq(described_class::Result.new('on_success')) }
+ end
end
describe 'Gitlab::Ci::Build::Rules::Result' do
diff --git a/spec/lib/gitlab/ci/config/entry/environment_spec.rb b/spec/lib/gitlab/ci/config/entry/environment_spec.rb
index dd8a79f0d84..36c26c8ee4f 100644
--- a/spec/lib/gitlab/ci/config/entry/environment_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/environment_spec.rb
@@ -92,24 +92,18 @@ RSpec.describe Gitlab::Ci::Config::Entry::Environment do
end
context 'when valid action is used' do
- let(:config) do
- { name: 'production',
- action: 'start' }
- end
-
- it 'is valid' do
- expect(entry).to be_valid
+ where(:action) do
+ %w(start stop prepare verify access)
end
- end
- context 'when prepare action is used' do
- let(:config) do
- { name: 'production',
- action: 'prepare' }
- end
+ with_them do
+ let(:config) do
+ { name: 'production', action: action }
+ end
- it 'is valid' do
- expect(entry).to be_valid
+ it 'is valid' do
+ expect(entry).to be_valid
+ end
end
end
@@ -148,7 +142,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Environment do
describe '#errors' do
it 'contains error about invalid action' do
expect(entry.errors)
- .to include 'environment action should be start, stop or prepare'
+ .to include 'environment action should be start, stop, prepare, verify, or access'
end
end
end
diff --git a/spec/lib/gitlab/ci/config/entry/job_spec.rb b/spec/lib/gitlab/ci/config/entry/job_spec.rb
index 97691504abd..ca336c3ecaa 100644
--- a/spec/lib/gitlab/ci/config/entry/job_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/job_spec.rb
@@ -27,7 +27,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Job do
subject { described_class.nodes.keys }
let(:result) do
- %i[before_script script stage type after_script cache
+ %i[before_script script stage after_script cache
image services only except rules needs variables artifacts
environment coverage retry interruptible timeout release tags
inherit parallel]
diff --git a/spec/lib/gitlab/ci/config/entry/reports_spec.rb b/spec/lib/gitlab/ci/config/entry/reports_spec.rb
index 061d8f34c8d..051cccb4833 100644
--- a/spec/lib/gitlab/ci/config/entry/reports_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/reports_spec.rb
@@ -45,10 +45,8 @@ RSpec.describe Gitlab::Ci::Config::Entry::Reports do
:load_performance | 'load-performance.json'
:lsif | 'lsif.json'
:dotenv | 'build.dotenv'
- :cobertura | 'cobertura-coverage.xml'
:terraform | 'tfplan.json'
:accessibility | 'gl-accessibility.json'
- :cluster_applications | 'gl-cluster-applications.json'
end
with_them do
@@ -90,18 +88,6 @@ RSpec.describe Gitlab::Ci::Config::Entry::Reports do
expect(entry.value).to eq({ coverage_report: coverage_report, dast: ['gl-dast-report.json'] })
end
end
-
- context 'and a direct coverage report format is specified' do
- let(:config) { { coverage_report: coverage_report, cobertura: 'cobertura-coverage.xml' } }
-
- it 'is not valid' do
- expect(entry).not_to be_valid
- end
-
- it 'reports error' do
- expect(entry.errors).to include /please use only one the following keys: coverage_report, cobertura/
- end
- end
end
end
diff --git a/spec/lib/gitlab/ci/config/entry/root_spec.rb b/spec/lib/gitlab/ci/config/entry/root_spec.rb
index b9c32bc51be..55ad119ea21 100644
--- a/spec/lib/gitlab/ci/config/entry/root_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/root_spec.rb
@@ -21,7 +21,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do
# The purpose of `Root` is have only globally defined configuration.
expect(described_class.nodes.keys)
.to match_array(%i[before_script image services after_script
- variables cache stages types include default workflow])
+ variables cache stages include default workflow])
end
end
end
@@ -55,41 +55,6 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do
}
end
- context 'when deprecated types/type keywords are defined' do
- let(:project) { create(:project, :repository) }
- let(:user) { create(:user) }
-
- let(:hash) do
- { types: %w(test deploy),
- rspec: { script: 'rspec', type: 'test' } }
- end
-
- before do
- root.compose!
- end
-
- it 'returns array of types as stages with a warning' do
- expect(root.jobs_value[:rspec][:stage]).to eq 'test'
- expect(root.stages_value).to eq %w[test deploy]
- expect(root.warnings).to match_array([
- "root `types` is deprecated in 9.0 and will be removed in 15.0.",
- "jobs:rspec `type` is deprecated in 9.0 and will be removed in 15.0."
- ])
- end
-
- it 'logs usage of keywords' do
- expect(Gitlab::AppJsonLogger).to(
- receive(:info)
- .with(event: 'ci_used_deprecated_keyword',
- entry: root[:stages].key.to_s,
- user_id: user.id,
- project_id: project.id)
- )
-
- root.compose!
- end
- end
-
describe '#compose!' do
before do
root.compose!
diff --git a/spec/lib/gitlab/ci/config/external/file/local_spec.rb b/spec/lib/gitlab/ci/config/external/file/local_spec.rb
index c0a0b0009ce..0e78498c98e 100644
--- a/spec/lib/gitlab/ci/config/external/file/local_spec.rb
+++ b/spec/lib/gitlab/ci/config/external/file/local_spec.rb
@@ -199,6 +199,8 @@ RSpec.describe Gitlab::Ci::Config::External::File::Local do
context_sha: '12345',
type: :local,
location: location,
+ blob: "http://localhost/#{project.full_path}/-/blob/12345/lib/gitlab/ci/templates/existent-file.yml",
+ raw: "http://localhost/#{project.full_path}/-/raw/12345/lib/gitlab/ci/templates/existent-file.yml",
extra: {}
)
}
diff --git a/spec/lib/gitlab/ci/config/external/file/project_spec.rb b/spec/lib/gitlab/ci/config/external/file/project_spec.rb
index 5d3412a148b..77e542cf933 100644
--- a/spec/lib/gitlab/ci/config/external/file/project_spec.rb
+++ b/spec/lib/gitlab/ci/config/external/file/project_spec.rb
@@ -207,6 +207,8 @@ RSpec.describe Gitlab::Ci::Config::External::File::Project do
context_sha: '12345',
type: :file,
location: '/file.yml',
+ blob: "http://localhost/#{project.full_path}/-/blob/#{project.commit('master').id}/file.yml",
+ raw: "http://localhost/#{project.full_path}/-/raw/#{project.commit('master').id}/file.yml",
extra: { project: project.full_path, ref: 'HEAD' }
)
}
@@ -227,6 +229,8 @@ RSpec.describe Gitlab::Ci::Config::External::File::Project do
context_sha: '12345',
type: :file,
location: '/file.yml',
+ blob: nil,
+ raw: nil,
extra: { project: 'xxxxxxxxxxxxxxxxxxxxxxxx', ref: 'xxxxxxxxxxxxxxxxxxxxxxxx' }
)
}
diff --git a/spec/lib/gitlab/ci/config/external/file/remote_spec.rb b/spec/lib/gitlab/ci/config/external/file/remote_spec.rb
index 5c07c87fd5a..3e1c4df4e32 100644
--- a/spec/lib/gitlab/ci/config/external/file/remote_spec.rb
+++ b/spec/lib/gitlab/ci/config/external/file/remote_spec.rb
@@ -213,6 +213,8 @@ RSpec.describe Gitlab::Ci::Config::External::File::Remote do
context_sha: '12345',
type: :remote,
location: 'https://gitlab.com/gitlab-org/gitlab-foss/blob/1234/.xxxxxxxxxxx.yml',
+ raw: 'https://gitlab.com/gitlab-org/gitlab-foss/blob/1234/.xxxxxxxxxxx.yml',
+ blob: nil,
extra: {}
)
}
diff --git a/spec/lib/gitlab/ci/config/external/file/template_spec.rb b/spec/lib/gitlab/ci/config/external/file/template_spec.rb
index 4da9a933a9f..074e7a1d32d 100644
--- a/spec/lib/gitlab/ci/config/external/file/template_spec.rb
+++ b/spec/lib/gitlab/ci/config/external/file/template_spec.rb
@@ -124,6 +124,8 @@ RSpec.describe Gitlab::Ci::Config::External::File::Template do
context_sha: '12345',
type: :template,
location: template,
+ raw: "https://gitlab.com/gitlab-org/gitlab/-/raw/master/lib/gitlab/ci/templates/#{template}",
+ blob: nil,
extra: {}
)
}
diff --git a/spec/lib/gitlab/ci/config/external/processor_spec.rb b/spec/lib/gitlab/ci/config/external/processor_spec.rb
index 56cd006717e..15a0ff40aa4 100644
--- a/spec/lib/gitlab/ci/config/external/processor_spec.rb
+++ b/spec/lib/gitlab/ci/config/external/processor_spec.rb
@@ -267,11 +267,41 @@ RSpec.describe Gitlab::Ci::Config::External::Processor do
perform
expect(context.includes).to contain_exactly(
- { type: :local, location: '/local/file.yml', extra: {}, context_project: project.full_path, context_sha: '12345' },
- { type: :template, location: 'Ruby.gitlab-ci.yml', extra: {}, context_project: project.full_path, context_sha: '12345' },
- { type: :remote, location: 'http://my.domain.com/config.yml', extra: {}, context_project: project.full_path, context_sha: '12345' },
- { type: :file, location: '/templates/my-workflow.yml', extra: { project: another_project.full_path, ref: 'HEAD' }, context_project: project.full_path, context_sha: '12345' },
- { type: :local, location: '/templates/my-build.yml', extra: {}, context_project: another_project.full_path, context_sha: another_project.commit.sha }
+ { type: :local,
+ location: '/local/file.yml',
+ blob: "http://localhost/#{project.full_path}/-/blob/12345/local/file.yml",
+ raw: "http://localhost/#{project.full_path}/-/raw/12345/local/file.yml",
+ extra: {},
+ context_project: project.full_path,
+ context_sha: '12345' },
+ { type: :template,
+ location: 'Ruby.gitlab-ci.yml',
+ blob: nil,
+ raw: 'https://gitlab.com/gitlab-org/gitlab/-/raw/master/lib/gitlab/ci/templates/Ruby.gitlab-ci.yml',
+ extra: {},
+ context_project: project.full_path,
+ context_sha: '12345' },
+ { type: :remote,
+ location: 'http://my.domain.com/config.yml',
+ blob: nil,
+ raw: "http://my.domain.com/config.yml",
+ extra: {},
+ context_project: project.full_path,
+ context_sha: '12345' },
+ { type: :file,
+ location: '/templates/my-workflow.yml',
+ blob: "http://localhost/#{another_project.full_path}/-/blob/#{another_project.commit.sha}/templates/my-workflow.yml",
+ raw: "http://localhost/#{another_project.full_path}/-/raw/#{another_project.commit.sha}/templates/my-workflow.yml",
+ extra: { project: another_project.full_path, ref: 'HEAD' },
+ context_project: project.full_path,
+ context_sha: '12345' },
+ { type: :local,
+ location: '/templates/my-build.yml',
+ blob: "http://localhost/#{another_project.full_path}/-/blob/#{another_project.commit.sha}/templates/my-build.yml",
+ raw: "http://localhost/#{another_project.full_path}/-/raw/#{another_project.commit.sha}/templates/my-build.yml",
+ extra: {},
+ context_project: another_project.full_path,
+ context_sha: another_project.commit.sha }
)
end
end
@@ -394,8 +424,20 @@ RSpec.describe Gitlab::Ci::Config::External::Processor do
perform
expect(context.includes).to contain_exactly(
- { type: :file, location: '/templates/my-build.yml', extra: { project: another_project.full_path, ref: 'HEAD' }, context_project: project.full_path, context_sha: '12345' },
- { type: :file, location: '/templates/my-test.yml', extra: { project: another_project.full_path, ref: 'HEAD' }, context_project: project.full_path, context_sha: '12345' }
+ { type: :file,
+ location: '/templates/my-build.yml',
+ blob: "http://localhost/#{another_project.full_path}/-/blob/#{another_project.commit.sha}/templates/my-build.yml",
+ raw: "http://localhost/#{another_project.full_path}/-/raw/#{another_project.commit.sha}/templates/my-build.yml",
+ extra: { project: another_project.full_path, ref: 'HEAD' },
+ context_project: project.full_path,
+ context_sha: '12345' },
+ { type: :file,
+ blob: "http://localhost/#{another_project.full_path}/-/blob/#{another_project.commit.sha}/templates/my-test.yml",
+ raw: "http://localhost/#{another_project.full_path}/-/raw/#{another_project.commit.sha}/templates/my-test.yml",
+ location: '/templates/my-test.yml',
+ extra: { project: another_project.full_path, ref: 'HEAD' },
+ context_project: project.full_path,
+ context_sha: '12345' }
)
end
end
@@ -438,8 +480,20 @@ RSpec.describe Gitlab::Ci::Config::External::Processor do
perform
expect(context.includes).to contain_exactly(
- { type: :local, location: 'myfolder/file1.yml', extra: {}, context_project: project.full_path, context_sha: '12345' },
- { type: :local, location: 'myfolder/file2.yml', extra: {}, context_project: project.full_path, context_sha: '12345' }
+ { type: :local,
+ location: 'myfolder/file1.yml',
+ blob: "http://localhost/#{project.full_path}/-/blob/12345/myfolder/file1.yml",
+ raw: "http://localhost/#{project.full_path}/-/raw/12345/myfolder/file1.yml",
+ extra: {},
+ context_project: project.full_path,
+ context_sha: '12345' },
+ { type: :local,
+ blob: "http://localhost/#{project.full_path}/-/blob/12345/myfolder/file2.yml",
+ raw: "http://localhost/#{project.full_path}/-/raw/12345/myfolder/file2.yml",
+ location: 'myfolder/file2.yml',
+ extra: {},
+ context_project: project.full_path,
+ context_sha: '12345' }
)
end
end
diff --git a/spec/lib/gitlab/ci/config_spec.rb b/spec/lib/gitlab/ci/config_spec.rb
index 3ba6a9059c6..5eb04d969eb 100644
--- a/spec/lib/gitlab/ci/config_spec.rb
+++ b/spec/lib/gitlab/ci/config_spec.rb
@@ -109,16 +109,22 @@ RSpec.describe Gitlab::Ci::Config do
expect(config.metadata[:includes]).to contain_exactly(
{ type: :template,
location: 'Jobs/Deploy.gitlab-ci.yml',
+ blob: nil,
+ raw: 'https://gitlab.com/gitlab-org/gitlab/-/raw/master/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml',
extra: {},
context_project: nil,
context_sha: nil },
{ type: :template,
location: 'Jobs/Build.gitlab-ci.yml',
+ blob: nil,
+ raw: 'https://gitlab.com/gitlab-org/gitlab/-/raw/master/lib/gitlab/ci/templates/Jobs/Build.gitlab-ci.yml',
extra: {},
context_project: nil,
context_sha: nil },
{ type: :remote,
location: 'https://example.com/gitlab-ci.yml',
+ blob: nil,
+ raw: 'https://example.com/gitlab-ci.yml',
extra: {},
context_project: nil,
context_sha: nil }
@@ -428,16 +434,22 @@ RSpec.describe Gitlab::Ci::Config do
expect(config.metadata[:includes]).to contain_exactly(
{ type: :local,
location: local_location,
+ blob: "http://localhost/#{project.full_path}/-/blob/12345/#{local_location}",
+ raw: "http://localhost/#{project.full_path}/-/raw/12345/#{local_location}",
extra: {},
context_project: project.full_path,
context_sha: '12345' },
{ type: :remote,
location: remote_location,
+ blob: nil,
+ raw: remote_location,
extra: {},
context_project: project.full_path,
context_sha: '12345' },
{ type: :file,
location: '.gitlab-ci.yml',
+ blob: "http://localhost/#{main_project.full_path}/-/blob/#{main_project.commit.sha}/.gitlab-ci.yml",
+ raw: "http://localhost/#{main_project.full_path}/-/raw/#{main_project.commit.sha}/.gitlab-ci.yml",
extra: { project: main_project.full_path, ref: 'HEAD' },
context_project: project.full_path,
context_sha: '12345' }
diff --git a/spec/lib/gitlab/ci/lint_spec.rb b/spec/lib/gitlab/ci/lint_spec.rb
index 747ff13c840..7e0b2b5aa8e 100644
--- a/spec/lib/gitlab/ci/lint_spec.rb
+++ b/spec/lib/gitlab/ci/lint_spec.rb
@@ -62,7 +62,7 @@ RSpec.describe Gitlab::Ci::Lint do
end
end
- shared_examples 'sets merged yaml' do
+ shared_examples 'sets config metadata' do
let(:content) do
<<~YAML
:include:
@@ -106,6 +106,20 @@ RSpec.describe Gitlab::Ci::Lint do
expect(subject.merged_yaml).to eq(expected_config.to_yaml)
end
+
+ it 'sets includes' do
+ expect(subject.includes).to contain_exactly(
+ {
+ type: :local,
+ location: 'another-gitlab-ci.yml',
+ blob: "http://localhost/#{project.full_path}/-/blob/#{project.commit.sha}/another-gitlab-ci.yml",
+ raw: "http://localhost/#{project.full_path}/-/raw/#{project.commit.sha}/another-gitlab-ci.yml",
+ extra: {},
+ context_project: project.full_path,
+ context_sha: project.commit.sha
+ }
+ )
+ end
end
shared_examples 'content with errors and warnings' do
@@ -220,7 +234,7 @@ RSpec.describe Gitlab::Ci::Lint do
end
end
- it_behaves_like 'sets merged yaml'
+ it_behaves_like 'sets config metadata'
include_context 'advanced validations' do
it 'does not catch advanced logical errors' do
@@ -275,7 +289,7 @@ RSpec.describe Gitlab::Ci::Lint do
end
end
- it_behaves_like 'sets merged yaml'
+ it_behaves_like 'sets config metadata'
include_context 'advanced validations' do
it 'runs advanced logical validations' do
diff --git a/spec/lib/gitlab/ci/parsers/security/common_spec.rb b/spec/lib/gitlab/ci/parsers/security/common_spec.rb
index dfc5dec1481..6495d1f654b 100644
--- a/spec/lib/gitlab/ci/parsers/security/common_spec.rb
+++ b/spec/lib/gitlab/ci/parsers/security/common_spec.rb
@@ -292,7 +292,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Common do
expect(scans.map(&:status).all?('success')).to be(true)
expect(scans.map(&:start_time).all?('placeholder-value')).to be(true)
expect(scans.map(&:end_time).all?('placeholder-value')).to be(true)
- expect(scans.size).to eq(3)
+ expect(scans.size).to eq(7)
expect(scans.first).to be_a(::Gitlab::Ci::Reports::Security::Scan)
end
@@ -348,22 +348,29 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Common do
it 'returns links object for each finding', :aggregate_failures do
links = report.findings.flat_map(&:links)
- expect(links.map(&:url)).to match_array(['https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-1020', 'https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-1030'])
- expect(links.map(&:name)).to match_array([nil, 'CVE-1030'])
- expect(links.size).to eq(2)
+ expect(links.map(&:url)).to match_array(['https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-1020', 'https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-1030',
+ "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-2137", "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-2138",
+ "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-2139", "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-2140"])
+ expect(links.map(&:name)).to match_array([nil, nil, nil, nil, nil, 'CVE-1030'])
+ expect(links.size).to eq(6)
expect(links.first).to be_a(::Gitlab::Ci::Reports::Security::Link)
end
end
describe 'parsing evidence' do
- it 'returns evidence object for each finding', :aggregate_failures do
- evidences = report.findings.map(&:evidence)
+ RSpec::Matchers.define_negated_matcher :have_values, :be_empty
- expect(evidences.first.data).not_to be_empty
- expect(evidences.first.data["summary"]).to match(/The Origin header was changed/)
- expect(evidences.size).to eq(3)
- expect(evidences.compact.size).to eq(2)
- expect(evidences.first).to be_a(::Gitlab::Ci::Reports::Security::Evidence)
+ it 'returns evidence object for each finding', :aggregate_failures do
+ all_evidences = report.findings.map(&:evidence)
+ evidences = all_evidences.compact
+ data = evidences.map(&:data)
+ summaries = evidences.map { |e| e.data["summary"] }
+
+ expect(all_evidences.size).to eq(7)
+ expect(evidences.size).to eq(2)
+ expect(evidences).to all( be_a(::Gitlab::Ci::Reports::Security::Evidence) )
+ expect(data).to all( have_values )
+ expect(summaries).to all( match(/The Origin header was changed/) )
end
end
diff --git a/spec/lib/gitlab/ci/parsers/security/validators/schema_validator_spec.rb b/spec/lib/gitlab/ci/parsers/security/validators/schema_validator_spec.rb
index f6409c8b01f..d06077d69b6 100644
--- a/spec/lib/gitlab/ci/parsers/security/validators/schema_validator_spec.rb
+++ b/spec/lib/gitlab/ci/parsers/security/validators/schema_validator_spec.rb
@@ -5,6 +5,8 @@ require 'spec_helper'
RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
let_it_be(:project) { create(:project) }
+ let(:supported_dast_versions) { described_class::SUPPORTED_VERSIONS[:dast].join(', ') }
+
let(:scanner) do
{
'id' => 'gemnasium',
@@ -22,7 +24,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
expect(described_class::SUPPORTED_VERSIONS.keys).to eq(described_class::DEPRECATED_VERSIONS.keys)
end
- context 'files under schema path are explicitly listed' do
+ context 'when a schema JSON file exists for a particular report type version' do
# We only care about the part that comes before report-format.json
# https://rubular.com/r/N8Juz7r8hYDYgD
filename_regex = /(?<report_type>[-\w]*)\-report-format.json/
@@ -36,14 +38,14 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
matches = filename_regex.match(file)
report_type = matches[:report_type].tr("-", "_").to_sym
- it "#{report_type} #{version}" do
+ it "#{report_type} #{version} is in the constant" do
expect(described_class::SUPPORTED_VERSIONS[report_type]).to include(version)
end
end
end
end
- context 'every SUPPORTED_VERSION has a corresponding JSON file' do
+ context 'when every SUPPORTED_VERSION has a corresponding JSON file' do
described_class::SUPPORTED_VERSIONS.each_key do |report_type|
# api_fuzzing is covered by DAST schema
next if report_type == :api_fuzzing
@@ -66,7 +68,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
let(:report_type) { :dast }
let(:report_version) { described_class::SUPPORTED_VERSIONS[report_type].last }
- context 'and the report is valid' do
+ context 'when the report is valid' do
let(:report_data) do
{
'version' => report_version,
@@ -77,7 +79,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
it { is_expected.to be_truthy }
end
- context 'and the report is invalid' do
+ context 'when the report is invalid' do
let(:report_data) do
{
'version' => report_version
@@ -104,9 +106,19 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
context 'when given a deprecated schema version' do
let(:report_type) { :dast }
+ let(:deprecations_hash) do
+ {
+ dast: %w[10.0.0]
+ }
+ end
+
let(:report_version) { described_class::DEPRECATED_VERSIONS[report_type].last }
- context 'and the report passes schema validation' do
+ before do
+ stub_const("#{described_class}::DEPRECATED_VERSIONS", deprecations_hash)
+ end
+
+ context 'when the report passes schema validation' do
let(:report_data) do
{
'version' => '10.0.0',
@@ -131,8 +143,8 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
end
end
- context 'and the report does not pass schema validation' do
- context 'and enforce_security_report_validation is enabled' do
+ context 'when the report does not pass schema validation' do
+ context 'when enforce_security_report_validation is enabled' do
before do
stub_feature_flags(enforce_security_report_validation: true)
end
@@ -146,7 +158,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
it { is_expected.to be_falsey }
end
- context 'and enforce_security_report_validation is disabled' do
+ context 'when enforce_security_report_validation is disabled' do
before do
stub_feature_flags(enforce_security_report_validation: false)
end
@@ -166,12 +178,12 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
let(:report_type) { :dast }
let(:report_version) { "12.37.0" }
- context 'if enforce_security_report_validation is enabled' do
+ context 'when enforce_security_report_validation is enabled' do
before do
stub_feature_flags(enforce_security_report_validation: true)
end
- context 'and the report is valid' do
+ context 'when the report is valid' do
let(:report_data) do
{
'version' => report_version,
@@ -196,14 +208,14 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
end
end
- context 'and the report is invalid' do
+ context 'when the report is invalid' do
let(:report_data) do
{
'version' => report_version
}
end
- context 'and scanner information is empty' do
+ context 'when scanner information is empty' do
let(:scanner) { {} }
it 'logs related information' do
@@ -235,12 +247,12 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
end
end
- context 'if enforce_security_report_validation is disabled' do
+ context 'when enforce_security_report_validation is disabled' do
before do
stub_feature_flags(enforce_security_report_validation: false)
end
- context 'and the report is valid' do
+ context 'when the report is valid' do
let(:report_data) do
{
'version' => report_version,
@@ -251,7 +263,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
it { is_expected.to be_truthy }
end
- context 'and the report is invalid' do
+ context 'when the report is invalid' do
let(:report_data) do
{
'version' => report_version
@@ -262,6 +274,30 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
end
end
end
+
+ context 'when not given a schema version' do
+ let(:report_type) { :dast }
+ let(:report_version) { nil }
+ let(:report_data) do
+ {
+ 'vulnerabilities' => []
+ }
+ end
+
+ before do
+ stub_feature_flags(enforce_security_report_validation: true)
+ end
+
+ it { is_expected.to be_falsey }
+
+ context 'when enforce_security_report_validation is disabled' do
+ before do
+ stub_feature_flags(enforce_security_report_validation: false)
+ end
+
+ it { is_expected.to be_truthy }
+ end
+ end
end
describe '#errors' do
@@ -271,7 +307,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
let(:report_type) { :dast }
let(:report_version) { described_class::SUPPORTED_VERSIONS[report_type].last }
- context 'and the report is valid' do
+ context 'when the report is valid' do
let(:report_data) do
{
'version' => report_version,
@@ -279,19 +315,17 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
}
end
- let(:expected_errors) { [] }
-
- it { is_expected.to match_array(expected_errors) }
+ it { is_expected.to be_empty }
end
- context 'and the report is invalid' do
+ context 'when the report is invalid' do
let(:report_data) do
{
'version' => report_version
}
end
- context 'if enforce_security_report_validation is enabled' do
+ context 'when enforce_security_report_validation is enabled' do
before do
stub_feature_flags(enforce_security_report_validation: project)
end
@@ -305,23 +339,31 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
it { is_expected.to match_array(expected_errors) }
end
- context 'if enforce_security_report_validation is disabled' do
+ context 'when enforce_security_report_validation is disabled' do
before do
stub_feature_flags(enforce_security_report_validation: false)
end
- let(:expected_errors) { [] }
-
- it { is_expected.to match_array(expected_errors) }
+ it { is_expected.to be_empty }
end
end
end
context 'when given a deprecated schema version' do
let(:report_type) { :dast }
+ let(:deprecations_hash) do
+ {
+ dast: %w[10.0.0]
+ }
+ end
+
let(:report_version) { described_class::DEPRECATED_VERSIONS[report_type].last }
- context 'and the report passes schema validation' do
+ before do
+ stub_const("#{described_class}::DEPRECATED_VERSIONS", deprecations_hash)
+ end
+
+ context 'when the report passes schema validation' do
let(:report_data) do
{
'version' => '10.0.0',
@@ -329,13 +371,11 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
}
end
- let(:expected_errors) { [] }
-
- it { is_expected.to match_array(expected_errors) }
+ it { is_expected.to be_empty }
end
- context 'and the report does not pass schema validation' do
- context 'and enforce_security_report_validation is enabled' do
+ context 'when the report does not pass schema validation' do
+ context 'when enforce_security_report_validation is enabled' do
before do
stub_feature_flags(enforce_security_report_validation: true)
end
@@ -356,7 +396,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
it { is_expected.to match_array(expected_errors) }
end
- context 'and enforce_security_report_validation is disabled' do
+ context 'when enforce_security_report_validation is disabled' do
before do
stub_feature_flags(enforce_security_report_validation: false)
end
@@ -367,9 +407,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
}
end
- let(:expected_errors) { [] }
-
- it { is_expected.to match_array(expected_errors) }
+ it { is_expected.to be_empty }
end
end
end
@@ -378,12 +416,12 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
let(:report_type) { :dast }
let(:report_version) { "12.37.0" }
- context 'if enforce_security_report_validation is enabled' do
+ context 'when enforce_security_report_validation is enabled' do
before do
stub_feature_flags(enforce_security_report_validation: true)
end
- context 'and the report is valid' do
+ context 'when the report is valid' do
let(:report_data) do
{
'version' => report_version,
@@ -393,14 +431,14 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
let(:expected_errors) do
[
- "Version 12.37.0 for report type dast is unsupported, supported versions for this report type are: 14.0.0, 14.0.1, 14.0.2, 14.0.3, 14.0.4, 14.0.5, 14.0.6, 14.1.0, 14.1.1"
+ "Version 12.37.0 for report type dast is unsupported, supported versions for this report type are: #{supported_dast_versions}"
]
end
it { is_expected.to match_array(expected_errors) }
end
- context 'and the report is invalid' do
+ context 'when the report is invalid' do
let(:report_data) do
{
'version' => report_version
@@ -409,7 +447,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
let(:expected_errors) do
[
- "Version 12.37.0 for report type dast is unsupported, supported versions for this report type are: 14.0.0, 14.0.1, 14.0.2, 14.0.3, 14.0.4, 14.0.5, 14.0.6, 14.1.0, 14.1.1",
+ "Version 12.37.0 for report type dast is unsupported, supported versions for this report type are: #{supported_dast_versions}",
"root is missing required keys: vulnerabilities"
]
end
@@ -418,12 +456,12 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
end
end
- context 'if enforce_security_report_validation is disabled' do
+ context 'when enforce_security_report_validation is disabled' do
before do
stub_feature_flags(enforce_security_report_validation: false)
end
- context 'and the report is valid' do
+ context 'when the report is valid' do
let(:report_data) do
{
'version' => report_version,
@@ -431,22 +469,45 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
}
end
- let(:expected_errors) { [] }
-
- it { is_expected.to match_array(expected_errors) }
+ it { is_expected.to be_empty }
end
- context 'and the report is invalid' do
+ context 'when the report is invalid' do
let(:report_data) do
{
'version' => report_version
}
end
- let(:expected_errors) { [] }
+ it { is_expected.to be_empty }
+ end
+ end
+ end
- it { is_expected.to match_array(expected_errors) }
+ context 'when not given a schema version' do
+ let(:report_type) { :dast }
+ let(:report_version) { nil }
+ let(:report_data) do
+ {
+ 'vulnerabilities' => []
+ }
+ end
+
+ let(:expected_errors) do
+ [
+ "root is missing required keys: version",
+ "Report version not provided, dast report type supports versions: #{supported_dast_versions}"
+ ]
+ end
+
+ it { is_expected.to match_array(expected_errors) }
+
+ context 'when enforce_security_report_validation is disabled' do
+ before do
+ stub_feature_flags(enforce_security_report_validation: false)
end
+
+ it { is_expected.to be_empty }
end
end
end
@@ -458,9 +519,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
let(:report_type) { :dast }
let(:report_version) { described_class::SUPPORTED_VERSIONS[report_type].last }
- let(:expected_deprecation_warnings) { [] }
-
- context 'and the report is valid' do
+ context 'when the report is valid' do
let(:report_data) do
{
'version' => report_version,
@@ -468,30 +527,40 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
}
end
- it { is_expected.to match_array(expected_deprecation_warnings) }
+ it { is_expected.to be_empty }
end
- context 'and the report is invalid' do
+ context 'when the report is invalid' do
let(:report_data) do
{
'version' => report_version
}
end
- it { is_expected.to match_array(expected_deprecation_warnings) }
+ it { is_expected.to be_empty }
end
end
context 'when given a deprecated schema version' do
let(:report_type) { :dast }
+ let(:deprecations_hash) do
+ {
+ dast: %w[V2.7.0]
+ }
+ end
+
let(:report_version) { described_class::DEPRECATED_VERSIONS[report_type].last }
let(:expected_deprecation_warnings) do
[
- "Version V2.7.0 for report type dast has been deprecated, supported versions for this report type are: 14.0.0, 14.0.1, 14.0.2, 14.0.3, 14.0.4, 14.0.5, 14.0.6, 14.1.0, 14.1.1"
+ "Version V2.7.0 for report type dast has been deprecated, supported versions for this report type are: #{supported_dast_versions}"
]
end
- context 'and the report passes schema validation' do
+ before do
+ stub_const("#{described_class}::DEPRECATED_VERSIONS", deprecations_hash)
+ end
+
+ context 'when the report passes schema validation' do
let(:report_data) do
{
'version' => report_version,
@@ -502,7 +571,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
it { is_expected.to match_array(expected_deprecation_warnings) }
end
- context 'and the report does not pass schema validation' do
+ context 'when the report does not pass schema validation' do
let(:report_data) do
{
'version' => 'V2.7.0'
@@ -535,7 +604,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
let(:report_type) { :dast }
let(:report_version) { described_class::SUPPORTED_VERSIONS[report_type].last }
- context 'and the report is valid' do
+ context 'when the report is valid' do
let(:report_data) do
{
'version' => report_version,
@@ -543,29 +612,25 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
}
end
- let(:expected_warnings) { [] }
-
- it { is_expected.to match_array(expected_warnings) }
+ it { is_expected.to be_empty }
end
- context 'and the report is invalid' do
+ context 'when the report is invalid' do
let(:report_data) do
{
'version' => report_version
}
end
- context 'if enforce_security_report_validation is enabled' do
+ context 'when enforce_security_report_validation is enabled' do
before do
stub_feature_flags(enforce_security_report_validation: project)
end
- let(:expected_warnings) { [] }
-
- it { is_expected.to match_array(expected_warnings) }
+ it { is_expected.to be_empty }
end
- context 'if enforce_security_report_validation is disabled' do
+ context 'when enforce_security_report_validation is disabled' do
before do
stub_feature_flags(enforce_security_report_validation: false)
end
@@ -583,38 +648,44 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
context 'when given a deprecated schema version' do
let(:report_type) { :dast }
+ let(:deprecations_hash) do
+ {
+ dast: %w[V2.7.0]
+ }
+ end
+
let(:report_version) { described_class::DEPRECATED_VERSIONS[report_type].last }
- context 'and the report passes schema validation' do
+ before do
+ stub_const("#{described_class}::DEPRECATED_VERSIONS", deprecations_hash)
+ end
+
+ context 'when the report passes schema validation' do
let(:report_data) do
{
'vulnerabilities' => []
}
end
- let(:expected_warnings) { [] }
-
- it { is_expected.to match_array(expected_warnings) }
+ it { is_expected.to be_empty }
end
- context 'and the report does not pass schema validation' do
+ context 'when the report does not pass schema validation' do
let(:report_data) do
{
'version' => 'V2.7.0'
}
end
- context 'and enforce_security_report_validation is enabled' do
+ context 'when enforce_security_report_validation is enabled' do
before do
stub_feature_flags(enforce_security_report_validation: true)
end
- let(:expected_warnings) { [] }
-
- it { is_expected.to match_array(expected_warnings) }
+ it { is_expected.to be_empty }
end
- context 'and enforce_security_report_validation is disabled' do
+ context 'when enforce_security_report_validation is disabled' do
before do
stub_feature_flags(enforce_security_report_validation: false)
end
@@ -635,12 +706,12 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
let(:report_type) { :dast }
let(:report_version) { "12.37.0" }
- context 'if enforce_security_report_validation is enabled' do
+ context 'when enforce_security_report_validation is enabled' do
before do
stub_feature_flags(enforce_security_report_validation: true)
end
- context 'and the report is valid' do
+ context 'when the report is valid' do
let(:report_data) do
{
'version' => report_version,
@@ -648,30 +719,26 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
}
end
- let(:expected_warnings) { [] }
-
- it { is_expected.to match_array(expected_warnings) }
+ it { is_expected.to be_empty }
end
- context 'and the report is invalid' do
+ context 'when the report is invalid' do
let(:report_data) do
{
'version' => report_version
}
end
- let(:expected_warnings) { [] }
-
- it { is_expected.to match_array(expected_warnings) }
+ it { is_expected.to be_empty }
end
end
- context 'if enforce_security_report_validation is disabled' do
+ context 'when enforce_security_report_validation is disabled' do
before do
stub_feature_flags(enforce_security_report_validation: false)
end
- context 'and the report is valid' do
+ context 'when the report is valid' do
let(:report_data) do
{
'version' => report_version,
@@ -681,14 +748,14 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
let(:expected_warnings) do
[
- "Version 12.37.0 for report type dast is unsupported, supported versions for this report type are: 14.0.0, 14.0.1, 14.0.2, 14.0.3, 14.0.4, 14.0.5, 14.0.6, 14.1.0, 14.1.1"
+ "Version 12.37.0 for report type dast is unsupported, supported versions for this report type are: #{supported_dast_versions}"
]
end
it { is_expected.to match_array(expected_warnings) }
end
- context 'and the report is invalid' do
+ context 'when the report is invalid' do
let(:report_data) do
{
'version' => report_version
@@ -697,7 +764,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
let(:expected_warnings) do
[
- "Version 12.37.0 for report type dast is unsupported, supported versions for this report type are: 14.0.0, 14.0.1, 14.0.2, 14.0.3, 14.0.4, 14.0.5, 14.0.6, 14.1.0, 14.1.1",
+ "Version 12.37.0 for report type dast is unsupported, supported versions for this report type are: #{supported_dast_versions}",
"root is missing required keys: vulnerabilities"
]
end
@@ -706,5 +773,32 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
end
end
end
+
+ context 'when not given a schema version' do
+ let(:report_type) { :dast }
+ let(:report_version) { nil }
+ let(:report_data) do
+ {
+ 'vulnerabilities' => []
+ }
+ end
+
+ it { is_expected.to be_empty }
+
+ context 'when enforce_security_report_validation is disabled' do
+ before do
+ stub_feature_flags(enforce_security_report_validation: false)
+ end
+
+ let(:expected_warnings) do
+ [
+ "root is missing required keys: version",
+ "Report version not provided, dast report type supports versions: #{supported_dast_versions}"
+ ]
+ end
+
+ it { is_expected.to match_array(expected_warnings) }
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/ci/pipeline/chain/cancel_pending_pipelines_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/cancel_pending_pipelines_spec.rb
index 25e81f6d538..b570f2a7f75 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/cancel_pending_pipelines_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/cancel_pending_pipelines_spec.rb
@@ -106,7 +106,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::CancelPendingPipelines do
create(:ci_build, :interruptible, :running, pipeline: child_pipeline)
end
- not_started_statuses = Ci::HasStatus::AVAILABLE_STATUSES - Ci::HasStatus::BUILD_STARTED_RUNNING_STATUSES
+ not_started_statuses = Ci::HasStatus::AVAILABLE_STATUSES - Ci::HasStatus::STARTED_STATUSES
context 'when the jobs are cancelable' do
cancelable_not_started_statuses = Set.new(not_started_statuses).intersection(Ci::HasStatus::CANCELABLE_STATUSES)
cancelable_not_started_statuses.each do |status|
diff --git a/spec/lib/gitlab/ci/pipeline/chain/limit/deployments_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/limit/deployments_spec.rb
index 1aa104310af..431073b5a09 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/limit/deployments_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/limit/deployments_spec.rb
@@ -87,7 +87,7 @@ RSpec.describe ::Gitlab::Ci::Pipeline::Chain::Limit::Deployments do
it 'logs the error' do
expect(Gitlab::ErrorTracking).to receive(:log_exception).with(
instance_of(Gitlab::Ci::Limit::LimitExceededError),
- project_id: project.id, plan: namespace.actual_plan_name
+ { project_id: project.id, plan: namespace.actual_plan_name }
)
perform
diff --git a/spec/lib/gitlab/ci/pipeline/expression/lexeme/matches_spec.rb b/spec/lib/gitlab/ci/pipeline/expression/lexeme/matches_spec.rb
index 0da04d8dcf7..83742699d3d 100644
--- a/spec/lib/gitlab/ci/pipeline/expression/lexeme/matches_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/expression/lexeme/matches_spec.rb
@@ -1,9 +1,13 @@
# frozen_string_literal: true
require 'fast_spec_helper'
+require 'support/helpers/stubbed_feature'
+require 'support/helpers/stub_feature_flags'
require_dependency 're2'
RSpec.describe Gitlab::Ci::Pipeline::Expression::Lexeme::Matches do
+ include StubFeatureFlags
+
let(:left) { double('left') }
let(:right) { double('right') }
@@ -148,5 +152,29 @@ RSpec.describe Gitlab::Ci::Pipeline::Expression::Lexeme::Matches do
it { is_expected.to eq(false) }
end
+
+ context 'when right value is a regexp string' do
+ let(:right_value) { '/^ab.*/' }
+
+ context 'when matching' do
+ let(:left_value) { 'abcde' }
+
+ it { is_expected.to eq(true) }
+
+ context 'when the FF ci_fix_rules_if_comparison_with_regexp_variable is disabled' do
+ before do
+ stub_feature_flags(ci_fix_rules_if_comparison_with_regexp_variable: false)
+ end
+
+ it { is_expected.to eq(false) }
+ end
+ end
+
+ context 'when not matching' do
+ let(:left_value) { 'dfg' }
+
+ it { is_expected.to eq(false) }
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/ci/pipeline/expression/lexeme/not_matches_spec.rb b/spec/lib/gitlab/ci/pipeline/expression/lexeme/not_matches_spec.rb
index 9bff2355d58..aad33106647 100644
--- a/spec/lib/gitlab/ci/pipeline/expression/lexeme/not_matches_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/expression/lexeme/not_matches_spec.rb
@@ -1,9 +1,13 @@
# frozen_string_literal: true
require 'fast_spec_helper'
+require 'support/helpers/stubbed_feature'
+require 'support/helpers/stub_feature_flags'
require_dependency 're2'
RSpec.describe Gitlab::Ci::Pipeline::Expression::Lexeme::NotMatches do
+ include StubFeatureFlags
+
let(:left) { double('left') }
let(:right) { double('right') }
@@ -148,5 +152,29 @@ RSpec.describe Gitlab::Ci::Pipeline::Expression::Lexeme::NotMatches do
it { is_expected.to eq(true) }
end
+
+ context 'when right value is a regexp string' do
+ let(:right_value) { '/^ab.*/' }
+
+ context 'when matching' do
+ let(:left_value) { 'abcde' }
+
+ it { is_expected.to eq(false) }
+
+ context 'when the FF ci_fix_rules_if_comparison_with_regexp_variable is disabled' do
+ before do
+ stub_feature_flags(ci_fix_rules_if_comparison_with_regexp_variable: false)
+ end
+
+ it { is_expected.to eq(true) }
+ end
+ end
+
+ context 'when not matching' do
+ let(:left_value) { 'dfg' }
+
+ it { is_expected.to eq(true) }
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/ci/pipeline/expression/lexeme/pattern_spec.rb b/spec/lib/gitlab/ci/pipeline/expression/lexeme/pattern_spec.rb
index fa4f8a20984..be205395b69 100644
--- a/spec/lib/gitlab/ci/pipeline/expression/lexeme/pattern_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/expression/lexeme/pattern_spec.rb
@@ -1,8 +1,32 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Ci::Pipeline::Expression::Lexeme::Pattern do
+ describe '#initialize' do
+ context 'when the value is a valid regular expression' do
+ it 'initializes the pattern' do
+ pattern = described_class.new('/foo/')
+
+ expect(pattern.value).to eq('/foo/')
+ end
+ end
+
+ context 'when the value is a valid regular expression with escaped slashes' do
+ it 'initializes the pattern' do
+ pattern = described_class.new('/foo\\/bar/')
+
+ expect(pattern.value).to eq('/foo/bar/')
+ end
+ end
+
+ context 'when the value is not a valid regular expression' do
+ it 'raises an error' do
+ expect { described_class.new('foo') }.to raise_error(Gitlab::Ci::Pipeline::Expression::Lexer::SyntaxError)
+ end
+ end
+ end
+
describe '.build' do
it 'creates a new instance of the token' do
expect(described_class.build('/.*/'))
@@ -15,6 +39,29 @@ RSpec.describe Gitlab::Ci::Pipeline::Expression::Lexeme::Pattern do
end
end
+ describe '.build_and_evaluate' do
+ context 'when the value is a valid regular expression' do
+ it 'returns the value as a Gitlab::UntrustedRegexp' do
+ expect(described_class.build_and_evaluate('/foo/'))
+ .to eq(Gitlab::UntrustedRegexp.new('foo'))
+ end
+ end
+
+ context 'when the value is a Gitlab::UntrustedRegexp' do
+ it 'returns the value itself' do
+ expect(described_class.build_and_evaluate(Gitlab::UntrustedRegexp.new('foo')))
+ .to eq(Gitlab::UntrustedRegexp.new('foo'))
+ end
+ end
+
+ context 'when the value is not a valid regular expression' do
+ it 'returns the value itself' do
+ expect(described_class.build_and_evaluate('foo'))
+ .to eq('foo')
+ end
+ end
+ end
+
describe '.type' do
it 'is a value lexeme' do
expect(described_class.type).to eq :value
diff --git a/spec/lib/gitlab/ci/pipeline/expression/statement_spec.rb b/spec/lib/gitlab/ci/pipeline/expression/statement_spec.rb
index 84713e2a798..bbd11a00149 100644
--- a/spec/lib/gitlab/ci/pipeline/expression/statement_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/expression/statement_spec.rb
@@ -12,7 +12,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Expression::Statement do
.to_hash
end
- subject do
+ subject(:statement) do
described_class.new(text, variables)
end
@@ -29,6 +29,8 @@ RSpec.describe Gitlab::Ci::Pipeline::Expression::Statement do
describe '#evaluate' do
using RSpec::Parameterized::TableSyntax
+ subject(:evaluate) { statement.evaluate }
+
where(:expression, :value) do
'$PRESENT_VARIABLE == "my variable"' | true
'"my variable" == $PRESENT_VARIABLE' | true
@@ -125,7 +127,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Expression::Statement do
let(:text) { expression }
it "evaluates to `#{params[:value].inspect}`" do
- expect(subject.evaluate).to eq(value)
+ expect(evaluate).to eq(value)
end
end
end
@@ -133,6 +135,8 @@ RSpec.describe Gitlab::Ci::Pipeline::Expression::Statement do
describe '#truthful?' do
using RSpec::Parameterized::TableSyntax
+ subject(:truthful?) { statement.truthful? }
+
where(:expression, :value) do
'$PRESENT_VARIABLE == "my variable"' | true
"$PRESENT_VARIABLE == 'no match'" | false
@@ -151,7 +155,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Expression::Statement do
let(:text) { expression }
it "returns `#{params[:value].inspect}`" do
- expect(subject.truthful?).to eq value
+ expect(truthful?).to eq value
end
end
@@ -159,10 +163,41 @@ RSpec.describe Gitlab::Ci::Pipeline::Expression::Statement do
let(:text) { '$PRESENT_VARIABLE' }
it 'returns false' do
- allow(subject).to receive(:evaluate)
+ allow(statement).to receive(:evaluate)
.and_raise(described_class::StatementError)
- expect(subject.truthful?).to be_falsey
+ expect(truthful?).to be_falsey
+ end
+ end
+
+ context 'when variables have patterns' do
+ let(:variables) do
+ Gitlab::Ci::Variables::Collection.new
+ .append(key: 'teststring', value: 'abcde')
+ .append(key: 'pattern1', value: '/^ab.*/')
+ .append(key: 'pattern2', value: '/^at.*/')
+ .to_hash
+ end
+
+ where(:expression, :ff, :result) do
+ '$teststring =~ "abcde"' | true | true
+ '$teststring =~ "abcde"' | false | true
+ '$teststring =~ $teststring' | true | true
+ '$teststring =~ $teststring' | false | true
+ '$teststring =~ $pattern1' | true | true
+ '$teststring =~ $pattern1' | false | false
+ '$teststring =~ $pattern2' | true | false
+ '$teststring =~ $pattern2' | false | false
+ end
+
+ with_them do
+ let(:text) { expression }
+
+ before do
+ stub_feature_flags(ci_fix_rules_if_comparison_with_regexp_variable: ff)
+ end
+
+ it { is_expected.to eq(result) }
end
end
end
diff --git a/spec/lib/gitlab/ci/pipeline/seed/deployment_spec.rb b/spec/lib/gitlab/ci/pipeline/seed/deployment_spec.rb
index 9f7281fb714..51185be3e74 100644
--- a/spec/lib/gitlab/ci/pipeline/seed/deployment_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/seed/deployment_spec.rb
@@ -90,29 +90,22 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Deployment do
end
end
- context 'when job has environment attribute with stop action' do
- let(:attributes) do
- {
- environment: 'production',
- options: { environment: { name: 'production', action: 'stop' } }
- }
- end
-
- it 'returns nothing' do
- is_expected.to be_nil
+ context 'when job does not start environment' do
+ where(:action) do
+ %w(stop prepare verify access)
end
- end
- context 'when job has environment attribute with prepare action' do
- let(:attributes) do
- {
- environment: 'production',
- options: { environment: { name: 'production', action: 'prepare' } }
- }
- end
+ with_them do
+ let(:attributes) do
+ {
+ environment: 'production',
+ options: { environment: { name: 'production', action: action } }
+ }
+ end
- it 'returns nothing' do
- is_expected.to be_nil
+ it 'returns nothing' do
+ is_expected.to be_nil
+ end
end
end
diff --git a/spec/lib/gitlab/ci/reports/security/scanner_spec.rb b/spec/lib/gitlab/ci/reports/security/scanner_spec.rb
index eb406e01b24..d7ac82e3b53 100644
--- a/spec/lib/gitlab/ci/reports/security/scanner_spec.rb
+++ b/spec/lib/gitlab/ci/reports/security/scanner_spec.rb
@@ -103,8 +103,6 @@ RSpec.describe Gitlab::Ci::Reports::Security::Scanner do
context 'when the `external_id` of the scanners are different' do
where(:scanner_1_attributes, :scanner_2_attributes, :expected_comparison_result) do
- { external_id: 'bundler_audit', name: 'foo', vendor: 'bar' } | { external_id: 'retire.js', name: 'foo', vendor: 'bar' } | -1
- { external_id: 'retire.js', name: 'foo', vendor: 'bar' } | { external_id: 'gemnasium', name: 'foo', vendor: 'bar' } | -1
{ external_id: 'gemnasium', name: 'foo', vendor: 'bar' } | { external_id: 'gemnasium-maven', name: 'foo', vendor: 'bar' } | -1
{ external_id: 'gemnasium-maven', name: 'foo', vendor: 'bar' } | { external_id: 'gemnasium-python', name: 'foo', vendor: 'bar' } | -1
{ external_id: 'gemnasium-python', name: 'foo', vendor: 'bar' } | { external_id: 'bandit', name: 'foo', vendor: 'bar' } | 1
diff --git a/spec/lib/gitlab/ci/runner_upgrade_check_spec.rb b/spec/lib/gitlab/ci/runner_upgrade_check_spec.rb
index b430da376dd..f2b4e7573c0 100644
--- a/spec/lib/gitlab/ci/runner_upgrade_check_spec.rb
+++ b/spec/lib/gitlab/ci/runner_upgrade_check_spec.rb
@@ -22,8 +22,8 @@ RSpec.describe Gitlab::Ci::RunnerUpgradeCheck do
context 'with nil runner_version' do
let(:runner_version) { nil }
- it 'raises :unknown' do
- is_expected.to eq(:unknown)
+ it 'returns :invalid' do
+ is_expected.to eq(:invalid)
end
end
diff --git a/spec/lib/gitlab/ci/templates/Jobs/sast_iac_gitlab_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/Jobs/sast_iac_gitlab_ci_yaml_spec.rb
index 0f97bc06a4e..85516d0bbb0 100644
--- a/spec/lib/gitlab/ci/templates/Jobs/sast_iac_gitlab_ci_yaml_spec.rb
+++ b/spec/lib/gitlab/ci/templates/Jobs/sast_iac_gitlab_ci_yaml_spec.rb
@@ -2,8 +2,8 @@
require 'spec_helper'
-RSpec.describe 'Jobs/SAST-IaC.latest.gitlab-ci.yml' do
- subject(:template) { Gitlab::Template::GitlabCiYmlTemplate.find('Jobs/SAST-IaC.latest') }
+RSpec.describe 'Jobs/SAST-IaC.gitlab-ci.yml' do
+ subject(:template) { Gitlab::Template::GitlabCiYmlTemplate.find('Jobs/SAST-IaC') }
describe 'the created pipeline' do
let_it_be(:project) { create(:project, :repository) }
diff --git a/spec/lib/gitlab/ci/templates/Jobs/sast_iac_latest_gitlab_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/Jobs/sast_iac_latest_gitlab_ci_yaml_spec.rb
new file mode 100644
index 00000000000..0f97bc06a4e
--- /dev/null
+++ b/spec/lib/gitlab/ci/templates/Jobs/sast_iac_latest_gitlab_ci_yaml_spec.rb
@@ -0,0 +1,65 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Jobs/SAST-IaC.latest.gitlab-ci.yml' do
+ subject(:template) { Gitlab::Template::GitlabCiYmlTemplate.find('Jobs/SAST-IaC.latest') }
+
+ describe 'the created pipeline' do
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:user) { project.first_owner }
+
+ let(:default_branch) { 'main' }
+ let(:pipeline_ref) { default_branch }
+ let(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_ref) }
+ let(:pipeline) { service.execute!(:push).payload }
+ let(:build_names) { pipeline.builds.pluck(:name) }
+
+ before do
+ stub_ci_pipeline_yaml_file(template.content)
+ allow_next_instance_of(Ci::BuildScheduleWorker) do |instance|
+ allow(instance).to receive(:perform).and_return(true)
+ end
+ allow(project).to receive(:default_branch).and_return(default_branch)
+ end
+
+ context 'on feature branch' do
+ let(:pipeline_ref) { 'feature' }
+
+ it 'creates the kics-iac-sast job' do
+ expect(build_names).to contain_exactly('kics-iac-sast')
+ end
+ end
+
+ context 'on merge request' do
+ let(:service) { MergeRequests::CreatePipelineService.new(project: project, current_user: user) }
+ let(:merge_request) { create(:merge_request, :simple, source_project: project) }
+ let(:pipeline) { service.execute(merge_request).payload }
+
+ it 'has no jobs' do
+ expect(pipeline).to be_merge_request_event
+ expect(build_names).to be_empty
+ end
+ end
+
+ context 'SAST_DISABLED is set' do
+ before do
+ create(:ci_variable, key: 'SAST_DISABLED', value: 'true', project: project)
+ end
+
+ context 'on default branch' do
+ it 'has no jobs' do
+ expect { pipeline }.to raise_error(Ci::CreatePipelineService::CreateError)
+ end
+ end
+
+ context 'on feature branch' do
+ let(:pipeline_ref) { 'feature' }
+
+ it 'has no jobs' do
+ expect { pipeline }.to raise_error(Ci::CreatePipelineService::CreateError)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/templates/MATLAB_spec.rb b/spec/lib/gitlab/ci/templates/MATLAB_spec.rb
index a12d69b67a6..432040c4a14 100644
--- a/spec/lib/gitlab/ci/templates/MATLAB_spec.rb
+++ b/spec/lib/gitlab/ci/templates/MATLAB_spec.rb
@@ -20,7 +20,7 @@ RSpec.describe 'MATLAB.gitlab-ci.yml' do
end
it 'creates all jobs' do
- expect(build_names).to include('command', 'test', 'test_artifacts_job')
+ expect(build_names).to include('command', 'test', 'test_artifacts')
end
end
end
diff --git a/spec/lib/gitlab/ci/templates/Terraform/base_gitlab_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/Terraform/base_gitlab_ci_yaml_spec.rb
index 5e9224cebd9..eca79f37779 100644
--- a/spec/lib/gitlab/ci/templates/Terraform/base_gitlab_ci_yaml_spec.rb
+++ b/spec/lib/gitlab/ci/templates/Terraform/base_gitlab_ci_yaml_spec.rb
@@ -16,7 +16,6 @@ RSpec.describe 'Terraform/Base.gitlab-ci.yml' do
before do
stub_ci_pipeline_yaml_file(template.content)
- allow_any_instance_of(Ci::BuildScheduleWorker).to receive(:perform).and_return(true)
allow(project).to receive(:default_branch).and_return(default_branch)
end
diff --git a/spec/lib/gitlab/ci/templates/managed_cluster_applications_gitlab_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/managed_cluster_applications_gitlab_ci_yaml_spec.rb
deleted file mode 100644
index 14aaf717453..00000000000
--- a/spec/lib/gitlab/ci/templates/managed_cluster_applications_gitlab_ci_yaml_spec.rb
+++ /dev/null
@@ -1,40 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'Managed-Cluster-Applications.gitlab-ci.yml' do
- subject(:template) { Gitlab::Template::GitlabCiYmlTemplate.find('Managed-Cluster-Applications') }
-
- describe 'the created pipeline' do
- let_it_be(:user) { create(:user) }
-
- let(:project) { create(:project, :custom_repo, namespace: user.namespace, files: { 'README.md' => '' }) }
- let(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_branch ) }
- let(:pipeline) { service.execute!(:push).payload }
- let(:build_names) { pipeline.builds.pluck(:name) }
- let(:default_branch) { project.default_branch_or_main }
- let(:pipeline_branch) { default_branch }
-
- before do
- stub_ci_pipeline_yaml_file(template.content)
- end
-
- context 'for a default branch' do
- it 'creates a apply job' do
- expect(build_names).to match_array('apply')
- end
- end
-
- context 'outside of default branch' do
- let(:pipeline_branch) { 'a_branch' }
-
- before do
- project.repository.create_branch(pipeline_branch, default_branch)
- end
-
- it 'has no jobs' do
- expect { pipeline }.to raise_error(Ci::CreatePipelineService::CreateError, 'No stages / jobs for this pipeline.')
- end
- end
- end
-end
diff --git a/spec/lib/gitlab/ci/templates/templates_spec.rb b/spec/lib/gitlab/ci/templates/templates_spec.rb
index ca096fcecc4..36c6e805bdf 100644
--- a/spec/lib/gitlab/ci/templates/templates_spec.rb
+++ b/spec/lib/gitlab/ci/templates/templates_spec.rb
@@ -7,10 +7,9 @@ RSpec.describe 'CI YML Templates' do
let(:all_templates) { Gitlab::Template::GitlabCiYmlTemplate.all.map(&:full_name) }
let(:excluded_templates) do
- excluded = all_templates.select do |name|
+ all_templates.select do |name|
Gitlab::Template::GitlabCiYmlTemplate.excluded_patterns.any? { |pattern| pattern.match?(name) }
end
- excluded + ["Terraform.gitlab-ci.yml"]
end
shared_examples 'require default stages to be included' do
diff --git a/spec/lib/gitlab/ci/templates/terraform_gitlab_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/terraform_gitlab_ci_yaml_spec.rb
index 346ab9f7af7..2fc4b509aab 100644
--- a/spec/lib/gitlab/ci/templates/terraform_gitlab_ci_yaml_spec.rb
+++ b/spec/lib/gitlab/ci/templates/terraform_gitlab_ci_yaml_spec.rb
@@ -20,13 +20,16 @@ RSpec.describe 'Terraform.gitlab-ci.yml' do
before do
stub_ci_pipeline_yaml_file(template.content)
+ allow_next_instance_of(Ci::BuildScheduleWorker) do |instance|
+ allow(instance).to receive(:perform).and_return(true)
+ end
allow(project).to receive(:default_branch).and_return(default_branch)
end
context 'on master branch' do
it 'creates init, validate and build jobs', :aggregate_failures do
expect(pipeline.errors).to be_empty
- expect(build_names).to include('init', 'validate', 'build', 'deploy')
+ expect(build_names).to include('validate', 'build', 'deploy')
end
end
diff --git a/spec/lib/gitlab/ci/templates/terraform_latest_gitlab_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/terraform_latest_gitlab_ci_yaml_spec.rb
index 6c06403adff..42e56c4ab3c 100644
--- a/spec/lib/gitlab/ci/templates/terraform_latest_gitlab_ci_yaml_spec.rb
+++ b/spec/lib/gitlab/ci/templates/terraform_latest_gitlab_ci_yaml_spec.rb
@@ -20,7 +20,9 @@ RSpec.describe 'Terraform.latest.gitlab-ci.yml' do
before do
stub_ci_pipeline_yaml_file(template.content)
- allow_any_instance_of(Ci::BuildScheduleWorker).to receive(:perform).and_return(true)
+ allow_next_instance_of(Ci::BuildScheduleWorker) do |instance|
+ allow(instance).to receive(:perform).and_return(true)
+ end
allow(project).to receive(:default_branch).and_return(default_branch)
end
diff --git a/spec/lib/gitlab/ci/variables/builder_spec.rb b/spec/lib/gitlab/ci/variables/builder_spec.rb
index b9aa5f7c431..e13a0993fa8 100644
--- a/spec/lib/gitlab/ci/variables/builder_spec.rb
+++ b/spec/lib/gitlab/ci/variables/builder_spec.rb
@@ -246,7 +246,7 @@ RSpec.describe Gitlab::Ci::Variables::Builder do
subject { builder.kubernetes_variables(environment: nil, job: job) }
before do
- allow(Ci::GenerateKubeconfigService).to receive(:new).with(job).and_return(service)
+ allow(Ci::GenerateKubeconfigService).to receive(:new).with(job.pipeline, token: job.token).and_return(service)
end
it { is_expected.to include(key: 'KUBECONFIG', value: 'example-kubeconfig', public: false, file: true) }
diff --git a/spec/lib/gitlab/ci/yaml_processor/result_spec.rb b/spec/lib/gitlab/ci/yaml_processor/result_spec.rb
index 25705fd4260..8416501e949 100644
--- a/spec/lib/gitlab/ci/yaml_processor/result_spec.rb
+++ b/spec/lib/gitlab/ci/yaml_processor/result_spec.rb
@@ -12,8 +12,8 @@ module Gitlab
let(:ci_config) { Gitlab::Ci::Config.new(config_content, user: user) }
let(:result) { described_class.new(ci_config: ci_config, warnings: ci_config&.warnings) }
- describe '#merged_yaml' do
- subject(:merged_yaml) { result.merged_yaml }
+ describe '#config_metadata' do
+ subject(:config_metadata) { result.config_metadata }
let(:config_content) do
YAML.dump(
@@ -33,11 +33,23 @@ module Gitlab
end
it 'returns expanded yaml config' do
- expanded_config = YAML.safe_load(merged_yaml, [Symbol])
+ expanded_config = YAML.safe_load(config_metadata[:merged_yaml], [Symbol])
included_config = YAML.safe_load(included_yml, [Symbol])
expect(expanded_config).to include(*included_config.keys)
end
+
+ it 'returns includes' do
+ expect(config_metadata[:includes]).to contain_exactly(
+ { type: :remote,
+ location: 'https://example.com/sample.yml',
+ blob: nil,
+ raw: 'https://example.com/sample.yml',
+ extra: {},
+ context_project: nil,
+ context_sha: nil }
+ )
+ end
end
describe '#yaml_variables_for' do
diff --git a/spec/lib/gitlab/ci/yaml_processor_spec.rb b/spec/lib/gitlab/ci/yaml_processor_spec.rb
index 9b68ee2d6a2..1910057622b 100644
--- a/spec/lib/gitlab/ci/yaml_processor_spec.rb
+++ b/spec/lib/gitlab/ci/yaml_processor_spec.rb
@@ -630,7 +630,7 @@ module Gitlab
describe 'only / except policies validations' do
context 'when `only` has an invalid value' do
- let(:config) { { rspec: { script: "rspec", type: "test", only: only } } }
+ let(:config) { { rspec: { script: "rspec", stage: "test", only: only } } }
subject { Gitlab::Ci::YamlProcessor.new(YAML.dump(config)).execute }
@@ -2606,19 +2606,19 @@ module Gitlab
end
context 'returns errors if job stage is not a string' do
- let(:config) { YAML.dump({ rspec: { script: "test", type: 1 } }) }
+ let(:config) { YAML.dump({ rspec: { script: "test", stage: 1 } }) }
- it_behaves_like 'returns errors', 'jobs:rspec:type config should be a string'
+ it_behaves_like 'returns errors', 'jobs:rspec:stage config should be a string'
end
context 'returns errors if job stage is not a pre-defined stage' do
- let(:config) { YAML.dump({ rspec: { script: "test", type: "acceptance" } }) }
+ let(:config) { YAML.dump({ rspec: { script: "test", stage: "acceptance" } }) }
it_behaves_like 'returns errors', 'rspec job: chosen stage does not exist; available stages are .pre, build, test, deploy, .post'
end
context 'returns errors if job stage is not a defined stage' do
- let(:config) { YAML.dump({ stages: %w(build test), rspec: { script: "test", type: "acceptance" } }) }
+ let(:config) { YAML.dump({ stages: %w(build test), rspec: { script: "test", stage: "acceptance" } }) }
it_behaves_like 'returns errors', 'rspec job: chosen stage does not exist; available stages are .pre, build, test, .post'
end
diff --git a/spec/lib/gitlab/color_spec.rb b/spec/lib/gitlab/color_spec.rb
index 8b16e13fa4d..28719aa6199 100644
--- a/spec/lib/gitlab/color_spec.rb
+++ b/spec/lib/gitlab/color_spec.rb
@@ -24,6 +24,48 @@ RSpec.describe Gitlab::Color do
end
end
+ describe '.color_for' do
+ subject { described_class.color_for(value) }
+
+ shared_examples 'deterministic' do
+ it 'is deterministoc' do
+ expect(subject.to_s).to eq(described_class.color_for(value).to_s)
+ end
+ end
+
+ context 'when generating color for nil value' do
+ let(:value) { nil }
+
+ specify { is_expected.to be_valid }
+
+ it_behaves_like 'deterministic'
+ end
+
+ context 'when generating color for empty string value' do
+ let(:value) { '' }
+
+ specify { is_expected.to be_valid }
+
+ it_behaves_like 'deterministic'
+ end
+
+ context 'when generating color for number value' do
+ let(:value) { 1 }
+
+ specify { is_expected.to be_valid }
+
+ it_behaves_like 'deterministic'
+ end
+
+ context 'when generating color for string value' do
+ let(:value) { "1" }
+
+ specify { is_expected.to be_valid }
+
+ it_behaves_like 'deterministic'
+ end
+ end
+
describe '#new' do
it 'handles nil values' do
expect(described_class.new(nil)).to eq(described_class.new(nil))
diff --git a/spec/lib/gitlab/content_security_policy/config_loader_spec.rb b/spec/lib/gitlab/content_security_policy/config_loader_spec.rb
index 44e2cb21677..2df85434f0e 100644
--- a/spec/lib/gitlab/content_security_policy/config_loader_spec.rb
+++ b/spec/lib/gitlab/content_security_policy/config_loader_spec.rb
@@ -183,6 +183,8 @@ RSpec.describe Gitlab::ContentSecurityPolicy::ConfigLoader do
end
describe '#load' do
+ let(:default_directives) { described_class.default_directives }
+
subject { described_class.new(csp_config[:directives]) }
def expected_config(directive)
@@ -207,5 +209,23 @@ RSpec.describe Gitlab::ContentSecurityPolicy::ConfigLoader do
expect(policy.directives['base-uri']).to be_nil
end
+
+ it 'returns default values for directives not defined by the user' do
+ # Explicitly disabling script_src and setting report_uri
+ csp_config[:directives] = {
+ script_src: false,
+ report_uri: 'https://example.org'
+ }
+
+ subject.load(policy)
+
+ expected_policy = ActionDispatch::ContentSecurityPolicy.new
+ # Creating a policy from default settings and manually overriding the custom values
+ described_class.new(default_directives).load(expected_policy)
+ expected_policy.script_src(nil)
+ expected_policy.report_uri('https://example.org')
+
+ expect(policy.directives).to eq(expected_policy.directives)
+ end
end
end
diff --git a/spec/lib/gitlab/hook_data/issuable_builder_spec.rb b/spec/lib/gitlab/data_builder/issuable_spec.rb
index 676396697fb..c1ae65c160f 100644
--- a/spec/lib/gitlab/hook_data/issuable_builder_spec.rb
+++ b/spec/lib/gitlab/data_builder/issuable_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Gitlab::HookData::IssuableBuilder do
+RSpec.describe Gitlab::DataBuilder::Issuable do
let_it_be(:user) { create(:user) }
# This shared example requires a `builder` and `user` variable
diff --git a/spec/lib/gitlab/database/background_migration/batched_migration_spec.rb b/spec/lib/gitlab/database/background_migration/batched_migration_spec.rb
index 7a433be0e2f..a1c979bba50 100644
--- a/spec/lib/gitlab/database/background_migration/batched_migration_spec.rb
+++ b/spec/lib/gitlab/database/background_migration/batched_migration_spec.rb
@@ -99,6 +99,15 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigration, type: :m
end
end
+ describe '.created_after' do
+ let!(:migration_old) { create :batched_background_migration, created_at: 2.days.ago }
+ let!(:migration_new) { create :batched_background_migration, created_at: 0.days.ago }
+
+ it 'only returns migrations created after the specified time' do
+ expect(described_class.created_after(1.day.ago)).to contain_exactly(migration_new)
+ end
+ end
+
describe '.queued' do
let!(:migration1) { create(:batched_background_migration, :finished) }
let!(:migration2) { create(:batched_background_migration, :paused) }
diff --git a/spec/lib/gitlab/database/background_migration/batched_migration_wrapper_spec.rb b/spec/lib/gitlab/database/background_migration/batched_migration_wrapper_spec.rb
index 6a4ac317cad..83c0275a870 100644
--- a/spec/lib/gitlab/database/background_migration/batched_migration_wrapper_spec.rb
+++ b/spec/lib/gitlab/database/background_migration/batched_migration_wrapper_spec.rb
@@ -3,23 +3,20 @@
require 'spec_helper'
RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigrationWrapper, '#perform' do
- subject { described_class.new(connection: connection, metrics: metrics_tracker).perform(job_record) }
+ subject(:perform) { described_class.new(connection: connection, metrics: metrics_tracker).perform(job_record) }
let(:connection) { Gitlab::Database.database_base_models[:main].connection }
let(:metrics_tracker) { instance_double('::Gitlab::Database::BackgroundMigration::PrometheusMetrics', track: nil) }
- let(:job_class) { Gitlab::BackgroundMigration::CopyColumnUsingBackgroundMigrationJob }
+ let(:job_class) { Class.new(Gitlab::BackgroundMigration::BatchedMigrationJob) }
let_it_be(:pause_ms) { 250 }
let_it_be(:active_migration) { create(:batched_background_migration, :active, job_arguments: [:id, :other_id]) }
let!(:job_record) do
- create(:batched_background_migration_job,
- batched_migration: active_migration,
- pause_ms: pause_ms
- )
+ create(:batched_background_migration_job, batched_migration: active_migration, pause_ms: pause_ms)
end
- let(:job_instance) { double('job instance', batch_metrics: {}) }
+ let(:job_instance) { instance_double('Gitlab::BackgroundMigration::BatchedMigrationJob') }
around do |example|
Gitlab::Database::SharedModel.using_connection(connection) do
@@ -28,23 +25,35 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigrationWrapper, '
end
before do
+ allow(active_migration).to receive(:job_class).and_return(job_class)
+
allow(job_class).to receive(:new).and_return(job_instance)
end
it 'runs the migration job' do
- expect(job_instance).to receive(:perform).with(1, 10, 'events', 'id', 1, pause_ms, 'id', 'other_id')
-
- subject
+ expect(job_class).to receive(:new)
+ .with(start_id: 1,
+ end_id: 10,
+ batch_table: 'events',
+ batch_column: 'id',
+ sub_batch_size: 1,
+ pause_ms: pause_ms,
+ connection: connection)
+ .and_return(job_instance)
+
+ expect(job_instance).to receive(:perform).with('id', 'other_id')
+
+ perform
end
it 'updates the tracking record in the database' do
- test_metrics = { 'my_metris' => 'some value' }
+ test_metrics = { 'my_metrics' => 'some value' }
- expect(job_instance).to receive(:perform)
+ expect(job_instance).to receive(:perform).with('id', 'other_id')
expect(job_instance).to receive(:batch_metrics).and_return(test_metrics)
freeze_time do
- subject
+ perform
reloaded_job_record = job_record.reload
@@ -69,11 +78,11 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigrationWrapper, '
it 'increments attempts and updates other fields' do
updated_metrics = { 'updated_metrics' => 'some_value' }
- expect(job_instance).to receive(:perform)
+ expect(job_instance).to receive(:perform).with('id', 'other_id')
expect(job_instance).to receive(:batch_metrics).and_return(updated_metrics)
freeze_time do
- subject
+ perform
job_record.reload
@@ -88,10 +97,10 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigrationWrapper, '
context 'when the migration job does not raise an error' do
it 'marks the tracking record as succeeded' do
- expect(job_instance).to receive(:perform).with(1, 10, 'events', 'id', 1, pause_ms, 'id', 'other_id')
+ expect(job_instance).to receive(:perform).with('id', 'other_id')
freeze_time do
- subject
+ perform
reloaded_job_record = job_record.reload
@@ -101,22 +110,20 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigrationWrapper, '
end
it 'tracks metrics of the execution' do
- expect(job_instance).to receive(:perform)
+ expect(job_instance).to receive(:perform).with('id', 'other_id')
expect(metrics_tracker).to receive(:track).with(job_record)
- subject
+ perform
end
end
context 'when the migration job raises an error' do
shared_examples 'an error is raised' do |error_class|
it 'marks the tracking record as failed' do
- expect(job_instance).to receive(:perform)
- .with(1, 10, 'events', 'id', 1, pause_ms, 'id', 'other_id')
- .and_raise(error_class)
+ expect(job_instance).to receive(:perform).with('id', 'other_id').and_raise(error_class)
freeze_time do
- expect { subject }.to raise_error(error_class)
+ expect { perform }.to raise_error(error_class)
reloaded_job_record = job_record.reload
@@ -126,10 +133,10 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigrationWrapper, '
end
it 'tracks metrics of the execution' do
- expect(job_instance).to receive(:perform).and_raise(error_class)
+ expect(job_instance).to receive(:perform).with('id', 'other_id').and_raise(error_class)
expect(metrics_tracker).to receive(:track).with(job_record)
- expect { subject }.to raise_error(error_class)
+ expect { perform }.to raise_error(error_class)
end
end
@@ -138,41 +145,14 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigrationWrapper, '
it_behaves_like 'an error is raised', ActiveRecord::StatementTimeout.new('Timeout!')
end
- context 'when the batched background migration does not inherit from BaseJob' do
- let(:migration_class) { Class.new }
-
- before do
- stub_const('Gitlab::BackgroundMigration::Foo', migration_class)
- end
+ context 'when the batched background migration does not inherit from BatchedMigrationJob' do
+ let(:job_class) { Class.new }
- let(:active_migration) { create(:batched_background_migration, :active, job_class_name: 'Foo') }
- let!(:job_record) { create(:batched_background_migration_job, batched_migration: active_migration) }
-
- it 'does not pass any argument' do
- expect(Gitlab::BackgroundMigration::Foo).to receive(:new).with(no_args).and_return(job_instance)
-
- expect(job_instance).to receive(:perform)
-
- subject
- end
- end
-
- context 'when the batched background migration inherits from BaseJob' do
- let(:active_migration) { create(:batched_background_migration, :active, job_class_name: 'Foo') }
- let!(:job_record) { create(:batched_background_migration_job, batched_migration: active_migration) }
-
- let(:migration_class) { Class.new(::Gitlab::BackgroundMigration::BaseJob) }
-
- before do
- stub_const('Gitlab::BackgroundMigration::Foo', migration_class)
- end
-
- it 'passes the correct connection' do
- expect(Gitlab::BackgroundMigration::Foo).to receive(:new).with(connection: connection).and_return(job_instance)
-
- expect(job_instance).to receive(:perform)
+ it 'runs the job with the correct arguments' do
+ expect(job_class).to receive(:new).with(no_args).and_return(job_instance)
+ expect(job_instance).to receive(:perform).with(1, 10, 'events', 'id', 1, pause_ms, 'id', 'other_id')
- subject
+ perform
end
end
end
diff --git a/spec/lib/gitlab/database/migration_helpers/restrict_gitlab_schema_spec.rb b/spec/lib/gitlab/database/migration_helpers/restrict_gitlab_schema_spec.rb
index e7b5bad8626..1009ec354c3 100644
--- a/spec/lib/gitlab/database/migration_helpers/restrict_gitlab_schema_spec.rb
+++ b/spec/lib/gitlab/database/migration_helpers/restrict_gitlab_schema_spec.rb
@@ -401,8 +401,8 @@ RSpec.describe Gitlab::Database::MigrationHelpers::RestrictGitlabSchema, query_a
ci: :dml_not_allowed
},
gitlab_schema_gitlab_shared: {
- main: :dml_access_denied,
- ci: :dml_access_denied
+ main: :runtime_error,
+ ci: :runtime_error
},
gitlab_schema_gitlab_main: {
main: :success,
@@ -465,7 +465,7 @@ RSpec.describe Gitlab::Database::MigrationHelpers::RestrictGitlabSchema, query_a
"does raise exception when accessing feature flags" => {
migration: ->(klass) do
def up
- Feature.enabled?(:redis_hll_tracking, type: :ops, default_enabled: :yaml)
+ Feature.enabled?(:redis_hll_tracking, type: :ops)
end
def down
@@ -486,6 +486,37 @@ RSpec.describe Gitlab::Database::MigrationHelpers::RestrictGitlabSchema, query_a
ci: :skipped
}
}
+ },
+ "does raise exception about cross schema access when suppressing restriction to ensure" => {
+ migration: ->(klass) do
+ # The purpose of this test is to ensure that we use ApplicationRecord
+ # a correct connection will be used:
+ # - this is a case for finalizing background migrations
+ def up
+ Gitlab::Database::QueryAnalyzers::RestrictAllowedSchemas.with_suppressed do
+ ::ApplicationRecord.connection.execute("SELECT 1 FROM ci_builds")
+ end
+ end
+
+ def down
+ end
+ end,
+ query_matcher: /FROM ci_builds/,
+ setup: -> (_) { skip_if_multiple_databases_not_setup },
+ expected: {
+ no_gitlab_schema: {
+ main: :cross_schema_error,
+ ci: :success
+ },
+ gitlab_schema_gitlab_shared: {
+ main: :cross_schema_error,
+ ci: :success
+ },
+ gitlab_schema_gitlab_main: {
+ main: :cross_schema_error,
+ ci: :skipped
+ }
+ }
}
}
end
@@ -517,6 +548,8 @@ RSpec.describe Gitlab::Database::MigrationHelpers::RestrictGitlabSchema, query_a
%i[no_gitlab_schema gitlab_schema_gitlab_main gitlab_schema_gitlab_shared].each do |restrict_gitlab_migration|
context "while restrict_gitlab_migration=#{restrict_gitlab_migration}" do
it "does run migrate :up and :down" do
+ instance_eval(&setup) if setup
+
expected_result = expected.fetch(restrict_gitlab_migration)[db_config_name.to_sym]
skip "not configured" unless expected_result
@@ -543,10 +576,18 @@ RSpec.describe Gitlab::Database::MigrationHelpers::RestrictGitlabSchema, query_a
expect { migration_class.migrate(:up) }.to raise_error(Gitlab::Database::QueryAnalyzers::RestrictAllowedSchemas::DMLAccessDeniedError)
expect { ignore_error(Gitlab::Database::QueryAnalyzers::RestrictAllowedSchemas::DMLAccessDeniedError) { migration_class.migrate(:down) } }.not_to raise_error
+ when :runtime_error
+ expect { migration_class.migrate(:up) }.to raise_error(RuntimeError)
+ expect { ignore_error(RuntimeError) { migration_class.migrate(:down) } }.not_to raise_error
+
when :ddl_not_allowed
expect { migration_class.migrate(:up) }.to raise_error(Gitlab::Database::QueryAnalyzers::RestrictAllowedSchemas::DDLNotAllowedError)
expect { ignore_error(Gitlab::Database::QueryAnalyzers::RestrictAllowedSchemas::DDLNotAllowedError) { migration_class.migrate(:down) } }.not_to raise_error
+ when :cross_schema_error
+ expect { migration_class.migrate(:up) }.to raise_error(Gitlab::Database::QueryAnalyzers::GitlabSchemasValidateConnection::CrossSchemaAccessError)
+ expect { ignore_error(Gitlab::Database::QueryAnalyzers::GitlabSchemasValidateConnection::CrossSchemaAccessError) { migration_class.migrate(:down) } }.not_to raise_error
+
when :skipped
expect_next_instance_of(migration_class) do |migration_object|
expect(migration_object).to receive(:migration_skipped).and_call_original
diff --git a/spec/lib/gitlab/database/migration_helpers_spec.rb b/spec/lib/gitlab/database/migration_helpers_spec.rb
index 798eee0de3e..04fe1fad10e 100644
--- a/spec/lib/gitlab/database/migration_helpers_spec.rb
+++ b/spec/lib/gitlab/database/migration_helpers_spec.rb
@@ -1537,10 +1537,12 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
expect(model).to receive(:add_concurrent_index)
.with(:issues,
%w(gl_project_id),
+ {
unique: false,
name: 'index_on_issues_gl_project_id',
length: [],
- order: [])
+ order: []
+ })
model.copy_indexes(:issues, :project_id, :gl_project_id)
end
@@ -1564,10 +1566,12 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
expect(model).to receive(:add_concurrent_index)
.with(:issues,
%w(gl_project_id foobar),
+ {
unique: false,
name: 'index_on_issues_gl_project_id_foobar',
length: [],
- order: [])
+ order: []
+ })
model.copy_indexes(:issues, :project_id, :gl_project_id)
end
@@ -1591,11 +1595,13 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
expect(model).to receive(:add_concurrent_index)
.with(:issues,
%w(gl_project_id),
+ {
unique: false,
name: 'index_on_issues_gl_project_id',
length: [],
order: [],
- where: 'foo')
+ where: 'foo'
+ })
model.copy_indexes(:issues, :project_id, :gl_project_id)
end
@@ -1619,11 +1625,13 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
expect(model).to receive(:add_concurrent_index)
.with(:issues,
%w(gl_project_id),
+ {
unique: false,
name: 'index_on_issues_gl_project_id',
length: [],
order: [],
- using: 'foo')
+ using: 'foo'
+ })
model.copy_indexes(:issues, :project_id, :gl_project_id)
end
@@ -1647,11 +1655,13 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
expect(model).to receive(:add_concurrent_index)
.with(:issues,
%w(gl_project_id),
+ {
unique: false,
name: 'index_on_issues_gl_project_id',
length: [],
order: [],
- opclass: { 'gl_project_id' => 'bar' })
+ opclass: { 'gl_project_id' => 'bar' }
+ })
model.copy_indexes(:issues, :project_id, :gl_project_id)
end
@@ -1660,14 +1670,16 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
context 'using an index with multiple columns and custom operator classes' do
it 'copies the index' do
index = double(:index,
- columns: %w(project_id foobar),
- name: 'index_on_issues_project_id_foobar',
- using: :gin,
- where: nil,
- opclasses: { 'project_id' => 'bar', 'foobar' => :gin_trgm_ops },
- unique: false,
- lengths: [],
- orders: [])
+ {
+ columns: %w(project_id foobar),
+ name: 'index_on_issues_project_id_foobar',
+ using: :gin,
+ where: nil,
+ opclasses: { 'project_id' => 'bar', 'foobar' => :gin_trgm_ops },
+ unique: false,
+ lengths: [],
+ orders: []
+ })
allow(model).to receive(:indexes_for).with(:issues, 'project_id')
.and_return([index])
@@ -1675,12 +1687,14 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
expect(model).to receive(:add_concurrent_index)
.with(:issues,
%w(gl_project_id foobar),
+ {
unique: false,
name: 'index_on_issues_gl_project_id_foobar',
length: [],
order: [],
opclass: { 'gl_project_id' => 'bar', 'foobar' => :gin_trgm_ops },
- using: :gin)
+ using: :gin
+ })
model.copy_indexes(:issues, :project_id, :gl_project_id)
end
@@ -1689,14 +1703,16 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
context 'using an index with multiple columns and a custom operator class on the non affected column' do
it 'copies the index' do
index = double(:index,
- columns: %w(project_id foobar),
- name: 'index_on_issues_project_id_foobar',
- using: :gin,
- where: nil,
- opclasses: { 'foobar' => :gin_trgm_ops },
- unique: false,
- lengths: [],
- orders: [])
+ {
+ columns: %w(project_id foobar),
+ name: 'index_on_issues_project_id_foobar',
+ using: :gin,
+ where: nil,
+ opclasses: { 'foobar' => :gin_trgm_ops },
+ unique: false,
+ lengths: [],
+ orders: []
+ })
allow(model).to receive(:indexes_for).with(:issues, 'project_id')
.and_return([index])
@@ -1704,12 +1720,14 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
expect(model).to receive(:add_concurrent_index)
.with(:issues,
%w(gl_project_id foobar),
+ {
unique: false,
name: 'index_on_issues_gl_project_id_foobar',
length: [],
order: [],
opclass: { 'foobar' => :gin_trgm_ops },
- using: :gin)
+ using: :gin
+ })
model.copy_indexes(:issues, :project_id, :gl_project_id)
end
@@ -2210,12 +2228,17 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
end
describe '#ensure_batched_background_migration_is_finished' do
+ let(:job_class_name) { 'CopyColumnUsingBackgroundMigrationJob' }
+ let(:table) { :events }
+ let(:column_name) { :id }
+ let(:job_arguments) { [["id"], ["id_convert_to_bigint"], nil] }
+
let(:configuration) do
{
- job_class_name: 'CopyColumnUsingBackgroundMigrationJob',
- table_name: :events,
- column_name: :id,
- job_arguments: [["id"], ["id_convert_to_bigint"], nil]
+ job_class_name: job_class_name,
+ table_name: table,
+ column_name: column_name,
+ job_arguments: job_arguments
}
end
@@ -2224,11 +2247,15 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
it 'raises an error when migration exists and is not marked as finished' do
create(:batched_background_migration, :active, configuration)
+ allow_next_instance_of(Gitlab::Database::BackgroundMigration::BatchedMigrationRunner) do |runner|
+ allow(runner).to receive(:finalize).with(job_class_name, table, column_name, job_arguments).and_return(false)
+ end
+
expect { ensure_batched_background_migration_is_finished }
.to raise_error "Expected batched background migration for the given configuration to be marked as 'finished', but it is 'active':" \
"\t#{configuration}" \
"\n\n" \
- "Finalize it manualy by running" \
+ "Finalize it manually by running" \
"\n\n" \
"\tsudo gitlab-rake gitlab:background_migrations:finalize[CopyColumnUsingBackgroundMigrationJob,events,id,'[[\"id\"]\\,[\"id_convert_to_bigint\"]\\,null]']" \
"\n\n" \
@@ -2251,6 +2278,28 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
expect { ensure_batched_background_migration_is_finished }
.not_to raise_error
end
+
+ it 'finalizes the migration' do
+ migration = create(:batched_background_migration, :active, configuration)
+
+ allow_next_instance_of(Gitlab::Database::BackgroundMigration::BatchedMigrationRunner) do |runner|
+ expect(runner).to receive(:finalize).with(job_class_name, table, column_name, job_arguments).and_return(migration.finish!)
+ end
+
+ ensure_batched_background_migration_is_finished
+ end
+
+ context 'when the flag finalize is false' do
+ it 'does not finalize the migration' do
+ create(:batched_background_migration, :active, configuration)
+
+ allow_next_instance_of(Gitlab::Database::BackgroundMigration::BatchedMigrationRunner) do |runner|
+ expect(runner).not_to receive(:finalize).with(job_class_name, table, column_name, job_arguments)
+ end
+
+ expect { model.ensure_batched_background_migration_is_finished(**configuration.merge(finalize: false)) }.to raise_error(RuntimeError)
+ end
+ end
end
describe '#index_exists_by_name?' do
@@ -3162,15 +3211,15 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
context 'without proper permissions' do
before do
- allow(model).to receive(:execute).with(/CREATE EXTENSION IF NOT EXISTS #{extension}/).and_raise(ActiveRecord::StatementInvalid, 'InsufficientPrivilege: permission denied')
+ allow(model).to receive(:execute)
+ .with(/CREATE EXTENSION IF NOT EXISTS #{extension}/)
+ .and_raise(ActiveRecord::StatementInvalid, 'InsufficientPrivilege: permission denied')
end
- it 'raises the exception' do
- expect { subject }.to raise_error(ActiveRecord::StatementInvalid, /InsufficientPrivilege/)
- end
-
- it 'prints an error message' do
- expect { subject }.to output(/user is not allowed/).to_stderr.and raise_error
+ it 'raises an exception and prints an error message' do
+ expect { subject }
+ .to output(/user is not allowed/).to_stderr
+ .and raise_error(ActiveRecord::StatementInvalid, /InsufficientPrivilege/)
end
end
end
@@ -3188,15 +3237,15 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
context 'without proper permissions' do
before do
- allow(model).to receive(:execute).with(/DROP EXTENSION IF EXISTS #{extension}/).and_raise(ActiveRecord::StatementInvalid, 'InsufficientPrivilege: permission denied')
- end
-
- it 'raises the exception' do
- expect { subject }.to raise_error(ActiveRecord::StatementInvalid, /InsufficientPrivilege/)
+ allow(model).to receive(:execute)
+ .with(/DROP EXTENSION IF EXISTS #{extension}/)
+ .and_raise(ActiveRecord::StatementInvalid, 'InsufficientPrivilege: permission denied')
end
- it 'prints an error message' do
- expect { subject }.to output(/user is not allowed/).to_stderr.and raise_error
+ it 'raises an exception and prints an error message' do
+ expect { subject }
+ .to output(/user is not allowed/).to_stderr
+ .and raise_error(ActiveRecord::StatementInvalid, /InsufficientPrivilege/)
end
end
end
diff --git a/spec/lib/gitlab/database/migrations/background_migration_helpers_spec.rb b/spec/lib/gitlab/database/migrations/background_migration_helpers_spec.rb
index e64f5807385..b0caa21e01a 100644
--- a/spec/lib/gitlab/database/migrations/background_migration_helpers_spec.rb
+++ b/spec/lib/gitlab/database/migrations/background_migration_helpers_spec.rb
@@ -3,16 +3,31 @@
require 'spec_helper'
RSpec.describe Gitlab::Database::Migrations::BackgroundMigrationHelpers do
+ let(:base_class) { ActiveRecord::Migration }
+
let(:model) do
- ActiveRecord::Migration.new.extend(described_class)
+ base_class.new
+ .extend(described_class)
+ .extend(Gitlab::Database::Migrations::ReestablishedConnectionStack)
end
- shared_examples_for 'helpers that enqueue background migrations' do |worker_class, tracking_database|
+ shared_examples_for 'helpers that enqueue background migrations' do |worker_class, connection_class, tracking_database|
before do
allow(model).to receive(:tracking_database).and_return(tracking_database)
+
+ # Due to lib/gitlab/database/load_balancing/configuration.rb:92 requiring RequestStore
+ # we cannot use stub_feature_flags(force_no_sharing_primary_model: true)
+ allow(connection_class.connection.load_balancer.configuration)
+ .to receive(:use_dedicated_connection?).and_return(true)
+
+ allow(model).to receive(:connection).and_return(connection_class.connection)
end
describe '#queue_background_migration_jobs_by_range_at_intervals' do
+ before do
+ allow(model).to receive(:transaction_open?).and_return(false)
+ end
+
context 'when the model has an ID column' do
let!(:id1) { create(:user).id }
let!(:id2) { create(:user).id }
@@ -196,6 +211,34 @@ RSpec.describe Gitlab::Database::Migrations::BackgroundMigrationHelpers do
end.to raise_error(StandardError, /does not have an ID/)
end
end
+
+ context 'when using Migration[2.0]' do
+ let(:base_class) { Class.new(Gitlab::Database::Migration[2.0]) }
+
+ context 'when restriction is set to gitlab_shared' do
+ before do
+ base_class.restrict_gitlab_migration gitlab_schema: :gitlab_shared
+ end
+
+ it 'does raise an exception' do
+ expect do
+ model.queue_background_migration_jobs_by_range_at_intervals(ProjectAuthorization, 'FooJob', 10.seconds)
+ end.to raise_error /use `restrict_gitlab_migration:` " with `:gitlab_shared`/
+ end
+ end
+ end
+
+ context 'when within transaction' do
+ before do
+ allow(model).to receive(:transaction_open?).and_return(true)
+ end
+
+ it 'does raise an exception' do
+ expect do
+ model.queue_background_migration_jobs_by_range_at_intervals(ProjectAuthorization, 'FooJob', 10.seconds)
+ end.to raise_error /The `#queue_background_migration_jobs_by_range_at_intervals` can not be run inside a transaction./
+ end
+ end
end
describe '#requeue_background_migration_jobs_by_range_at_intervals' do
@@ -205,6 +248,10 @@ RSpec.describe Gitlab::Database::Migrations::BackgroundMigrationHelpers do
let!(:successful_job_1) { create(:background_migration_job, class_name: job_class_name, status: :succeeded, arguments: [5, 6]) }
let!(:successful_job_2) { create(:background_migration_job, class_name: job_class_name, status: :succeeded, arguments: [7, 8]) }
+ before do
+ allow(model).to receive(:transaction_open?).and_return(false)
+ end
+
around do |example|
freeze_time do
Sidekiq::Testing.fake! do
@@ -219,6 +266,38 @@ RSpec.describe Gitlab::Database::Migrations::BackgroundMigrationHelpers do
expect(subject).to eq(20.minutes)
end
+ context 'when using Migration[2.0]' do
+ let(:base_class) { Class.new(Gitlab::Database::Migration[2.0]) }
+
+ it 'does re-enqueue pending jobs' do
+ subject
+
+ expect(worker_class.jobs).not_to be_empty
+ end
+
+ context 'when restriction is set' do
+ before do
+ base_class.restrict_gitlab_migration gitlab_schema: :gitlab_main
+ end
+
+ it 'does raise an exception' do
+ expect { subject }
+ .to raise_error /The `#requeue_background_migration_jobs_by_range_at_intervals` cannot use `restrict_gitlab_migration:`./
+ end
+ end
+ end
+
+ context 'when within transaction' do
+ before do
+ allow(model).to receive(:transaction_open?).and_return(true)
+ end
+
+ it 'does raise an exception' do
+ expect { subject }
+ .to raise_error /The `#requeue_background_migration_jobs_by_range_at_intervals` can not be run inside a transaction./
+ end
+ end
+
context 'when nothing is queued' do
subject { model.requeue_background_migration_jobs_by_range_at_intervals('FakeJob', 10.minutes) }
@@ -290,7 +369,7 @@ RSpec.describe Gitlab::Database::Migrations::BackgroundMigrationHelpers do
end
end
- describe '#finalized_background_migration' do
+ describe '#finalize_background_migration' do
let(:coordinator) { Gitlab::BackgroundMigration::JobCoordinator.new(worker_class) }
let!(:tracked_pending_job) { create(:background_migration_job, class_name: job_class_name, status: :pending, arguments: [1]) }
@@ -309,8 +388,8 @@ RSpec.describe Gitlab::Database::Migrations::BackgroundMigrationHelpers do
allow(Gitlab::BackgroundMigration).to receive(:coordinator_for_database)
.with(tracking_database).and_return(coordinator)
- expect(coordinator).to receive(:migration_class_for)
- .with(job_class_name).at_least(:once) { job_class }
+ allow(coordinator).to receive(:migration_class_for)
+ .with(job_class_name) { job_class }
Sidekiq::Testing.disable! do
worker_class.perform_async(job_class_name, [1, 2])
@@ -318,6 +397,8 @@ RSpec.describe Gitlab::Database::Migrations::BackgroundMigrationHelpers do
worker_class.perform_in(10, job_class_name, [5, 6])
worker_class.perform_in(20, job_class_name, [7, 8])
end
+
+ allow(model).to receive(:transaction_open?).and_return(false)
end
it_behaves_like 'finalized tracked background migration', worker_class do
@@ -326,6 +407,52 @@ RSpec.describe Gitlab::Database::Migrations::BackgroundMigrationHelpers do
end
end
+ context 'when within transaction' do
+ before do
+ allow(model).to receive(:transaction_open?).and_return(true)
+ end
+
+ it 'does raise an exception' do
+ expect { model.finalize_background_migration(job_class_name, delete_tracking_jobs: %w[pending succeeded]) }
+ .to raise_error /The `#finalize_background_migration` can not be run inside a transaction./
+ end
+ end
+
+ context 'when using Migration[2.0]' do
+ let(:base_class) { Class.new(Gitlab::Database::Migration[2.0]) }
+
+ it_behaves_like 'finalized tracked background migration', worker_class do
+ before do
+ model.finalize_background_migration(job_class_name)
+ end
+ end
+
+ context 'when restriction is set' do
+ before do
+ base_class.restrict_gitlab_migration gitlab_schema: :gitlab_main
+ end
+
+ it 'does raise an exception' do
+ expect { model.finalize_background_migration(job_class_name, delete_tracking_jobs: %w[pending succeeded]) }
+ .to raise_error /The `#finalize_background_migration` cannot use `restrict_gitlab_migration:`./
+ end
+ end
+ end
+
+ context 'when running migration in reconfigured ActiveRecord::Base context' do
+ it_behaves_like 'reconfigures connection stack', tracking_database do
+ it 'does restore connection hierarchy' do
+ expect_next_instances_of(job_class, 1..) do |job|
+ expect(job).to receive(:perform) do
+ validate_connections!
+ end
+ end
+
+ model.finalize_background_migration(job_class_name, delete_tracking_jobs: %w[pending succeeded])
+ end
+ end
+ end
+
context 'when removing all tracked job records' do
let!(:job_class) do
Class.new do
@@ -443,7 +570,7 @@ RSpec.describe Gitlab::Database::Migrations::BackgroundMigrationHelpers do
end
context 'when the migration is running against the main database' do
- it_behaves_like 'helpers that enqueue background migrations', BackgroundMigrationWorker, 'main'
+ it_behaves_like 'helpers that enqueue background migrations', BackgroundMigrationWorker, ActiveRecord::Base, 'main'
end
context 'when the migration is running against the ci database', if: Gitlab::Database.has_config?(:ci) do
@@ -453,7 +580,7 @@ RSpec.describe Gitlab::Database::Migrations::BackgroundMigrationHelpers do
end
end
- it_behaves_like 'helpers that enqueue background migrations', BackgroundMigration::CiDatabaseWorker, 'ci'
+ it_behaves_like 'helpers that enqueue background migrations', BackgroundMigration::CiDatabaseWorker, Ci::ApplicationRecord, 'ci'
end
describe '#delete_job_tracking' do
diff --git a/spec/lib/gitlab/database/migrations/base_background_runner_spec.rb b/spec/lib/gitlab/database/migrations/base_background_runner_spec.rb
new file mode 100644
index 00000000000..34c83c42056
--- /dev/null
+++ b/spec/lib/gitlab/database/migrations/base_background_runner_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Database::Migrations::BaseBackgroundRunner, :freeze_time do
+ let(:result_dir) { Dir.mktmpdir }
+
+ after do
+ FileUtils.rm_rf(result_dir)
+ end
+
+ context 'subclassing' do
+ subject { described_class.new(result_dir: result_dir) }
+
+ it 'requires that jobs_by_migration_name be implemented' do
+ expect { subject.jobs_by_migration_name }.to raise_error(NotImplementedError)
+ end
+
+ it 'requires that run_job be implemented' do
+ expect { subject.run_job(nil) }.to raise_error(NotImplementedError)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/database/migrations/batched_background_migration_helpers_spec.rb b/spec/lib/gitlab/database/migrations/batched_background_migration_helpers_spec.rb
index f9347a174c4..d1a66036149 100644
--- a/spec/lib/gitlab/database/migrations/batched_background_migration_helpers_spec.rb
+++ b/spec/lib/gitlab/database/migrations/batched_background_migration_helpers_spec.rb
@@ -163,4 +163,45 @@ RSpec.describe Gitlab::Database::Migrations::BatchedBackgroundMigrationHelpers d
end
end
end
+
+ describe '#finalize_batched_background_migration' do
+ let!(:batched_migration) { create(:batched_background_migration, job_class_name: 'MyClass', table_name: :projects, column_name: :id, job_arguments: []) }
+
+ it 'finalizes the migration' do
+ allow_next_instance_of(Gitlab::Database::BackgroundMigration::BatchedMigrationRunner) do |runner|
+ expect(runner).to receive(:finalize).with('MyClass', :projects, :id, [])
+ end
+
+ migration.finalize_batched_background_migration(job_class_name: 'MyClass', table_name: :projects, column_name: :id, job_arguments: [])
+ end
+
+ context 'when the migration does not exist' do
+ it 'raises an exception' do
+ expect do
+ migration.finalize_batched_background_migration(job_class_name: 'MyJobClass', table_name: :projects, column_name: :id, job_arguments: [])
+ end.to raise_error(RuntimeError, 'Could not find batched background migration')
+ end
+ end
+
+ context 'when uses a CI connection', :reestablished_active_record_base do
+ before do
+ skip_if_multiple_databases_not_setup
+
+ ActiveRecord::Base.establish_connection(:ci) # rubocop:disable Database/EstablishConnection
+ end
+
+ it 'raises an exception' do
+ ci_migration = create(:batched_background_migration, :active)
+
+ expect do
+ migration.finalize_batched_background_migration(
+ job_class_name: ci_migration.job_class_name,
+ table_name: ci_migration.table_name,
+ column_name: ci_migration.column_name,
+ job_arguments: ci_migration.job_arguments
+ )
+ end.to raise_error /is currently not supported when running in decomposed/
+ end
+ end
+ end
end
diff --git a/spec/lib/gitlab/database/migrations/observers/query_statistics_spec.rb b/spec/lib/gitlab/database/migrations/observers/query_statistics_spec.rb
index 2515f0d4a06..66de25d65bb 100644
--- a/spec/lib/gitlab/database/migrations/observers/query_statistics_spec.rb
+++ b/spec/lib/gitlab/database/migrations/observers/query_statistics_spec.rb
@@ -43,6 +43,7 @@ RSpec.describe Gitlab::Database::Migrations::Observers::QueryStatistics do
<<~SQL
SELECT query, calls, total_time, max_time, mean_time, rows
FROM pg_stat_statements
+ WHERE pg_get_userbyid(userid) = current_user
ORDER BY total_time DESC
SQL
end
diff --git a/spec/lib/gitlab/database/migrations/reestablished_connection_stack_spec.rb b/spec/lib/gitlab/database/migrations/reestablished_connection_stack_spec.rb
new file mode 100644
index 00000000000..cfb308c63e4
--- /dev/null
+++ b/spec/lib/gitlab/database/migrations/reestablished_connection_stack_spec.rb
@@ -0,0 +1,57 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Database::Migrations::ReestablishedConnectionStack do
+ let(:base_class) { ActiveRecord::Migration }
+
+ let(:model) do
+ base_class.new
+ .extend(described_class)
+ end
+
+ describe '#with_restored_connection_stack' do
+ Gitlab::Database.database_base_models.each do |db_config_name, _|
+ context db_config_name do
+ it_behaves_like "reconfigures connection stack", db_config_name do
+ it 'does restore connection hierarchy' do
+ model.with_restored_connection_stack do
+ validate_connections!
+ end
+ end
+
+ primary_db_config = ActiveRecord::Base.configurations.primary?(db_config_name)
+
+ it 'does reconfigure connection handler', unless: primary_db_config do
+ original_handler = ActiveRecord::Base.connection_handler
+ new_handler = nil
+
+ model.with_restored_connection_stack do
+ new_handler = ActiveRecord::Base.connection_handler
+
+ # establish connection
+ ApplicationRecord.connection.select_one("SELECT 1 FROM projects LIMIT 1")
+ Ci::ApplicationRecord.connection.select_one("SELECT 1 FROM ci_builds LIMIT 1")
+ end
+
+ expect(new_handler).not_to eq(original_handler), "is reconnected"
+ expect(new_handler).not_to be_active_connections
+ expect(ActiveRecord::Base.connection_handler).to eq(original_handler), "is restored"
+ end
+
+ it 'does keep original connection handler', if: primary_db_config do
+ original_handler = ActiveRecord::Base.connection_handler
+ new_handler = nil
+
+ model.with_restored_connection_stack do
+ new_handler = ActiveRecord::Base.connection_handler
+ end
+
+ expect(new_handler).to eq(original_handler)
+ expect(ActiveRecord::Base.connection_handler).to eq(original_handler)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/database/migrations/runner_spec.rb b/spec/lib/gitlab/database/migrations/runner_spec.rb
index 8b1ccf05eb1..e7f68e3e4a8 100644
--- a/spec/lib/gitlab/database/migrations/runner_spec.rb
+++ b/spec/lib/gitlab/database/migrations/runner_spec.rb
@@ -2,6 +2,8 @@
require 'spec_helper'
RSpec.describe Gitlab::Database::Migrations::Runner do
+ include Database::MultipleDatabases
+
let(:result_dir) { Pathname.new(Dir.mktmpdir) }
let(:migration_runs) { [] } # This list gets populated as the runner tries to run migrations
@@ -136,4 +138,35 @@ RSpec.describe Gitlab::Database::Migrations::Runner do
expect(runner.result_dir).to eq(described_class::BASE_RESULT_DIR.join( 'background_migrations'))
end
end
+
+ describe '.batched_background_migrations' do
+ it 'is a TestBatchedBackgroundRunner' do
+ expect(described_class.batched_background_migrations(for_database: 'main')).to be_a(Gitlab::Database::Migrations::TestBatchedBackgroundRunner)
+ end
+
+ context 'choosing the database to test against' do
+ it 'chooses the main database' do
+ runner = described_class.batched_background_migrations(for_database: 'main')
+
+ chosen_connection_name = Gitlab::Database.db_config_name(runner.connection)
+
+ expect(chosen_connection_name).to eq('main')
+ end
+
+ it 'chooses the ci database' do
+ skip_if_multiple_databases_not_setup
+
+ runner = described_class.batched_background_migrations(for_database: 'ci')
+
+ chosen_connection_name = Gitlab::Database.db_config_name(runner.connection)
+
+ expect(chosen_connection_name).to eq('ci')
+ end
+
+ it 'throws an error with an invalid name' do
+ expect { described_class.batched_background_migrations(for_database: 'not_a_database') }
+ .to raise_error(/not a valid database name/)
+ end
+ end
+ end
end
diff --git a/spec/lib/gitlab/database/migrations/test_background_runner_spec.rb b/spec/lib/gitlab/database/migrations/test_background_runner_spec.rb
index 9407efad91f..a2fe91712c7 100644
--- a/spec/lib/gitlab/database/migrations/test_background_runner_spec.rb
+++ b/spec/lib/gitlab/database/migrations/test_background_runner_spec.rb
@@ -3,7 +3,9 @@
require 'spec_helper'
RSpec.describe Gitlab::Database::Migrations::TestBackgroundRunner, :redis do
+ include Gitlab::Database::Migrations::ReestablishedConnectionStack
include Gitlab::Database::Migrations::BackgroundMigrationHelpers
+ include Database::MigrationTestingHelpers
# In order to test the interaction between queueing sidekiq jobs and seeing those jobs in queues,
# we need to disable sidekiq's testing mode and actually send our jobs to redis
@@ -12,6 +14,7 @@ RSpec.describe Gitlab::Database::Migrations::TestBackgroundRunner, :redis do
end
let(:result_dir) { Dir.mktmpdir }
+ let(:connection) { ApplicationRecord.connection }
after do
FileUtils.rm_rf(result_dir)
@@ -41,40 +44,6 @@ RSpec.describe Gitlab::Database::Migrations::TestBackgroundRunner, :redis do
end
context 'running migrations', :freeze_time do
- def define_background_migration(name)
- klass = Class.new do
- # Can't simply def perform here as we won't have access to the block,
- # similarly can't define_method(:perform, &block) here as it would change the block receiver
- define_method(:perform) { |*args| yield(*args) }
- end
- stub_const("Gitlab::BackgroundMigration::#{name}", klass)
- klass
- end
-
- def expect_migration_call_counts(migrations_to_calls)
- migrations_to_calls.each do |migration, calls|
- expect_next_instances_of(migration, calls) do |m|
- expect(m).to receive(:perform).and_call_original
- end
- end
- end
-
- def expect_recorded_migration_runs(migrations_to_runs)
- migrations_to_runs.each do |migration, runs|
- path = File.join(result_dir, migration.name.demodulize)
- num_subdirs = Pathname(path).children.count(&:directory?)
- expect(num_subdirs).to eq(runs)
- end
- end
-
- def expect_migration_runs(migrations_to_run_counts)
- expect_migration_call_counts(migrations_to_run_counts)
-
- yield
-
- expect_recorded_migration_runs(migrations_to_run_counts)
- end
-
it 'runs the migration class correctly' do
calls = []
define_background_migration(migration_name) do |i|
diff --git a/spec/lib/gitlab/database/migrations/test_batched_background_runner_spec.rb b/spec/lib/gitlab/database/migrations/test_batched_background_runner_spec.rb
new file mode 100644
index 00000000000..fbfff1268cc
--- /dev/null
+++ b/spec/lib/gitlab/database/migrations/test_batched_background_runner_spec.rb
@@ -0,0 +1,87 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Database::Migrations::TestBatchedBackgroundRunner, :freeze_time do
+ include Gitlab::Database::MigrationHelpers
+ include Gitlab::Database::Migrations::BatchedBackgroundMigrationHelpers
+ include Database::MigrationTestingHelpers
+
+ let(:result_dir) { Dir.mktmpdir }
+
+ after do
+ FileUtils.rm_rf(result_dir)
+ end
+
+ let(:connection) { ApplicationRecord.connection }
+
+ let(:table_name) { "_test_column_copying"}
+
+ before do
+ connection.execute(<<~SQL)
+ CREATE TABLE #{table_name} (
+ id bigint primary key not null,
+ data bigint
+ );
+
+ insert into #{table_name} (id) select i from generate_series(1, 1000) g(i);
+ SQL
+ end
+
+ context 'running a real background migration' do
+ it 'runs sampled jobs from the batched background migration' do
+ queue_batched_background_migration('CopyColumnUsingBackgroundMigrationJob',
+ table_name, :id,
+ :id, :data,
+ batch_size: 100,
+ job_interval: 5.minutes) # job_interval is skipped when testing
+ described_class.new(result_dir: result_dir, connection: connection).run_jobs(for_duration: 1.minute)
+ unmigrated_row_count = define_batchable_model(table_name).where('id != data').count
+
+ expect(unmigrated_row_count).to eq(0)
+ end
+ end
+
+ context 'with jobs to run' do
+ let(:migration_name) { 'TestBackgroundMigration' }
+
+ before do
+ queue_batched_background_migration(migration_name, table_name, :id, job_interval: 5.minutes, batch_size: 100)
+ end
+
+ it 'samples jobs' do
+ calls = []
+ define_background_migration(migration_name) do |*args|
+ calls << args
+ end
+
+ described_class.new(result_dir: result_dir, connection: connection).run_jobs(for_duration: 3.minutes)
+
+ expect(calls.count).to eq(10) # 1000 rows / batch size 100 = 10
+ end
+
+ context 'with multiple jobs to run' do
+ it 'runs all jobs created within the last 48 hours' do
+ old_migration = define_background_migration(migration_name)
+
+ travel 3.days
+
+ new_migration = define_background_migration('NewMigration') { travel 1.second }
+ queue_batched_background_migration('NewMigration', table_name, :id,
+ job_interval: 5.minutes,
+ batch_size: 10,
+ sub_batch_size: 5)
+
+ other_new_migration = define_background_migration('NewMigration2') { travel 2.seconds }
+ queue_batched_background_migration('NewMigration2', table_name, :id,
+ job_interval: 5.minutes,
+ batch_size: 10,
+ sub_batch_size: 5)
+
+ expect_migration_runs(new_migration => 3, other_new_migration => 2, old_migration => 0) do
+ described_class.new(result_dir: result_dir, connection: connection).run_jobs(for_duration: 5.seconds)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/database/partitioning_migration_helpers/index_helpers_spec.rb b/spec/lib/gitlab/database/partitioning_migration_helpers/index_helpers_spec.rb
index 8ab3816529b..edb8ae36c45 100644
--- a/spec/lib/gitlab/database/partitioning_migration_helpers/index_helpers_spec.rb
+++ b/spec/lib/gitlab/database/partitioning_migration_helpers/index_helpers_spec.rb
@@ -54,7 +54,7 @@ RSpec.describe Gitlab::Database::PartitioningMigrationHelpers::IndexHelpers do
expect_add_concurrent_index_and_call_original(partition2_identifier, column_name, partition2_index)
expect(migration).to receive(:with_lock_retries).ordered.and_yield
- expect(migration).to receive(:add_index).with(table_name, column_name, name: index_name).ordered.and_call_original
+ expect(migration).to receive(:add_index).with(table_name, column_name, { name: index_name }).ordered.and_call_original
migration.add_concurrent_partitioned_index(table_name, column_name, name: index_name)
@@ -64,7 +64,7 @@ RSpec.describe Gitlab::Database::PartitioningMigrationHelpers::IndexHelpers do
end
def expect_add_concurrent_index_and_call_original(table, column, index)
- expect(migration).to receive(:add_concurrent_index).ordered.with(table, column, name: index)
+ expect(migration).to receive(:add_concurrent_index).ordered.with(table, column, { name: index })
.and_wrap_original { |_, table, column, options| connection.add_index(table, column, **options) }
end
end
@@ -90,13 +90,13 @@ RSpec.describe Gitlab::Database::PartitioningMigrationHelpers::IndexHelpers do
it 'forwards them to the index helper methods', :aggregate_failures do
expect(migration).to receive(:add_concurrent_index)
- .with(partition1_identifier, column_name, name: partition1_index, where: 'x > 0', unique: true)
+ .with(partition1_identifier, column_name, { name: partition1_index, where: 'x > 0', unique: true })
expect(migration).to receive(:add_index)
- .with(table_name, column_name, name: index_name, where: 'x > 0', unique: true)
+ .with(table_name, column_name, { name: index_name, where: 'x > 0', unique: true })
migration.add_concurrent_partitioned_index(table_name, column_name,
- name: index_name, where: 'x > 0', unique: true)
+ { name: index_name, where: 'x > 0', unique: true })
end
end
diff --git a/spec/lib/gitlab/database/query_analyzer_spec.rb b/spec/lib/gitlab/database/query_analyzer_spec.rb
index 3b4cbc79de2..0b849063562 100644
--- a/spec/lib/gitlab/database/query_analyzer_spec.rb
+++ b/spec/lib/gitlab/database/query_analyzer_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
RSpec.describe Gitlab::Database::QueryAnalyzer, query_analyzers: false do
let(:analyzer) { double(:query_analyzer) }
- let(:user_analyzer) { double(:query_analyzer) }
+ let(:user_analyzer) { double(:user_query_analyzer) }
let(:disabled_analyzer) { double(:disabled_query_analyzer) }
before do
@@ -49,14 +49,36 @@ RSpec.describe Gitlab::Database::QueryAnalyzer, query_analyzers: false do
end
end
- it 'does not evaluate enabled? again do yield block' do
- expect(analyzer).not_to receive(:enabled?)
+ it 'does initialize analyzer only once' do
+ expect(analyzer).to receive(:enabled?).once
+ expect(analyzer).to receive(:begin!).once
+ expect(analyzer).to receive(:end!).once
expect { |b| described_class.instance.within(&b) }.to yield_control
end
- it 'raises exception when trying to re-define analyzers' do
- expect { |b| described_class.instance.within([user_analyzer], &b) }.to raise_error /Query analyzers are already defined, cannot re-define them/
+ it 'does initialize user analyzer when enabled' do
+ expect(user_analyzer).to receive(:enabled?).and_return(true)
+ expect(user_analyzer).to receive(:begin!)
+ expect(user_analyzer).to receive(:end!)
+
+ expect { |b| described_class.instance.within([user_analyzer], &b) }.to yield_control
+ end
+
+ it 'does initialize user analyzer only once' do
+ expect(user_analyzer).to receive(:enabled?).and_return(false, true)
+ expect(user_analyzer).to receive(:begin!).once
+ expect(user_analyzer).to receive(:end!).once
+
+ expect { |b| described_class.instance.within([user_analyzer, user_analyzer, user_analyzer], &b) }.to yield_control
+ end
+
+ it 'does not initializer user analyzer when disabled' do
+ expect(user_analyzer).to receive(:enabled?).and_return(false)
+ expect(user_analyzer).not_to receive(:begin!)
+ expect(user_analyzer).not_to receive(:end!)
+
+ expect { |b| described_class.instance.within([user_analyzer], &b) }.to yield_control
end
end
@@ -162,7 +184,7 @@ RSpec.describe Gitlab::Database::QueryAnalyzer, query_analyzers: false do
def process_sql(sql)
described_class.instance.within do
ApplicationRecord.load_balancer.read_write do |connection|
- described_class.instance.process_sql(sql, connection)
+ described_class.instance.send(:process_sql, sql, connection)
end
end
end
diff --git a/spec/lib/gitlab/database/query_analyzers/gitlab_schemas_metrics_spec.rb b/spec/lib/gitlab/database/query_analyzers/gitlab_schemas_metrics_spec.rb
index b8c1ecd9089..0d687db0f96 100644
--- a/spec/lib/gitlab/database/query_analyzers/gitlab_schemas_metrics_spec.rb
+++ b/spec/lib/gitlab/database/query_analyzers/gitlab_schemas_metrics_spec.rb
@@ -140,7 +140,7 @@ RSpec.describe Gitlab::Database::QueryAnalyzers::GitlabSchemasMetrics, query_ana
def process_sql(model, sql)
Gitlab::Database::QueryAnalyzer.instance.within do
# Skip load balancer and retrieve connection assigned to model
- Gitlab::Database::QueryAnalyzer.instance.process_sql(sql, model.retrieve_connection)
+ Gitlab::Database::QueryAnalyzer.instance.send(:process_sql, sql, model.retrieve_connection)
end
end
end
diff --git a/spec/lib/gitlab/database/query_analyzers/gitlab_schemas_validate_connection_spec.rb b/spec/lib/gitlab/database/query_analyzers/gitlab_schemas_validate_connection_spec.rb
new file mode 100644
index 00000000000..5e8afc0102e
--- /dev/null
+++ b/spec/lib/gitlab/database/query_analyzers/gitlab_schemas_validate_connection_spec.rb
@@ -0,0 +1,70 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Database::QueryAnalyzers::GitlabSchemasValidateConnection, query_analyzers: false do
+ let(:analyzer) { described_class }
+
+ context 'properly observes all queries', :request_store do
+ using RSpec::Parameterized::TableSyntax
+
+ where do
+ {
+ "for simple query observes schema correctly" => {
+ model: ApplicationRecord,
+ sql: "SELECT 1 FROM projects",
+ expect_error: nil,
+ setup: nil
+ },
+ "for query accessing gitlab_ci and gitlab_main" => {
+ model: ApplicationRecord,
+ sql: "SELECT 1 FROM projects LEFT JOIN ci_builds ON ci_builds.project_id=projects.id",
+ expect_error: /The query tried to access \["projects", "ci_builds"\]/,
+ setup: -> (_) { skip_if_multiple_databases_not_setup }
+ },
+ "for query accessing gitlab_ci and gitlab_main the gitlab_schemas is always ordered" => {
+ model: ApplicationRecord,
+ sql: "SELECT 1 FROM ci_builds LEFT JOIN projects ON ci_builds.project_id=projects.id",
+ expect_error: /The query tried to access \["ci_builds", "projects"\]/,
+ setup: -> (_) { skip_if_multiple_databases_not_setup }
+ },
+ "for query accessing main table from CI database" => {
+ model: Ci::ApplicationRecord,
+ sql: "SELECT 1 FROM projects",
+ expect_error: /The query tried to access \["projects"\]/,
+ setup: -> (_) { skip_if_multiple_databases_not_setup }
+ },
+ "for query accessing CI database" => {
+ model: Ci::ApplicationRecord,
+ sql: "SELECT 1 FROM ci_builds",
+ expect_error: nil
+ },
+ "for query accessing CI table from main database" => {
+ model: ::ApplicationRecord,
+ sql: "SELECT 1 FROM ci_builds",
+ expect_error: /The query tried to access \["ci_builds"\]/,
+ setup: -> (_) { skip_if_multiple_databases_not_setup }
+ }
+ }
+ end
+
+ with_them do
+ it do
+ instance_eval(&setup) if setup
+
+ if expect_error
+ expect { process_sql(model, sql) }.to raise_error(expect_error)
+ else
+ expect { process_sql(model, sql) }.not_to raise_error
+ end
+ end
+ end
+ end
+
+ def process_sql(model, sql)
+ Gitlab::Database::QueryAnalyzer.instance.within([analyzer]) do
+ # Skip load balancer and retrieve connection assigned to model
+ Gitlab::Database::QueryAnalyzer.instance.send(:process_sql, sql, model.retrieve_connection)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/database/query_analyzers/restrict_allowed_schemas_spec.rb b/spec/lib/gitlab/database/query_analyzers/restrict_allowed_schemas_spec.rb
index a2c7916fa01..261bef58bb6 100644
--- a/spec/lib/gitlab/database/query_analyzers/restrict_allowed_schemas_spec.rb
+++ b/spec/lib/gitlab/database/query_analyzers/restrict_allowed_schemas_spec.rb
@@ -155,7 +155,7 @@ RSpec.describe Gitlab::Database::QueryAnalyzers::RestrictAllowedSchemas, query_a
yield if block_given?
# Skip load balancer and retrieve connection assigned to model
- Gitlab::Database::QueryAnalyzer.instance.process_sql(sql, model.retrieve_connection)
+ Gitlab::Database::QueryAnalyzer.instance.send(:process_sql, sql, model.retrieve_connection)
end
end
end
diff --git a/spec/lib/gitlab/database/shared_model_spec.rb b/spec/lib/gitlab/database/shared_model_spec.rb
index 54af4a0c4dc..574111f4c01 100644
--- a/spec/lib/gitlab/database/shared_model_spec.rb
+++ b/spec/lib/gitlab/database/shared_model_spec.rb
@@ -51,7 +51,7 @@ RSpec.describe Gitlab::Database::SharedModel do
expect do
described_class.using_connection(second_connection) {}
- end.to raise_error(/cannot nest connection overrides/)
+ end.to raise_error(/Cannot change connection for Gitlab::Database::SharedModel/)
expect(described_class.connection).to be(new_connection)
end
diff --git a/spec/lib/gitlab/database_importers/work_items/base_type_importer_spec.rb b/spec/lib/gitlab/database_importers/work_items/base_type_importer_spec.rb
index 8c3d372cc55..d044170dc75 100644
--- a/spec/lib/gitlab/database_importers/work_items/base_type_importer_spec.rb
+++ b/spec/lib/gitlab/database_importers/work_items/base_type_importer_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe Gitlab::DatabaseImporters::WorkItems::BaseTypeImporter do
- subject { described_class.import }
+ subject { described_class.upsert_types }
it_behaves_like 'work item base types importer'
end
diff --git a/spec/lib/gitlab/database_spec.rb b/spec/lib/gitlab/database_spec.rb
index ac8616f84a7..23f4f0e7089 100644
--- a/spec/lib/gitlab/database_spec.rb
+++ b/spec/lib/gitlab/database_spec.rb
@@ -70,40 +70,6 @@ RSpec.describe Gitlab::Database do
end
end
- describe '.main_database?' do
- using RSpec::Parameterized::TableSyntax
-
- where(:database_name, :result) do
- :main | true
- 'main' | true
- :ci | false
- 'ci' | false
- :archive | false
- 'archive' | false
- end
-
- with_them do
- it { expect(described_class.main_database?(database_name)).to eq(result) }
- end
- end
-
- describe '.ci_database?' do
- using RSpec::Parameterized::TableSyntax
-
- where(:database_name, :result) do
- :main | false
- 'main' | false
- :ci | true
- 'ci' | true
- :archive | false
- 'archive' | false
- end
-
- with_them do
- it { expect(described_class.ci_database?(database_name)).to eq(result) }
- end
- end
-
describe '.check_for_non_superuser' do
subject { described_class.check_for_non_superuser }
diff --git a/spec/lib/gitlab/diff/file_spec.rb b/spec/lib/gitlab/diff/file_spec.rb
index 0d7a183bb11..b7262629e0a 100644
--- a/spec/lib/gitlab/diff/file_spec.rb
+++ b/spec/lib/gitlab/diff/file_spec.rb
@@ -99,6 +99,22 @@ RSpec.describe Gitlab::Diff::File do
end
end
+ describe '#ipynb?' do
+ context 'is ipynb' do
+ let(:commit) { project.commit("532c837") }
+
+ it 'is true' do
+ expect(diff_file.ipynb?).to be_truthy
+ end
+ end
+
+ context 'is not ipynb' do
+ it 'is false' do
+ expect(diff_file.ipynb?).to be_falsey
+ end
+ end
+ end
+
describe '#has_renderable?' do
context 'file is ipynb' do
let(:commit) { project.commit("532c837") }
diff --git a/spec/lib/gitlab/diff/rendered/notebook/diff_file_spec.rb b/spec/lib/gitlab/diff/rendered/notebook/diff_file_spec.rb
index 89b284feee0..1b74e24bf81 100644
--- a/spec/lib/gitlab/diff/rendered/notebook/diff_file_spec.rb
+++ b/spec/lib/gitlab/diff/rendered/notebook/diff_file_spec.rb
@@ -72,7 +72,7 @@ RSpec.describe Gitlab::Diff::Rendered::Notebook::DiffFile do
end
it 'falls back to nil on timeout' do
- allow(Gitlab::ErrorTracking).to receive(:track_and_raise_for_dev_exception)
+ expect(Gitlab::ErrorTracking).to receive(:log_exception)
expect(Timeout).to receive(:timeout).and_raise(Timeout::Error)
expect(nb_file.diff).to be_nil
@@ -101,6 +101,22 @@ RSpec.describe Gitlab::Diff::Rendered::Notebook::DiffFile do
expect(nb_file.has_renderable?).to be_truthy
end
end
+
+ context 'when old blob file is truncated' do
+ it 'is false' do
+ allow(source.old_blob).to receive(:truncated?).and_return(true)
+
+ expect(nb_file.has_renderable?).to be_falsey
+ end
+ end
+
+ context 'when new blob file is truncated' do
+ it 'is false' do
+ allow(source.new_blob).to receive(:truncated?).and_return(true)
+
+ expect(nb_file.has_renderable?).to be_falsey
+ end
+ end
end
describe '#highlighted_diff_lines?' do
@@ -125,5 +141,9 @@ RSpec.describe Gitlab::Diff::Rendered::Notebook::DiffFile do
expect(nb_file.highlighted_diff_lines[12].old_pos).to eq(18)
end
end
+
+ it 'computes de first line where the remove would appear' do
+ expect(nb_file.highlighted_diff_lines.map(&:text).join('')).to include('[Hidden Image Output]')
+ end
end
end
diff --git a/spec/lib/gitlab/doctor/secrets_spec.rb b/spec/lib/gitlab/doctor/secrets_spec.rb
index f95a7eb1492..efdd6cc1199 100644
--- a/spec/lib/gitlab/doctor/secrets_spec.rb
+++ b/spec/lib/gitlab/doctor/secrets_spec.rb
@@ -7,10 +7,25 @@ RSpec.describe Gitlab::Doctor::Secrets do
let!(:group) { create(:group, runners_token: "test") }
let!(:project) { create(:project) }
let!(:grafana_integration) { create(:grafana_integration, project: project, token: "test") }
+ let!(:integration) { create(:integration, project: project, properties: { test_key: "test_value" }) }
let(:logger) { double(:logger).as_null_object }
subject { described_class.new(logger).run! }
+ before do
+ allow(Gitlab::Runtime).to receive(:rake?).and_return(true)
+ end
+
+ context 'when not ran in a Rake runtime' do
+ before do
+ allow(Gitlab::Runtime).to receive(:rake?).and_return(false)
+ end
+
+ it 'raises an error' do
+ expect { subject }.to raise_error(StandardError, 'can only be used in a Rake environment')
+ end
+ end
+
context 'when encrypted attributes are properly set' do
it 'detects decryptable secrets' do
expect(logger).to receive(:info).with(/User failures: 0/)
@@ -42,6 +57,25 @@ RSpec.describe Gitlab::Doctor::Secrets do
end
end
+ context 'when initializers attempt to use encrypted data' do
+ it 'skips the initializers and detects bad data' do
+ integration.encrypted_properties = "invalid"
+ integration.save!
+
+ expect(logger).to receive(:info).with(/Integration failures: 1/)
+
+ subject
+ end
+
+ it 'resets the initializers after the task runs' do
+ subject
+
+ expect(integration).to receive(:initialize_properties)
+
+ integration.run_callbacks(:initialize)
+ end
+ end
+
context 'when GrafanaIntegration token is set via private method' do
it 'can access GrafanaIntegration token value' do
expect(logger).to receive(:info).with(/GrafanaIntegration failures: 0/)
diff --git a/spec/lib/gitlab/email/handler/create_note_handler_spec.rb b/spec/lib/gitlab/email/handler/create_note_handler_spec.rb
index 59b87c5d8e7..9ff395070ea 100644
--- a/spec/lib/gitlab/email/handler/create_note_handler_spec.rb
+++ b/spec/lib/gitlab/email/handler/create_note_handler_spec.rb
@@ -62,7 +62,7 @@ RSpec.describe Gitlab::Email::Handler::CreateNoteHandler do
end
it 'does not raise a UserNotFoundError' do
- expect { receiver.execute }.not_to raise_error(Gitlab::Email::UserNotFoundError)
+ expect { receiver.execute }.not_to raise_error
end
end
end
@@ -71,7 +71,7 @@ RSpec.describe Gitlab::Email::Handler::CreateNoteHandler do
let(:original_recipient) { User.support_bot }
it 'does not raise a UserNotFoundError' do
- expect { receiver.execute }.not_to raise_error(Gitlab::Email::UserNotFoundError)
+ expect { receiver.execute }.not_to raise_error
end
end
end
diff --git a/spec/lib/gitlab/email/message/build_ios_app_guide_spec.rb b/spec/lib/gitlab/email/message/build_ios_app_guide_spec.rb
new file mode 100644
index 00000000000..3089f955252
--- /dev/null
+++ b/spec/lib/gitlab/email/message/build_ios_app_guide_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Email::Message::BuildIosAppGuide do
+ subject(:message) { described_class.new }
+
+ before do
+ allow(Gitlab).to receive(:com?) { true }
+ end
+
+ it 'contains the correct message', :aggregate_failures do
+ expect(message.subject_line).to eq 'Get set up to build for iOS'
+ expect(message.title).to eq "Building for iOS? We've got you covered."
+ expect(message.body_line1).to eq "Want to get your iOS app up and running, including " \
+ "publishing all the way to TestFlight? Follow our guide to set up GitLab and fastlane to publish iOS apps to " \
+ "the App Store."
+ expect(message.cta_text).to eq 'Learn how to build for iOS'
+ expect(message.cta2_text).to eq 'Watch iOS building in action.'
+ expect(message.logo_path).to eq 'mailers/in_product_marketing/create-0.png'
+ expect(message.unsubscribe).to include('%tag_unsubscribe_url%')
+ end
+end
diff --git a/spec/lib/gitlab/email/message/in_product_marketing/base_spec.rb b/spec/lib/gitlab/email/message/in_product_marketing/base_spec.rb
index 8bd873cf008..dfa18c27d5e 100644
--- a/spec/lib/gitlab/email/message/in_product_marketing/base_spec.rb
+++ b/spec/lib/gitlab/email/message/in_product_marketing/base_spec.rb
@@ -24,7 +24,7 @@ RSpec.describe Gitlab::Email::Message::InProductMarketing::Base do
let(:series) { 0 }
it 'does not raise error' do
- expect { subject }.not_to raise_error(ArgumentError)
+ expect { subject }.not_to raise_error
end
end
end
diff --git a/spec/lib/gitlab/email/message/in_product_marketing/helper_spec.rb b/spec/lib/gitlab/email/message/in_product_marketing/helper_spec.rb
new file mode 100644
index 00000000000..3c0d83d0f9e
--- /dev/null
+++ b/spec/lib/gitlab/email/message/in_product_marketing/helper_spec.rb
@@ -0,0 +1,79 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Email::Message::InProductMarketing::Helper do
+ describe 'unsubscribe_message' do
+ include Gitlab::Routing
+
+ let(:dummy_class_with_helper) do
+ Class.new do
+ include Gitlab::Email::Message::InProductMarketing::Helper
+ include Gitlab::Routing
+
+ def initialize(format = :html)
+ @format = format
+ end
+
+ def default_url_options
+ {}
+ end
+
+ attr_accessor :format
+ end
+ end
+
+ let(:format) { :html }
+
+ subject(:class_with_helper) { dummy_class_with_helper.new(format) }
+
+ context 'gitlab.com' do
+ before do
+ allow(Gitlab).to receive(:com?) { true }
+ end
+
+ context 'format is HTML' do
+ it 'returns the correct HTML' do
+ message = "If you no longer wish to receive marketing emails from us, " \
+ "you may <a href=\"%tag_unsubscribe_url%\">unsubscribe</a> at any time."
+ expect(class_with_helper.unsubscribe_message).to match message
+ end
+ end
+
+ context 'format is text' do
+ let(:format) { :text }
+
+ it 'returns the correct string' do
+ message = "If you no longer wish to receive marketing emails from us, " \
+ "you may unsubscribe (%tag_unsubscribe_url%) at any time."
+ expect(class_with_helper.unsubscribe_message.squish).to match message
+ end
+ end
+ end
+
+ context 'self-managed' do
+ context 'format is HTML' do
+ it 'returns the correct HTML' do
+ preferences_link = "http://example.com/preferences"
+ message = "To opt out of these onboarding emails, " \
+ "<a href=\"#{profile_notifications_url}\">unsubscribe</a>. " \
+ "If you don't want to receive marketing emails directly from GitLab, #{preferences_link}."
+ expect(class_with_helper.unsubscribe_message(preferences_link))
+ .to match message
+ end
+ end
+
+ context 'format is text' do
+ let(:format) { :text }
+
+ it 'returns the correct string' do
+ preferences_link = "http://example.com/preferences"
+ message = "To opt out of these onboarding emails, " \
+ "unsubscribe (#{profile_notifications_url}). " \
+ "If you don't want to receive marketing emails directly from GitLab, #{preferences_link}."
+ expect(class_with_helper.unsubscribe_message(preferences_link).squish).to match message
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/experiment/rollout/feature_spec.rb b/spec/lib/gitlab/experiment/rollout/feature_spec.rb
index 82603e6fe0f..a66f4fea207 100644
--- a/spec/lib/gitlab/experiment/rollout/feature_spec.rb
+++ b/spec/lib/gitlab/experiment/rollout/feature_spec.rb
@@ -53,8 +53,7 @@ RSpec.describe Gitlab::Experiment::Rollout::Feature, :experiment do
expect(Feature).to receive(:enabled?).with(
'namespaced_stub',
subject,
- type: :experiment,
- default_enabled: :yaml
+ type: :experiment
).and_return(false)
expect(subject.execute_assignment).to be_nil
diff --git a/spec/lib/gitlab/experimentation/controller_concern_spec.rb b/spec/lib/gitlab/experimentation/controller_concern_spec.rb
index 435a0d56301..799884d7a74 100644
--- a/spec/lib/gitlab/experimentation/controller_concern_spec.rb
+++ b/spec/lib/gitlab/experimentation/controller_concern_spec.rb
@@ -274,7 +274,7 @@ RSpec.describe Gitlab::Experimentation::ControllerConcern, type: :controller do
action: 'start',
property: 'control_group',
value: 1,
- label: Digest::MD5.hexdigest('abc'),
+ label: Digest::SHA256.hexdigest('abc'),
user: user
)
end
@@ -289,7 +289,7 @@ RSpec.describe Gitlab::Experimentation::ControllerConcern, type: :controller do
action: 'start',
property: 'control_group',
value: 1,
- label: Digest::MD5.hexdigest('somestring'),
+ label: Digest::SHA256.hexdigest('somestring'),
user: user
)
end
diff --git a/spec/lib/gitlab/git/diff_spec.rb b/spec/lib/gitlab/git/diff_spec.rb
index 46f544797bb..2c931a999f1 100644
--- a/spec/lib/gitlab/git/diff_spec.rb
+++ b/spec/lib/gitlab/git/diff_spec.rb
@@ -165,17 +165,21 @@ EOT
context 'when diff contains invalid characters' do
let(:bad_string) { [0xae].pack("C*") }
let(:bad_string_two) { [0x89].pack("C*") }
+ let(:bad_string_three) { "@@ -1,5 +1,6 @@\n \xFF\xFE#\x00l\x00a\x00n\x00g\x00u\x00" }
let(:diff) { described_class.new(@raw_diff_hash.merge({ diff: bad_string })) }
let(:diff_two) { described_class.new(@raw_diff_hash.merge({ diff: bad_string_two })) }
+ let(:diff_three) { described_class.new(@raw_diff_hash.merge({ diff: bad_string_three })) }
context 'when replace_invalid_utf8_chars is true' do
it 'will convert invalid characters and not cause an encoding error' do
expect(diff.diff).to include(Gitlab::EncodingHelper::UNICODE_REPLACEMENT_CHARACTER)
expect(diff_two.diff).to include(Gitlab::EncodingHelper::UNICODE_REPLACEMENT_CHARACTER)
+ expect(diff_three.diff).to include(Gitlab::EncodingHelper::UNICODE_REPLACEMENT_CHARACTER)
- expect { Oj.dump(diff) }.not_to raise_error(EncodingError)
- expect { Oj.dump(diff_two) }.not_to raise_error(EncodingError)
+ expect { Oj.dump(diff) }.not_to raise_error
+ expect { Oj.dump(diff_two) }.not_to raise_error
+ expect { Oj.dump(diff_three) }.not_to raise_error
end
context 'when the diff is binary' do
diff --git a/spec/lib/gitlab/git_access_spec.rb b/spec/lib/gitlab/git_access_spec.rb
index d6ef1836ad9..e628a06a542 100644
--- a/spec/lib/gitlab/git_access_spec.rb
+++ b/spec/lib/gitlab/git_access_spec.rb
@@ -228,6 +228,15 @@ RSpec.describe Gitlab::GitAccess do
project.add_maintainer(user)
end
+ context 'key is expired' do
+ let(:actor) { create(:rsa_key_2048, :expired) }
+
+ it 'does not allow expired keys', :aggregate_failures do
+ expect { pull_access_check }.to raise_forbidden('Your SSH key has expired.')
+ expect { push_access_check }.to raise_forbidden('Your SSH key has expired.')
+ end
+ end
+
context 'key is too small' do
before do
stub_application_setting(rsa_key_restriction: 4096)
diff --git a/spec/lib/gitlab/gitaly_client/commit_service_spec.rb b/spec/lib/gitlab/gitaly_client/commit_service_spec.rb
index 50a0f20e775..92860c9232f 100644
--- a/spec/lib/gitlab/gitaly_client/commit_service_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/commit_service_spec.rb
@@ -339,11 +339,18 @@ RSpec.describe Gitlab::GitalyClient::CommitService do
describe '#list_new_commits' do
let(:revisions) { [revision] }
let(:gitaly_commits) { create_list(:gitaly_commit, 3) }
- let(:commits) { gitaly_commits.map { |c| Gitlab::Git::Commit.new(repository, c) }}
+ let(:expected_commits) { gitaly_commits.map { |c| Gitlab::Git::Commit.new(repository, c) }}
+ let(:filter_quarantined_commits) { false }
- subject { client.list_new_commits(revisions, allow_quarantine: allow_quarantine) }
+ subject do
+ client.list_new_commits(revisions, allow_quarantine: allow_quarantine)
+ end
shared_examples 'a #list_all_commits message' do
+ before do
+ stub_feature_flags(filter_quarantined_commits: filter_quarantined_commits)
+ end
+
it 'sends a list_all_commits message' do
expected_repository = repository.gitaly_repository.dup
expected_repository.git_alternate_object_directories = Google::Protobuf::RepeatedField.new(:string)
@@ -352,9 +359,33 @@ RSpec.describe Gitlab::GitalyClient::CommitService do
expect(service).to receive(:list_all_commits)
.with(gitaly_request_with_params(repository: expected_repository), kind_of(Hash))
.and_return([Gitaly::ListAllCommitsResponse.new(commits: gitaly_commits)])
+
+ if filter_quarantined_commits
+ # The object directory of the repository must not be set so that we
+ # don't use the quarantine directory.
+ objects_exist_repo = repository.gitaly_repository.dup
+ objects_exist_repo.git_object_directory = ""
+
+ # The first request contains the repository, the second request the
+ # commit IDs we want to check for existence.
+ objects_exist_request = [
+ gitaly_request_with_params(repository: objects_exist_repo),
+ gitaly_request_with_params(revisions: gitaly_commits.map(&:id))
+ ]
+
+ objects_exist_response = Gitaly::CheckObjectsExistResponse.new(revisions: revision_existence.map do
+ |rev, exists| Gitaly::CheckObjectsExistResponse::RevisionExistence.new(name: rev, exists: exists)
+ end)
+
+ expect(service).to receive(:check_objects_exist)
+ .with(objects_exist_request, kind_of(Hash))
+ .and_return([objects_exist_response])
+ else
+ expect(service).not_to receive(:check_objects_exist)
+ end
end
- expect(subject).to eq(commits)
+ expect(subject).to eq(expected_commits)
end
end
@@ -366,7 +397,7 @@ RSpec.describe Gitlab::GitalyClient::CommitService do
.and_return([Gitaly::ListCommitsResponse.new(commits: gitaly_commits)])
end
- expect(subject).to eq(commits)
+ expect(subject).to eq(expected_commits)
end
end
@@ -390,7 +421,40 @@ RSpec.describe Gitlab::GitalyClient::CommitService do
context 'with allowed quarantine' do
let(:allow_quarantine) { true }
- it_behaves_like 'a #list_all_commits message'
+ context 'without commit filtering' do
+ it_behaves_like 'a #list_all_commits message'
+ end
+
+ context 'with commit filtering' do
+ let(:filter_quarantined_commits) { true }
+
+ context 'reject commits which exist in target repository' do
+ let(:revision_existence) { gitaly_commits.to_h { |c| [c.id, true] } }
+ let(:expected_commits) { [] }
+
+ it_behaves_like 'a #list_all_commits message'
+ end
+
+ context 'keep commits which do not exist in target repository' do
+ let(:revision_existence) { gitaly_commits.to_h { |c| [c.id, false] } }
+
+ it_behaves_like 'a #list_all_commits message'
+ end
+
+ context 'mixed existing and nonexisting commits' do
+ let(:revision_existence) do
+ {
+ gitaly_commits[0].id => true,
+ gitaly_commits[1].id => false,
+ gitaly_commits[2].id => true
+ }
+ end
+
+ let(:expected_commits) { [Gitlab::Git::Commit.new(repository, gitaly_commits[1])] }
+
+ it_behaves_like 'a #list_all_commits message'
+ end
+ end
end
context 'with disallowed quarantine' do
@@ -493,6 +557,61 @@ RSpec.describe Gitlab::GitalyClient::CommitService do
end
end
+ describe '#object_existence_map' do
+ shared_examples 'a CheckObjectsExistRequest' do
+ before do
+ ::Gitlab::GitalyClient.clear_stubs!
+ end
+
+ it 'returns expected results' do
+ expect_next_instance_of(Gitaly::CommitService::Stub) do |service|
+ expect(service)
+ .to receive(:check_objects_exist)
+ .and_call_original
+ end
+
+ expect(client.object_existence_map(revisions.keys)).to eq(revisions)
+ end
+ end
+
+ context 'with empty request' do
+ let(:revisions) { {} }
+
+ it_behaves_like 'a CheckObjectsExistRequest'
+ end
+
+ context 'when revision exists' do
+ let(:revisions) { { 'refs/heads/master' => true } }
+
+ it_behaves_like 'a CheckObjectsExistRequest'
+ end
+
+ context 'when revision does not exist' do
+ let(:revisions) { { 'refs/does/not/exist' => false } }
+
+ it_behaves_like 'a CheckObjectsExistRequest'
+ end
+
+ context 'when request contains mixed revisions' do
+ let(:revisions) do
+ {
+ "refs/heads/master" => true,
+ "refs/does/not/exist" => false
+ }
+ end
+
+ it_behaves_like 'a CheckObjectsExistRequest'
+ end
+
+ context 'when requesting many revisions' do
+ let(:revisions) do
+ Array(1..1234).to_h { |i| ["refs/heads/#{i}", false] }
+ end
+
+ it_behaves_like 'a CheckObjectsExistRequest'
+ end
+ end
+
describe '#commits_by_message' do
shared_examples 'a CommitsByMessageRequest' do
let(:commits) { create_list(:gitaly_commit, 2) }
diff --git a/spec/lib/gitlab/github_import/importer/diff_note_importer_spec.rb b/spec/lib/gitlab/github_import/importer/diff_note_importer_spec.rb
index 321ad7d3238..8eeb2332131 100644
--- a/spec/lib/gitlab/github_import/importer/diff_note_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/diff_note_importer_spec.rb
@@ -181,8 +181,10 @@ RSpec.describe Gitlab::GithubImport::Importer::DiffNoteImporter, :aggregate_fail
expect(Gitlab::GithubImport::Logger)
.to receive(:warn)
.with(
- message: "Validation failed: Line code can't be blank, Line code must be a valid line code, Position is incomplete",
- 'error.class': 'Gitlab::GithubImport::Importer::DiffNoteImporter::DiffNoteCreationError'
+ {
+ message: "Validation failed: Line code can't be blank, Line code must be a valid line code, Position is incomplete",
+ 'error.class': 'Gitlab::GithubImport::Importer::DiffNoteImporter::DiffNoteCreationError'
+ }
)
expect { subject.execute }
@@ -204,8 +206,10 @@ RSpec.describe Gitlab::GithubImport::Importer::DiffNoteImporter, :aggregate_fail
expect(Gitlab::GithubImport::Logger)
.to receive(:warn)
.with(
- message: 'Failed to create diff note file',
- 'error.class': 'DiffNote::NoteDiffFileCreationError'
+ {
+ message: 'Failed to create diff note file',
+ 'error.class': 'DiffNote::NoteDiffFileCreationError'
+ }
)
expect { subject.execute }
diff --git a/spec/lib/gitlab/github_import/importer/pull_requests_reviews_importer_spec.rb b/spec/lib/gitlab/github_import/importer/pull_requests_reviews_importer_spec.rb
index c5fa67e50aa..0eb86feb040 100644
--- a/spec/lib/gitlab/github_import/importer/pull_requests_reviews_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/pull_requests_reviews_importer_spec.rb
@@ -48,7 +48,7 @@ RSpec.describe Gitlab::GithubImport::Importer::PullRequestsReviewsImporter do
expect(client)
.to receive(:each_page)
.exactly(:once) # ensure to be cached on the second call
- .with(:pull_request_reviews, 'github/repo', merge_request.iid, page: 1)
+ .with(:pull_request_reviews, 'github/repo', merge_request.iid, { page: 1 })
.and_yield(page)
expect { |b| subject.each_object_to_import(&b) }
@@ -67,7 +67,7 @@ RSpec.describe Gitlab::GithubImport::Importer::PullRequestsReviewsImporter do
expect(client)
.to receive(:each_page)
.exactly(:once) # ensure to be cached on the second call
- .with(:pull_request_reviews, 'github/repo', merge_request.iid, page: 2)
+ .with(:pull_request_reviews, 'github/repo', merge_request.iid, { page: 2 })
subject.each_object_to_import {}
end
diff --git a/spec/lib/gitlab/github_import/importer/single_endpoint_diff_notes_importer_spec.rb b/spec/lib/gitlab/github_import/importer/single_endpoint_diff_notes_importer_spec.rb
index 8c71d7d0ed7..471302cb31b 100644
--- a/spec/lib/gitlab/github_import/importer/single_endpoint_diff_notes_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/single_endpoint_diff_notes_importer_spec.rb
@@ -33,7 +33,7 @@ RSpec.describe Gitlab::GithubImport::Importer::SingleEndpointDiffNotesImporter d
expect(client)
.to receive(:each_page)
.exactly(:once) # ensure to be cached on the second call
- .with(:pull_request_comments, 'github/repo', merge_request.iid, page: 1)
+ .with(:pull_request_comments, 'github/repo', merge_request.iid, { page: 1 })
.and_yield(page)
expect { |b| subject.each_object_to_import(&b) }.to yield_with_args(note)
@@ -56,7 +56,7 @@ RSpec.describe Gitlab::GithubImport::Importer::SingleEndpointDiffNotesImporter d
expect(client)
.to receive(:each_page)
.exactly(:once) # ensure to be cached on the second call
- .with(:pull_request_comments, 'github/repo', merge_request.iid, page: 2)
+ .with(:pull_request_comments, 'github/repo', merge_request.iid, { page: 2 })
subject.each_object_to_import {}
end
diff --git a/spec/lib/gitlab/github_import/importer/single_endpoint_issue_notes_importer_spec.rb b/spec/lib/gitlab/github_import/importer/single_endpoint_issue_notes_importer_spec.rb
index 8d8f2730880..d769f4fdcf5 100644
--- a/spec/lib/gitlab/github_import/importer/single_endpoint_issue_notes_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/single_endpoint_issue_notes_importer_spec.rb
@@ -32,7 +32,7 @@ RSpec.describe Gitlab::GithubImport::Importer::SingleEndpointIssueNotesImporter
expect(client)
.to receive(:each_page)
.exactly(:once) # ensure to be cached on the second call
- .with(:issue_comments, 'github/repo', issue.iid, page: 1)
+ .with(:issue_comments, 'github/repo', issue.iid, { page: 1 })
.and_yield(page)
expect { |b| subject.each_object_to_import(&b) }.to yield_with_args(note)
@@ -55,7 +55,7 @@ RSpec.describe Gitlab::GithubImport::Importer::SingleEndpointIssueNotesImporter
expect(client)
.to receive(:each_page)
.exactly(:once) # ensure to be cached on the second call
- .with(:issue_comments, 'github/repo', issue.iid, page: 2)
+ .with(:issue_comments, 'github/repo', issue.iid, { page: 2 })
subject.each_object_to_import {}
end
diff --git a/spec/lib/gitlab/github_import/importer/single_endpoint_merge_request_notes_importer_spec.rb b/spec/lib/gitlab/github_import/importer/single_endpoint_merge_request_notes_importer_spec.rb
index b8282212a90..1dcc466d34c 100644
--- a/spec/lib/gitlab/github_import/importer/single_endpoint_merge_request_notes_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/single_endpoint_merge_request_notes_importer_spec.rb
@@ -33,7 +33,7 @@ RSpec.describe Gitlab::GithubImport::Importer::SingleEndpointMergeRequestNotesIm
expect(client)
.to receive(:each_page)
.exactly(:once) # ensure to be cached on the second call
- .with(:issue_comments, 'github/repo', merge_request.iid, page: 1)
+ .with(:issue_comments, 'github/repo', merge_request.iid, { page: 1 })
.and_yield(page)
expect { |b| subject.each_object_to_import(&b) }.to yield_with_args(note)
@@ -56,7 +56,7 @@ RSpec.describe Gitlab::GithubImport::Importer::SingleEndpointMergeRequestNotesIm
expect(client)
.to receive(:each_page)
.exactly(:once) # ensure to be cached on the second call
- .with(:issue_comments, 'github/repo', merge_request.iid, page: 2)
+ .with(:issue_comments, 'github/repo', merge_request.iid, { page: 2 })
subject.each_object_to_import {}
end
diff --git a/spec/lib/gitlab/github_import/milestone_finder_spec.rb b/spec/lib/gitlab/github_import/milestone_finder_spec.rb
index fe8652eb5a2..e7f47d334e8 100644
--- a/spec/lib/gitlab/github_import/milestone_finder_spec.rb
+++ b/spec/lib/gitlab/github_import/milestone_finder_spec.rb
@@ -44,7 +44,7 @@ RSpec.describe Gitlab::GithubImport::MilestoneFinder, :clean_gitlab_redis_cache
it 'builds the cache of all project milestones' do
expect(Gitlab::Cache::Import::Caching)
.to receive(:write_multiple)
- .with("github-import/milestone-finder/#{project.id}/1" => milestone.id)
+ .with({ "github-import/milestone-finder/#{project.id}/1" => milestone.id })
.and_call_original
finder.build_cache
diff --git a/spec/lib/gitlab/github_import/parallel_scheduling_spec.rb b/spec/lib/gitlab/github_import/parallel_scheduling_spec.rb
index 200898f8f03..999f8ffb21e 100644
--- a/spec/lib/gitlab/github_import/parallel_scheduling_spec.rb
+++ b/spec/lib/gitlab/github_import/parallel_scheduling_spec.rb
@@ -87,19 +87,23 @@ RSpec.describe Gitlab::GithubImport::ParallelScheduling do
expect(Gitlab::GithubImport::Logger)
.to receive(:info)
.with(
- message: 'starting importer',
- parallel: false,
- project_id: project.id,
- importer: 'Class'
+ {
+ message: 'starting importer',
+ parallel: false,
+ project_id: project.id,
+ importer: 'Class'
+ }
)
expect(Gitlab::GithubImport::Logger)
.to receive(:info)
.with(
- message: 'importer finished',
- parallel: false,
- project_id: project.id,
- importer: 'Class'
+ {
+ message: 'importer finished',
+ parallel: false,
+ project_id: project.id,
+ importer: 'Class'
+ }
)
importer.execute
@@ -118,20 +122,24 @@ RSpec.describe Gitlab::GithubImport::ParallelScheduling do
expect(Gitlab::GithubImport::Logger)
.to receive(:info)
.with(
- message: 'starting importer',
- parallel: false,
- project_id: project.id,
- importer: 'Class'
+ {
+ message: 'starting importer',
+ parallel: false,
+ project_id: project.id,
+ importer: 'Class'
+ }
)
expect(Gitlab::Import::ImportFailureService)
.to receive(:track)
.with(
- project_id: project.id,
- exception: exception,
- error_source: 'MyImporter',
- fail_import: false,
- metrics: true
+ {
+ project_id: project.id,
+ exception: exception,
+ error_source: 'MyImporter',
+ fail_import: false,
+ metrics: true
+ }
).and_call_original
expect { importer.execute }
@@ -184,10 +192,12 @@ RSpec.describe Gitlab::GithubImport::ParallelScheduling do
expect(Gitlab::GithubImport::Logger)
.to receive(:info)
.with(
- message: 'starting importer',
- parallel: false,
- project_id: project.id,
- importer: 'Class'
+ {
+ message: 'starting importer',
+ parallel: false,
+ project_id: project.id,
+ importer: 'Class'
+ }
)
expect(Gitlab::Import::ImportFailureService)
@@ -290,25 +300,6 @@ RSpec.describe Gitlab::GithubImport::ParallelScheduling do
importer.parallel_import
end
end
-
- context 'when distribute_github_parallel_import feature flag is disabled' do
- before do
- stub_feature_flags(distribute_github_parallel_import: false)
- end
-
- it 'imports data in parallel' do
- expect(importer)
- .to receive(:each_object_to_import)
- .and_yield(object)
-
- expect(worker_class)
- .to receive(:perform_async)
- .with(project.id, { title: 'Foo' }, an_instance_of(String))
-
- expect(importer.parallel_import)
- .to be_an_instance_of(Gitlab::JobWaiter)
- end
- end
end
describe '#each_object_to_import' do
diff --git a/spec/lib/gitlab/gon_helper_spec.rb b/spec/lib/gitlab/gon_helper_spec.rb
index 28cb9125af1..dd4dcca809b 100644
--- a/spec/lib/gitlab/gon_helper_spec.rb
+++ b/spec/lib/gitlab/gon_helper_spec.rb
@@ -44,6 +44,7 @@ RSpec.describe Gitlab::GonHelper do
describe '#push_frontend_feature_flag' do
before do
skip_feature_flags_yaml_validation
+ skip_default_enabled_yaml_check
end
it 'pushes a feature flag to the frontend' do
diff --git a/spec/lib/gitlab/graphql/find_argument_in_parent_spec.rb b/spec/lib/gitlab/graphql/find_argument_in_parent_spec.rb
deleted file mode 100644
index 1b9301cd1aa..00000000000
--- a/spec/lib/gitlab/graphql/find_argument_in_parent_spec.rb
+++ /dev/null
@@ -1,45 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Gitlab::Graphql::FindArgumentInParent do
- describe '#find' do
- def build_node(parent = nil, args: {})
- props = { irep_node: double(arguments: args) }
- props[:parent] = parent if parent # The root node shouldn't respond to parent
-
- double(props)
- end
-
- let(:parent) do
- build_node(
- build_node(
- build_node(
- build_node,
- args: { myArg: 1 }
- )
- )
- )
- end
-
- let(:arg_name) { :my_arg }
-
- it 'searches parents and returns the argument' do
- expect(described_class.find(parent, :my_arg)).to eq(1)
- end
-
- it 'can find argument when passed in as both Ruby and GraphQL-formatted symbols and strings' do
- [:my_arg, :myArg, 'my_arg', 'myArg'].each do |arg|
- expect(described_class.find(parent, arg)).to eq(1)
- end
- end
-
- it 'returns nil if no arguments found in parents' do
- expect(described_class.find(parent, :bar)).to eq(nil)
- end
-
- it 'can limit the depth it searches to' do
- expect(described_class.find(parent, :my_arg, limit_depth: 1)).to eq(nil)
- end
- end
-end
diff --git a/spec/lib/gitlab/graphql/pagination/keyset/connection_generic_keyset_spec.rb b/spec/lib/gitlab/graphql/pagination/keyset/connection_generic_keyset_spec.rb
index 86e7d4e344c..b6c3cb4e04a 100644
--- a/spec/lib/gitlab/graphql/pagination/keyset/connection_generic_keyset_spec.rb
+++ b/spec/lib/gitlab/graphql/pagination/keyset/connection_generic_keyset_spec.rb
@@ -3,13 +3,15 @@
require 'spec_helper'
RSpec.describe Gitlab::Graphql::Pagination::Keyset::Connection do
+ include GraphqlHelpers
+
# https://gitlab.com/gitlab-org/gitlab/-/issues/334973
# The spec will be merged with connection_spec.rb in the future.
let(:nodes) { Project.all.order(id: :asc) }
let(:arguments) { {} }
let(:query_type) { GraphQL::ObjectType.new }
let(:schema) { GraphQL::Schema.define(query: query_type, mutation: nil)}
- let(:context) { GraphQL::Query::Context.new(query: double('query', schema: schema), values: nil, object: nil) }
+ let(:context) { GraphQL::Query::Context.new(query: query_double(schema: schema), values: nil, object: nil) }
let_it_be(:column_order_id) { Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(attribute_name: 'id', order_expression: Project.arel_table[:id].asc) }
let_it_be(:column_order_id_desc) { Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(attribute_name: 'id', order_expression: Project.arel_table[:id].desc) }
diff --git a/spec/lib/gitlab/graphql/pagination/keyset/connection_spec.rb b/spec/lib/gitlab/graphql/pagination/keyset/connection_spec.rb
index f31ec6c09fd..a4ba288b7f1 100644
--- a/spec/lib/gitlab/graphql/pagination/keyset/connection_spec.rb
+++ b/spec/lib/gitlab/graphql/pagination/keyset/connection_spec.rb
@@ -3,11 +3,13 @@
require 'spec_helper'
RSpec.describe Gitlab::Graphql::Pagination::Keyset::Connection do
+ include GraphqlHelpers
+
let(:nodes) { Project.all.order(id: :asc) }
let(:arguments) { {} }
let(:query_type) { GraphQL::ObjectType.new }
let(:schema) { GraphQL::Schema.define(query: query_type, mutation: nil)}
- let(:context) { GraphQL::Query::Context.new(query: double('query', schema: schema), values: nil, object: nil) }
+ let(:context) { GraphQL::Query::Context.new(query: query_double(schema: schema), values: nil, object: nil) }
subject(:connection) do
described_class.new(nodes, **{ context: context, max_page_size: 3 }.merge(arguments))
diff --git a/spec/lib/gitlab/graphql/queries_spec.rb b/spec/lib/gitlab/graphql/queries_spec.rb
index ad1aaac712e..2c2ec821385 100644
--- a/spec/lib/gitlab/graphql/queries_spec.rb
+++ b/spec/lib/gitlab/graphql/queries_spec.rb
@@ -85,11 +85,15 @@ RSpec.describe Gitlab::Graphql::Queries do
describe '.all' do
it 'is the combination of finding queries in CE and EE' do
expect(described_class)
- .to receive(:find).with(Rails.root / 'app/assets/javascripts').and_return([:ce])
+ .to receive(:find).with(Rails.root / 'app/assets/javascripts').and_return([:ce_assets])
expect(described_class)
- .to receive(:find).with(Rails.root / 'ee/app/assets/javascripts').and_return([:ee])
+ .to receive(:find).with(Rails.root / 'ee/app/assets/javascripts').and_return([:ee_assets])
+ expect(described_class)
+ .to receive(:find).with(Rails.root / 'app/graphql/queries').and_return([:ce_gql])
+ expect(described_class)
+ .to receive(:find).with(Rails.root / 'ee/app/graphql/queries').and_return([:ee_gql])
- expect(described_class.all).to eq([:ce, :ee])
+ expect(described_class.all).to contain_exactly(:ce_assets, :ee_assets, :ce_gql, :ee_gql)
end
end
diff --git a/spec/lib/gitlab/metrics/exporter/health_checks_middleware_spec.rb b/spec/lib/gitlab/health_checks/middleware_spec.rb
index 9ee46a45e7a..3b644539acc 100644
--- a/spec/lib/gitlab/metrics/exporter/health_checks_middleware_spec.rb
+++ b/spec/lib/gitlab/health_checks/middleware_spec.rb
@@ -2,12 +2,12 @@
require 'fast_spec_helper'
-RSpec.describe Gitlab::Metrics::Exporter::HealthChecksMiddleware do
- let(:app) { double(:app) }
+RSpec.describe Gitlab::HealthChecks::Middleware do
+ let(:app) { instance_double(Proc) }
let(:env) { { 'PATH_INFO' => path } }
- let(:readiness_probe) { double(:readiness_probe) }
- let(:liveness_probe) { double(:liveness_probe) }
+ let(:readiness_probe) { instance_double(Gitlab::HealthChecks::Probes::Collection) }
+ let(:liveness_probe) { instance_double(Gitlab::HealthChecks::Probes::Collection) }
let(:probe_result) { Gitlab::HealthChecks::Probes::Status.new(200, { status: 'ok' }) }
subject(:middleware) { described_class.new(app, readiness_probe, liveness_probe) }
diff --git a/spec/lib/gitlab/health_checks/server_spec.rb b/spec/lib/gitlab/health_checks/server_spec.rb
new file mode 100644
index 00000000000..65d24acbf22
--- /dev/null
+++ b/spec/lib/gitlab/health_checks/server_spec.rb
@@ -0,0 +1,64 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::HealthChecks::Server do
+ context 'with running server thread' do
+ subject(:server) { described_class.new(address: 'localhost', port: 8082) }
+
+ before do
+ # We need to send a request to localhost
+ WebMock.allow_net_connect!
+
+ server.start
+ end
+
+ after do
+ webmock_enable!
+
+ server.stop
+ end
+
+ shared_examples 'serves health check at' do |path|
+ it 'responds with 200 OK' do
+ response = Gitlab::HTTP.try_get("http://localhost:8082/#{path}", allow_local_requests: true)
+
+ expect(response.code).to eq(200)
+ end
+ end
+
+ describe '/readiness' do
+ it_behaves_like 'serves health check at', 'readiness'
+ end
+
+ describe '/liveness' do
+ it_behaves_like 'serves health check at', 'liveness'
+ end
+
+ describe 'other routes' do
+ it 'serves 404' do
+ response = Gitlab::HTTP.try_get("http://localhost:8082/other", allow_local_requests: true)
+
+ expect(response.code).to eq(404)
+ end
+ end
+ end
+
+ context 'when server thread goes away' do
+ before do
+ expect_next_instance_of(::WEBrick::HTTPServer) do |webrick|
+ allow(webrick).to receive(:start)
+ expect(webrick).to receive(:listeners).and_call_original
+ end
+ end
+
+ specify 'stop closes TCP socket' do
+ server = described_class.new(address: 'localhost', port: 8082)
+ server.start
+
+ expect(server.thread).to receive(:alive?).and_return(false).at_least(:once)
+
+ server.stop
+ end
+ end
+end
diff --git a/spec/lib/gitlab/http_spec.rb b/spec/lib/gitlab/http_spec.rb
index 7dbd21e6914..c2fb987d195 100644
--- a/spec/lib/gitlab/http_spec.rb
+++ b/spec/lib/gitlab/http_spec.rb
@@ -246,10 +246,10 @@ RSpec.describe Gitlab::HTTP do
context 'when :timeout is set' do
it 'does not set any default timeouts' do
expect(described_class).to receive(:httparty_perform_request).with(
- Net::HTTP::Get, 'http://example.org', timeout: 1
+ Net::HTTP::Get, 'http://example.org', { timeout: 1 }
).and_call_original
- described_class.get('http://example.org', timeout: 1)
+ described_class.get('http://example.org', { timeout: 1 })
end
end
diff --git a/spec/lib/gitlab/import/import_failure_service_spec.rb b/spec/lib/gitlab/import/import_failure_service_spec.rb
index e3fec63adde..eb71b307b8d 100644
--- a/spec/lib/gitlab/import/import_failure_service_spec.rb
+++ b/spec/lib/gitlab/import/import_failure_service_spec.rb
@@ -64,19 +64,23 @@ RSpec.describe Gitlab::Import::ImportFailureService, :aggregate_failures do
.to receive(:track_exception)
.with(
exception,
- project_id: project.id,
- import_type: import_type,
- source: 'SomeImporter'
+ {
+ project_id: project.id,
+ import_type: import_type,
+ source: 'SomeImporter'
+ }
)
expect(Gitlab::Import::Logger)
.to receive(:error)
.with(
- message: 'importer failed',
- 'error.message': 'some error',
- project_id: project.id,
- import_type: import_type,
- source: 'SomeImporter'
+ {
+ message: 'importer failed',
+ 'error.message': 'some error',
+ project_id: project.id,
+ import_type: import_type,
+ source: 'SomeImporter'
+ }
)
service.execute
@@ -96,19 +100,23 @@ RSpec.describe Gitlab::Import::ImportFailureService, :aggregate_failures do
.to receive(:track_exception)
.with(
exception,
- project_id: project.id,
- import_type: import_type,
- source: 'SomeImporter'
+ {
+ project_id: project.id,
+ import_type: import_type,
+ source: 'SomeImporter'
+ }
)
expect(Gitlab::Import::Logger)
.to receive(:error)
.with(
- message: 'importer failed',
- 'error.message': 'some error',
- project_id: project.id,
- import_type: import_type,
- source: 'SomeImporter'
+ {
+ message: 'importer failed',
+ 'error.message': 'some error',
+ project_id: project.id,
+ import_type: import_type,
+ source: 'SomeImporter'
+ }
)
service.execute
diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml
index 730f9035293..1546b6a26c8 100644
--- a/spec/lib/gitlab/import_export/all_models.yml
+++ b/spec/lib/gitlab/import_export/all_models.yml
@@ -550,6 +550,7 @@ project:
- project_registry
- packages
- package_files
+- packages_cleanup_policy
- tracing_setting
- alerting_setting
- project_setting
diff --git a/spec/lib/gitlab/import_export/group/relation_factory_spec.rb b/spec/lib/gitlab/import_export/group/relation_factory_spec.rb
index 8e7fe8849d4..9dbe8426f52 100644
--- a/spec/lib/gitlab/import_export/group/relation_factory_spec.rb
+++ b/spec/lib/gitlab/import_export/group/relation_factory_spec.rb
@@ -88,6 +88,21 @@ RSpec.describe Gitlab::ImportExport::Group::RelationFactory do
end
end
+ context 'when relation is namespace_settings' do
+ let(:relation_sym) { :namespace_settings }
+ let(:relation_hash) do
+ {
+ 'namespace_id' => 1,
+ 'prevent_forking_outside_group' => true,
+ 'prevent_sharing_groups_outside_hierarchy' => true
+ }
+ end
+
+ it do
+ expect(created_object).to eq(nil)
+ end
+ end
+
def random_id
rand(1000..10000)
end
diff --git a/spec/lib/gitlab/import_export/project/tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project/tree_restorer_spec.rb
index a3e891db658..d3397e89f1f 100644
--- a/spec/lib/gitlab/import_export/project/tree_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/project/tree_restorer_spec.rb
@@ -383,7 +383,7 @@ RSpec.describe Gitlab::ImportExport::Project::TreeRestorer do
end
end
- it 'restores releases with links' do
+ it 'restores releases with links & milestones' do
release = @project.releases.last
link = release.links.last
@@ -393,6 +393,8 @@ RSpec.describe Gitlab::ImportExport::Project::TreeRestorer do
expect(release.name).to eq('release-1.1')
expect(release.sha).to eq('901de3a8bd5573f4a049b1457d28bc1592ba6bf9')
expect(release.released_at).to eq('2019-12-26T10:17:14.615Z')
+ expect(release.milestone_releases.count).to eq(1)
+ expect(release.milestone_releases.first.milestone.title).to eq('test milestone')
expect(link.url).to eq('http://localhost/namespace6/project6/-/jobs/140463678/artifacts/download')
expect(link.name).to eq('release-1.1.dmg')
diff --git a/spec/lib/gitlab/inactive_projects_deletion_warning_tracker_spec.rb b/spec/lib/gitlab/inactive_projects_deletion_warning_tracker_spec.rb
new file mode 100644
index 00000000000..4eb2388f3f7
--- /dev/null
+++ b/spec/lib/gitlab/inactive_projects_deletion_warning_tracker_spec.rb
@@ -0,0 +1,67 @@
+# frozen_string_literal: true
+
+require "spec_helper"
+
+RSpec.describe Gitlab::InactiveProjectsDeletionWarningTracker do
+ let_it_be(:project_id) { 1 }
+
+ describe '.notified_projects', :clean_gitlab_redis_shared_state do
+ before do
+ freeze_time do
+ Gitlab::InactiveProjectsDeletionWarningTracker.new(project_id).mark_notified
+ end
+ end
+
+ it 'returns the list of projects for which deletion warning email has been sent' do
+ expected_hash = { "project:1" => "#{Date.current}" }
+
+ expect(Gitlab::InactiveProjectsDeletionWarningTracker.notified_projects).to eq(expected_hash)
+ end
+ end
+
+ describe '.reset_all' do
+ before do
+ Gitlab::InactiveProjectsDeletionWarningTracker.new(project_id).mark_notified
+ end
+
+ it 'deletes all the projects for which deletion warning email was sent' do
+ Gitlab::InactiveProjectsDeletionWarningTracker.reset_all
+
+ expect(Gitlab::InactiveProjectsDeletionWarningTracker.notified_projects).to eq({})
+ end
+ end
+
+ describe '#notified?' do
+ before do
+ Gitlab::InactiveProjectsDeletionWarningTracker.new(project_id).mark_notified
+ end
+
+ it 'returns true if the project has already been notified' do
+ expect(Gitlab::InactiveProjectsDeletionWarningTracker.new(project_id).notified?).to eq(true)
+ end
+
+ it 'returns false if the project has not been notified' do
+ expect(Gitlab::InactiveProjectsDeletionWarningTracker.new(2).notified?).to eq(false)
+ end
+ end
+
+ describe '#mark_notified' do
+ it 'marks the project as being notified' do
+ Gitlab::InactiveProjectsDeletionWarningTracker.new(project_id).mark_notified
+
+ expect(Gitlab::InactiveProjectsDeletionWarningTracker.new(project_id).notified?).to eq(true)
+ end
+ end
+
+ describe '#reset' do
+ before do
+ Gitlab::InactiveProjectsDeletionWarningTracker.new(project_id).mark_notified
+ end
+
+ it 'resets the project as not being notified' do
+ Gitlab::InactiveProjectsDeletionWarningTracker.new(project_id).reset
+
+ expect(Gitlab::InactiveProjectsDeletionWarningTracker.new(project_id).notified?).to eq(false)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/instrumentation/rate_limiting_gates_spec.rb b/spec/lib/gitlab/instrumentation/rate_limiting_gates_spec.rb
new file mode 100644
index 00000000000..ac308eb7c80
--- /dev/null
+++ b/spec/lib/gitlab/instrumentation/rate_limiting_gates_spec.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Instrumentation::RateLimitingGates, :request_store do
+ describe '.gates' do
+ it 'returns an empty array when no gates are tracked' do
+ expect(described_class.gates).to eq([])
+ end
+
+ it 'returns all gates used in the request' do
+ described_class.track(:foo)
+
+ RequestStore.clear!
+
+ described_class.track(:bar)
+ described_class.track(:baz)
+
+ expect(described_class.gates).to contain_exactly(:bar, :baz)
+ end
+
+ it 'deduplicates its results' do
+ described_class.track(:foo)
+ described_class.track(:bar)
+ described_class.track(:foo)
+
+ expect(described_class.gates).to contain_exactly(:foo, :bar)
+ end
+ end
+
+ describe '.payload' do
+ it 'returns the gates in a hash' do
+ described_class.track(:foo)
+ described_class.track(:bar)
+
+ expect(described_class.payload).to eq(described_class::GATES => [:foo, :bar])
+ end
+ end
+end
diff --git a/spec/lib/gitlab/instrumentation_helper_spec.rb b/spec/lib/gitlab/instrumentation_helper_spec.rb
index a9663012e9a..5fea355ab4f 100644
--- a/spec/lib/gitlab/instrumentation_helper_spec.rb
+++ b/spec/lib/gitlab/instrumentation_helper_spec.rb
@@ -77,6 +77,27 @@ RSpec.describe Gitlab::InstrumentationHelper do
end
end
+ context 'rate-limiting gates' do
+ context 'when the request did not pass through any rate-limiting gates' do
+ it 'logs an empty array of gates' do
+ subject
+
+ expect(payload[:rate_limiting_gates]).to eq([])
+ end
+ end
+
+ context 'when the request passed through rate-limiting gates' do
+ it 'logs an array of gates used' do
+ Gitlab::Instrumentation::RateLimitingGates.track(:foo)
+ Gitlab::Instrumentation::RateLimitingGates.track(:bar)
+
+ subject
+
+ expect(payload[:rate_limiting_gates]).to contain_exactly(:foo, :bar)
+ end
+ end
+ end
+
it 'logs cpu_s duration' do
subject
diff --git a/spec/lib/gitlab/jira/middleware_spec.rb b/spec/lib/gitlab/jira/middleware_spec.rb
index 1fe22b145a6..e7a79e40ac5 100644
--- a/spec/lib/gitlab/jira/middleware_spec.rb
+++ b/spec/lib/gitlab/jira/middleware_spec.rb
@@ -23,8 +23,8 @@ RSpec.describe Gitlab::Jira::Middleware do
describe '#call' do
it 'adjusts HTTP_AUTHORIZATION env when request from Jira DVCS user agent' do
- expect(app).to receive(:call).with('HTTP_USER_AGENT' => jira_user_agent,
- 'HTTP_AUTHORIZATION' => 'Bearer hash-123')
+ expect(app).to receive(:call).with({ 'HTTP_USER_AGENT' => jira_user_agent,
+ 'HTTP_AUTHORIZATION' => 'Bearer hash-123' })
middleware.call('HTTP_USER_AGENT' => jira_user_agent, 'HTTP_AUTHORIZATION' => 'token hash-123')
end
diff --git a/spec/lib/gitlab/json_cache_spec.rb b/spec/lib/gitlab/json_cache_spec.rb
index d7d28a94cfe..f4f6624bae9 100644
--- a/spec/lib/gitlab/json_cache_spec.rb
+++ b/spec/lib/gitlab/json_cache_spec.rb
@@ -313,9 +313,9 @@ RSpec.describe Gitlab::JsonCache do
it 'passes options the underlying cache implementation' do
expect(backend).to receive(:write)
- .with(expanded_key, "true", expires_in: 15.seconds)
+ .with(expanded_key, "true", { expires_in: 15.seconds })
- cache.fetch(key, expires_in: 15.seconds) { true }
+ cache.fetch(key, { expires_in: 15.seconds }) { true }
end
context 'when the given key does not exist in the cache' do
diff --git a/spec/lib/gitlab/json_spec.rb b/spec/lib/gitlab/json_spec.rb
index 5ffe736da54..7c093049e18 100644
--- a/spec/lib/gitlab/json_spec.rb
+++ b/spec/lib/gitlab/json_spec.rb
@@ -290,7 +290,7 @@ RSpec.describe Gitlab::Json do
end
it "skips legacy mode handling" do
- expect(Feature).not_to receive(:enabled?).with(:json_wrapper_legacy_mode, default_enabled: true)
+ expect(Feature).not_to receive(:enabled?).with(:json_wrapper_legacy_mode)
subject.send(:handle_legacy_mode!, {})
end
diff --git a/spec/lib/gitlab/kubernetes/cilium_network_policy_spec.rb b/spec/lib/gitlab/kubernetes/cilium_network_policy_spec.rb
deleted file mode 100644
index ec1f46100a4..00000000000
--- a/spec/lib/gitlab/kubernetes/cilium_network_policy_spec.rb
+++ /dev/null
@@ -1,274 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Gitlab::Kubernetes::CiliumNetworkPolicy do
- let(:policy) do
- described_class.new(
- name: name,
- namespace: namespace,
- description: description,
- selector: selector,
- ingress: ingress,
- egress: egress,
- labels: labels,
- resource_version: resource_version,
- annotations: annotations
- )
- end
-
- let(:resource) do
- ::Kubeclient::Resource.new(
- apiVersion: Gitlab::Kubernetes::CiliumNetworkPolicy::API_VERSION,
- kind: Gitlab::Kubernetes::CiliumNetworkPolicy::KIND,
- metadata: { name: name, namespace: namespace, resourceVersion: resource_version, annotations: annotations },
- spec: { endpointSelector: endpoint_selector, ingress: ingress, egress: egress },
- description: description
- )
- end
-
- let(:selector) { endpoint_selector }
- let(:labels) { nil }
- let(:name) { 'example-name' }
- let(:namespace) { 'example-namespace' }
- let(:endpoint_selector) { { matchLabels: { role: 'db' } } }
- let(:description) { 'example-description' }
- let(:partial_class_name) { described_class.name.split('::').last }
- let(:resource_version) { 101 }
- let(:annotations) { { 'app.gitlab.com/alert': 'true' } }
- let(:ingress) do
- [
- {
- fromEndpoints: [
- { matchLabels: { project: 'myproject' } }
- ]
- }
- ]
- end
-
- let(:egress) do
- [
- {
- ports: [{ port: 5978 }]
- }
- ]
- end
-
- include_examples 'network policy common specs'
-
- describe '.from_yaml' do
- let(:manifest) do
- <<~POLICY
- apiVersion: cilium.io/v2
- kind: CiliumNetworkPolicy
- description: example-description
- metadata:
- name: example-name
- namespace: example-namespace
- resourceVersion: 101
- annotations:
- app.gitlab.com/alert: "true"
- spec:
- endpointSelector:
- matchLabels:
- role: db
- ingress:
- - fromEndpoints:
- - matchLabels:
- project: myproject
- egress:
- - ports:
- - port: 5978
- POLICY
- end
-
- subject { Gitlab::Kubernetes::CiliumNetworkPolicy.from_yaml(manifest)&.generate }
-
- it { is_expected.to eq(resource) }
-
- context 'with nil manifest' do
- let(:manifest) { nil }
-
- it { is_expected.to be_nil }
- end
-
- context 'with invalid manifest' do
- let(:manifest) { "\tfoo: bar" }
-
- it { is_expected.to be_nil }
- end
-
- context 'with manifest without metadata' do
- let(:manifest) do
- <<~POLICY
- apiVersion: cilium.io/v2
- kind: CiliumNetworkPolicy
- spec:
- endpointSelector:
- matchLabels:
- role: db
- ingress:
- - fromEndpoints:
- matchLabels:
- project: myproject
- POLICY
- end
-
- it { is_expected.to be_nil }
- end
-
- context 'with manifest without spec' do
- let(:manifest) do
- <<~POLICY
- apiVersion: cilium.io/v2
- kind: CiliumNetworkPolicy
- metadata:
- name: example-name
- namespace: example-namespace
- POLICY
- end
-
- it { is_expected.to be_nil }
- end
-
- context 'with disallowed class' do
- let(:manifest) do
- <<~POLICY
- apiVersion: cilium.io/v2
- kind: CiliumNetworkPolicy
- metadata:
- name: example-name
- namespace: example-namespace
- creationTimestamp: 2020-04-14T00:08:30Z
- spec:
- endpointSelector:
- matchLabels:
- role: db
- ingress:
- - fromEndpoints:
- matchLabels:
- project: myproject
- POLICY
- end
-
- it { is_expected.to be_nil }
- end
- end
-
- describe '.from_resource' do
- let(:resource) do
- ::Kubeclient::Resource.new(
- description: description,
- metadata: {
- name: name, namespace: namespace, creationTimestamp: '2020-04-14T00:08:30Z',
- labels: { app: 'foo' }, resourceVersion: resource_version, annotations: annotations
- },
- spec: { endpointSelector: endpoint_selector, ingress: ingress, egress: nil, labels: nil }
- )
- end
-
- let(:generated_resource) do
- ::Kubeclient::Resource.new(
- apiVersion: Gitlab::Kubernetes::CiliumNetworkPolicy::API_VERSION,
- kind: Gitlab::Kubernetes::CiliumNetworkPolicy::KIND,
- description: description,
- metadata: { name: name, namespace: namespace, resourceVersion: resource_version, labels: { app: 'foo' }, annotations: annotations },
- spec: { endpointSelector: endpoint_selector, ingress: ingress }
- )
- end
-
- subject { Gitlab::Kubernetes::CiliumNetworkPolicy.from_resource(resource)&.generate }
-
- it { is_expected.to eq(generated_resource) }
-
- context 'with nil resource' do
- let(:resource) { nil }
-
- it { is_expected.to be_nil }
- end
-
- context 'with resource without metadata' do
- let(:resource) do
- ::Kubeclient::Resource.new(
- spec: { endpointSelector: endpoint_selector, ingress: ingress, egress: nil, labels: nil }
- )
- end
-
- it { is_expected.to be_nil }
- end
-
- context 'with resource without spec' do
- let(:resource) do
- ::Kubeclient::Resource.new(
- metadata: { name: name, namespace: namespace, uid: '128cf288-7de4-11ea-aceb-42010a800089', resourceVersion: resource_version }
- )
- end
-
- it { is_expected.to be_nil }
- end
-
- context 'with environment_ids' do
- subject { Gitlab::Kubernetes::CiliumNetworkPolicy.from_resource(resource, [1, 2, 3]) }
-
- it 'includes environment_ids in as_json result' do
- expect(subject.as_json).to include(environment_ids: [1, 2, 3])
- end
- end
- end
-
- describe '#resource' do
- subject { policy.resource }
-
- let(:resource) do
- {
- apiVersion: Gitlab::Kubernetes::CiliumNetworkPolicy::API_VERSION,
- kind: Gitlab::Kubernetes::CiliumNetworkPolicy::KIND,
- metadata: { name: name, namespace: namespace, resourceVersion: resource_version, annotations: annotations },
- spec: { endpointSelector: endpoint_selector, ingress: ingress, egress: egress },
- description: description
- }
- end
-
- it { is_expected.to eq(resource) }
-
- context 'with labels' do
- let(:labels) { { app: 'foo' } }
-
- before do
- resource[:metadata][:labels] = { app: 'foo' }
- end
-
- it { is_expected.to eq(resource) }
- end
-
- context 'without resource_version' do
- let(:resource_version) { nil }
-
- before do
- resource[:metadata].delete(:resourceVersion)
- end
-
- it { is_expected.to eq(resource) }
- end
-
- context 'with nil egress' do
- let(:egress) { nil }
-
- before do
- resource[:spec].delete(:egress)
- end
-
- it { is_expected.to eq(resource) }
- end
-
- context 'without annotations' do
- let(:annotations) { nil }
-
- before do
- resource[:metadata].delete(:annotations)
- end
-
- it { is_expected.to eq(resource) }
- end
- end
-end
diff --git a/spec/lib/gitlab/kubernetes/kube_client_spec.rb b/spec/lib/gitlab/kubernetes/kube_client_spec.rb
index 521f13dc9cc..dfd5092b54d 100644
--- a/spec/lib/gitlab/kubernetes/kube_client_spec.rb
+++ b/spec/lib/gitlab/kubernetes/kube_client_spec.rb
@@ -227,20 +227,6 @@ RSpec.describe Gitlab::Kubernetes::KubeClient do
end
end
- describe '#cilium_networking_client' do
- subject { client.cilium_networking_client }
-
- it_behaves_like 'a Kubeclient'
-
- it 'has the cilium API group endpoint' do
- expect(subject.api_endpoint.to_s).to match(%r{\/apis\/cilium.io\Z})
- end
-
- it 'has the api_version' do
- expect(subject.instance_variable_get(:@api_version)).to eq('v2')
- end
- end
-
describe '#metrics_client' do
subject { client.metrics_client }
@@ -428,56 +414,6 @@ RSpec.describe Gitlab::Kubernetes::KubeClient do
end
end
- describe 'networking API group' do
- let(:networking_client) { client.networking_client }
-
- [
- :create_network_policy,
- :get_network_policies,
- :get_network_policy,
- :update_network_policy,
- :delete_network_policy
- ].each do |method|
- describe "##{method}" do
- include_examples 'redirection not allowed', method
- include_examples 'dns rebinding not allowed', method
-
- it 'delegates to the networking client' do
- expect(client).to delegate_method(method).to(:networking_client)
- end
-
- it 'responds to the method' do
- expect(client).to respond_to method
- end
- end
- end
- end
-
- describe 'cilium API group' do
- let(:cilium_networking_client) { client.cilium_networking_client }
-
- [
- :create_cilium_network_policy,
- :get_cilium_network_policies,
- :get_cilium_network_policy,
- :update_cilium_network_policy,
- :delete_cilium_network_policy
- ].each do |method|
- describe "##{method}" do
- include_examples 'redirection not allowed', method
- include_examples 'dns rebinding not allowed', method
-
- it 'delegates to the cilium client' do
- expect(client).to delegate_method(method).to(:cilium_networking_client)
- end
-
- it 'responds to the method' do
- expect(client).to respond_to method
- end
- end
- end
- end
-
describe 'non-entity methods' do
it 'does not proxy for non-entity methods' do
expect(client).not_to respond_to :proxy_url
diff --git a/spec/lib/gitlab/kubernetes/network_policy_spec.rb b/spec/lib/gitlab/kubernetes/network_policy_spec.rb
deleted file mode 100644
index 2cba37a1302..00000000000
--- a/spec/lib/gitlab/kubernetes/network_policy_spec.rb
+++ /dev/null
@@ -1,235 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Gitlab::Kubernetes::NetworkPolicy do
- let(:policy) do
- described_class.new(
- name: name,
- namespace: namespace,
- selector: selector,
- ingress: ingress,
- labels: labels
- )
- end
-
- let(:resource) do
- ::Kubeclient::Resource.new(
- kind: Gitlab::Kubernetes::NetworkPolicy::KIND,
- metadata: { name: name, namespace: namespace },
- spec: { podSelector: pod_selector, policyTypes: %w(Ingress), ingress: ingress, egress: nil }
- )
- end
-
- let(:selector) { pod_selector }
- let(:labels) { nil }
- let(:name) { 'example-name' }
- let(:namespace) { 'example-namespace' }
- let(:pod_selector) { { matchLabels: { role: 'db' } } }
-
- let(:ingress) do
- [
- {
- from: [
- { namespaceSelector: { matchLabels: { project: 'myproject' } } }
- ]
- }
- ]
- end
-
- let(:egress) do
- [
- {
- ports: [{ port: 5978 }]
- }
- ]
- end
-
- include_examples 'network policy common specs'
-
- describe '.from_yaml' do
- let(:manifest) do
- <<~POLICY
- apiVersion: networking.k8s.io/v1
- kind: NetworkPolicy
- metadata:
- name: example-name
- namespace: example-namespace
- spec:
- podSelector:
- matchLabels:
- role: db
- policyTypes:
- - Ingress
- ingress:
- - from:
- - namespaceSelector:
- matchLabels:
- project: myproject
- POLICY
- end
-
- subject { Gitlab::Kubernetes::NetworkPolicy.from_yaml(manifest)&.generate }
-
- it { is_expected.to eq(resource) }
-
- context 'with nil manifest' do
- let(:manifest) { nil }
-
- it { is_expected.to be_nil }
- end
-
- context 'with invalid manifest' do
- let(:manifest) { "\tfoo: bar" }
-
- it { is_expected.to be_nil }
- end
-
- context 'with manifest without metadata' do
- let(:manifest) do
- <<~POLICY
- apiVersion: networking.k8s.io/v1
- kind: NetworkPolicy
- spec:
- podSelector:
- matchLabels:
- role: db
- policyTypes:
- - Ingress
- ingress:
- - from:
- - namespaceSelector:
- matchLabels:
- project: myproject
- POLICY
- end
-
- it { is_expected.to be_nil }
- end
-
- context 'with manifest without spec' do
- let(:manifest) do
- <<~POLICY
- apiVersion: networking.k8s.io/v1
- kind: NetworkPolicy
- metadata:
- name: example-name
- namespace: example-namespace
- POLICY
- end
-
- it { is_expected.to be_nil }
- end
-
- context 'with disallowed class' do
- let(:manifest) do
- <<~POLICY
- apiVersion: networking.k8s.io/v1
- kind: NetworkPolicy
- metadata:
- name: example-name
- namespace: example-namespace
- creationTimestamp: 2020-04-14T00:08:30Z
- spec:
- podSelector:
- matchLabels:
- role: db
- policyTypes:
- - Ingress
- ingress:
- - from:
- - namespaceSelector:
- matchLabels:
- project: myproject
- POLICY
- end
-
- it { is_expected.to be_nil }
- end
- end
-
- describe '.from_resource' do
- let(:resource) do
- ::Kubeclient::Resource.new(
- metadata: {
- name: name, namespace: namespace, creationTimestamp: '2020-04-14T00:08:30Z',
- labels: { app: 'foo' }, resourceVersion: '4990'
- },
- spec: { podSelector: pod_selector, policyTypes: %w(Ingress), ingress: ingress, egress: nil }
- )
- end
-
- let(:generated_resource) do
- ::Kubeclient::Resource.new(
- kind: Gitlab::Kubernetes::NetworkPolicy::KIND,
- metadata: { name: name, namespace: namespace, labels: { app: 'foo' } },
- spec: { podSelector: pod_selector, policyTypes: %w(Ingress), ingress: ingress, egress: nil }
- )
- end
-
- subject { Gitlab::Kubernetes::NetworkPolicy.from_resource(resource)&.generate }
-
- it { is_expected.to eq(generated_resource) }
-
- context 'with nil resource' do
- let(:resource) { nil }
-
- it { is_expected.to be_nil }
- end
-
- context 'with resource without metadata' do
- let(:resource) do
- ::Kubeclient::Resource.new(
- spec: { podSelector: pod_selector, policyTypes: %w(Ingress), ingress: ingress, egress: nil }
- )
- end
-
- it { is_expected.to be_nil }
- end
-
- context 'with resource without spec' do
- let(:resource) do
- ::Kubeclient::Resource.new(
- metadata: { name: name, namespace: namespace, uid: '128cf288-7de4-11ea-aceb-42010a800089', resourceVersion: '4990' }
- )
- end
-
- it { is_expected.to be_nil }
- end
-
- context 'with environment_ids' do
- subject { Gitlab::Kubernetes::NetworkPolicy.from_resource(resource, [1, 2, 3]) }
-
- it 'includes environment_ids in as_json result' do
- expect(subject.as_json).to include(environment_ids: [1, 2, 3])
- end
- end
- end
-
- describe '#resource' do
- subject { policy.resource }
-
- let(:resource) do
- {
- kind: Gitlab::Kubernetes::NetworkPolicy::KIND,
- metadata: { name: name, namespace: namespace },
- spec: { podSelector: pod_selector, policyTypes: %w(Ingress), ingress: ingress, egress: nil }
- }
- end
-
- it { is_expected.to eq(resource) }
-
- context 'with labels' do
- let(:labels) { { app: 'foo' } }
- let(:resource) do
- {
- kind: Gitlab::Kubernetes::NetworkPolicy::KIND,
- metadata: { name: name, namespace: namespace, labels: { app: 'foo' } },
- spec: { podSelector: pod_selector, policyTypes: %w(Ingress), ingress: ingress, egress: nil }
- }
- end
-
- it { is_expected.to eq(resource) }
- end
- end
-end
diff --git a/spec/lib/gitlab/legacy_github_import/importer_spec.rb b/spec/lib/gitlab/legacy_github_import/importer_spec.rb
index 9a4d7bd996e..e69edbe6dc0 100644
--- a/spec/lib/gitlab/legacy_github_import/importer_spec.rb
+++ b/spec/lib/gitlab/legacy_github_import/importer_spec.rb
@@ -274,8 +274,7 @@ RSpec.describe Gitlab::LegacyGithubImport::Importer do
it 'instantiates a Client' do
allow(project).to receive(:import_data).and_return(double(credentials: credentials))
expect(Gitlab::LegacyGithubImport::Client).to receive(:new).with(
- credentials[:user],
- **{}
+ credentials[:user]
)
subject.client
diff --git a/spec/lib/gitlab/lograge/custom_options_spec.rb b/spec/lib/gitlab/lograge/custom_options_spec.rb
index d8f351bb8a3..58b05be6ff9 100644
--- a/spec/lib/gitlab/lograge/custom_options_spec.rb
+++ b/spec/lib/gitlab/lograge/custom_options_spec.rb
@@ -96,23 +96,15 @@ RSpec.describe Gitlab::Lograge::CustomOptions do
end
end
- context 'when feature flags are present', :request_store do
+ context 'when feature flags are present', :request_store do
before do
allow(Feature).to receive(:log_feature_flag_states?).and_return(false)
- definitions = {}
[:enabled_feature, :disabled_feature].each do |flag_name|
- definitions[flag_name] = Feature::Definition.new("development/enabled_feature.yml",
- name: flag_name,
- type: 'development',
- log_state_changes: true,
- default_enabled: false)
-
+ stub_feature_flag_definition(flag_name, log_state_changes: true)
allow(Feature).to receive(:log_feature_flag_states?).with(flag_name).and_call_original
end
- allow(Feature::Definition).to receive(:definitions).and_return(definitions)
-
Feature.enable(:enabled_feature)
Feature.disable(:disabled_feature)
end
diff --git a/spec/lib/gitlab/markdown_cache/active_record/extension_spec.rb b/spec/lib/gitlab/markdown_cache/active_record/extension_spec.rb
index 98385cd80cc..d22bef5bda9 100644
--- a/spec/lib/gitlab/markdown_cache/active_record/extension_spec.rb
+++ b/spec/lib/gitlab/markdown_cache/active_record/extension_spec.rb
@@ -171,9 +171,9 @@ RSpec.describe Gitlab::MarkdownCache::ActiveRecord::Extension do
expect(thing).to receive(:persisted?).and_return(true)
expect(thing).to receive(:update_columns)
- .with("title_html" => updated_html,
+ .with({ "title_html" => updated_html,
"description_html" => "",
- "cached_markdown_version" => cache_version)
+ "cached_markdown_version" => cache_version })
thing.refresh_markdown_cache!
end
diff --git a/spec/lib/gitlab/metrics/dashboard/importers/prometheus_metrics_spec.rb b/spec/lib/gitlab/metrics/dashboard/importers/prometheus_metrics_spec.rb
index ff8f5797f9d..c15e717b126 100644
--- a/spec/lib/gitlab/metrics/dashboard/importers/prometheus_metrics_spec.rb
+++ b/spec/lib/gitlab/metrics/dashboard/importers/prometheus_metrics_spec.rb
@@ -12,12 +12,6 @@ RSpec.describe Gitlab::Metrics::Dashboard::Importers::PrometheusMetrics do
subject { described_class.new(dashboard_hash, project: project, dashboard_path: dashboard_path) }
- before do
- allow_next_instance_of(::Clusters::Applications::ScheduleUpdateService) do |update_service|
- allow(update_service).to receive(:execute)
- end
- end
-
context 'valid dashboard' do
let(:dashboard_hash) { load_sample_dashboard }
@@ -45,13 +39,6 @@ RSpec.describe Gitlab::Metrics::Dashboard::Importers::PrometheusMetrics do
create(:prometheus_metric, existing_metric_attributes)
end
- let!(:existing_alert) do
- alert = create(:prometheus_alert, project: project, prometheus_metric: existing_metric)
- existing_metric.prometheus_alerts << alert
-
- alert
- end
-
it 'updates existing PrometheusMetrics' do
subject.execute
@@ -68,15 +55,6 @@ RSpec.describe Gitlab::Metrics::Dashboard::Importers::PrometheusMetrics do
expect { subject.execute }.to change { PrometheusMetric.count }.by(2)
end
- it 'updates affected environments' do
- expect(::Clusters::Applications::ScheduleUpdateService).to receive(:new).with(
- existing_alert.environment.cluster_prometheus_adapter,
- project
- ).and_return(double('ScheduleUpdateService', execute: true))
-
- subject.execute
- end
-
context 'with stale metrics' do
let!(:stale_metric) do
create(:prometheus_metric,
@@ -87,13 +65,6 @@ RSpec.describe Gitlab::Metrics::Dashboard::Importers::PrometheusMetrics do
)
end
- let!(:stale_alert) do
- alert = create(:prometheus_alert, project: project, prometheus_metric: stale_metric)
- stale_metric.prometheus_alerts << alert
-
- alert
- end
-
it 'updates existing PrometheusMetrics' do
subject.execute
@@ -111,21 +82,6 @@ RSpec.describe Gitlab::Metrics::Dashboard::Importers::PrometheusMetrics do
expect { stale_metric.reload }.to raise_error(ActiveRecord::RecordNotFound)
end
-
- it 'deletes stale alert' do
- subject.execute
-
- expect { stale_alert.reload }.to raise_error(ActiveRecord::RecordNotFound)
- end
-
- it 'updates affected environments' do
- expect(::Clusters::Applications::ScheduleUpdateService).to receive(:new).with(
- existing_alert.environment.cluster_prometheus_adapter,
- project
- ).and_return(double('ScheduleUpdateService', execute: true))
-
- subject.execute
- end
end
end
end
diff --git a/spec/lib/gitlab/metrics/exporter/base_exporter_spec.rb b/spec/lib/gitlab/metrics/exporter/base_exporter_spec.rb
index c7afc02f0af..66fba7ab683 100644
--- a/spec/lib/gitlab/metrics/exporter/base_exporter_spec.rb
+++ b/spec/lib/gitlab/metrics/exporter/base_exporter_spec.rb
@@ -152,8 +152,6 @@ RSpec.describe Gitlab::Metrics::Exporter::BaseExporter do
where(:method_class, :path, :http_status) do
Net::HTTP::Get | '/metrics' | 200
- Net::HTTP::Get | '/liveness' | 200
- Net::HTTP::Get | '/readiness' | 200
Net::HTTP::Get | '/' | 404
end
diff --git a/spec/lib/gitlab/metrics/methods_spec.rb b/spec/lib/gitlab/metrics/methods_spec.rb
index 71135a6e9c5..eb7c8891e98 100644
--- a/spec/lib/gitlab/metrics/methods_spec.rb
+++ b/spec/lib/gitlab/metrics/methods_spec.rb
@@ -35,7 +35,7 @@ RSpec.describe Gitlab::Metrics::Methods do
context 'metric is not cached' do
it 'calls fetch_metric' do
- expect(subject).to receive(:init_metric).with(metric_type, metric_name, docstring: docstring)
+ expect(subject).to receive(:init_metric).with(metric_type, metric_name, { docstring: docstring })
subject.public_send(metric_name)
end
diff --git a/spec/lib/gitlab/metrics/rails_slis_spec.rb b/spec/lib/gitlab/metrics/rails_slis_spec.rb
index 2ba06316507..b30eb57101f 100644
--- a/spec/lib/gitlab/metrics/rails_slis_spec.rb
+++ b/spec/lib/gitlab/metrics/rails_slis_spec.rb
@@ -36,18 +36,8 @@ RSpec.describe Gitlab::Metrics::RailsSlis do
}
end
- expect(Gitlab::Metrics::Sli).to receive(:initialized?).with(:rails_request_apdex) { false }
- expect(Gitlab::Metrics::Sli).to receive(:initialized?).with(:graphql_query_apdex) { false }
- expect(Gitlab::Metrics::Sli).to receive(:initialize_sli).with(:rails_request_apdex, array_including(*possible_labels)).and_call_original
- expect(Gitlab::Metrics::Sli).to receive(:initialize_sli).with(:graphql_query_apdex, array_including(*possible_graphql_labels)).and_call_original
-
- described_class.initialize_request_slis!
- end
-
- it 'does not initialize the SLI if they were initialized already', :aggregate_failures do
- expect(Gitlab::Metrics::Sli).to receive(:initialized?).with(:rails_request_apdex) { true }
- expect(Gitlab::Metrics::Sli).to receive(:initialized?).with(:graphql_query_apdex) { true }
- expect(Gitlab::Metrics::Sli).not_to receive(:initialize_sli)
+ expect(Gitlab::Metrics::Sli::Apdex).to receive(:initialize_sli).with(:rails_request, array_including(*possible_labels)).and_call_original
+ expect(Gitlab::Metrics::Sli::Apdex).to receive(:initialize_sli).with(:graphql_query, array_including(*possible_graphql_labels)).and_call_original
described_class.initialize_request_slis!
end
diff --git a/spec/lib/gitlab/metrics/sli_spec.rb b/spec/lib/gitlab/metrics/sli_spec.rb
index 8ba4bf29568..9b776d6738d 100644
--- a/spec/lib/gitlab/metrics/sli_spec.rb
+++ b/spec/lib/gitlab/metrics/sli_spec.rb
@@ -10,72 +10,151 @@ RSpec.describe Gitlab::Metrics::Sli do
end
describe 'Class methods' do
- before do
- described_class.instance_variable_set(:@known_slis, nil)
+ it 'does not allow them to be called on the parent module' do
+ expect(described_class).not_to respond_to(:[])
+ expect(described_class).not_to respond_to(:initialize_sli)
end
- describe '.[]' do
- it 'warns about an uninitialized SLI but returns and stores a new one' do
- sli = described_class[:bar]
+ it 'allows different SLIs to be defined on each subclass' do
+ apdex_counters = [
+ fake_total_counter('foo', 'apdex'),
+ fake_numerator_counter('foo', 'apdex', 'success')
+ ]
- expect(described_class[:bar]).to be(sli)
- end
+ error_rate_counters = [
+ fake_total_counter('foo', 'error_rate'),
+ fake_numerator_counter('foo', 'error_rate', 'error')
+ ]
- it 'returns the same object for multiple accesses' do
- sli = described_class.initialize_sli(:huzzah, [])
+ apdex = described_class::Apdex.initialize_sli(:foo, [{ hello: :world }])
- 2.times do
- expect(described_class[:huzzah]).to be(sli)
- end
- end
- end
+ expect(apdex_counters).to all(have_received(:get).with(hello: :world))
- describe '.initialized?' do
- before do
- fake_total_counter(:boom)
- fake_success_counter(:boom)
- end
+ error_rate = described_class::ErrorRate.initialize_sli(:foo, [{ other: :labels }])
- it 'is true when an SLI was initialized with labels' do
- expect { described_class.initialize_sli(:boom, [{ hello: :world }]) }
- .to change { described_class.initialized?(:boom) }.from(false).to(true)
- end
+ expect(error_rate_counters).to all(have_received(:get).with(other: :labels))
- it 'is false when an SLI was not initialized with labels' do
- expect { described_class.initialize_sli(:boom, []) }
- .not_to change { described_class.initialized?(:boom) }.from(false)
- end
+ expect(described_class::Apdex[:foo]).to be(apdex)
+ expect(described_class::ErrorRate[:foo]).to be(error_rate)
end
end
- describe '#initialize_counters' do
- it 'initializes counters for the passed label combinations' do
- counters = [fake_total_counter(:hey), fake_success_counter(:hey)]
+ subclasses = {
+ Gitlab::Metrics::Sli::Apdex => :success,
+ Gitlab::Metrics::Sli::ErrorRate => :error
+ }
- described_class.new(:hey).initialize_counters([{ foo: 'bar' }, { foo: 'baz' }])
+ subclasses.each do |subclass, numerator_type|
+ subclass_type = subclass.to_s.demodulize.underscore
- expect(counters).to all(have_received(:get).with({ foo: 'bar' }))
- expect(counters).to all(have_received(:get).with({ foo: 'baz' }))
- end
- end
+ describe subclass do
+ describe 'Class methods' do
+ before do
+ described_class.instance_variable_set(:@known_slis, nil)
+ end
- describe "#increment" do
- let!(:sli) { described_class.new(:heyo) }
- let!(:total_counter) { fake_total_counter(:heyo) }
- let!(:success_counter) { fake_success_counter(:heyo) }
+ describe '.[]' do
+ it 'returns and stores a new, uninitialized SLI' do
+ sli = described_class[:bar]
- it 'increments both counters for labels successes' do
- sli.increment(labels: { hello: "world" }, success: true)
+ expect(described_class[:bar]).to be(sli)
+ expect(described_class[:bar]).not_to be_initialized
+ end
- expect(total_counter).to have_received(:increment).with({ hello: 'world' })
- expect(success_counter).to have_received(:increment).with({ hello: 'world' })
- end
+ it 'returns the same object for multiple accesses' do
+ sli = described_class.initialize_sli(:huzzah, [])
+
+ 2.times do
+ expect(described_class[:huzzah]).to be(sli)
+ end
+ end
+ end
+
+ describe '.initialize_sli' do
+ it 'returns and stores a new initialized SLI' do
+ counters = [
+ fake_total_counter(:bar, subclass_type),
+ fake_numerator_counter(:bar, subclass_type, numerator_type)
+ ]
+
+ sli = described_class.initialize_sli(:bar, [{ hello: :world }])
+
+ expect(sli).to be_initialized
+ expect(counters).to all(have_received(:get).with(hello: :world))
+ expect(counters).to all(have_received(:get).with(hello: :world))
+ end
+
+ it 'does not change labels for an already-initialized SLI' do
+ counters = [
+ fake_total_counter(:bar, subclass_type),
+ fake_numerator_counter(:bar, subclass_type, numerator_type)
+ ]
+
+ sli = described_class.initialize_sli(:bar, [{ hello: :world }])
- it 'only increments the total counters for labels when not successful' do
- sli.increment(labels: { hello: "world" }, success: false)
+ expect(sli).to be_initialized
+ expect(counters).to all(have_received(:get).with(hello: :world))
+ expect(counters).to all(have_received(:get).with(hello: :world))
- expect(total_counter).to have_received(:increment).with({ hello: 'world' })
- expect(success_counter).not_to have_received(:increment).with({ hello: 'world' })
+ counters.each do |counter|
+ expect(counter).not_to receive(:get)
+ end
+
+ expect(described_class.initialize_sli(:bar, [{ other: :labels }])).to eq(sli)
+ end
+ end
+
+ describe '.initialized?' do
+ before do
+ fake_total_counter(:boom, subclass_type)
+ fake_numerator_counter(:boom, subclass_type, numerator_type)
+ end
+
+ it 'is true when an SLI was initialized with labels' do
+ expect { described_class.initialize_sli(:boom, [{ hello: :world }]) }
+ .to change { described_class.initialized?(:boom) }.from(false).to(true)
+ end
+
+ it 'is false when an SLI was not initialized with labels' do
+ expect { described_class.initialize_sli(:boom, []) }
+ .not_to change { described_class.initialized?(:boom) }.from(false)
+ end
+ end
+ end
+
+ describe '#initialize_counters' do
+ it 'initializes counters for the passed label combinations' do
+ counters = [
+ fake_total_counter(:hey, subclass_type),
+ fake_numerator_counter(:hey, subclass_type, numerator_type)
+ ]
+
+ described_class.new(:hey).initialize_counters([{ foo: 'bar' }, { foo: 'baz' }])
+
+ expect(counters).to all(have_received(:get).with({ foo: 'bar' }))
+ expect(counters).to all(have_received(:get).with({ foo: 'baz' }))
+ end
+ end
+
+ describe "#increment" do
+ let!(:sli) { described_class.new(:heyo) }
+ let!(:total_counter) { fake_total_counter(:heyo, subclass_type) }
+ let!(:numerator_counter) { fake_numerator_counter(:heyo, subclass_type, numerator_type) }
+
+ it "increments both counters for labels when #{numerator_type} is true" do
+ sli.increment(labels: { hello: "world" }, numerator_type => true)
+
+ expect(total_counter).to have_received(:increment).with({ hello: 'world' })
+ expect(numerator_counter).to have_received(:increment).with({ hello: 'world' })
+ end
+
+ it "only increments the total counters for labels when #{numerator_type} is false" do
+ sli.increment(labels: { hello: "world" }, numerator_type => false)
+
+ expect(total_counter).to have_received(:increment).with({ hello: 'world' })
+ expect(numerator_counter).not_to have_received(:increment).with({ hello: 'world' })
+ end
+ end
end
end
@@ -89,11 +168,11 @@ RSpec.describe Gitlab::Metrics::Sli do
fake_counter
end
- def fake_total_counter(name)
- fake_prometheus_counter("gitlab_sli:#{name}:total")
+ def fake_total_counter(name, type)
+ fake_prometheus_counter("gitlab_sli:#{name}_#{type}:total")
end
- def fake_success_counter(name)
- fake_prometheus_counter("gitlab_sli:#{name}:success_total")
+ def fake_numerator_counter(name, type, numerator_name)
+ fake_prometheus_counter("gitlab_sli:#{name}_#{type}:#{numerator_name}_total")
end
end
diff --git a/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb b/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb
index 389b0ef1044..28c3ef229ab 100644
--- a/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb
+++ b/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb
@@ -10,6 +10,124 @@ RSpec.describe Gitlab::Metrics::Subscribers::ActiveRecord do
let(:connection) { ActiveRecord::Base.retrieve_connection }
let(:db_config_name) { ::Gitlab::Database.db_config_name(connection) }
+ describe '.load_balancing_metric_counter_keys' do
+ context 'multiple databases' do
+ before do
+ skip_if_multiple_databases_not_setup
+ end
+
+ it 'has expected keys' do
+ expect(described_class.load_balancing_metric_counter_keys).to include(
+ :db_replica_count,
+ :db_primary_count,
+ :db_main_count,
+ :db_main_replica_count,
+ :db_ci_count,
+ :db_ci_replica_count,
+ :db_replica_cached_count,
+ :db_primary_cached_count,
+ :db_main_cached_count,
+ :db_main_replica_cached_count,
+ :db_ci_cached_count,
+ :db_ci_replica_cached_count,
+ :db_replica_wal_count,
+ :db_primary_wal_count,
+ :db_main_wal_count,
+ :db_main_replica_wal_count,
+ :db_ci_wal_count,
+ :db_ci_replica_wal_count,
+ :db_replica_wal_cached_count,
+ :db_primary_wal_cached_count,
+ :db_main_wal_cached_count,
+ :db_main_replica_wal_cached_count,
+ :db_ci_wal_cached_count,
+ :db_ci_replica_wal_cached_count
+ )
+ end
+ end
+
+ context 'single database' do
+ before do
+ skip_if_multiple_databases_are_setup
+ end
+
+ it 'has expected keys' do
+ expect(described_class.load_balancing_metric_counter_keys).to include(
+ :db_replica_count,
+ :db_primary_count,
+ :db_main_count,
+ :db_main_replica_count,
+ :db_replica_cached_count,
+ :db_primary_cached_count,
+ :db_main_cached_count,
+ :db_main_replica_cached_count,
+ :db_replica_wal_count,
+ :db_primary_wal_count,
+ :db_main_wal_count,
+ :db_main_replica_wal_count,
+ :db_replica_wal_cached_count,
+ :db_primary_wal_cached_count,
+ :db_main_wal_cached_count,
+ :db_main_replica_wal_cached_count
+ )
+ end
+
+ it 'does not have ci keys' do
+ expect(described_class.load_balancing_metric_counter_keys).not_to include(
+ :db_ci_count,
+ :db_ci_replica_count,
+ :db_ci_cached_count,
+ :db_ci_replica_cached_count,
+ :db_ci_wal_count,
+ :db_ci_replica_wal_count,
+ :db_ci_wal_cached_count,
+ :db_ci_replica_wal_cached_count
+ )
+ end
+ end
+ end
+
+ describe '.load_balancing_metric_duration_keys' do
+ context 'multiple databases' do
+ before do
+ skip_if_multiple_databases_not_setup
+ end
+
+ it 'has expected keys' do
+ expect(described_class.load_balancing_metric_duration_keys).to include(
+ :db_replica_duration_s,
+ :db_primary_duration_s,
+ :db_main_duration_s,
+ :db_main_replica_duration_s,
+ :db_ci_duration_s,
+ :db_ci_replica_duration_s
+ )
+ end
+ end
+
+ context 'single database' do
+ before do
+ skip_if_multiple_databases_are_setup
+ end
+
+ it 'has expected keys' do
+ expect(described_class.load_balancing_metric_duration_keys).to include(
+ :db_replica_duration_s,
+ :db_primary_duration_s,
+ :db_main_duration_s,
+ :db_main_replica_duration_s
+ )
+ end
+
+ it 'does not have ci keys' do
+ expect(described_class.load_balancing_metric_duration_keys).not_to include(
+ :db_ci_duration_s,
+ :db_ci_replica_duration_s
+ )
+ end
+ end
+ end
+
describe '#transaction' do
let(:web_transaction) { double('Gitlab::Metrics::WebTransaction') }
let(:background_transaction) { double('Gitlab::Metrics::WebTransaction') }
@@ -37,7 +155,7 @@ RSpec.describe Gitlab::Metrics::Subscribers::ActiveRecord do
end
it 'captures the metrics for web only' do
- expect(web_transaction).to receive(:observe).with(:gitlab_database_transaction_seconds, 0.23, db_config_name: db_config_name)
+ expect(web_transaction).to receive(:observe).with(:gitlab_database_transaction_seconds, 0.23, { db_config_name: db_config_name })
expect(background_transaction).not_to receive(:observe)
expect(background_transaction).not_to receive(:increment)
@@ -77,7 +195,7 @@ RSpec.describe Gitlab::Metrics::Subscribers::ActiveRecord do
end
it 'captures the metrics for web only' do
- expect(background_transaction).to receive(:observe).with(:gitlab_database_transaction_seconds, 0.23, db_config_name: db_config_name)
+ expect(background_transaction).to receive(:observe).with(:gitlab_database_transaction_seconds, 0.23, { db_config_name: db_config_name })
expect(web_transaction).not_to receive(:observe)
expect(web_transaction).not_to receive(:increment)
diff --git a/spec/lib/gitlab/metrics/subscribers/rack_attack_spec.rb b/spec/lib/gitlab/metrics/subscribers/rack_attack_spec.rb
index fda4b94bd78..9f939d0d7d6 100644
--- a/spec/lib/gitlab/metrics/subscribers/rack_attack_spec.rb
+++ b/spec/lib/gitlab/metrics/subscribers/rack_attack_spec.rb
@@ -77,8 +77,8 @@ RSpec.describe Gitlab::Metrics::Subscribers::RackAttack, :request_store do
end
it 'logs request information' do
- expect(Gitlab::AuthLogger).to receive(:error).with(
- include(
+ expect(Gitlab::AuthLogger).to receive(:error) do |arguments|
+ expect(arguments).to include(
message: 'Rack_Attack',
env: match_type,
remote_ip: '1.2.3.4',
@@ -86,7 +86,14 @@ RSpec.describe Gitlab::Metrics::Subscribers::RackAttack, :request_store do
path: '/api/v4/internal/authorized_keys',
matched: 'throttle_unauthenticated'
)
- )
+
+ if expected_status
+ expect(arguments).to include(status: expected_status)
+ else
+ expect(arguments).not_to have_key(:status)
+ end
+ end
+
subscriber.send(match_type, event)
end
end
@@ -111,8 +118,8 @@ RSpec.describe Gitlab::Metrics::Subscribers::RackAttack, :request_store do
end
it 'logs request information and user id' do
- expect(Gitlab::AuthLogger).to receive(:error).with(
- include(
+ expect(Gitlab::AuthLogger).to receive(:error) do |arguments|
+ expect(arguments).to include(
message: 'Rack_Attack',
env: match_type,
remote_ip: '1.2.3.4',
@@ -121,7 +128,14 @@ RSpec.describe Gitlab::Metrics::Subscribers::RackAttack, :request_store do
matched: 'throttle_authenticated_api',
user_id: non_existing_record_id
)
- )
+
+ if expected_status
+ expect(arguments).to include(status: expected_status)
+ else
+ expect(arguments).not_to have_key(:status)
+ end
+ end
+
subscriber.send(match_type, event)
end
end
@@ -145,8 +159,8 @@ RSpec.describe Gitlab::Metrics::Subscribers::RackAttack, :request_store do
end
it 'logs request information and user meta' do
- expect(Gitlab::AuthLogger).to receive(:error).with(
- include(
+ expect(Gitlab::AuthLogger).to receive(:error) do |arguments|
+ expect(arguments).to include(
message: 'Rack_Attack',
env: match_type,
remote_ip: '1.2.3.4',
@@ -156,7 +170,14 @@ RSpec.describe Gitlab::Metrics::Subscribers::RackAttack, :request_store do
user_id: user.id,
'meta.user' => user.username
)
- )
+
+ if expected_status
+ expect(arguments).to include(status: expected_status)
+ else
+ expect(arguments).not_to have_key(:status)
+ end
+ end
+
subscriber.send(match_type, event)
end
end
@@ -182,8 +203,8 @@ RSpec.describe Gitlab::Metrics::Subscribers::RackAttack, :request_store do
end
it 'logs request information and user meta' do
- expect(Gitlab::AuthLogger).to receive(:error).with(
- include(
+ expect(Gitlab::AuthLogger).to receive(:error) do |arguments|
+ expect(arguments).to include(
message: 'Rack_Attack',
env: match_type,
remote_ip: '1.2.3.4',
@@ -192,7 +213,14 @@ RSpec.describe Gitlab::Metrics::Subscribers::RackAttack, :request_store do
matched: 'throttle_authenticated_api',
deploy_token_id: deploy_token.id
)
- )
+
+ if expected_status
+ expect(arguments).to include(status: expected_status)
+ else
+ expect(arguments).not_to have_key(:status)
+ end
+ end
+
subscriber.send(match_type, event)
end
end
@@ -202,6 +230,7 @@ RSpec.describe Gitlab::Metrics::Subscribers::RackAttack, :request_store do
describe '#throttle' do
let(:match_type) { :throttle }
+ let(:expected_status) { 429 }
let(:event_name) { 'throttle.rack_attack' }
it_behaves_like 'log into auth logger'
@@ -209,6 +238,7 @@ RSpec.describe Gitlab::Metrics::Subscribers::RackAttack, :request_store do
describe '#blocklist' do
let(:match_type) { :blocklist }
+ let(:expected_status) { 403 }
let(:event_name) { 'blocklist.rack_attack' }
it_behaves_like 'log into auth logger'
@@ -216,6 +246,7 @@ RSpec.describe Gitlab::Metrics::Subscribers::RackAttack, :request_store do
describe '#track' do
let(:match_type) { :track }
+ let(:expected_status) { nil }
let(:event_name) { 'track.rack_attack' }
it_behaves_like 'log into auth logger'
diff --git a/spec/lib/gitlab/patch/database_config_spec.rb b/spec/lib/gitlab/patch/database_config_spec.rb
index d6f36ab86d5..73dc84bb2ef 100644
--- a/spec/lib/gitlab/patch/database_config_spec.rb
+++ b/spec/lib/gitlab/patch/database_config_spec.rb
@@ -34,9 +34,8 @@ RSpec.describe Gitlab::Patch::DatabaseConfig do
end
end
- context 'when a new syntax is used' do
- let(:database_yml) do
- <<-EOS
+ let(:database_yml) do
+ <<-EOS
production:
main:
adapter: postgresql
@@ -68,59 +67,9 @@ RSpec.describe Gitlab::Patch::DatabaseConfig do
prepared_statements: false
variables:
statement_timeout: 15s
- EOS
- end
-
- include_examples 'hash containing main: connection name'
-
- it 'configuration is not legacy one' do
- configuration.database_configuration
-
- expect(configuration.uses_legacy_database_config).to eq(false)
- end
+ EOS
end
- context 'when a legacy syntax is used' do
- let(:database_yml) do
- <<-EOS
- production:
- adapter: postgresql
- encoding: unicode
- database: gitlabhq_production
- username: git
- password: "secure password"
- host: localhost
-
- development:
- adapter: postgresql
- encoding: unicode
- database: gitlabhq_development
- username: postgres
- password: "secure password"
- host: localhost
- variables:
- statement_timeout: 15s
-
- test: &test
- adapter: postgresql
- encoding: unicode
- database: gitlabhq_test
- username: postgres
- password:
- host: localhost
- prepared_statements: false
- variables:
- statement_timeout: 15s
- EOS
- end
-
- include_examples 'hash containing main: connection name'
-
- it 'configuration is legacy' do
- configuration.database_configuration
-
- expect(configuration.uses_legacy_database_config).to eq(true)
- end
- end
+ include_examples 'hash containing main: connection name'
end
end
diff --git a/spec/lib/gitlab/path_regex_spec.rb b/spec/lib/gitlab/path_regex_spec.rb
index e5fa7538515..0a647befb50 100644
--- a/spec/lib/gitlab/path_regex_spec.rb
+++ b/spec/lib/gitlab/path_regex_spec.rb
@@ -183,7 +183,7 @@ RSpec.describe Gitlab::PathRegex do
# We ban new items in this list, see https://gitlab.com/gitlab-org/gitlab/-/issues/215362
it 'does not allow expansion' do
- expect(described_class::TOP_LEVEL_ROUTES.size).to eq(40)
+ expect(described_class::TOP_LEVEL_ROUTES.size).to eq(39)
end
end
diff --git a/spec/lib/gitlab/popen_spec.rb b/spec/lib/gitlab/popen_spec.rb
index 8211806a809..0a186b07d19 100644
--- a/spec/lib/gitlab/popen_spec.rb
+++ b/spec/lib/gitlab/popen_spec.rb
@@ -103,7 +103,7 @@ RSpec.describe Gitlab::Popen do
it 'raises error' do
expect do
@klass.new.popen(%w[foobar])
- end.to raise_error
+ end.to raise_error(Errno::ENOENT)
end
end
end
diff --git a/spec/lib/gitlab/process_supervisor_spec.rb b/spec/lib/gitlab/process_supervisor_spec.rb
index 60b127dadda..8356197805c 100644
--- a/spec/lib/gitlab/process_supervisor_spec.rb
+++ b/spec/lib/gitlab/process_supervisor_spec.rb
@@ -6,6 +6,7 @@ RSpec.describe Gitlab::ProcessSupervisor do
let(:health_check_interval_seconds) { 0.1 }
let(:check_terminate_interval_seconds) { 1 }
let(:forwarded_signals) { [] }
+ let(:term_signals) { [] }
let(:process_ids) { [spawn_process, spawn_process] }
def spawn_process
@@ -19,7 +20,8 @@ RSpec.describe Gitlab::ProcessSupervisor do
health_check_interval_seconds: health_check_interval_seconds,
check_terminate_interval_seconds: check_terminate_interval_seconds,
terminate_timeout_seconds: 1 + check_terminate_interval_seconds,
- forwarded_signals: forwarded_signals
+ forwarded_signals: forwarded_signals,
+ term_signals: term_signals
)
end
@@ -29,6 +31,8 @@ RSpec.describe Gitlab::ProcessSupervisor do
rescue Errno::ESRCH
# Ignore if a process wasn't actually alive.
end
+
+ supervisor.stop
end
describe '#supervise' do
@@ -60,7 +64,7 @@ RSpec.describe Gitlab::ProcessSupervisor do
[42] # Fake starting a new process in place of the terminated one.
end
- # Terminate the supervised process.
+ # Terminate a supervised process.
Process.kill('TERM', process_ids.first)
await_condition(sleep_sec: health_check_interval_seconds) do
@@ -71,6 +75,72 @@ RSpec.describe Gitlab::ProcessSupervisor do
expect(Gitlab::ProcessManagement.process_alive?(process_ids.last)).to be(true)
expect(supervisor.supervised_pids).to match_array([process_ids.last, 42])
end
+
+ it 'deduplicates PIDs returned from callback' do
+ expect(Gitlab::ProcessManagement.all_alive?(process_ids)).to be(true)
+ pids_killed = []
+
+ supervisor.supervise(process_ids) do |dead_pids|
+ pids_killed = dead_pids
+ # Fake a new process having the same pid as one that was just terminated.
+ [process_ids.last]
+ end
+
+ # Terminate a supervised process.
+ Process.kill('TERM', process_ids.first)
+
+ await_condition(sleep_sec: health_check_interval_seconds) do
+ pids_killed == [process_ids.first]
+ end
+
+ expect(supervisor.supervised_pids).to contain_exactly(process_ids.last)
+ end
+
+ it 'accepts single PID returned from callback' do
+ expect(Gitlab::ProcessManagement.all_alive?(process_ids)).to be(true)
+ pids_killed = []
+
+ supervisor.supervise(process_ids) do |dead_pids|
+ pids_killed = dead_pids
+ 42
+ end
+
+ # Terminate a supervised process.
+ Process.kill('TERM', process_ids.first)
+
+ await_condition(sleep_sec: health_check_interval_seconds) do
+ pids_killed == [process_ids.first]
+ end
+
+ expect(supervisor.supervised_pids).to contain_exactly(42, process_ids.last)
+ end
+
+ context 'but supervisor has entered shutdown' do
+ it 'does not trigger callback again' do
+ expect(Gitlab::ProcessManagement.all_alive?(process_ids)).to be(true)
+ callback_count = 0
+
+ supervisor.supervise(process_ids) do |dead_pids|
+ callback_count += 1
+
+ Thread.new { supervisor.shutdown }
+
+ [42]
+ end
+
+ # Terminate the supervised processes to trigger more than 1 callback.
+ Process.kill('TERM', process_ids.first)
+ Process.kill('TERM', process_ids.last)
+
+ await_condition(sleep_sec: health_check_interval_seconds * 3) do
+ supervisor.alive == false
+ end
+
+ # Since we shut down the supervisor during the first callback, it should not
+ # be called anymore.
+ expect(callback_count).to eq(1)
+ end
+ end
end
context 'signal handling' do
@@ -82,6 +152,8 @@ RSpec.describe Gitlab::ProcessSupervisor do
end
context 'termination signals' do
+ let(:term_signals) { %i(INT TERM) }
+
context 'when TERM results in timely shutdown of processes' do
it 'forwards them to observed processes without waiting for grace period to expire' do
allow(Gitlab::ProcessManagement).to receive(:any_alive?).and_return(false)
diff --git a/spec/lib/gitlab/query_limiting/transaction_spec.rb b/spec/lib/gitlab/query_limiting/transaction_spec.rb
index 76bb2b4c4cc..27da1f23556 100644
--- a/spec/lib/gitlab/query_limiting/transaction_spec.rb
+++ b/spec/lib/gitlab/query_limiting/transaction_spec.rb
@@ -78,6 +78,21 @@ RSpec.describe Gitlab::QueryLimiting::Transaction do
expect { transaction.increment }.not_to change { transaction.count }
end
+
+ it 'does not increment the number of executed queries when the query is known to be ignorable' do
+ transaction = described_class.new
+
+ expect do
+ transaction.increment(described_class::GEO_NODES_LOAD)
+ transaction.increment(described_class::LICENSES_LOAD)
+ transaction.increment('SELECT a.attname, a.other_column FROM pg_attribute a')
+ transaction.increment('SELECT x.foo, a.attname FROM some_table x JOIN pg_attribute a')
+ transaction.increment(<<-SQL)
+ SELECT a.attname, a.other_column
+ FROM pg_attribute a
+ SQL
+ end.not_to change(transaction, :count)
+ end
end
describe '#raise_error?' do
diff --git a/spec/lib/gitlab/request_profiler/profile_spec.rb b/spec/lib/gitlab/request_profiler/profile_spec.rb
deleted file mode 100644
index 30e23a99b22..00000000000
--- a/spec/lib/gitlab/request_profiler/profile_spec.rb
+++ /dev/null
@@ -1,61 +0,0 @@
-# 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
deleted file mode 100644
index 4d3b361efcb..00000000000
--- a/spec/lib/gitlab/request_profiler_spec.rb
+++ /dev/null
@@ -1,56 +0,0 @@
-# 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/gitlab/saas_spec.rb b/spec/lib/gitlab/saas_spec.rb
index 1be36a60a97..a8656c44831 100644
--- a/spec/lib/gitlab/saas_spec.rb
+++ b/spec/lib/gitlab/saas_spec.rb
@@ -3,11 +3,11 @@
require 'spec_helper'
RSpec.describe Gitlab::Saas do
+ include SaasTestHelper
+
describe '.canary_toggle_com_url' do
subject { described_class.canary_toggle_com_url }
- let(:next_url) { 'https://next.gitlab.com' }
-
- it { is_expected.to eq(next_url) }
+ it { is_expected.to eq(get_next_url) }
end
end
diff --git a/spec/lib/gitlab/safe_request_purger_spec.rb b/spec/lib/gitlab/safe_request_purger_spec.rb
new file mode 100644
index 00000000000..02f3f11d469
--- /dev/null
+++ b/spec/lib/gitlab/safe_request_purger_spec.rb
@@ -0,0 +1,73 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::SafeRequestPurger do
+ let(:resource_key) { '_key_' }
+ let(:resource_ids) { ['foo'] }
+ let(:args) { { resource_key: resource_key, resource_ids: resource_ids } }
+ let(:resource_data) { { 'foo' => 'bar' } }
+
+ before do
+ Gitlab::SafeRequestStore[resource_key] = resource_data
+ end
+
+ describe '.execute', :request_store do
+ subject(:execute_instance) { described_class.execute(**args) }
+
+ it 'purges an entry from the store' do
+ execute_instance
+
+ expect(Gitlab::SafeRequestStore.fetch(resource_key)).to be_empty
+ end
+ end
+
+ describe '#execute' do
+ subject(:execute_instance) { described_class.new(**args).execute }
+
+ context 'when request store is active', :request_store do
+ it 'purges an entry from the store' do
+ execute_instance
+
+ expect(Gitlab::SafeRequestStore.fetch(resource_key)).to be_empty
+ end
+
+ context 'when there are multiple resource_ids to purge' do
+ let(:resource_data) do
+ {
+ 'foo' => 'bar',
+ 'two' => '_two_',
+ 'three' => '_three_',
+ 'four' => '_four_'
+ }
+ end
+
+ let(:resource_ids) { %w[two three] }
+
+ it 'purges an entry from the store' do
+ execute_instance
+
+ expect(Gitlab::SafeRequestStore.fetch(resource_key)).to eq resource_data.slice('foo', 'four')
+ end
+ end
+
+ context 'when there is no matching resource_ids' do
+ let(:resource_ids) { ['_bogus_resource_id_'] }
+
+ it 'purges an entry from the store' do
+ execute_instance
+
+ expect(Gitlab::SafeRequestStore.fetch(resource_key)).to eq resource_data
+ end
+ end
+ end
+
+ context 'when request store is not active' do
+ let(:resource_ids) { ['_bogus_resource_id_'] }
+
+ it 'does offer the ability to interact with data store' do
+ expect(execute_instance).to eq({})
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/setup_helper/praefect_spec.rb b/spec/lib/gitlab/setup_helper/praefect_spec.rb
new file mode 100644
index 00000000000..f7da6c19d68
--- /dev/null
+++ b/spec/lib/gitlab/setup_helper/praefect_spec.rb
@@ -0,0 +1,79 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::SetupHelper::Praefect do
+ describe '.configuration_toml' do
+ let(:opt_per_repo) do
+ { per_repository: true,
+ pghost: 'my-host',
+ pgport: 555432,
+ pguser: 'me' }
+ end
+
+ it 'defaults to in memory queue' do
+ toml = described_class.configuration_toml('/here', nil, {})
+
+ expect(toml).to match(/i_understand_my_election_strategy_is_unsupported_and_will_be_removed_without_warning/)
+ expect(toml).to match(/memory_queue_enabled = true/)
+ expect(toml).to match(/election_strategy = "local"/)
+ expect(toml).not_to match(/\[database\]/)
+ end
+
+ it 'provides database details if wanted' do
+ toml = described_class.configuration_toml('/here', nil, opt_per_repo)
+
+ expect(toml).not_to match(/i_understand_my_election_strategy_is_unsupported_and_will_be_removed_without_warning/)
+ expect(toml).not_to match(/memory_queue_enabled = true/)
+ expect(toml).to match(/\[database\]/)
+ expect(toml).to match(/election_strategy = "per_repository"/)
+ end
+
+ %i[pghost pgport pguser].each do |pg_key|
+ it "fails when #{pg_key} is missing" do
+ opt = opt_per_repo.dup
+ opt.delete(pg_key)
+
+ expect do
+ described_class.configuration_toml('/here', nil, opt)
+ end.to raise_error(KeyError)
+ end
+
+ it "uses the provided #{pg_key}" do
+ toml = described_class.configuration_toml('/here', nil, opt_per_repo)
+
+ expect(toml).to match(/#{pg_key.to_s.delete_prefix('pg')} = "?#{opt_per_repo[pg_key]}"?/)
+ end
+ end
+
+ it 'defaults to praefect_test if dbname is missing' do
+ toml = described_class.configuration_toml('/here', nil, opt_per_repo)
+
+ expect(toml).to match(/dbname = "praefect_test"/)
+ end
+
+ it 'uses the provided dbname' do
+ opt = opt_per_repo.merge(dbname: 'my_db')
+
+ toml = described_class.configuration_toml('/here', nil, opt)
+
+ expect(toml).to match(/dbname = "my_db"/)
+ end
+ end
+
+ describe '.get_config_path' do
+ it 'defaults to praefect.config.toml' do
+ expect(described_class).to receive(:generate_configuration).with(anything, '/tmp/praefect.config.toml', anything)
+
+ described_class.create_configuration('/tmp', {})
+ end
+
+ it 'takes the provided config_filename' do
+ opt = { config_filename: 'yo.toml' }
+
+ expect(described_class).to receive(:generate_configuration).with(anything, '/tmp/yo.toml', anything)
+
+ described_class.create_configuration('/tmp', {}, options: opt)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/sidekiq_config_spec.rb b/spec/lib/gitlab/sidekiq_config_spec.rb
index da135f202f6..4a1a9beb21a 100644
--- a/spec/lib/gitlab/sidekiq_config_spec.rb
+++ b/spec/lib/gitlab/sidekiq_config_spec.rb
@@ -3,6 +3,11 @@
require 'spec_helper'
RSpec.describe Gitlab::SidekiqConfig do
+ before do
+ # Remove cache
+ described_class.instance_variable_set(:@workers, nil)
+ end
+
describe '.workers' do
it 'includes all workers' do
worker_classes = described_class.workers.map(&:klass)
@@ -44,9 +49,10 @@ RSpec.describe Gitlab::SidekiqConfig do
before do
allow(described_class).to receive(:workers).and_return(workers)
allow(Gitlab).to receive(:ee?).and_return(false)
+ allow(Gitlab).to receive(:jh?).and_return(false)
end
- it 'returns true if the YAML file does not matcph the application code' do
+ it 'returns true if the YAML file does not match the application code' do
allow(YAML).to receive(:load_file)
.with(described_class::FOSS_QUEUE_CONFIG_PATH)
.and_return(workers.first(2).map(&:to_yaml))
@@ -96,6 +102,7 @@ RSpec.describe Gitlab::SidekiqConfig do
].map { |worker| described_class::Worker.new(worker, ee: false) }
allow(described_class).to receive(:workers).and_return(workers)
+ allow(Gitlab).to receive(:jh?).and_return(false)
end
let(:expected_queues) do
@@ -161,4 +168,35 @@ RSpec.describe Gitlab::SidekiqConfig do
expect(mappings).not_to include('AdminEmailWorker' => 'cronjob:admin_email')
end
end
+
+ describe '.routing_queues' do
+ let(:test_routes) do
+ [
+ ['tags=needs_own_queue', nil],
+ ['urgency=high', 'high_urgency'],
+ ['feature_category=gitaly', 'gitaly'],
+ ['feature_category=not_exist', 'not_exist'],
+ ['*', 'default']
+ ]
+ end
+
+ before do
+ described_class.instance_variable_set(:@routing_queues, nil)
+ allow(::Gitlab::SidekiqConfig::WorkerRouter)
+ .to receive(:global).and_return(::Gitlab::SidekiqConfig::WorkerRouter.new(test_routes))
+ end
+
+ after do
+ described_class.instance_variable_set(:@routing_queues, nil)
+ end
+
+ it 'returns worker queue mappings that have queues in the current Sidekiq options' do
+ queues = described_class.routing_queues
+
+ expect(queues).to match_array(%w[
+ default mailers high_urgency gitaly email_receiver service_desk_email_receiver
+ ])
+ expect(queues).not_to include('not_exist')
+ end
+ end
end
diff --git a/spec/lib/gitlab/sidekiq_death_handler_spec.rb b/spec/lib/gitlab/sidekiq_death_handler_spec.rb
index 96fef88de4e..e3f9f8277a0 100644
--- a/spec/lib/gitlab/sidekiq_death_handler_spec.rb
+++ b/spec/lib/gitlab/sidekiq_death_handler_spec.rb
@@ -23,9 +23,9 @@ RSpec.describe Gitlab::SidekiqDeathHandler, :clean_gitlab_redis_queues do
it 'uses the attributes from the worker' do
expect(described_class.counter)
.to receive(:increment)
- .with(queue: 'test_queue', worker: 'TestWorker',
+ .with({ queue: 'test_queue', worker: 'TestWorker',
urgency: 'low', external_dependencies: 'yes',
- feature_category: 'users', boundary: 'cpu')
+ feature_category: 'users', boundary: 'cpu' })
described_class.handler({ 'class' => 'TestWorker', 'queue' => 'test_queue' }, nil)
end
@@ -39,9 +39,9 @@ RSpec.describe Gitlab::SidekiqDeathHandler, :clean_gitlab_redis_queues do
it 'uses blank attributes' do
expect(described_class.counter)
.to receive(:increment)
- .with(queue: 'test_queue', worker: 'TestWorker',
+ .with({ queue: 'test_queue', worker: 'TestWorker',
urgency: '', external_dependencies: 'no',
- feature_category: '', boundary: '')
+ feature_category: '', boundary: '' })
described_class.handler({ 'class' => 'TestWorker', 'queue' => 'test_queue' }, nil)
end
diff --git a/spec/lib/gitlab/sidekiq_logging/structured_logger_spec.rb b/spec/lib/gitlab/sidekiq_logging/structured_logger_spec.rb
index 210b9162be0..00ae55237e9 100644
--- a/spec/lib/gitlab/sidekiq_logging/structured_logger_spec.rb
+++ b/spec/lib/gitlab/sidekiq_logging/structured_logger_spec.rb
@@ -287,7 +287,8 @@ RSpec.describe Gitlab::SidekiqLogging::StructuredLogger do
'job_status' => 'done',
'duration_s' => 0.0,
'completed_at' => timestamp.to_f,
- 'cpu_s' => 1.111112
+ 'cpu_s' => 1.111112,
+ 'rate_limiting_gates' => []
)
end
diff --git a/spec/lib/gitlab/sidekiq_middleware/server_metrics_spec.rb b/spec/lib/gitlab/sidekiq_middleware/server_metrics_spec.rb
index ffa92126cc9..7d31979a393 100644
--- a/spec/lib/gitlab/sidekiq_middleware/server_metrics_spec.rb
+++ b/spec/lib/gitlab/sidekiq_middleware/server_metrics_spec.rb
@@ -21,40 +21,40 @@ RSpec.describe Gitlab::SidekiqMiddleware::ServerMetrics do
.and_return('MergeWorker' => 'merge', 'Ci::BuildFinishedWorker' => 'default')
expect(completion_seconds_metric)
- .to receive(:get).with(queue: 'merge',
+ .to receive(:get).with({ queue: 'merge',
worker: 'MergeWorker',
urgency: 'high',
external_dependencies: 'no',
feature_category: 'source_code_management',
boundary: '',
- job_status: 'done')
+ job_status: 'done' })
expect(completion_seconds_metric)
- .to receive(:get).with(queue: 'merge',
+ .to receive(:get).with({ queue: 'merge',
worker: 'MergeWorker',
urgency: 'high',
external_dependencies: 'no',
feature_category: 'source_code_management',
boundary: '',
- job_status: 'fail')
+ job_status: 'fail' })
expect(completion_seconds_metric)
- .to receive(:get).with(queue: 'default',
+ .to receive(:get).with({ queue: 'default',
worker: 'Ci::BuildFinishedWorker',
urgency: 'high',
external_dependencies: 'no',
feature_category: 'continuous_integration',
boundary: 'cpu',
- job_status: 'done')
+ job_status: 'done' })
expect(completion_seconds_metric)
- .to receive(:get).with(queue: 'default',
+ .to receive(:get).with({ queue: 'default',
worker: 'Ci::BuildFinishedWorker',
urgency: 'high',
external_dependencies: 'no',
feature_category: 'continuous_integration',
boundary: 'cpu',
- job_status: 'fail')
+ job_status: 'fail' })
described_class.initialize_process_metrics
end
diff --git a/spec/lib/gitlab/subscription_portal_spec.rb b/spec/lib/gitlab/subscription_portal_spec.rb
index fd3654afee0..8d5a39baf77 100644
--- a/spec/lib/gitlab/subscription_portal_spec.rb
+++ b/spec/lib/gitlab/subscription_portal_spec.rb
@@ -56,6 +56,7 @@ RSpec.describe ::Gitlab::SubscriptionPortal do
where(:method_name, :result) do
:default_subscriptions_url | 'https://customers.staging.gitlab.com'
:payment_form_url | 'https://customers.staging.gitlab.com/payment_forms/cc_validation'
+ :payment_validation_form_id | 'payment_method_validation'
:registration_validation_form_url | 'https://customers.staging.gitlab.com/payment_forms/cc_registration_validation'
:subscriptions_graphql_url | 'https://customers.staging.gitlab.com/graphql'
:subscriptions_more_minutes_url | 'https://customers.staging.gitlab.com/buy_pipeline_minutes'
diff --git a/spec/lib/gitlab/template/gitlab_ci_yml_template_spec.rb b/spec/lib/gitlab/template/gitlab_ci_yml_template_spec.rb
index 226fdb9c948..26c83ed6793 100644
--- a/spec/lib/gitlab/template/gitlab_ci_yml_template_spec.rb
+++ b/spec/lib/gitlab/template/gitlab_ci_yml_template_spec.rb
@@ -21,55 +21,6 @@ RSpec.describe Gitlab::Template::GitlabCiYmlTemplate do
end
end
- describe '.find' do
- let_it_be(:project) { create(:project) }
- let_it_be(:other_project) { create(:project) }
-
- described_class::TEMPLATES_WITH_LATEST_VERSION.keys.each do |key|
- it "finds the latest template for #{key}" do
- result = described_class.find(key, project)
- expect(result.full_name).to eq("#{key}.latest.gitlab-ci.yml")
- expect(result.content).to be_present
- end
-
- context 'when `redirect_to_latest_template` feature flag is disabled' do
- before do
- stub_feature_flags("redirect_to_latest_template_#{key.underscore.tr('/', '_')}".to_sym => false)
- end
-
- it "finds the stable template for #{key}" do
- result = described_class.find(key, project)
- expect(result.full_name).to eq("#{key}.gitlab-ci.yml")
- expect(result.content).to be_present
- end
- end
-
- context 'when `redirect_to_latest_template` feature flag is enabled on the project' do
- before do
- stub_feature_flags("redirect_to_latest_template_#{key.underscore.tr('/', '_')}".to_sym => project)
- end
-
- it "finds the latest template for #{key}" do
- result = described_class.find(key, project)
- expect(result.full_name).to eq("#{key}.latest.gitlab-ci.yml")
- expect(result.content).to be_present
- end
- end
-
- context 'when `redirect_to_latest_template` feature flag is enabled on the other project' do
- before do
- stub_feature_flags("redirect_to_latest_template_#{key.underscore.tr('/', '_')}".to_sym => other_project)
- end
-
- it "finds the stable template for #{key}" do
- result = described_class.find(key, project)
- expect(result.full_name).to eq("#{key}.gitlab-ci.yml")
- expect(result.content).to be_present
- end
- end
- end
- end
-
describe '#content' do
it 'loads the full file' do
gitignore = subject.new(Rails.root.join('lib/gitlab/ci/templates/Ruby.gitlab-ci.yml'))
diff --git a/spec/lib/gitlab/tracking/event_definition_spec.rb b/spec/lib/gitlab/tracking/event_definition_spec.rb
index 51c62840819..623009e9a30 100644
--- a/spec/lib/gitlab/tracking/event_definition_spec.rb
+++ b/spec/lib/gitlab/tracking/event_definition_spec.rb
@@ -33,7 +33,7 @@ RSpec.describe Gitlab::Tracking::EventDefinition do
end
it 'has all definitions valid' do
- expect { described_class.definitions }.not_to raise_error(Gitlab::Tracking::InvalidEventError)
+ expect { described_class.definitions }.not_to raise_error
end
describe '#validate' do
diff --git a/spec/lib/gitlab/url_builder_spec.rb b/spec/lib/gitlab/url_builder_spec.rb
index 8e372ba795b..d4f96f1a37f 100644
--- a/spec/lib/gitlab/url_builder_spec.rb
+++ b/spec/lib/gitlab/url_builder_spec.rb
@@ -22,6 +22,8 @@ RSpec.describe Gitlab::UrlBuilder do
:group_board | ->(board) { "/groups/#{board.group.full_path}/-/boards/#{board.id}" }
:commit | ->(commit) { "/#{commit.project.full_path}/-/commit/#{commit.id}" }
:issue | ->(issue) { "/#{issue.project.full_path}/-/issues/#{issue.iid}" }
+ [:issue, :task] | ->(issue) { "/#{issue.project.full_path}/-/work_items/#{issue.id}" }
+ :work_item | ->(work_item) { "/#{work_item.project.full_path}/-/work_items/#{work_item.id}" }
:merge_request | ->(merge_request) { "/#{merge_request.project.full_path}/-/merge_requests/#{merge_request.iid}" }
:project_milestone | ->(milestone) { "/#{milestone.project.full_path}/-/milestones/#{milestone.iid}" }
:project_snippet | ->(snippet) { "/#{snippet.project.full_path}/-/snippets/#{snippet.id}" }
@@ -57,7 +59,7 @@ RSpec.describe Gitlab::UrlBuilder do
end
with_them do
- let(:object) { build_stubbed(factory) }
+ let(:object) { build_stubbed(*Array(factory)) }
let(:path) { path_generator.call(object) }
it 'returns the full URL' do
@@ -69,6 +71,18 @@ RSpec.describe Gitlab::UrlBuilder do
end
end
+ context 'when work_items feature flag is disabled' do
+ before do
+ stub_feature_flags(work_items: false)
+ end
+
+ it 'returns an issue path for an issue of type task' do
+ task = create(:issue, :task)
+
+ expect(subject.build(task, only_path: true)).to eq("/#{task.project.full_path}/-/issues/#{task.iid}")
+ end
+ end
+
context 'when passing a compare' do
# NOTE: The Compare requires an actual repository, which isn't available
# with the `build_stubbed` strategy used by the table tests above
diff --git a/spec/lib/gitlab/usage/metric_definition_spec.rb b/spec/lib/gitlab/usage/metric_definition_spec.rb
index 1127d1cd477..070586319a5 100644
--- a/spec/lib/gitlab/usage/metric_definition_spec.rb
+++ b/spec/lib/gitlab/usage/metric_definition_spec.rb
@@ -20,7 +20,8 @@ RSpec.describe Gitlab::Usage::MetricDefinition do
distribution: %w(ee ce),
tier: %w(free starter premium ultimate bronze silver gold),
name: 'uuid',
- data_category: 'standard'
+ data_category: 'standard',
+ removed_by_url: 'http://gdk.test'
}
end
@@ -132,6 +133,7 @@ RSpec.describe Gitlab::Usage::MetricDefinition do
:tier | %w(test ee)
:name | 'count_<adjective_describing>_boards'
:repair_issue_url | nil
+ :removed_by_url | 1
:instrumentation_class | 'Metric_Class'
:instrumentation_class | 'metricClass'
@@ -177,6 +179,24 @@ RSpec.describe Gitlab::Usage::MetricDefinition do
end
end
+ describe '#valid_service_ping_status?' do
+ context 'when metric has active status' do
+ it 'has to return true' do
+ attributes[:status] = 'active'
+
+ expect(described_class.new(path, attributes).valid_service_ping_status?).to be_truthy
+ end
+ end
+
+ context 'when metric has removed status' do
+ it 'has to return false' do
+ attributes[:status] = 'removed'
+
+ expect(described_class.new(path, attributes).valid_service_ping_status?).to be_falsey
+ end
+ end
+ end
+
describe 'statuses' do
using RSpec::Parameterized::TableSyntax
diff --git a/spec/lib/gitlab/usage/metric_spec.rb b/spec/lib/gitlab/usage/metric_spec.rb
index 19d2d3048eb..10ae94e746b 100644
--- a/spec/lib/gitlab/usage/metric_spec.rb
+++ b/spec/lib/gitlab/usage/metric_spec.rb
@@ -51,4 +51,31 @@ RSpec.describe Gitlab::Usage::Metric do
expect(described_class.new(issue_count_metric_definiton).with_suggested_name).to eq({ counts: { issues: 'count_issues' } })
end
end
+
+ context 'unavailable metric' do
+ let(:instrumentation_class) { "UnavailableMetric" }
+ let(:issue_count_metric_definiton) do
+ double(:issue_count_metric_definiton,
+ attributes.merge({ attributes: attributes, instrumentation_class: instrumentation_class })
+ )
+ end
+
+ before do
+ unavailable_metric_class = Class.new(Gitlab::Usage::Metrics::Instrumentations::CountIssuesMetric) do
+ def available?
+ false
+ end
+ end
+
+ stub_const("Gitlab::Usage::Metrics::Instrumentations::#{instrumentation_class}", unavailable_metric_class)
+ end
+
+ [:with_value, :with_instrumentation, :with_suggested_name].each do |method_name|
+ describe "##{method_name}" do
+ it 'returns an empty hash' do
+ expect(described_class.new(issue_count_metric_definiton).public_send(method_name)).to eq({})
+ end
+ end
+ end
+ end
end
diff --git a/spec/lib/gitlab/usage/metrics/instrumentations/collected_data_categories_metric_spec.rb b/spec/lib/gitlab/usage/metrics/instrumentations/collected_data_categories_metric_spec.rb
index 1b2170baf17..92d4de3c462 100644
--- a/spec/lib/gitlab/usage/metrics/instrumentations/collected_data_categories_metric_spec.rb
+++ b/spec/lib/gitlab/usage/metrics/instrumentations/collected_data_categories_metric_spec.rb
@@ -7,7 +7,7 @@ RSpec.describe Gitlab::Usage::Metrics::Instrumentations::CollectedDataCategories
let(:expected_value) { %w[standard subscription operational optional] }
before do
- allow_next_instance_of(ServicePing::PermitDataCategoriesService) do |instance|
+ allow_next_instance_of(ServicePing::PermitDataCategories) do |instance|
expect(instance).to receive(:execute).and_return(expected_value)
end
end
diff --git a/spec/lib/gitlab/usage/metrics/instrumentations/count_bulk_imports_entities_metric_spec.rb b/spec/lib/gitlab/usage/metrics/instrumentations/count_bulk_imports_entities_metric_spec.rb
new file mode 100644
index 00000000000..b85d5a3ebf9
--- /dev/null
+++ b/spec/lib/gitlab/usage/metrics/instrumentations/count_bulk_imports_entities_metric_spec.rb
@@ -0,0 +1,106 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Usage::Metrics::Instrumentations::CountBulkImportsEntitiesMetric do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:bulk_import_projects) do
+ create_list(:bulk_import_entity, 3, source_type: 'project_entity', created_at: 3.weeks.ago)
+ end
+
+ let_it_be(:bulk_import_groups) do
+ create_list(:bulk_import_entity, 3, source_type: 'group_entity', created_at: 3.weeks.ago)
+ end
+
+ let_it_be(:old_bulk_import_project) do
+ create(:bulk_import_entity, source_type: 'project_entity', created_at: 2.months.ago)
+ end
+
+ context 'with no source_type' do
+ context 'with all time frame' do
+ let(:expected_value) { 7 }
+ let(:expected_query) do
+ "SELECT COUNT(\"bulk_import_entities\".\"id\") FROM \"bulk_import_entities\""
+ end
+
+ it_behaves_like 'a correct instrumented metric value and query', time_frame: 'all', options: {}
+ end
+
+ context 'for 28d time frame' do
+ let(:expected_value) { 6 }
+ let(:start) { 30.days.ago.to_s(:db) }
+ let(:finish) { 2.days.ago.to_s(:db) }
+ let(:expected_query) do
+ "SELECT COUNT(\"bulk_import_entities\".\"id\") FROM \"bulk_import_entities\""\
+ " WHERE \"bulk_import_entities\".\"created_at\" BETWEEN '#{start}' AND '#{finish}'"
+ end
+
+ it_behaves_like 'a correct instrumented metric value and query', time_frame: '28d', options: {}
+ end
+ end
+
+ context 'with invalid source_type' do
+ it 'raises ArgumentError' do
+ expect { described_class.new(time_frame: 'all', options: { source_type: 'random' }) }
+ .to raise_error(ArgumentError, /source_type/)
+ end
+ end
+
+ context 'with source_type project_entity' do
+ context 'with all time frame' do
+ let(:expected_value) { 4 }
+ let(:expected_query) do
+ "SELECT COUNT(\"bulk_import_entities\".\"id\") FROM \"bulk_import_entities\""\
+ " WHERE \"bulk_import_entities\".\"source_type\" = 1"
+ end
+
+ it_behaves_like 'a correct instrumented metric value and query',
+ time_frame: 'all',
+ options: { source_type: 'project_entity' }
+ end
+
+ context 'for 28d time frame' do
+ let(:expected_value) { 3 }
+ let(:start) { 30.days.ago.to_s(:db) }
+ let(:finish) { 2.days.ago.to_s(:db) }
+ let(:expected_query) do
+ "SELECT COUNT(\"bulk_import_entities\".\"id\") FROM \"bulk_import_entities\""\
+ " WHERE \"bulk_import_entities\".\"created_at\" BETWEEN '#{start}' AND '#{finish}'"\
+ " AND \"bulk_import_entities\".\"source_type\" = 1"
+ end
+
+ it_behaves_like 'a correct instrumented metric value and query',
+ time_frame: '28d',
+ options: { source_type: 'project_entity' }
+ end
+ end
+
+ context 'with source_type group_entity' do
+ context 'with all time frame' do
+ let(:expected_value) { 3 }
+ let(:expected_query) do
+ "SELECT COUNT(\"bulk_import_entities\".\"id\") FROM \"bulk_import_entities\""\
+ " WHERE \"bulk_import_entities\".\"source_type\" = 0"
+ end
+
+ it_behaves_like 'a correct instrumented metric value and query',
+ time_frame: 'all',
+ options: { source_type: 'group_entity' }
+ end
+
+ context 'for 28d time frame' do
+ let(:expected_value) { 3 }
+ let(:start) { 30.days.ago.to_s(:db) }
+ let(:finish) { 2.days.ago.to_s(:db) }
+ let(:expected_query) do
+ "SELECT COUNT(\"bulk_import_entities\".\"id\") FROM \"bulk_import_entities\""\
+ " WHERE \"bulk_import_entities\".\"created_at\" BETWEEN '#{start}' AND '#{finish}'"\
+ " AND \"bulk_import_entities\".\"source_type\" = 0"
+ end
+
+ it_behaves_like 'a correct instrumented metric value and query',
+ time_frame: '28d',
+ options: { source_type: 'group_entity' }
+ end
+ end
+end
diff --git a/spec/lib/gitlab/usage/metrics/instrumentations/count_imported_projects_metric_spec.rb b/spec/lib/gitlab/usage/metrics/instrumentations/count_imported_projects_metric_spec.rb
new file mode 100644
index 00000000000..4c86410d609
--- /dev/null
+++ b/spec/lib/gitlab/usage/metrics/instrumentations/count_imported_projects_metric_spec.rb
@@ -0,0 +1,70 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Usage::Metrics::Instrumentations::CountImportedProjectsMetric do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:gitea_imports) do
+ create_list(:project, 3, import_type: 'gitea', creator_id: user.id, created_at: 3.weeks.ago)
+ end
+
+ let_it_be(:bitbucket_imports) do
+ create_list(:project, 2, import_type: 'bitbucket', creator_id: user.id, created_at: 3.weeks.ago)
+ end
+
+ let_it_be(:old_import) { create(:project, import_type: 'gitea', creator_id: user.id, created_at: 2.months.ago) }
+
+ context 'with import_type gitea' do
+ context 'with all time frame' do
+ let(:expected_value) { 4 }
+ let(:expected_query) do
+ "SELECT COUNT(\"projects\".\"id\") FROM \"projects\" WHERE \"projects\".\"import_type\" = 'gitea'"
+ end
+
+ it_behaves_like 'a correct instrumented metric value and query',
+ time_frame: 'all',
+ options: { import_type: 'gitea' }
+ end
+
+ context 'for 28d time frame' do
+ let(:expected_value) { 3 }
+ let(:start) { 30.days.ago.to_s(:db) }
+ let(:finish) { 2.days.ago.to_s(:db) }
+ let(:expected_query) do
+ "SELECT COUNT(\"projects\".\"id\") FROM \"projects\" WHERE \"projects\".\"created_at\""\
+ " BETWEEN '#{start}' AND '#{finish}' AND \"projects\".\"import_type\" = 'gitea'"
+ end
+
+ it_behaves_like 'a correct instrumented metric value and query',
+ time_frame: '28d',
+ options: { import_type: 'gitea' }
+ end
+ end
+
+ context 'with import_type bitbucket' do
+ context 'with all time frame' do
+ let(:expected_value) { 2 }
+ let(:expected_query) do
+ "SELECT COUNT(\"projects\".\"id\") FROM \"projects\" WHERE \"projects\".\"import_type\" = 'bitbucket'"
+ end
+
+ it_behaves_like 'a correct instrumented metric value and query',
+ time_frame: 'all',
+ options: { import_type: 'bitbucket' }
+ end
+
+ context 'for 28d time frame' do
+ let(:expected_value) { 2 }
+ let(:start) { 30.days.ago.to_s(:db) }
+ let(:finish) { 2.days.ago.to_s(:db) }
+ let(:expected_query) do
+ "SELECT COUNT(\"projects\".\"id\") FROM \"projects\" WHERE \"projects\".\"created_at\""\
+ " BETWEEN '#{start}' AND '#{finish}' AND \"projects\".\"import_type\" = 'bitbucket'"
+ end
+
+ it_behaves_like 'a correct instrumented metric value and query',
+ time_frame: '28d',
+ options: { import_type: 'bitbucket' }
+ end
+ end
+end
diff --git a/spec/lib/gitlab/usage/metrics/instrumentations/database_metric_spec.rb b/spec/lib/gitlab/usage/metrics/instrumentations/database_metric_spec.rb
index ea5ae1970de..8e7bd7b84e6 100644
--- a/spec/lib/gitlab/usage/metrics/instrumentations/database_metric_spec.rb
+++ b/spec/lib/gitlab/usage/metrics/instrumentations/database_metric_spec.rb
@@ -71,6 +71,33 @@ RSpec.describe Gitlab::Usage::Metrics::Instrumentations::DatabaseMetric do
end
end
+ context 'with availability defined' do
+ subject do
+ described_class.tap do |metric_class|
+ metric_class.relation { Issue }
+ metric_class.operation :count
+ metric_class.available? { false }
+ end.new(time_frame: 'all')
+ end
+
+ it 'responds to #available? properly' do
+ expect(subject.available?).to eq(false)
+ end
+ end
+
+ context 'with availability not defined' do
+ subject do
+ Class.new(described_class) do
+ relation { Issue }
+ operation :count
+ end.new(time_frame: 'all')
+ end
+
+ it 'responds to #available? properly' do
+ expect(subject.available?).to eq(true)
+ end
+ end
+
context 'with cache_start_and_finish_as called' do
subject do
described_class.tap do |metric_class|
@@ -134,4 +161,17 @@ RSpec.describe Gitlab::Usage::Metrics::Instrumentations::DatabaseMetric do
end
end
end
+
+ context 'with unimplemented operation method used' do
+ subject do
+ described_class.tap do |metric_class|
+ metric_class.relation { Issue }
+ metric_class.operation :invalid_operation
+ end.new(time_frame: 'all')
+ end
+
+ it 'raises an error' do
+ expect { subject }.to raise_error(described_class::UnimplementedOperationError)
+ end
+ end
end
diff --git a/spec/lib/gitlab/usage/metrics/instrumentations/redis_hll_metric_spec.rb b/spec/lib/gitlab/usage/metrics/instrumentations/redis_hll_metric_spec.rb
index 347a2c779cb..97306051533 100644
--- a/spec/lib/gitlab/usage/metrics/instrumentations/redis_hll_metric_spec.rb
+++ b/spec/lib/gitlab/usage/metrics/instrumentations/redis_hll_metric_spec.rb
@@ -25,4 +25,28 @@ RSpec.describe Gitlab::Usage::Metrics::Instrumentations::RedisHLLMetric, :clean_
it 'raise exception if events options is not present' do
expect { described_class.new(time_frame: '28d') }.to raise_error(ArgumentError)
end
+
+ describe 'children classes' do
+ let(:options) { { events: ['i_quickactions_approve'] } }
+
+ context 'availability not defined' do
+ subject { Class.new(described_class).new(time_frame: nil, options: options) }
+
+ it 'returns default availability' do
+ expect(subject.available?).to eq(true)
+ end
+ end
+
+ context 'availability defined' do
+ subject do
+ Class.new(described_class) do
+ available? { false }
+ end.new(time_frame: nil, options: options)
+ end
+
+ it 'returns defined availability' do
+ expect(subject.available?).to eq(false)
+ end
+ end
+ end
end
diff --git a/spec/lib/gitlab/usage/metrics/instrumentations/redis_metric_spec.rb b/spec/lib/gitlab/usage/metrics/instrumentations/redis_metric_spec.rb
index fb3bd1ba834..831f775ec9a 100644
--- a/spec/lib/gitlab/usage/metrics/instrumentations/redis_metric_spec.rb
+++ b/spec/lib/gitlab/usage/metrics/instrumentations/redis_metric_spec.rb
@@ -20,4 +20,28 @@ RSpec.describe Gitlab::Usage::Metrics::Instrumentations::RedisMetric, :clean_git
it 'raises an exception if counter_class option is not present' do
expect { described_class.new(event: 'pushes') }.to raise_error(ArgumentError)
end
+
+ describe 'children classes' do
+ let(:options) { { event: 'pushes', counter_class: 'SourceCodeCounter' } }
+
+ context 'availability not defined' do
+ subject { Class.new(described_class).new(time_frame: nil, options: options) }
+
+ it 'returns default availability' do
+ expect(subject.available?).to eq(true)
+ end
+ end
+
+ context 'availability defined' do
+ subject do
+ Class.new(described_class) do
+ available? { false }
+ end.new(time_frame: nil, options: options)
+ end
+
+ it 'returns defined availability' do
+ expect(subject.available?).to eq(false)
+ end
+ end
+ end
end
diff --git a/spec/lib/gitlab/usage/metrics/query_spec.rb b/spec/lib/gitlab/usage/metrics/query_spec.rb
index 60c8d044a64..65b8a7a046b 100644
--- a/spec/lib/gitlab/usage/metrics/query_spec.rb
+++ b/spec/lib/gitlab/usage/metrics/query_spec.rb
@@ -11,6 +11,22 @@ RSpec.describe Gitlab::Usage::Metrics::Query do
it 'does not mix a nil column with keyword arguments' do
expect(described_class.for(:count, User, nil)).to eq('SELECT COUNT("users"."id") FROM "users"')
end
+
+ it 'removes order from passed relation' do
+ expect(described_class.for(:count, User.order(:email), nil)).to eq('SELECT COUNT("users"."id") FROM "users"')
+ end
+
+ it 'returns valid raw SQL for join relations' do
+ expect(described_class.for(:count, User.joins(:issues), :email)).to eq(
+ 'SELECT COUNT("users"."email") FROM "users" INNER JOIN "issues" ON "issues"."author_id" = "users"."id"'
+ )
+ end
+
+ it 'returns valid raw SQL for join relations with joined columns' do
+ expect(described_class.for(:count, User.joins(:issues), 'issue.weight')).to eq(
+ 'SELECT COUNT("issue"."weight") FROM "users" INNER JOIN "issues" ON "issues"."author_id" = "users"."id"'
+ )
+ end
end
describe '.distinct_count' do
@@ -21,6 +37,22 @@ RSpec.describe Gitlab::Usage::Metrics::Query do
it 'does not mix a nil column with keyword arguments' do
expect(described_class.for(:distinct_count, Issue, nil)).to eq('SELECT COUNT(DISTINCT "issues"."id") FROM "issues"')
end
+
+ it 'removes order from passed relation' do
+ expect(described_class.for(:distinct_count, User.order(:email), nil)).to eq('SELECT COUNT(DISTINCT "users"."id") FROM "users"')
+ end
+
+ it 'returns valid raw SQL for join relations' do
+ expect(described_class.for(:distinct_count, User.joins(:issues), :email)).to eq(
+ 'SELECT COUNT(DISTINCT "users"."email") FROM "users" INNER JOIN "issues" ON "issues"."author_id" = "users"."id"'
+ )
+ end
+
+ it 'returns valid raw SQL for join relations with joined columns' do
+ expect(described_class.for(:distinct_count, User.joins(:issues), 'issue.weight')).to eq(
+ 'SELECT COUNT(DISTINCT "issue"."weight") FROM "users" INNER JOIN "issues" ON "issues"."author_id" = "users"."id"'
+ )
+ end
end
describe '.sum' do
diff --git a/spec/lib/gitlab/usage/service_ping/legacy_metric_timing_decorator_spec.rb b/spec/lib/gitlab/usage/service_ping/legacy_metric_timing_decorator_spec.rb
new file mode 100644
index 00000000000..46592379b3d
--- /dev/null
+++ b/spec/lib/gitlab/usage/service_ping/legacy_metric_timing_decorator_spec.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Usage::ServicePing::LegacyMetricTimingDecorator do
+ using RSpec::Parameterized::TableSyntax
+
+ let(:duration) { 123 }
+
+ where(:metric_value, :metric_class) do
+ 1 | Integer
+ "value" | String
+ true | TrueClass
+ false | FalseClass
+ nil | NilClass
+ end
+
+ with_them do
+ let(:decorated_object) { described_class.new(metric_value, duration) }
+
+ it 'exposes a duration with the correct value' do
+ expect(decorated_object.duration).to eq(duration)
+ end
+
+ it 'imitates wrapped class', :aggregate_failures do
+ expect(decorated_object).to eq metric_value
+ expect(decorated_object.class).to eq metric_class
+ expect(decorated_object.is_a?(metric_class)).to be_truthy
+ # rubocop:disable Style/ClassCheck
+ expect(decorated_object.kind_of?(metric_class)).to be_truthy
+ # rubocop:enable Style/ClassCheck
+ expect({ metric: decorated_object }.to_json).to eql({ metric: metric_value }.to_json)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/usage/service_ping_report_spec.rb b/spec/lib/gitlab/usage/service_ping_report_spec.rb
index b6119ab52ec..e7096988035 100644
--- a/spec/lib/gitlab/usage/service_ping_report_spec.rb
+++ b/spec/lib/gitlab/usage/service_ping_report_spec.rb
@@ -92,49 +92,6 @@ RSpec.describe Gitlab::Usage::ServicePingReport, :use_clean_rails_memory_store_c
end
context 'cross test values against queries' do
- # TODO: fix failing metrics https://gitlab.com/gitlab-org/gitlab/-/issues/353559
- let(:failing_todo_metrics) do
- ["counts.labels",
- "counts.jira_imports_total_imported_issues_count",
- "counts.in_product_marketing_email_create_0_sent",
- "counts.in_product_marketing_email_create_0_cta_clicked",
- "counts.in_product_marketing_email_create_1_sent",
- "counts.in_product_marketing_email_create_1_cta_clicked",
- "counts.in_product_marketing_email_create_2_sent",
- "counts.in_product_marketing_email_create_2_cta_clicked",
- "counts.in_product_marketing_email_verify_0_sent",
- "counts.in_product_marketing_email_verify_0_cta_clicked",
- "counts.in_product_marketing_email_verify_1_sent",
- "counts.in_product_marketing_email_verify_1_cta_clicked",
- "counts.in_product_marketing_email_verify_2_sent",
- "counts.in_product_marketing_email_verify_2_cta_clicked",
- "counts.in_product_marketing_email_trial_0_sent",
- "counts.in_product_marketing_email_trial_0_cta_clicked",
- "counts.in_product_marketing_email_trial_1_sent",
- "counts.in_product_marketing_email_trial_1_cta_clicked",
- "counts.in_product_marketing_email_trial_2_sent",
- "counts.in_product_marketing_email_trial_2_cta_clicked",
- "counts.in_product_marketing_email_team_0_sent",
- "counts.in_product_marketing_email_team_0_cta_clicked",
- "counts.in_product_marketing_email_team_1_sent",
- "counts.in_product_marketing_email_team_1_cta_clicked",
- "counts.in_product_marketing_email_team_2_sent",
- "counts.in_product_marketing_email_team_2_cta_clicked",
- "counts.in_product_marketing_email_experience_0_sent",
- "counts.in_product_marketing_email_team_short_0_sent",
- "counts.in_product_marketing_email_team_short_0_cta_clicked",
- "counts.in_product_marketing_email_trial_short_0_sent",
- "counts.in_product_marketing_email_trial_short_0_cta_clicked",
- "counts.in_product_marketing_email_admin_verify_0_sent",
- "counts.in_product_marketing_email_admin_verify_0_cta_clicked",
- "counts.ldap_users",
- "usage_activity_by_stage.create.projects_with_sectional_code_owner_rules",
- "usage_activity_by_stage.monitor.clusters_integrations_prometheus",
- "usage_activity_by_stage.monitor.projects_with_enabled_alert_integrations_histogram",
- "usage_activity_by_stage_monthly.create.projects_with_sectional_code_owner_rules",
- "usage_activity_by_stage_monthly.monitor.clusters_integrations_prometheus"]
- end
-
def fetch_value_by_query(query)
# Because test cases are run inside a transaction, if any query raise and error all queries that follows
# it are automatically canceled by PostgreSQL, to avoid that problem, and to provide exhaustive information
@@ -157,6 +114,24 @@ RSpec.describe Gitlab::Usage::ServicePingReport, :use_clean_rails_memory_store_c
accumulator
end
+ def type_cast_to_defined_type(value, metric_definition)
+ case metric_definition&.attributes&.fetch(:value_type)
+ when "string"
+ value.to_s
+ when "number"
+ value.to_i
+ when "object"
+ case metric_definition&.json_schema&.fetch("type")
+ when "array"
+ value.to_a
+ else
+ value.to_h
+ end
+ else
+ value
+ end
+ end
+
before do
stub_usage_data_connections
stub_object_store_settings
@@ -169,12 +144,13 @@ RSpec.describe Gitlab::Usage::ServicePingReport, :use_clean_rails_memory_store_c
let(:service_ping_payload) { described_class.for(output: :all_metrics_values) }
let(:metrics_queries_with_values) { build_payload_from_queries(described_class.for(output: :metrics_queries)) }
+ let(:metric_definitions) { ::Gitlab::Usage::MetricDefinition.definitions }
it 'generates queries that match collected data', :aggregate_failures do
message = "Expected %{query} result to match %{value} for %{key_path} metric"
metrics_queries_with_values.each do |key_path, query, value|
- next if failing_todo_metrics.include?(key_path.join('.'))
+ value = type_cast_to_defined_type(value, metric_definitions[key_path.join('.')])
expect(value).to(
eq(service_ping_payload.dig(*key_path)),
diff --git a/spec/lib/gitlab/usage_counters/pod_logs_spec.rb b/spec/lib/gitlab/usage_counters/pod_logs_spec.rb
deleted file mode 100644
index 1059c519b19..00000000000
--- a/spec/lib/gitlab/usage_counters/pod_logs_spec.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Gitlab::UsageCounters::PodLogs, :clean_gitlab_redis_shared_state do
- it_behaves_like 'a usage counter'
-end
diff --git a/spec/lib/gitlab/usage_data_counters/editor_unique_counter_spec.rb b/spec/lib/gitlab/usage_data_counters/editor_unique_counter_spec.rb
index 5f66387c82b..9aecb8f8b25 100644
--- a/spec/lib/gitlab/usage_data_counters/editor_unique_counter_spec.rb
+++ b/spec/lib/gitlab/usage_data_counters/editor_unique_counter_spec.rb
@@ -80,10 +80,13 @@ RSpec.describe Gitlab::UsageDataCounters::EditorUniqueCounter, :clean_gitlab_red
it 'can return the count of actions per user deduplicated' do
described_class.track_web_ide_edit_action(author: user1)
+ described_class.track_live_preview_edit_action(author: user1)
described_class.track_snippet_editor_edit_action(author: user1)
described_class.track_sfe_edit_action(author: user1)
described_class.track_web_ide_edit_action(author: user2, time: time - 2.days)
described_class.track_web_ide_edit_action(author: user3, time: time - 3.days)
+ described_class.track_live_preview_edit_action(author: user2, time: time - 2.days)
+ described_class.track_live_preview_edit_action(author: user3, time: time - 3.days)
described_class.track_snippet_editor_edit_action(author: user3, time: time - 3.days)
described_class.track_sfe_edit_action(author: user3, time: time - 3.days)
diff --git a/spec/lib/gitlab/usage_data_counters/ipynb_diff_activity_counter_spec.rb b/spec/lib/gitlab/usage_data_counters/ipynb_diff_activity_counter_spec.rb
new file mode 100644
index 00000000000..60c4424d2ae
--- /dev/null
+++ b/spec/lib/gitlab/usage_data_counters/ipynb_diff_activity_counter_spec.rb
@@ -0,0 +1,107 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::UsageDataCounters::IpynbDiffActivityCounter, :clean_gitlab_redis_shared_state do
+ let(:user) { build(:user, id: 1) }
+ let(:for_mr) { false }
+ let(:for_commit) { false }
+ let(:first_note) { build(:note, author: user, id: 1) }
+ let(:second_note) { build(:note, author: user, id: 2) }
+
+ before do
+ allow(first_note).to receive(:for_merge_request?).and_return(for_mr)
+ allow(second_note).to receive(:for_merge_request?).and_return(for_mr)
+ allow(first_note).to receive(:for_commit?).and_return(for_commit)
+ allow(second_note).to receive(:for_commit?).and_return(for_commit)
+ end
+
+ subject do
+ described_class.note_created(first_note)
+ described_class.note_created(first_note)
+ described_class.note_created(second_note)
+ end
+
+ shared_examples_for 'an action that tracks events' do
+ specify do
+ expect { 2.times { subject } }
+ .to change { event_count(action) }.by(2)
+ .and change { event_count(per_user_action) }.by(1)
+ end
+ end
+
+ shared_examples_for 'an action that does not track events' do
+ specify do
+ expect { 2.times { subject } }
+ .to change { event_count(action) }.by(0)
+ .and change { event_count(per_user_action) }.by(0)
+ end
+ end
+
+ describe '#track_note_created_in_ipynb_diff' do
+ context 'note is for commit' do
+ let(:for_commit) { true }
+
+ it_behaves_like 'an action that tracks events' do
+ let(:action) {described_class::NOTE_CREATED_IN_IPYNB_DIFF_ACTION}
+ let(:per_user_action) {described_class::USER_CREATED_NOTE_IN_IPYNB_DIFF_ACTION}
+ end
+
+ it_behaves_like 'an action that tracks events' do
+ let(:action) {described_class::NOTE_CREATED_IN_IPYNB_DIFF_COMMIT_ACTION}
+ let(:per_user_action) {described_class::USER_CREATED_NOTE_IN_IPYNB_DIFF_COMMIT_ACTION}
+ end
+
+ it_behaves_like 'an action that does not track events' do
+ let(:action) {described_class::NOTE_CREATED_IN_IPYNB_DIFF_MR_ACTION}
+ let(:per_user_action) {described_class::USER_CREATED_NOTE_IN_IPYNB_DIFF_MR_ACTION}
+ end
+ end
+
+ context 'note is for MR' do
+ let(:for_mr) { true }
+
+ it_behaves_like 'an action that tracks events' do
+ let(:action) {described_class::NOTE_CREATED_IN_IPYNB_DIFF_MR_ACTION}
+ let(:per_user_action) {described_class::USER_CREATED_NOTE_IN_IPYNB_DIFF_MR_ACTION}
+ end
+
+ it_behaves_like 'an action that tracks events' do
+ let(:action) {described_class::NOTE_CREATED_IN_IPYNB_DIFF_ACTION}
+ let(:per_user_action) {described_class::USER_CREATED_NOTE_IN_IPYNB_DIFF_ACTION}
+ end
+
+ it_behaves_like 'an action that does not track events' do
+ let(:action) {described_class::NOTE_CREATED_IN_IPYNB_DIFF_COMMIT_ACTION}
+ let(:per_user_action) {described_class::USER_CREATED_NOTE_IN_IPYNB_DIFF_COMMIT_ACTION}
+ end
+ end
+
+ context 'note is for neither MR nor Commit' do
+ it_behaves_like 'an action that does not track events' do
+ let(:action) {described_class::NOTE_CREATED_IN_IPYNB_DIFF_ACTION}
+ let(:per_user_action) {described_class::USER_CREATED_NOTE_IN_IPYNB_DIFF_ACTION}
+ end
+
+ it_behaves_like 'an action that does not track events' do
+ let(:action) {described_class::NOTE_CREATED_IN_IPYNB_DIFF_MR_ACTION}
+ let(:per_user_action) {described_class::USER_CREATED_NOTE_IN_IPYNB_DIFF_MR_ACTION}
+ end
+
+ it_behaves_like 'an action that does not track events' do
+ let(:action) {described_class::NOTE_CREATED_IN_IPYNB_DIFF_COMMIT_ACTION}
+ let(:per_user_action) {described_class::USER_CREATED_NOTE_IN_IPYNB_DIFF_COMMIT_ACTION}
+ end
+ end
+ end
+
+ private
+
+ def event_count(event_name)
+ Gitlab::UsageDataCounters::HLLRedisCounter.unique_events(
+ event_names: event_name,
+ start_date: 2.weeks.ago,
+ end_date: 2.weeks.from_now
+ )
+ end
+end
diff --git a/spec/lib/gitlab/usage_data_queries_spec.rb b/spec/lib/gitlab/usage_data_queries_spec.rb
index 88322e1b971..7c64a31c499 100644
--- a/spec/lib/gitlab/usage_data_queries_spec.rb
+++ b/spec/lib/gitlab/usage_data_queries_spec.rb
@@ -11,6 +11,12 @@ RSpec.describe Gitlab::UsageDataQueries do
end
end
+ describe '.with_duration' do
+ it 'yields passed block' do
+ expect { |block| described_class.with_duration(&block) }.to yield_with_no_args
+ end
+ end
+
describe '.count' do
it 'returns the raw SQL' do
expect(described_class.count(User)).to start_with('SELECT COUNT("users"."id") FROM "users"')
diff --git a/spec/lib/gitlab/usage_data_spec.rb b/spec/lib/gitlab/usage_data_spec.rb
index 8a919a0a72e..7edec6d13f4 100644
--- a/spec/lib/gitlab/usage_data_spec.rb
+++ b/spec/lib/gitlab/usage_data_spec.rb
@@ -1080,7 +1080,7 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
it 'reports collected data categories' do
expected_value = %w[standard subscription operational optional]
- allow_next_instance_of(ServicePing::PermitDataCategoriesService) do |instance|
+ allow_next_instance_of(ServicePing::PermitDataCategories) do |instance|
expect(instance).to receive(:execute).and_return(expected_value)
end
@@ -1470,4 +1470,31 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
end
end
end
+
+ describe ".with_duration" do
+ context 'with feature flag measure_service_ping_metric_collection turned off' do
+ before do
+ stub_feature_flags(measure_service_ping_metric_collection: false)
+ end
+
+ it 'does NOT record duration and return block response' do
+ expect(::Gitlab::Usage::ServicePing::LegacyMetricTimingDecorator).not_to receive(:new)
+
+ expect(described_class.with_duration { 1 + 1 }).to be 2
+ end
+ end
+
+ context 'with feature flag measure_service_ping_metric_collection turned off' do
+ before do
+ stub_feature_flags(measure_service_ping_metric_collection: true)
+ end
+
+ it 'records duration' do
+ expect(::Gitlab::Usage::ServicePing::LegacyMetricTimingDecorator)
+ .to receive(:new).with(2, kind_of(Float))
+
+ described_class.with_duration { 1 + 1 }
+ end
+ end
+ end
end
diff --git a/spec/lib/gitlab/user_access_spec.rb b/spec/lib/gitlab/user_access_spec.rb
index 01890305df4..b1de3e21b77 100644
--- a/spec/lib/gitlab/user_access_spec.rb
+++ b/spec/lib/gitlab/user_access_spec.rb
@@ -30,17 +30,6 @@ RSpec.describe Gitlab::UserAccess do
end
end
- describe 'push to branch in an internal project' do
- it 'will not infinitely loop when a project is internal' do
- project.visibility_level = Gitlab::VisibilityLevel::INTERNAL
- project.save!
-
- expect(project).not_to receive(:branch_allows_collaboration?)
-
- access.can_push_to_branch?('master')
- end
- end
-
describe 'push to empty project' do
let(:empty_project) { create(:project_empty_repo) }
let(:project_access) { described_class.new(user, container: empty_project) }
diff --git a/spec/lib/gitlab/utils/usage_data_spec.rb b/spec/lib/gitlab/utils/usage_data_spec.rb
index b44c6565538..a74a9f06c6f 100644
--- a/spec/lib/gitlab/utils/usage_data_spec.rb
+++ b/spec/lib/gitlab/utils/usage_data_spec.rb
@@ -31,6 +31,12 @@ RSpec.describe Gitlab::Utils::UsageData do
end
end
+ describe '.with_duration' do
+ it 'yields passed block' do
+ expect { |block| described_class.with_duration(&block) }.to yield_with_no_args
+ end
+ end
+
describe '#add_metric' do
let(:metric) { 'UuidMetric'}
@@ -48,6 +54,13 @@ RSpec.describe Gitlab::Utils::UsageData do
expect(described_class.count(relation, batch: false)).to eq(1)
end
+ it 'records duration' do
+ expect(described_class).to receive(:with_duration)
+ allow(relation).to receive(:count).and_return(1)
+
+ described_class.count(relation, batch: false)
+ end
+
context 'when counting fails' do
subject { described_class.count(relation, batch: false) }
@@ -68,6 +81,13 @@ RSpec.describe Gitlab::Utils::UsageData do
expect(described_class.distinct_count(relation, batch: false)).to eq(1)
end
+ it 'records duration' do
+ expect(described_class).to receive(:with_duration)
+ allow(relation).to receive(:distinct_count_by).and_return(1)
+
+ described_class.distinct_count(relation, batch: false)
+ end
+
context 'when counting fails' do
subject { described_class.distinct_count(relation, batch: false) }
@@ -206,14 +226,6 @@ RSpec.describe Gitlab::Utils::UsageData do
it_behaves_like 'failing hardening method'
end
-
- it 'logs error and returns DISTRIBUTED_HLL_FALLBACK value when counting raises any error', :aggregate_failures do
- error = StandardError.new('')
- allow(Gitlab::Database::PostgresHll::BatchDistinctCounter).to receive(:new).and_raise(error)
-
- expect(Gitlab::ErrorTracking).to receive(:track_and_raise_for_dev_exception).with(error)
- expect(described_class.estimate_batch_distinct_count(relation)).to eq(4)
- end
end
end
@@ -229,6 +241,13 @@ RSpec.describe Gitlab::Utils::UsageData do
expect(described_class.sum(relation, :column, batch_size: 100, start: 2, finish: 3)).to eq(1)
end
+ it 'records duration' do
+ expect(described_class).to receive(:with_duration)
+ allow(Gitlab::Database::BatchCount).to receive(:batch_sum).and_return(1)
+
+ described_class.sum(relation, :column)
+ end
+
context 'when counting fails' do
subject { described_class.sum(relation, :column) }
@@ -316,6 +335,12 @@ RSpec.describe Gitlab::Utils::UsageData do
expect(histogram).to eq('2' => 1)
end
+ it 'records duration' do
+ expect(described_class).to receive(:with_duration)
+
+ described_class.histogram(relation, column, buckets: 1..100)
+ end
+
context 'when query timeout' do
subject do
with_statement_timeout(0.001) do
@@ -368,6 +393,12 @@ RSpec.describe Gitlab::Utils::UsageData do
expect(described_class.add).to eq(0)
end
+ it 'records duration' do
+ expect(described_class).to receive(:with_duration)
+
+ described_class.add
+ end
+
context 'when adding fails' do
subject { described_class.add(nil, 3) }
@@ -392,6 +423,12 @@ RSpec.describe Gitlab::Utils::UsageData do
it_behaves_like 'failing hardening method', StandardError
end
+ it 'records duration' do
+ expect(described_class).to receive(:with_duration)
+
+ described_class.alt_usage_data
+ end
+
it 'returns the evaluated block when give' do
expect(described_class.alt_usage_data { Gitlab::CurrentSettings.uuid } ).to eq(Gitlab::CurrentSettings.uuid)
end
@@ -402,6 +439,12 @@ RSpec.describe Gitlab::Utils::UsageData do
end
describe '#redis_usage_data' do
+ it 'records duration' do
+ expect(described_class).to receive(:with_duration)
+
+ described_class.redis_usage_data
+ end
+
context 'with block given' do
context 'when method fails' do
subject { described_class.redis_usage_data { raise ::Redis::CommandError } }
@@ -445,6 +488,12 @@ RSpec.describe Gitlab::Utils::UsageData do
end
describe '#with_prometheus_client' do
+ it 'records duration' do
+ expect(described_class).to receive(:with_duration)
+
+ described_class.with_prometheus_client { |client| client }
+ end
+
it 'returns fallback with for an exception in yield block' do
allow(described_class).to receive(:prometheus_client).and_return(Gitlab::PrometheusClient.new('http://localhost:9090'))
result = described_class.with_prometheus_client(fallback: -42) { |client| raise StandardError }
diff --git a/spec/lib/gitlab/utils_spec.rb b/spec/lib/gitlab/utils_spec.rb
index 6b12fb4a84a..0648d276a6b 100644
--- a/spec/lib/gitlab/utils_spec.rb
+++ b/spec/lib/gitlab/utils_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe Gitlab::Utils do
using RSpec::Parameterized::TableSyntax
- delegate :to_boolean, :boolean_to_yes_no, :slugify, :random_string, :which,
+ delegate :to_boolean, :boolean_to_yes_no, :slugify, :which,
:ensure_array_from_string, :to_exclusive_sentence, :bytes_to_megabytes,
:append_path, :check_path_traversal!, :allowlisted?, :check_allowed_absolute_path!, :decode_path, :ms_to_round_sec, :check_allowed_absolute_path_and_path_traversal!, to: :described_class
@@ -311,12 +311,6 @@ RSpec.describe Gitlab::Utils do
end
end
- describe '.random_string' do
- it 'generates a string' do
- expect(random_string).to be_kind_of(String)
- end
- end
-
describe '.which' do
before do
stub_env('PATH', '/sbin:/usr/bin:/home/joe/bin')
diff --git a/spec/lib/gitlab/workhorse_spec.rb b/spec/lib/gitlab/workhorse_spec.rb
index 3bab9aec454..703a4b5399e 100644
--- a/spec/lib/gitlab/workhorse_spec.rb
+++ b/spec/lib/gitlab/workhorse_spec.rb
@@ -244,15 +244,13 @@ RSpec.describe Gitlab::Workhorse do
GitalyServer: {
features: { 'gitaly-feature-enforce-requests-limits' => 'true' },
address: Gitlab::GitalyClient.address('default'),
- token: Gitlab::GitalyClient.token('default'),
- sidechannel: false
+ token: Gitlab::GitalyClient.token('default')
}
}
end
before do
allow(Gitlab.config.gitaly).to receive(:enabled).and_return(true)
- stub_feature_flags(workhorse_use_sidechannel: false)
end
it 'includes a Repository param' do
@@ -334,46 +332,6 @@ RSpec.describe Gitlab::Workhorse do
it { expect { subject }.to raise_exception('Unsupported action: download') }
end
-
- context 'when workhorse_use_sidechannel flag is set' do
- context 'when a feature flag is set globally' do
- before do
- stub_feature_flags(workhorse_use_sidechannel: true)
- end
-
- it 'sets the flag to true' do
- response = described_class.git_http_ok(repository, Gitlab::GlRepository::PROJECT, user, action)
-
- expect(response.dig(:GitalyServer, :sidechannel)).to eq(true)
- end
- end
-
- context 'when a feature flag is set for a single project' do
- before do
- stub_feature_flags(workhorse_use_sidechannel: project)
- end
-
- it 'sets the flag to true for that project' do
- response = described_class.git_http_ok(repository, Gitlab::GlRepository::PROJECT, user, action)
-
- expect(response.dig(:GitalyServer, :sidechannel)).to eq(true)
- end
-
- it 'sets the flag to false for other projects' do
- other_project = create(:project, :public, :repository)
- response = described_class.git_http_ok(other_project.repository, Gitlab::GlRepository::PROJECT, user, action)
-
- expect(response.dig(:GitalyServer, :sidechannel)).to eq(false)
- end
-
- it 'sets the flag to false when there is no project' do
- snippet = create(:personal_snippet, :repository)
- response = described_class.git_http_ok(snippet.repository, Gitlab::GlRepository::SNIPPET, user, action)
-
- expect(response.dig(:GitalyServer, :sidechannel)).to eq(false)
- end
- end
- end
end
context 'when receive_max_input_size has been updated' do
@@ -448,6 +406,14 @@ RSpec.describe Gitlab::Workhorse do
end
end
+ describe '.detect_content_type' do
+ subject { described_class.detect_content_type }
+
+ it 'returns array setting detect content type in workhorse' do
+ expect(subject).to eq(%w[Gitlab-Workhorse-Detect-Content-Type true])
+ end
+ end
+
describe '.send_git_blob' do
include FakeBlobHelpers
diff --git a/spec/lib/gitlab/zentao/client_spec.rb b/spec/lib/gitlab/zentao/client_spec.rb
index 86b310fe417..135f13e6265 100644
--- a/spec/lib/gitlab/zentao/client_spec.rb
+++ b/spec/lib/gitlab/zentao/client_spec.rb
@@ -130,4 +130,36 @@ RSpec.describe Gitlab::Zentao::Client do
end
end
end
+
+ describe '#url' do
+ context 'api url' do
+ shared_examples 'joins api_url correctly' do
+ it 'verify url' do
+ expect(integration.send(:url, "products/1").to_s)
+ .to eq("https://jihudemo.zentao.net/zentao/api.php/v1/products/1")
+ end
+ end
+
+ context 'no ends slash' do
+ let(:zentao_integration) { create(:zentao_integration, api_url: 'https://jihudemo.zentao.net/zentao') }
+
+ include_examples 'joins api_url correctly'
+ end
+
+ context 'ends slash' do
+ let(:zentao_integration) { create(:zentao_integration, api_url: 'https://jihudemo.zentao.net/zentao/') }
+
+ include_examples 'joins api_url correctly'
+ end
+ end
+
+ context 'no api url' do
+ let(:zentao_integration) { create(:zentao_integration, url: 'https://jihudemo.zentao.net') }
+
+ it 'joins url correctly' do
+ expect(integration.send(:url, "products/1").to_s)
+ .to eq("https://jihudemo.zentao.net/api.php/v1/products/1")
+ end
+ end
+ end
end