summaryrefslogtreecommitdiff
path: root/qa
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-09-19 23:18:09 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2022-09-19 23:18:09 +0000
commit6ed4ec3e0b1340f96b7c043ef51d1b33bbe85fde (patch)
treedc4d20fe6064752c0bd323187252c77e0a89144b /qa
parent9868dae7fc0655bd7ce4a6887d4e6d487690eeed (diff)
downloadgitlab-ce-6ed4ec3e0b1340f96b7c043ef51d1b33bbe85fde.tar.gz
Add latest changes from gitlab-org/gitlab@15-4-stable-eev15.4.0-rc42
Diffstat (limited to 'qa')
-rw-r--r--qa/.confiner/master.yml2
-rw-r--r--qa/Gemfile2
-rw-r--r--qa/Gemfile.lock23
-rw-r--r--qa/README.md6
-rw-r--r--qa/Rakefile28
-rw-r--r--qa/lib/gitlab/page/admin/subscription.rb9
-rw-r--r--qa/qa.rb1
-rw-r--r--qa/qa/fixtures/auto_devops_rack/Dockerfile9
-rw-r--r--qa/qa/fixtures/auto_devops_rack/Gemfile5
-rw-r--r--qa/qa/fixtures/auto_devops_rack/Gemfile.lock15
-rw-r--r--qa/qa/fixtures/auto_devops_rack/Rakefile9
-rw-r--r--qa/qa/fixtures/auto_devops_rack/config.ru3
-rw-r--r--qa/qa/fixtures/package_managers/terraform/module_upload.yaml.erb21
-rw-r--r--qa/qa/flow/login.rb4
-rw-r--r--qa/qa/flow/merge_request.rb7
-rw-r--r--qa/qa/flow/pipeline.rb4
-rw-r--r--qa/qa/flow/project.rb4
-rw-r--r--qa/qa/flow/purchase.rb2
-rw-r--r--qa/qa/flow/saml.rb4
-rw-r--r--qa/qa/flow/settings.rb4
-rw-r--r--qa/qa/flow/sign_up.rb4
-rw-r--r--qa/qa/flow/user.rb4
-rw-r--r--qa/qa/flow/user_onboarding.rb4
-rw-r--r--qa/qa/page/base.rb2
-rw-r--r--qa/qa/page/component/access_tokens.rb8
-rw-r--r--qa/qa/page/component/ci_badge_link.rb4
-rw-r--r--qa/qa/page/component/content_editor.rb9
-rw-r--r--qa/qa/page/component/note.rb4
-rw-r--r--qa/qa/page/component/project/templates.rb2
-rw-r--r--qa/qa/page/component/web_ide/modal/create_new_file.rb2
-rw-r--r--qa/qa/page/component/wiki_page_form.rb4
-rw-r--r--qa/qa/page/file/shared/commit_message.rb4
-rw-r--r--qa/qa/page/file/show.rb5
-rw-r--r--qa/qa/page/group/members.rb4
-rw-r--r--qa/qa/page/group/menu.rb6
-rw-r--r--qa/qa/page/group/settings/package_registries.rb23
-rw-r--r--qa/qa/page/main/menu.rb33
-rw-r--r--qa/qa/page/merge_request/show.rb2
-rw-r--r--qa/qa/page/profile/ssh_keys.rb15
-rw-r--r--qa/qa/page/project/fork/new.rb29
-rw-r--r--qa/qa/page/project/infrastructure/kubernetes/index.rb9
-rw-r--r--qa/qa/page/project/job/show.rb6
-rw-r--r--qa/qa/page/project/monitor/metrics/show.rb8
-rw-r--r--qa/qa/page/project/new.rb2
-rw-r--r--qa/qa/page/project/packages/index.rb9
-rw-r--r--qa/qa/page/project/pipeline/new.rb2
-rw-r--r--qa/qa/page/project/settings/main.rb11
-rw-r--r--qa/qa/page/project/settings/merge_request.rb2
-rw-r--r--qa/qa/page/project/settings/mirroring_repositories.rb3
-rw-r--r--qa/qa/page/project/settings/pages.rb1
-rw-r--r--qa/qa/page/project/settings/protected_branches.rb12
-rw-r--r--qa/qa/page/project/show.rb8
-rw-r--r--qa/qa/page/project/sub_menus/packages.rb12
-rw-r--r--qa/qa/page/project/sub_menus/repository.rb8
-rw-r--r--qa/qa/page/project/sub_menus/settings.rb8
-rw-r--r--qa/qa/page/project/web_ide/edit.rb2
-rw-r--r--qa/qa/resource/base.rb24
-rw-r--r--qa/qa/resource/ci_variable.rb6
-rw-r--r--qa/qa/resource/clusters/agent.rb17
-rw-r--r--qa/qa/resource/clusters/agent_token.rb21
-rw-r--r--qa/qa/resource/fork.rb2
-rw-r--r--qa/qa/resource/group.rb5
-rw-r--r--qa/qa/resource/group_access_token.rb47
-rw-r--r--qa/qa/resource/group_base.rb4
-rw-r--r--qa/qa/resource/impersonation_token.rb2
-rw-r--r--qa/qa/resource/job.rb9
-rw-r--r--qa/qa/resource/kubernetes_cluster/project_cluster.rb50
-rw-r--r--qa/qa/resource/personal_access_token.rb42
-rw-r--r--qa/qa/resource/project_access_token.rb48
-rw-r--r--qa/qa/resource/runner.rb5
-rw-r--r--qa/qa/resource/sandbox.rb6
-rw-r--r--qa/qa/resource/user.rb4
-rw-r--r--qa/qa/runtime/browser.rb8
-rw-r--r--qa/qa/runtime/env.rb20
-rw-r--r--qa/qa/runtime/fixtures.rb2
-rw-r--r--qa/qa/runtime/user.rb5
-rw-r--r--qa/qa/scenario/test/instance/cloud_activation.rb13
-rw-r--r--qa/qa/scenario/test/instance/integrations.rb13
-rw-r--r--qa/qa/scenario/test/instance/jira.rb13
-rw-r--r--qa/qa/scenario/test/instance/large_setup.rb13
-rw-r--r--qa/qa/scenario/test/instance/metrics.rb13
-rw-r--r--qa/qa/scenario/test/instance/object_storage.rb13
-rw-r--r--qa/qa/scenario/test/instance/packages.rb13
-rw-r--r--qa/qa/scenario/test/instance/repository_storage.rb13
-rw-r--r--qa/qa/scenario/test/instance/review_blocking.rb16
-rw-r--r--qa/qa/scenario/test/instance/review_non_blocking.rb16
-rw-r--r--qa/qa/service/cluster_provider/gcloud.rb38
-rw-r--r--qa/qa/service/kubernetes_cluster.rb14
-rw-r--r--qa/qa/specs/features/api/12_systems/gitaly/automatic_failover_and_recovery_spec.rb (renamed from qa/qa/specs/features/api/3_create/gitaly/automatic_failover_and_recovery_spec.rb)14
-rw-r--r--qa/qa/specs/features/api/12_systems/gitaly/backend_node_recovery_spec.rb63
-rw-r--r--qa/qa/specs/features/api/12_systems/gitaly/changing_repository_storage_spec.rb (renamed from qa/qa/specs/features/api/3_create/gitaly/changing_repository_storage_spec.rb)18
-rw-r--r--qa/qa/specs/features/api/12_systems/gitaly/distributed_reads_spec.rb96
-rw-r--r--qa/qa/specs/features/api/12_systems/gitaly/gitaly_mtls_spec.rb41
-rw-r--r--qa/qa/specs/features/api/12_systems/gitaly/praefect_connectivity_spec.rb (renamed from qa/qa/specs/features/api/3_create/gitaly/praefect_connectivity_spec.rb)16
-rw-r--r--qa/qa/specs/features/api/12_systems/gitaly/praefect_dataloss_spec.rb (renamed from qa/qa/specs/features/api/3_create/gitaly/praefect_dataloss_spec.rb)44
-rw-r--r--qa/qa/specs/features/api/12_systems/gitaly/praefect_replication_queue_spec.rb (renamed from qa/qa/specs/features/api/3_create/gitaly/praefect_replication_queue_spec.rb)7
-rw-r--r--qa/qa/specs/features/api/12_systems/gitaly/praefect_repo_sync_spec.rb (renamed from qa/qa/specs/features/api/3_create/gitaly/praefect_repo_sync_spec.rb)63
-rw-r--r--qa/qa/specs/features/api/1_manage/import_large_github_repo_spec.rb9
-rw-r--r--qa/qa/specs/features/api/1_manage/user_inherited_access_spec.rb7
-rw-r--r--qa/qa/specs/features/api/3_create/gitaly/backend_node_recovery_spec.rb65
-rw-r--r--qa/qa/specs/features/api/3_create/gitaly/distributed_reads_spec.rb92
-rw-r--r--qa/qa/specs/features/api/3_create/gitaly/gitaly_mtls_spec.rb43
-rw-r--r--qa/qa/specs/features/api/3_create/repository/commit_to_templated_project_spec.rb2
-rw-r--r--qa/qa/specs/features/api/3_create/repository/default_branch_name_setting_spec.rb2
-rw-r--r--qa/qa/specs/features/api/3_create/repository/files_spec.rb2
-rw-r--r--qa/qa/specs/features/api/3_create/repository/project_archive_compare_spec.rb16
-rw-r--r--qa/qa/specs/features/api/3_create/repository/push_postreceive_idempotent_spec.rb2
-rw-r--r--qa/qa/specs/features/api/3_create/repository/storage_size_spec.rb2
-rw-r--r--qa/qa/specs/features/api/3_create/repository/tag_revision_trigger_prereceive_hook_spec.rb43
-rw-r--r--qa/qa/specs/features/api/4_verify/file_variable_spec.rb142
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/login/log_in_with_2fa_spec.rb6
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/login/register_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/project/dashboard_images_spec.rb6
-rw-r--r--qa/qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/merge_request/merge_merge_request_from_fork_spec.rb6
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/merge_request/rebase_merge_request_spec.rb8
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/pages/new_static_page_spec.rb (renamed from qa/qa/specs/features/browser_ui/3_create/pages/pages_pipeline_spec.rb)17
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/add_file_template_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/add_list_delete_branches_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/branch_with_unusual_name_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/clone_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/file/create_file_via_web_spec.rb4
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/file/delete_file_via_web_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/file/edit_file_via_web_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/file/file_with_unusual_name_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/license_detecton_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/move_project_create_fork_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/protected_tags_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/protocol_v2_push_http_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/protocol_v2_push_ssh_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/push_http_private_token_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/push_mirroring_lfs_over_http_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/push_mirroring_over_http_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/push_over_http_file_size_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/push_over_http_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/push_over_ssh_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/push_protected_branch_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/push_to_canary_gitaly_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/ssh_key_support_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/user_views_commit_diff_patch_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/4_verify/ci_variable/prefill_variables_spec.rb72
-rw-r--r--qa/qa/specs/features/browser_ui/4_verify/pipeline/pipeline_with_image_pull_policy_spec.rb21
-rw-r--r--qa/qa/specs/features/browser_ui/5_package/infrastructure_registry/terraform_module_registry_spec.rb81
-rw-r--r--qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_group_level_spec.rb8
-rw-r--r--qa/qa/specs/features/browser_ui/5_package/package_registry/pypi_repository_spec.rb4
-rw-r--r--qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb160
-rw-r--r--qa/qa/specs/features/browser_ui/7_configure/kubernetes/kubernetes_integration_spec.rb38
-rw-r--r--qa/qa/specs/features/shared_examples/merge_with_code_owner_shared_examples.rb8
-rw-r--r--qa/qa/specs/helpers/context_selector.rb4
-rw-r--r--qa/qa/specs/runner.rb3
-rw-r--r--qa/qa/specs/spec_helper.rb4
-rw-r--r--qa/qa/support/api.rb23
-rw-r--r--qa/qa/support/json_formatter.rb1
-rw-r--r--qa/qa/support/matchers/eventually_matcher.rb1
-rw-r--r--qa/qa/tools/ci/ff_changes.rb66
-rw-r--r--qa/qa/tools/ci/helpers.rb50
-rw-r--r--qa/qa/tools/ci/non_empty_suites.rb98
-rw-r--r--qa/qa/tools/ci/qa_changes.rb116
-rw-r--r--qa/qa/tools/ci/test_results.rb78
-rw-r--r--qa/qa/tools/revoke_all_personal_access_tokens.rb44
-rw-r--r--qa/qa/tools/revoke_user_personal_access_tokens.rb94
-rw-r--r--qa/qa/tools/test_resources_handler.rb1
-rw-r--r--qa/spec/fixtures/ff/bulk_import_projects.yml8
-rw-r--r--qa/spec/resource/base_spec.rb22
-rw-r--r--qa/spec/resource/user_spec.rb9
-rw-r--r--qa/spec/specs/helpers/context_selector_spec.rb20
-rw-r--r--qa/spec/tools/ci/ff_changes_spec.rb51
-rw-r--r--qa/spec/tools/ci/non_empty_suites_spec.rb19
-rw-r--r--qa/spec/tools/ci/qa_changes_spec.rb87
-rw-r--r--qa/tasks/ci.rake61
-rw-r--r--qa/tasks/helpers/util.rb49
171 files changed, 2260 insertions, 873 deletions
diff --git a/qa/.confiner/master.yml b/qa/.confiner/master.yml
index bfb44facd7d..e6fc3e68747 100644
--- a/qa/.confiner/master.yml
+++ b/qa/.confiner/master.yml
@@ -4,7 +4,7 @@
args:
threshold: 3 # 3 failures
private_token: $QA_GITLAB_CI_TOKEN
- project_id: gitlab-org/gitlab-qa-mirror # https://gitlab.com/gitlab-org/gitlab-qa-mirror/
+ project_id: gitlab-org/gitlab
target_project: gitlab-org/gitlab
failure_issue_labels: QA,Quality
failure_issue_prefix: "Failure in "
diff --git a/qa/Gemfile b/qa/Gemfile
index 7c46d35bb48..cf939f8e301 100644
--- a/qa/Gemfile
+++ b/qa/Gemfile
@@ -2,7 +2,7 @@
source 'https://rubygems.org'
-gem 'gitlab-qa', '~> 7', require: 'gitlab/qa'
+gem 'gitlab-qa', '~> 8', require: 'gitlab/qa'
gem 'activesupport', '~> 6.1.4.7' # This should stay in sync with the root's Gemfile
gem 'allure-rspec', '~> 2.16.0'
gem 'capybara', '~> 3.35.0'
diff --git a/qa/Gemfile.lock b/qa/Gemfile.lock
index ff382788c5a..dd14b675769 100644
--- a/qa/Gemfile.lock
+++ b/qa/Gemfile.lock
@@ -11,8 +11,8 @@ GEM
adamantium (0.2.0)
ice_nine (~> 0.11.0)
memoizable (~> 0.4.0)
- addressable (2.8.0)
- public_suffix (>= 2.0.2, < 5.0)
+ addressable (2.8.1)
+ public_suffix (>= 2.0.2, < 6.0)
airborne (0.3.4)
activesupport
rack
@@ -118,13 +118,14 @@ GEM
gitlab (4.18.0)
httparty (~> 0.18)
terminal-table (>= 1.5.1)
- gitlab-qa (7.33.0)
+ gitlab-qa (8.4.2)
activesupport (~> 6.1)
gitlab (~> 4.18.0)
http (~> 5.0)
nokogiri (~> 1.10)
rainbow (~> 3.0.0)
table_print (= 1.5.7)
+ zeitwerk (~> 2.4)
google-apis-compute_v1 (0.21.0)
google-apis-core (>= 0.4, < 2.a)
google-apis-core (0.4.1)
@@ -157,7 +158,7 @@ GEM
multi_json (~> 1.11)
os (>= 0.9, < 2.0)
signet (>= 0.16, < 2.a)
- http (5.0.4)
+ http (5.1.0)
addressable (~> 2.8)
http-cookie (~> 1.0)
http-form_data (~> 2.2)
@@ -170,7 +171,7 @@ GEM
mime-types (~> 3.0)
multi_xml (>= 0.5.2)
httpclient (2.8.3)
- i18n (1.10.0)
+ i18n (1.12.0)
concurrent-ruby (~> 1.0)
ice_nine (0.11.2)
influxdb-client (1.17.0)
@@ -193,7 +194,7 @@ GEM
mime-types-data (3.2022.0105)
mini_mime (1.1.0)
mini_portile2 (2.8.0)
- minitest (5.15.0)
+ minitest (5.16.3)
multi_json (1.15.0)
multi_xml (0.6.0)
multipart-post (2.1.1)
@@ -209,7 +210,7 @@ GEM
parallel (1.19.2)
parallel_tests (2.29.0)
parallel
- parser (3.0.3.2)
+ parser (3.1.2.1)
ast (~> 2.4.1)
proc_to_ast (0.1.0)
coderay
@@ -222,7 +223,7 @@ GEM
pry-byebug (3.5.1)
byebug (~> 9.1)
pry (~> 0.10)
- public_suffix (4.0.7)
+ public_suffix (5.0.0)
racc (1.6.0)
rack (2.2.3.1)
rack-test (1.1.0)
@@ -290,13 +291,13 @@ GEM
thread_safe (0.3.6)
timecop (0.9.1)
trailblazer-option (0.1.2)
- tzinfo (2.0.4)
+ tzinfo (2.0.5)
concurrent-ruby (~> 1.0)
uber (0.1.0)
unf (0.1.4)
unf_ext
unf_ext (0.0.8.2)
- unicode-display_width (2.1.0)
+ unicode-display_width (2.2.0)
unparser (0.4.7)
abstract_type (~> 0.0.7)
adamantium (~> 0.2.0)
@@ -335,7 +336,7 @@ DEPENDENCIES
deprecation_toolkit (~> 1.5.1)
faker (~> 2.19, >= 2.19.0)
fog-google (~> 1.17)
- gitlab-qa (~> 7)
+ gitlab-qa (~> 8)
influxdb-client (~> 1.17)
knapsack (~> 4.0)
nokogiri (~> 1.12)
diff --git a/qa/README.md b/qa/README.md
index dbc70f55f1c..564beb4c6e8 100644
--- a/qa/README.md
+++ b/qa/README.md
@@ -1,12 +1,12 @@
# GitLab QA - End-to-end tests for GitLab
-This directory contains [end-to-end tests](../../../doc/development/testing_guide/end_to_end/index.md)
+This directory contains [end-to-end tests](../doc/development/testing_guide/end_to_end/index.md)
for GitLab. It includes the test framework and the tests themselves.
The tests can be found in `qa/specs/features` (not to be confused with the unit
tests for the test framework, which are in `spec/`).
-It is part of the [GitLab QA project](https://gitlab.com/gitlab-org/gitlab-qa).
+Tests use [GitLab QA project](https://gitlab.com/gitlab-org/gitlab-qa) for environment orchestration in CI jobs.
## What is it?
@@ -46,7 +46,7 @@ Note that tests are using `Chrome` web browser by default so it should be instal
Tests are executed in merge request pipelines as part of the development lifecycle.
- [Review app environment](../doc/development/testing_guide/review_apps.md)
-- [package-and-qa](../doc/development/testing_guide/end_to_end/index.md#testing-code-in-merge-requests)
+- [e2e:package-and-test](../doc/development/testing_guide/end_to_end/index.md#testing-code-in-merge-requests)
### Logging
diff --git a/qa/Rakefile b/qa/Rakefile
index d3e39d8ed1e..ada27596ae4 100644
--- a/qa/Rakefile
+++ b/qa/Rakefile
@@ -4,23 +4,18 @@ require_relative "qa"
Dir['tasks/*.rake'].each { |file| load file }
-desc "Revokes all personal access tokens"
-task :revoke_personal_access_tokens do
- QA::Tools::RevokeAllPersonalAccessTokens.new.run
-end
-
desc "Deletes subgroups within a provided group"
task :delete_subgroups do
QA::Tools::DeleteSubgroups.new.run
end
desc "Initialize GitLab with an access token"
-task :initialize_gitlab_auth, [:address] do |t, args|
+task :initialize_gitlab_auth, [:address] do |_, args|
QA::Tools::InitializeGitLabAuth.new(args).run
end
desc "Generate Performance Testdata"
-task :generate_perf_testdata, :type do |t, args|
+task :generate_perf_testdata, :type do |_, args|
args.with_defaults(type: :all)
QA::Tools::GeneratePerfTestdata.new.method(args[:type]).call
end
@@ -50,7 +45,7 @@ desc "Generate data and run load tests"
task generate_data_and_run_load_test: [:generate_perf_testdata, :run_artillery_load_tests]
desc "Deletes test ssh keys a user"
-task :delete_test_ssh_keys, [:title_portion, :delete_before, :dry_run] do |t, args|
+task :delete_test_ssh_keys, [:title_portion, :delete_before, :dry_run] do |_, args|
QA::Tools::DeleteTestSSHKeys.new(args).run
end
@@ -60,33 +55,38 @@ task :delete_projects do
end
desc "Deletes test users"
-task :delete_test_users, [:delete_before, :dry_run, :exclude_users] do |t, args|
+task :delete_test_users, [:delete_before, :dry_run, :exclude_users] do |_, args|
QA::Tools::DeleteTestUsers.new(args).run
end
desc "Deletes snippets"
-task :delete_test_snippets, [:delete_before, :dry_run] do |t, args|
+task :delete_test_snippets, [:delete_before, :dry_run] do |_, args|
QA::Tools::DeleteTestSnippets.new(args).run
end
namespace :test_resources do
desc "Deletes resources created during E2E test runs"
- task :delete, [:file_pattern] do |t, args|
+ task :delete, [:file_pattern] do |_, args|
QA::Tools::TestResourcesHandler.new(args[:file_pattern]).run_delete
end
desc "Upload test resources JSON files to GCS"
- task :upload, [:file_pattern, :ci_project_name] do |t, args|
+ task :upload, [:file_pattern, :ci_project_name] do |_, args|
QA::Tools::TestResourcesHandler.new(args[:file_pattern]).upload(args[:ci_project_name])
end
desc "Download test resources JSON files from GCS"
- task :download, [:ci_project_name] do |t, args|
+ task :download, [:ci_project_name] do |_, args|
QA::Tools::TestResourcesHandler.new.download(args[:ci_project_name])
end
end
desc "Deletes user's projects"
-task :delete_user_projects, [:delete_before, :dry_run] do |t, args|
+task :delete_user_projects, [:delete_before, :dry_run] do |_, args|
QA::Tools::DeleteUserProjects.new(args).run
end
+
+desc "Revokes user's personal access tokens"
+task :revoke_user_pats, [:revoke_before, :dry_run] do |_, args|
+ QA::Tools::RevokeUserPersonalAccessTokens.new(args).run
+end
diff --git a/qa/lib/gitlab/page/admin/subscription.rb b/qa/lib/gitlab/page/admin/subscription.rb
index 1538384f6ed..ef73bad2879 100644
--- a/qa/lib/gitlab/page/admin/subscription.rb
+++ b/qa/lib/gitlab/page/admin/subscription.rb
@@ -10,8 +10,8 @@ module Gitlab
text_field :activation_code
button :activate
label :terms_of_services, text: /I agree that/
- link :remove_license, 'data-testid': 'license-remove-action'
- button :confirm_ok_button
+ button :remove_license
+ button :confirm_remove_license
p :plan
p :started
p :name
@@ -30,6 +30,11 @@ module Gitlab
terms_of_services_element.click # workaround for hidden checkbox
end
+ def remove_license_file
+ remove_license
+ confirm_remove_license
+ end
+
# Checks if a subscription record exists in subscription history table
#
# @param plan [Hash] Name of the plan
diff --git a/qa/qa.rb b/qa/qa.rb
index dd6462cfe27..99a8a34d6d8 100644
--- a/qa/qa.rb
+++ b/qa/qa.rb
@@ -51,7 +51,6 @@ module QA
"repo_by_url" => "RepoByURL",
"oauth" => "OAuth",
"saml_sso_sign_in" => "SamlSSOSignIn",
- "saml_sso_sign_up" => "SamlSSOSignUp",
"group_saml" => "GroupSAML",
"instance_saml" => "InstanceSAML",
"saml_sso" => "SamlSSO",
diff --git a/qa/qa/fixtures/auto_devops_rack/Dockerfile b/qa/qa/fixtures/auto_devops_rack/Dockerfile
deleted file mode 100644
index 6ab2795dd40..00000000000
--- a/qa/qa/fixtures/auto_devops_rack/Dockerfile
+++ /dev/null
@@ -1,9 +0,0 @@
-FROM ruby:2.6.5-alpine
-ADD ./ /app/
-WORKDIR /app
-ENV RACK_ENV production
-ENV PORT 5000
-EXPOSE 5000
-
-RUN bundle install
-CMD ["bundle","exec", "rackup", "-p", "5000"]
diff --git a/qa/qa/fixtures/auto_devops_rack/Gemfile b/qa/qa/fixtures/auto_devops_rack/Gemfile
deleted file mode 100644
index 2c7c77adf94..00000000000
--- a/qa/qa/fixtures/auto_devops_rack/Gemfile
+++ /dev/null
@@ -1,5 +0,0 @@
-# frozen_string_literal: true
-
-source 'https://rubygems.org'
-gem 'rack'
-gem 'rake'
diff --git a/qa/qa/fixtures/auto_devops_rack/Gemfile.lock b/qa/qa/fixtures/auto_devops_rack/Gemfile.lock
deleted file mode 100644
index 04a85be4b2f..00000000000
--- a/qa/qa/fixtures/auto_devops_rack/Gemfile.lock
+++ /dev/null
@@ -1,15 +0,0 @@
-GEM
- remote: https://rubygems.org/
- specs:
- rack (2.2.3)
- rake (12.3.3)
-
-PLATFORMS
- ruby
-
-DEPENDENCIES
- rack
- rake
-
-BUNDLED WITH
- 1.17.3
diff --git a/qa/qa/fixtures/auto_devops_rack/Rakefile b/qa/qa/fixtures/auto_devops_rack/Rakefile
deleted file mode 100644
index a6d08103d55..00000000000
--- a/qa/qa/fixtures/auto_devops_rack/Rakefile
+++ /dev/null
@@ -1,9 +0,0 @@
-# frozen_string_literal: true
-
-require 'rake/testtask'
-
-task default: %w[test]
-
-task :test do
- puts "ok"
-end
diff --git a/qa/qa/fixtures/auto_devops_rack/config.ru b/qa/qa/fixtures/auto_devops_rack/config.ru
deleted file mode 100644
index aea28ef1893..00000000000
--- a/qa/qa/fixtures/auto_devops_rack/config.ru
+++ /dev/null
@@ -1,3 +0,0 @@
-# frozen_string_literal: true
-
-run lambda { |env| [200, { 'Content-Type' => 'text/plain' }, StringIO.new("Hello World! #{ENV['OPTIONAL_MESSAGE']}\n")] }
diff --git a/qa/qa/fixtures/package_managers/terraform/module_upload.yaml.erb b/qa/qa/fixtures/package_managers/terraform/module_upload.yaml.erb
new file mode 100644
index 00000000000..3a7313b0712
--- /dev/null
+++ b/qa/qa/fixtures/package_managers/terraform/module_upload.yaml.erb
@@ -0,0 +1,21 @@
+stages:
+ - upload
+
+upload:
+ stage: upload
+ image: curlimages/curl:latest
+ variables:
+ TERRAFORM_MODULE_DIR: ${CI_PROJECT_DIR} # The path to your Terraform module
+ TERRAFORM_MODULE_NAME: ${CI_PROJECT_NAME} # The name of your Terraform module
+ TERRAFORM_MODULE_SYSTEM: local # The system or provider your Terraform module targets (ex. local, aws, google)
+ TERRAFORM_MODULE_VERSION: ${CI_COMMIT_TAG} # Tag commits with SemVer for the version of your Terraform module to be published
+ script:
+ - TERRAFORM_MODULE_NAME=$(echo "${TERRAFORM_MODULE_NAME}" | tr " _" -) # module-name must not have spaces or underscores, so translate them to hyphens
+ - tar -vczf ${TERRAFORM_MODULE_NAME}-${TERRAFORM_MODULE_SYSTEM}-${TERRAFORM_MODULE_VERSION}.tgz -C ${TERRAFORM_MODULE_DIR} --exclude=./.git .
+ - 'curl --location --header "JOB-TOKEN: ${CI_JOB_TOKEN}"
+ --upload-file ${TERRAFORM_MODULE_NAME}-${TERRAFORM_MODULE_SYSTEM}-${TERRAFORM_MODULE_VERSION}.tgz
+ ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/terraform/modules/${TERRAFORM_MODULE_NAME}/${TERRAFORM_MODULE_SYSTEM}/${TERRAFORM_MODULE_VERSION}/file'
+ rules:
+ - if: $CI_COMMIT_TAG
+ tags:
+ - runner-for-<%= imported_project.name %> \ No newline at end of file
diff --git a/qa/qa/flow/login.rb b/qa/qa/flow/login.rb
index 05f114acbc5..ec205e0aa86 100644
--- a/qa/qa/flow/login.rb
+++ b/qa/qa/flow/login.rb
@@ -3,7 +3,7 @@
module QA
module Flow
module Login
- module_function
+ extend self
def while_signed_in(as: nil, address: :gitlab, admin: false)
sign_in(as: as, address: address, admin: admin)
@@ -52,3 +52,5 @@ module QA
end
end
end
+
+QA::Flow::Login.prepend_mod_with('Flow::Login', namespace: QA)
diff --git a/qa/qa/flow/merge_request.rb b/qa/qa/flow/merge_request.rb
index f1cab2c7d1a..24abfa9e356 100644
--- a/qa/qa/flow/merge_request.rb
+++ b/qa/qa/flow/merge_request.rb
@@ -3,11 +3,10 @@
module QA
module Flow
module MergeRequest
- module_function
+ extend self
def enable_merge_trains
- Page::Project::Menu.perform(&:go_to_general_settings)
- Page::Project::Settings::Main.perform(&:expand_merge_requests_settings)
+ Page::Project::Menu.perform(&:go_to_merge_request_settings)
Page::Project::Settings::MergeRequest.perform(&:enable_merge_train)
end
@@ -33,3 +32,5 @@ module QA
end
end
end
+
+QA::Flow::MergeRequest.prepend_mod_with('Flow::MergeRequest', namespace: QA)
diff --git a/qa/qa/flow/pipeline.rb b/qa/qa/flow/pipeline.rb
index d19b2530bb8..fb6a5425a6e 100644
--- a/qa/qa/flow/pipeline.rb
+++ b/qa/qa/flow/pipeline.rb
@@ -3,7 +3,7 @@
module QA
module Flow
module Pipeline
- module_function
+ extend self
# Acceptable statuses:
# canceled, created, failed, manual, passed
@@ -27,3 +27,5 @@ module QA
end
end
end
+
+QA::Flow::Pipeline.prepend_mod_with('Flow::Pipeline', namespace: QA)
diff --git a/qa/qa/flow/project.rb b/qa/qa/flow/project.rb
index 397806b33a3..70bdcfcb719 100644
--- a/qa/qa/flow/project.rb
+++ b/qa/qa/flow/project.rb
@@ -3,7 +3,7 @@
module QA
module Flow
module Project
- module_function
+ extend self
def go_to_create_project_from_template
Page::Project::New.perform(&:click_create_from_template_link)
@@ -11,3 +11,5 @@ module QA
end
end
end
+
+QA::Flow::Project.prepend_mod_with('Flow::Project', namespace: QA)
diff --git a/qa/qa/flow/purchase.rb b/qa/qa/flow/purchase.rb
index e0efa8a8178..c07e03c104d 100644
--- a/qa/qa/flow/purchase.rb
+++ b/qa/qa/flow/purchase.rb
@@ -5,7 +5,7 @@ module QA
module Purchase
include QA::Support::Helpers::Plan
- module_function
+ extend self
def upgrade_subscription(plan: PREMIUM)
Page::Group::Menu.perform(&:go_to_billing)
diff --git a/qa/qa/flow/saml.rb b/qa/qa/flow/saml.rb
index 1280f59c3c2..8a0ebb9f551 100644
--- a/qa/qa/flow/saml.rb
+++ b/qa/qa/flow/saml.rb
@@ -3,7 +3,7 @@
module QA
module Flow
module Saml
- module_function
+ extend self
def page
Capybara.current_session
@@ -72,3 +72,5 @@ module QA
end
end
end
+
+QA::Flow::Saml.prepend_mod_with('Flow::Saml', namespace: QA)
diff --git a/qa/qa/flow/settings.rb b/qa/qa/flow/settings.rb
index 775b7686c10..8e6ce667475 100644
--- a/qa/qa/flow/settings.rb
+++ b/qa/qa/flow/settings.rb
@@ -3,7 +3,7 @@
module QA
module Flow
module Settings
- module_function
+ extend self
def disable_snowplow
Flow::Login.while_signed_in_as_admin do
@@ -23,3 +23,5 @@ module QA
end
end
end
+
+QA::Flow::Settings.prepend_mod_with('Flow::Settings', namespace: QA)
diff --git a/qa/qa/flow/sign_up.rb b/qa/qa/flow/sign_up.rb
index ec7886ef969..52c92293bad 100644
--- a/qa/qa/flow/sign_up.rb
+++ b/qa/qa/flow/sign_up.rb
@@ -3,7 +3,7 @@
module QA
module Flow
module SignUp
- module_function
+ extend self
def page
Capybara.current_session
@@ -55,3 +55,5 @@ module QA
end
end
end
+
+QA::Flow::SignUp.prepend_mod_with('Flow::SignUp', namespace: QA)
diff --git a/qa/qa/flow/user.rb b/qa/qa/flow/user.rb
index c0bd475adb7..8f11d4ef26f 100644
--- a/qa/qa/flow/user.rb
+++ b/qa/qa/flow/user.rb
@@ -3,7 +3,7 @@
module QA
module Flow
module User
- module_function
+ extend self
def page
Capybara.current_session
@@ -24,3 +24,5 @@ module QA
end
end
end
+
+QA::Flow::User.prepend_mod_with('Flow::User', namespace: QA)
diff --git a/qa/qa/flow/user_onboarding.rb b/qa/qa/flow/user_onboarding.rb
index 066e1878869..62397d5641d 100644
--- a/qa/qa/flow/user_onboarding.rb
+++ b/qa/qa/flow/user_onboarding.rb
@@ -3,7 +3,7 @@
module QA
module Flow
module UserOnboarding
- module_function
+ extend self
def onboard_user
Page::Registration::Welcome.perform do |welcome_page|
@@ -17,3 +17,5 @@ module QA
end
end
end
+
+QA::Flow::UserOnboarding.prepend_mod_with('Flow::UserOnboarding', namespace: QA)
diff --git a/qa/qa/page/base.rb b/qa/qa/page/base.rb
index d7e0101ff2c..03f753b1d61 100644
--- a/qa/qa/page/base.rb
+++ b/qa/qa/page/base.rb
@@ -500,3 +500,5 @@ module QA
end
end
end
+
+QA::Page::Base.prepend_mod_with('Page::Base', namespace: QA)
diff --git a/qa/qa/page/component/access_tokens.rb b/qa/qa/page/component/access_tokens.rb
index 36c0f8c2f00..3c8a608cdc2 100644
--- a/qa/qa/page/component/access_tokens.rb
+++ b/qa/qa/page/component/access_tokens.rb
@@ -18,6 +18,10 @@ module QA
element :expiry_date_field
end
+ base.view 'app/views/shared/access_tokens/_created_container.html.haml' do
+ element :created_access_token_field
+ end
+
base.view 'app/views/shared/access_tokens/_form.html.haml' do
element :access_token_name_field
element :create_token_button
@@ -32,7 +36,7 @@ module QA
end
base.view 'app/assets/javascripts/access_tokens/components/new_access_token_app.vue' do
- element :created_access_token
+ element :created_access_token_field
end
base.view 'app/assets/javascripts/access_tokens/components/access_token_table_app.vue' do
@@ -53,7 +57,7 @@ module QA
end
def created_access_token
- find_element(:created_access_token, wait: 30).value
+ find_element(:created_access_token_field, wait: 30).value
end
def fill_expiry_date(date)
diff --git a/qa/qa/page/component/ci_badge_link.rb b/qa/qa/page/component/ci_badge_link.rb
index 2ba198621fc..485e363d960 100644
--- a/qa/qa/page/component/ci_badge_link.rb
+++ b/qa/qa/page/component/ci_badge_link.rb
@@ -32,12 +32,12 @@ module QA
super
base.view 'app/assets/javascripts/vue_shared/components/ci_badge_link.vue' do
- element :status_badge
+ element :status_badge_link
end
end
def status_badge
- find_element(:status_badge).text
+ find_element(:status_badge_link).text
end
def completed?(timeout: 60)
diff --git a/qa/qa/page/component/content_editor.rb b/qa/qa/page/component/content_editor.rb
index b3a42634fe7..f7b055b6052 100644
--- a/qa/qa/page/component/content_editor.rb
+++ b/qa/qa/page/component/content_editor.rb
@@ -21,6 +21,10 @@ module QA
base.view 'app/assets/javascripts/content_editor/components/toolbar_image_button.vue' do
element :file_upload_field
end
+
+ base.view 'app/assets/javascripts/pages/shared/wikis/components/wiki_form.vue' do
+ element :wiki_hidden_content
+ end
end
def add_heading(heading, text)
@@ -41,6 +45,11 @@ module QA
text_area.send_keys(:return)
find_element(:file_upload_field, visible: false).send_keys(image_path)
end
+
+ QA::Support::Retrier.retry_on_exception do
+ source = find_element(:wiki_hidden_content, visible: false)
+ source.value =~ %r{uploads/.*#{::File.basename(image_path)}}
+ end
end
private
diff --git a/qa/qa/page/component/note.rb b/qa/qa/page/component/note.rb
index 8494518723d..37c833e77c2 100644
--- a/qa/qa/page/component/note.rb
+++ b/qa/qa/page/component/note.rb
@@ -28,7 +28,7 @@ module QA
end
base.view 'app/assets/javascripts/notes/components/discussion_filter.vue' do
- element :discussion_filter_dropdown, required: true
+ element :discussion_preferences_dropdown, required: true
element :filter_menu_item
end
@@ -169,7 +169,7 @@ module QA
def select_filter_with_text(text)
retry_on_exception do
click_element(:title_content)
- click_element :discussion_filter_dropdown
+ click_element :discussion_preferences_dropdown
find_element(:filter_menu_item, text: text).click
wait_for_requests
diff --git a/qa/qa/page/component/project/templates.rb b/qa/qa/page/component/project/templates.rb
index 8baf15acdff..6e86455fc52 100644
--- a/qa/qa/page/component/project/templates.rb
+++ b/qa/qa/page/component/project/templates.rb
@@ -5,7 +5,7 @@ module QA
module Project
module Templates
def use_template_for_project(project_name)
- within find_element(:template_option_row, text: project_name) do
+ within find_element(:template_option_container, text: project_name) do
click_element :use_template_button
end
end
diff --git a/qa/qa/page/component/web_ide/modal/create_new_file.rb b/qa/qa/page/component/web_ide/modal/create_new_file.rb
index 7c55f775476..2869bb9c331 100644
--- a/qa/qa/page/component/web_ide/modal/create_new_file.rb
+++ b/qa/qa/page/component/web_ide/modal/create_new_file.rb
@@ -9,7 +9,7 @@ module QA
view 'app/assets/javascripts/ide/components/new_dropdown/modal.vue' do
element :file_name_field, required: true
element :new_file_modal, required: true
- element :template_list
+ element :template_list_content
end
end
end
diff --git a/qa/qa/page/component/wiki_page_form.rb b/qa/qa/page/component/wiki_page_form.rb
index 22b9a4c8b0d..9e558844469 100644
--- a/qa/qa/page/component/wiki_page_form.rb
+++ b/qa/qa/page/component/wiki_page_form.rb
@@ -35,6 +35,10 @@ module QA
end
def click_submit
+ # In case any changes were just made, wait for the hidden content field to be updated via a deferred call
+ # before clicking submit. See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/97693#note_1098728562
+ sleep 0.5
+
click_element(:wiki_submit_button)
QA::Support::Retrier.retry_on_exception do
diff --git a/qa/qa/page/file/shared/commit_message.rb b/qa/qa/page/file/shared/commit_message.rb
index a3658fa11af..12154cdb728 100644
--- a/qa/qa/page/file/shared/commit_message.rb
+++ b/qa/qa/page/file/shared/commit_message.rb
@@ -14,6 +14,10 @@ module QA
element :commit_message_field
end
+ base.view 'app/assets/javascripts/repository/components/last_commit.vue' do
+ element :commit_content
+ end
+
base.view 'app/views/shared/_commit_message_container.html.haml' do
element :commit_message_field
end
diff --git a/qa/qa/page/file/show.rb b/qa/qa/page/file/show.rb
index 581aa835ada..31899d9a0c7 100644
--- a/qa/qa/page/file/show.rb
+++ b/qa/qa/page/file/show.rb
@@ -14,11 +14,6 @@ module QA
element :lock_button
end
- view 'app/helpers/blob_helper.rb' do
- element :edit_button, "_('Edit')" # rubocop:disable QA/ElementWithPattern
- element :delete_button, '_("Delete")' # rubocop:disable QA/ElementWithPattern
- end
-
view 'app/assets/javascripts/vue_shared/components/web_ide_link.vue' do
element :edit_button
end
diff --git a/qa/qa/page/group/members.rb b/qa/qa/page/group/members.rb
index c80bdadb11f..58febaddfa0 100644
--- a/qa/qa/page/group/members.rb
+++ b/qa/qa/page/group/members.rb
@@ -9,7 +9,7 @@ module QA
include Page::Component::MembersFilter
view 'app/assets/javascripts/members/components/modals/remove_member_modal.vue' do
- element :remove_member_modal_content
+ element :remove_member_modal
end
view 'app/assets/javascripts/pages/groups/group_members/index.js' do
@@ -45,7 +45,7 @@ module QA
click_element :delete_member_button
end
- within_element(:remove_member_modal_content) do
+ within_element(:remove_member_modal) do
click_button("Remove member")
end
end
diff --git a/qa/qa/page/group/menu.rb b/qa/qa/page/group/menu.rb
index 783fbd25929..de065ca187d 100644
--- a/qa/qa/page/group/menu.rb
+++ b/qa/qa/page/group/menu.rb
@@ -47,7 +47,7 @@ module QA
def go_to_package_settings
hover_group_settings do
within_submenu do
- click_element(:sidebar_menu_item_link, menu_item: 'Packages & Registries')
+ click_element(:sidebar_menu_item_link, menu_item: 'Packages and registries')
end
end
end
@@ -122,8 +122,8 @@ module QA
def hover_group_packages
within_sidebar do
- scroll_to_element(:sidebar_menu_link, menu_item: 'Packages & Registries')
- find_element(:sidebar_menu_link, menu_item: 'Packages & Registries').hover
+ scroll_to_element(:sidebar_menu_link, menu_item: 'Packages and registries')
+ find_element(:sidebar_menu_link, menu_item: 'Packages and registries').hover
yield
end
diff --git a/qa/qa/page/group/settings/package_registries.rb b/qa/qa/page/group/settings/package_registries.rb
index 35186159dc9..bcf51f0f223 100644
--- a/qa/qa/page/group/settings/package_registries.rb
+++ b/qa/qa/page/group/settings/package_registries.rb
@@ -8,8 +8,7 @@ module QA
view 'app/assets/javascripts/packages_and_registries/settings/group/components/packages_settings.vue' do
element :package_registry_settings_content
- element :reject_duplicates_toggle
- element :reject_duplicates_label
+ element :allow_duplicates_toggle
end
view 'app/assets/javascripts/packages_and_registries/settings/group/components/dependency_proxy_settings.vue' do
@@ -17,32 +16,32 @@ module QA
element :dependency_proxy_setting_toggle
end
- def set_reject_duplicates_disabled
+ def set_allow_duplicates_disabled
within_element :package_registry_settings_content do
- click_on_reject_duplicates_button if duplicates_disabled?
+ click_on_allow_duplicates_button if duplicates_enabled?
end
end
- def set_reject_duplicates_enabled
+ def set_allow_duplicates_enabled
within_element :package_registry_settings_content do
- click_on_reject_duplicates_button unless duplicates_disabled?
+ click_on_allow_duplicates_button unless duplicates_enabled?
end
end
- def click_on_reject_duplicates_button
- with_reject_duplicates_button do |button|
+ def click_on_allow_duplicates_button
+ with_allow_duplicates_button do |button|
button.click
end
end
- def duplicates_disabled?
- with_reject_duplicates_button do |button|
+ def duplicates_enabled?
+ with_allow_duplicates_button do |button|
button[:class].include?('is-checked')
end
end
- def with_reject_duplicates_button
- within_element :reject_duplicates_toggle do
+ def with_allow_duplicates_button
+ within_element :allow_duplicates_toggle do
toggle = find('button.gl-toggle:not(.is-disabled)')
yield(toggle)
end
diff --git a/qa/qa/page/main/menu.rb b/qa/qa/page/main/menu.rb
index 90b419f8cef..aaf10e12e82 100644
--- a/qa/qa/page/main/menu.rb
+++ b/qa/qa/page/main/menu.rb
@@ -53,28 +53,45 @@ module QA
element :search_term_field
end
+ view 'app/views/layouts/header/_new_dropdown.html.haml' do
+ element :new_menu_toggle
+ end
+
+ view 'app/helpers/nav/new_dropdown_helper.rb' do
+ element :global_new_group_link
+ element :global_new_project_link
+ end
+
def go_to_groups
within_groups_menu do
- click_element(:menu_item_link, title: 'Your groups')
+ # Remove if statement once :remove_extra_primary_submenu_options ff is enabled by default
+ if has_element?(:menu_item_link, title: 'Your groups')
+ click_element(:menu_item_link, title: 'Your groups')
+ else
+ click_element(:menu_item_link, title: 'View all groups')
+ end
end
end
def go_to_create_group
- within_groups_menu do
- click_element(:menu_item_link, title: 'Create group')
- end
+ click_element(:new_menu_toggle)
+ click_element(:global_new_group_link)
end
def go_to_projects
within_projects_menu do
- click_element(:menu_item_link, title: 'Your projects')
+ # Remove if statement once :remove_extra_primary_submenu_options ff is enabled by default
+ if has_element?(:menu_item_link, title: 'Your projects')
+ click_element(:menu_item_link, title: 'Your projects')
+ else
+ click_element(:menu_item_link, title: 'View all projects')
+ end
end
end
def go_to_create_project
- within_projects_menu do
- click_element(:menu_item_link, title: 'Create new project')
- end
+ click_element(:new_menu_toggle)
+ click_element(:global_new_project_link)
end
def go_to_menu_dropdown_option(option_name)
diff --git a/qa/qa/page/merge_request/show.rb b/qa/qa/page/merge_request/show.rb
index 9fc0cf0ccf8..2587241ed18 100644
--- a/qa/qa/page/merge_request/show.rb
+++ b/qa/qa/page/merge_request/show.rb
@@ -363,7 +363,7 @@ module QA
# Revisit after merge page re-architect is done https://gitlab.com/gitlab-org/gitlab/-/issues/300042
# To remove page refresh logic if possible
wait_until_ready_to_merge
- wait_until { !find_element(:merge_button).has_text?("when pipeline succeeds") }
+ wait_until { !find_element(:merge_button).text.include?('when pipeline succeeds') }
click_element(:merge_button)
end
diff --git a/qa/qa/page/profile/ssh_keys.rb b/qa/qa/page/profile/ssh_keys.rb
index db71062cec6..0653df62560 100644
--- a/qa/qa/page/profile/ssh_keys.rb
+++ b/qa/qa/page/profile/ssh_keys.rb
@@ -5,12 +5,15 @@ module QA
module Profile
class SSHKeys < Page::Base
view 'app/views/profiles/keys/_form.html.haml' do
- element :key_expiry_date_field
element :key_title_field
element :key_public_key_field
element :add_key_button
end
+ view 'app/assets/javascripts/access_tokens/components/expires_at_field.vue' do
+ element :expiry_date_field
+ end
+
view 'app/helpers/ssh_keys_helper.rb' do
element :delete_ssh_key_button
element :ssh_key_delete_modal
@@ -25,19 +28,21 @@ module QA
fill_element(:key_title_field, title)
# Expire in 2 days just in case the key is created just before midnight
fill_expiry_date(Date.today + 2)
+ # Close the datepicker
+ find_element(:expiry_date_field).find('input').send_keys(:enter)
click_element(:add_key_button)
end
def fill_expiry_date(date)
- date = date.strftime('%m/%d/%Y') if date.is_a?(Date)
+ date = date.strftime('%Y-%m-%d') if date.is_a?(Date)
begin
- Date.strptime(date, '%m/%d/%Y')
+ Date.strptime(date, '%Y-%m-%d')
rescue ArgumentError
- raise "Expiry date must be in mm/dd/yyyy format"
+ raise "Expiry date must be in YYYY-MM-DD format"
end
- fill_element(:key_expiry_date_field, date)
+ fill_element(:expiry_date_field, date)
end
def remove_key(title)
diff --git a/qa/qa/page/project/fork/new.rb b/qa/qa/page/project/fork/new.rb
index e1b5e47dd0b..b622b341685 100644
--- a/qa/qa/page/project/fork/new.rb
+++ b/qa/qa/page/project/fork/new.rb
@@ -6,19 +6,40 @@ module QA
module Fork
class New < Page::Base
view 'app/assets/javascripts/pages/projects/forks/new/components/fork_form.vue' do
- element :fork_namespace_dropdown
element :fork_project_button
element :fork_privacy_button
end
+ view 'app/assets/javascripts/pages/projects/forks/new/components/project_namespace.vue' do
+ element :select_namespace_dropdown
+ element :select_namespace_dropdown_item
+ element :select_namespace_dropdown_search_field
+ element :select_namespace_dropdown_item
+ end
+
def fork_project(namespace = Runtime::Namespace.path)
- select_element(:fork_namespace_dropdown, namespace)
+ choose_namespace(namespace)
click_element(:fork_privacy_button, privacy_level: 'public')
click_element(:fork_project_button)
end
- def fork_namespace_dropdown_values
- find_element(:fork_namespace_dropdown).all(:option).map { |option| option.text.tr("\n", '').strip }
+ def get_list_of_namespaces
+ click_element(:select_namespace_dropdown)
+ wait_until(reload: false) do
+ has_element?(:select_namespace_dropdown_item)
+ end
+ all_elements(:select_namespace_dropdown_item, minimum: 1).map(&:text)
+ end
+
+ def choose_namespace(namespace)
+ retry_on_exception do
+ click_element(:select_namespace_dropdown)
+ fill_element(:select_namespace_dropdown_search_field, namespace)
+ wait_until(reload: false) do
+ has_element?(:select_namespace_dropdown_item, text: namespace)
+ end
+ click_button(namespace)
+ end
end
end
end
diff --git a/qa/qa/page/project/infrastructure/kubernetes/index.rb b/qa/qa/page/project/infrastructure/kubernetes/index.rb
index 34d2ad55429..4c759a049e1 100644
--- a/qa/qa/page/project/infrastructure/kubernetes/index.rb
+++ b/qa/qa/page/project/infrastructure/kubernetes/index.rb
@@ -10,18 +10,13 @@ module QA
element :clusters_actions_button
end
- def connect_existing_cluster
- within_element(:clusters_actions_button) { click_button(class: 'dropdown-toggle-split') }
- click_link 'Connect a cluster (certificate - deprecated)'
+ def connect_cluster
+ click_element(:clusters_actions_button)
end
def has_cluster?(cluster)
has_element?(:cluster, cluster_name: cluster.to_s)
end
-
- def click_on_cluster(cluster)
- click_on cluster.cluster_name
- end
end
end
end
diff --git a/qa/qa/page/project/job/show.rb b/qa/qa/page/project/job/show.rb
index 2fb925b3930..5506c5ed4d9 100644
--- a/qa/qa/page/project/job/show.rb
+++ b/qa/qa/page/project/job/show.rb
@@ -11,15 +11,15 @@ module QA
element :job_log_content
end
- view 'app/assets/javascripts/jobs/components/stages_dropdown.vue' do
+ view 'app/assets/javascripts/jobs/components/job/sidebar/stages_dropdown.vue' do
element :pipeline_path, required: true
end
- view 'app/assets/javascripts/jobs/components/sidebar.vue' do
+ view 'app/assets/javascripts/jobs/components/job/sidebar/legacy_sidebar_header.vue' do
element :retry_button
end
- view 'app/assets/javascripts/jobs/components/artifacts_block.vue' do
+ view 'app/assets/javascripts/jobs/components/job/sidebar/artifacts_block.vue' do
element :browse_artifacts_button
end
diff --git a/qa/qa/page/project/monitor/metrics/show.rb b/qa/qa/page/project/monitor/metrics/show.rb
index 70ebc795595..59602d0fcf7 100644
--- a/qa/qa/page/project/monitor/metrics/show.rb
+++ b/qa/qa/page/project/monitor/metrics/show.rb
@@ -10,7 +10,7 @@ module QA
LOADING_MESSAGE = 'Waiting for performance data'
view 'app/assets/javascripts/monitoring/components/dashboard.vue' do
- element :prometheus_graphs
+ element :prometheus_graphs_content
end
view 'app/assets/javascripts/monitoring/components/dashboard_header.vue' do
@@ -54,7 +54,7 @@ module QA
end
def has_metrics?
- within_element :prometheus_graphs do
+ within_element :prometheus_graphs_content do
has_text?(EXPECTED_TITLE)
end
end
@@ -102,7 +102,7 @@ module QA
end
def has_custom_metric?(metric)
- within_element :prometheus_graphs do
+ within_element :prometheus_graphs_content do
has_text?(metric)
end
end
@@ -114,7 +114,7 @@ module QA
end
def has_template_metric?(metric)
- within_element :prometheus_graphs do
+ within_element :prometheus_graphs_content do
has_text?(metric)
end
end
diff --git a/qa/qa/page/project/new.rb b/qa/qa/page/project/new.rb
index bb4fb84f2d2..fd650d8ca20 100644
--- a/qa/qa/page/project/new.rb
+++ b/qa/qa/page/project/new.rb
@@ -23,7 +23,7 @@ module QA
view 'app/views/projects/project_templates/_template.html.haml' do
element :use_template_button
- element :template_option_row
+ element :template_option_container
end
view 'app/assets/javascripts/projects/new/components/new_project_url_select.vue' do
diff --git a/qa/qa/page/project/packages/index.rb b/qa/qa/page/project/packages/index.rb
index 86a86c05c12..e58ffba3cd5 100644
--- a/qa/qa/page/project/packages/index.rb
+++ b/qa/qa/page/project/packages/index.rb
@@ -6,7 +6,10 @@ module QA
module Packages
class Index < QA::Page::Base
view 'app/assets/javascripts/packages_and_registries/package_registry/components/list/package_list_row.vue' do
- element :package_row
+ element :package_link
+ end
+
+ view 'app/assets/javascripts/packages_and_registries/infrastructure_registry/shared/package_list_row.vue' do
element :package_link
end
@@ -18,6 +21,10 @@ module QA
has_element?(:package_link, text: name, wait: 20)
end
+ def has_module?(name)
+ has_element?(:package_link, text: name, wait: 20)
+ end
+
def has_no_package?(name)
has_no_element?(:package_link, text: name)
end
diff --git a/qa/qa/page/project/pipeline/new.rb b/qa/qa/page/project/pipeline/new.rb
index 6cf5c3b1134..742fcad5c07 100644
--- a/qa/qa/page/project/pipeline/new.rb
+++ b/qa/qa/page/project/pipeline/new.rb
@@ -5,7 +5,7 @@ module QA
module Project
module Pipeline
class New < QA::Page::Base
- view 'app/assets/javascripts/pipeline_new/components/pipeline_new_form.vue' do
+ view 'app/assets/javascripts/pipeline_new/components/legacy_pipeline_new_form.vue' do
element :run_pipeline_button, required: true
element :ci_variable_row_container
element :ci_variable_key_field
diff --git a/qa/qa/page/project/settings/main.rb b/qa/qa/page/project/settings/main.rb
index 52ed630ac66..ca5d13abdae 100644
--- a/qa/qa/page/project/settings/main.rb
+++ b/qa/qa/page/project/settings/main.rb
@@ -13,11 +13,14 @@ module QA
view 'app/views/projects/edit.html.haml' do
element :advanced_settings_content
- element :merge_request_settings_content
element :visibility_features_permissions_content
element :badges_settings_content
end
+ view 'app/views/projects/settings/merge_requests/show.html.haml' do
+ element :merge_request_settings_content
+ end
+
view 'app/views/projects/settings/_general.html.haml' do
element :project_name_field
element :save_naming_topics_avatar_button
@@ -42,12 +45,6 @@ module QA
end
end
- def expand_merge_requests_settings(&block)
- expand_content(:merge_request_settings_content) do
- MergeRequest.perform(&block)
- end
- end
-
def expand_visibility_project_features_permissions(&block)
expand_content(:visibility_features_permissions_content) do
VisibilityFeaturesPermissions.perform(&block)
diff --git a/qa/qa/page/project/settings/merge_request.rb b/qa/qa/page/project/settings/merge_request.rb
index dd9c94ebbb7..d862979aeec 100644
--- a/qa/qa/page/project/settings/merge_request.rb
+++ b/qa/qa/page/project/settings/merge_request.rb
@@ -7,7 +7,7 @@ module QA
class MergeRequest < QA::Page::Base
include QA::Page::Settings::Common
- view 'app/views/projects/edit.html.haml' do
+ view 'app/views/projects/settings/merge_requests/show.html.haml' do
element :save_merge_request_changes_button
end
diff --git a/qa/qa/page/project/settings/mirroring_repositories.rb b/qa/qa/page/project/settings/mirroring_repositories.rb
index 501b31f8a95..7eeeeefdae6 100644
--- a/qa/qa/page/project/settings/mirroring_repositories.rb
+++ b/qa/qa/page/project/settings/mirroring_repositories.rb
@@ -13,6 +13,9 @@ module QA
view 'app/views/projects/mirrors/_mirror_repos.html.haml' do
element :mirror_repository_url_input
element :mirror_repository_button
+ end
+
+ view 'app/views/projects/mirrors/_mirror_repos_list.html.haml' do
element :mirror_repository_url_cell
element :mirror_last_update_at_cell
element :mirror_error_badge
diff --git a/qa/qa/page/project/settings/pages.rb b/qa/qa/page/project/settings/pages.rb
index 1417f7ec1b5..c5b8560ba9a 100644
--- a/qa/qa/page/project/settings/pages.rb
+++ b/qa/qa/page/project/settings/pages.rb
@@ -14,6 +14,7 @@ module QA
def go_to_access_page
within_element(:access_page_container) do
find('a').click
+ page.driver.browser.switch_to.window(page.driver.browser.window_handles.last)
end
end
end
diff --git a/qa/qa/page/project/settings/protected_branches.rb b/qa/qa/page/project/settings/protected_branches.rb
index 35fc87f717c..a78d8a6ccf4 100644
--- a/qa/qa/page/project/settings/protected_branches.rb
+++ b/qa/qa/page/project/settings/protected_branches.rb
@@ -11,14 +11,10 @@ module QA
end
view 'app/views/projects/protected_branches/_create_protected_branch.html.haml' do
- element :allowed_to_push_select
element :allowed_to_push_dropdown
- element :allowed_to_merge_select
+ element :allowed_to_push_dropdown_content
element :allowed_to_merge_dropdown
- end
-
- view 'app/views/projects/protected_branches/shared/_branches_list.html.haml' do
- element :protected_branches_list
+ element :allowed_to_merge_dropdown_content
end
view 'app/views/projects/protected_branches/shared/_create_protected_branch.html.haml' do
@@ -49,11 +45,11 @@ module QA
private
def select_allowed(action, allowed)
- click_element :"allowed_to_#{action}_select"
+ click_element :"allowed_to_#{action}_dropdown"
allowed[:roles] = Resource::ProtectedBranch::Roles::NO_ONE unless allowed.key?(:roles)
- within_element(:"allowed_to_#{action}_dropdown") do
+ within_element(:"allowed_to_#{action}_dropdown_content") do
click_on allowed[:roles][:description]
allowed[:users].each { |user| click_on user.username } if allowed.key?(:users)
allowed[:groups].each { |group| click_on group.name } if allowed.key?(:groups)
diff --git a/qa/qa/page/project/show.rb b/qa/qa/page/project/show.rb
index e048afee8b3..26c2da07b34 100644
--- a/qa/qa/page/project/show.rb
+++ b/qa/qa/page/project/show.rb
@@ -58,8 +58,8 @@ module QA
end
view 'app/assets/javascripts/repository/components/breadcrumbs.vue' do
- element :add_to_tree
- element :new_file_option
+ element :add_to_tree_dropdown
+ element :new_file_menu_item
end
view 'app/assets/javascripts/vue_shared/components/web_ide_link.vue' do
@@ -90,8 +90,8 @@ module QA
end
def create_new_file!
- click_element :add_to_tree
- click_element :new_file_option
+ click_element :add_to_tree_dropdown
+ click_element :new_file_menu_item
end
def fork_project
diff --git a/qa/qa/page/project/sub_menus/packages.rb b/qa/qa/page/project/sub_menus/packages.rb
index 88e2101a86d..9600540c5bc 100644
--- a/qa/qa/page/project/sub_menus/packages.rb
+++ b/qa/qa/page/project/sub_menus/packages.rb
@@ -23,12 +23,20 @@ module QA
end
end
+ def go_to_infrastructure_registry
+ hover_registry do
+ within_submenu do
+ click_link('Infrastructure Registry')
+ end
+ end
+ end
+
private
def hover_registry
within_sidebar do
- scroll_to_element(:sidebar_menu_link, menu_item: 'Packages & Registries')
- find_element(:sidebar_menu_link, menu_item: 'Packages & Registries').hover
+ scroll_to_element(:sidebar_menu_link, menu_item: 'Packages and registries')
+ find_element(:sidebar_menu_link, menu_item: 'Packages and registries').hover
yield
end
diff --git a/qa/qa/page/project/sub_menus/repository.rb b/qa/qa/page/project/sub_menus/repository.rb
index e35828ecd6a..f9d55c0009c 100644
--- a/qa/qa/page/project/sub_menus/repository.rb
+++ b/qa/qa/page/project/sub_menus/repository.rb
@@ -37,6 +37,14 @@ module QA
end
end
+ def go_to_repository_contributors
+ hover_repository do
+ within_submenu do
+ click_element(:sidebar_menu_item_link, menu_item: 'Contributors')
+ end
+ end
+ end
+
private
def hover_repository
diff --git a/qa/qa/page/project/sub_menus/settings.rb b/qa/qa/page/project/sub_menus/settings.rb
index 53a5eaf60c5..2ed4c28afb7 100644
--- a/qa/qa/page/project/sub_menus/settings.rb
+++ b/qa/qa/page/project/sub_menus/settings.rb
@@ -77,6 +77,14 @@ module QA
end
end
+ def go_to_merge_request_settings
+ hover_settings do
+ within_submenu do
+ click_element(:sidebar_menu_item_link, menu_item: 'Merge requests')
+ end
+ end
+ end
+
private
def hover_settings
diff --git a/qa/qa/page/project/web_ide/edit.rb b/qa/qa/page/project/web_ide/edit.rb
index e572569e496..293fcd1e676 100644
--- a/qa/qa/page/project/web_ide/edit.rb
+++ b/qa/qa/page/project/web_ide/edit.rb
@@ -163,7 +163,7 @@ module QA
def create_new_file_from_template(file_name, template)
click_element(:new_file_button, Page::Component::WebIDE::Modal::CreateNewFile)
- within_element(:template_list) do
+ within_element(:template_list_content) do
click_on file_name
rescue Capybara::ElementNotFound
raise ElementNotFound, %Q(Couldn't find file template named "#{file_name}". Please confirm that it is a valid option.)
diff --git a/qa/qa/resource/base.rb b/qa/qa/resource/base.rb
index ba1b581b100..6025dd6ec40 100644
--- a/qa/qa/resource/base.rb
+++ b/qa/qa/resource/base.rb
@@ -12,6 +12,8 @@ module QA
NoValueError = Class.new(RuntimeError)
+ attr_reader :retrieved_from_cache
+
class << self
# Initialize new instance of class without fabrication
#
@@ -81,7 +83,7 @@ module QA
Support::FabricationTracker.start_fabrication
result = yield.tap do
fabrication_time = Time.now - start
- fabrication_http_method = if resource.api_fabrication_http_method == :get
+ fabrication_http_method = if resource.api_fabrication_http_method == :get || resource.retrieved_from_cache
if include?(Reusable)
"Retrieved for reuse"
else
@@ -92,24 +94,28 @@ module QA
end
Support::FabricationTracker.save_fabrication(:"#{fabrication_method}_fabrication", fabrication_time)
- Tools::TestResourceDataProcessor.collect(
- resource: resource,
- info: resource.identifier,
- fabrication_method: fabrication_method,
- fabrication_time: fabrication_time
- )
+
+ unless resource.retrieved_from_cache
+ Tools::TestResourceDataProcessor.collect(
+ resource: resource,
+ info: resource.identifier,
+ fabrication_method: fabrication_method,
+ fabrication_time: fabrication_time
+ )
+ end
Runtime::Logger.info do
msg = ["==#{'=' * parents.size}>"]
msg << "#{fabrication_http_method} a #{Rainbow(name).black.bg(:white)}"
msg << resource.identifier
msg << "as a dependency of #{parents.last}" if parents.any?
- msg << "via #{fabrication_method}"
+ msg << "via #{resource.retrieved_from_cache ? 'cache' : fabrication_method}"
msg << "in #{fabrication_time.round(2)} seconds"
msg.compact.join(' ')
end
end
+
Support::FabricationTracker.finish_fabrication
result
@@ -263,6 +269,8 @@ module QA
end
def log_having_both_api_result_and_block(name, api_value)
+ api_value = "[MASKED]" if name == :token
+
QA::Runtime::Logger.debug(<<~MSG.strip)
<#{self.class}> Attribute #{name.inspect} has both API response `#{api_value}` and a block. API response will be picked. Block will be ignored.
MSG
diff --git a/qa/qa/resource/ci_variable.rb b/qa/qa/resource/ci_variable.rb
index ef663bb613f..b632446623d 100644
--- a/qa/qa/resource/ci_variable.rb
+++ b/qa/qa/resource/ci_variable.rb
@@ -3,7 +3,7 @@
module QA
module Resource
class CiVariable < Base
- attr_accessor :key, :value, :masked, :protected
+ attr_accessor :key, :value, :masked, :protected, :variable_type
attribute :project do
Project.fabricate! do |resource|
@@ -15,6 +15,7 @@ module QA
def initialize
@masked = false
@protected = false
+ @variable_type = 'env_var'
end
def fabricate!
@@ -55,7 +56,8 @@ module QA
key: key,
value: value,
masked: masked,
- protected: protected
+ protected: protected,
+ variable_type: variable_type
}
end
end
diff --git a/qa/qa/resource/clusters/agent.rb b/qa/qa/resource/clusters/agent.rb
index b190634f357..9574289a2ed 100644
--- a/qa/qa/resource/clusters/agent.rb
+++ b/qa/qa/resource/clusters/agent.rb
@@ -26,25 +26,18 @@ module QA
end
def api_get_path
- "gid://gitlab/Clusters::Agent/#{id}"
+ "/projects/#{project.id}/cluster_agents/#{id}"
end
def api_post_path
- "/graphql"
+ "/projects/#{project.id}/cluster_agents"
end
def api_post_body
- <<~GQL
- mutation createAgent {
- createClusterAgent(input: { projectPath: "#{project.full_path}", name: "#{@name}" }) {
- clusterAgent {
- id
- name
- }
- errors
- }
+ {
+ id: project.id,
+ name: name
}
- GQL
end
end
end
diff --git a/qa/qa/resource/clusters/agent_token.rb b/qa/qa/resource/clusters/agent_token.rb
index c1cf5c2f37b..cbd2964c31d 100644
--- a/qa/qa/resource/clusters/agent_token.rb
+++ b/qa/qa/resource/clusters/agent_token.rb
@@ -5,7 +5,7 @@ module QA
module Clusters
class AgentToken < QA::Resource::Base
attribute :id
- attribute :secret
+ attribute :token
attribute :agent do
QA::Resource::Clusters::Agent.fabricate_via_api!
end
@@ -20,26 +20,19 @@ module QA
end
def api_get_path
- "gid://gitlab/Clusters::AgentToken/#{id}"
+ "/projects/#{agent.project.id}/cluster_agents/#{agent.id}/tokens/#{id}"
end
def api_post_path
- "/graphql"
+ "/projects/#{agent.project.id}/cluster_agents/#{agent.id}/tokens"
end
def api_post_body
- <<~GQL
- mutation createToken {
- clusterAgentTokenCreate(input: { clusterAgentId: "gid://gitlab/Clusters::Agent/#{agent.id}" name: "token-#{agent.id}" }) {
- secret # This is the value you need to use on the next step
- token {
- createdAt
- id
- }
- errors
- }
+ {
+ id: agent.project.id,
+ agent_id: agent.id,
+ name: agent.name
}
- GQL
end
end
end
diff --git a/qa/qa/resource/fork.rb b/qa/qa/resource/fork.rb
index 0e6dd626312..2016b1d948d 100644
--- a/qa/qa/resource/fork.rb
+++ b/qa/qa/resource/fork.rb
@@ -36,7 +36,7 @@ module QA
def fabricate!
populate(:upstream, :user)
- namespace_path ||= user.name
+ namespace_path ||= user.username
# Sign out as admin and sign is as the fork user
Flow::Login.sign_in(as: user)
diff --git a/qa/qa/resource/group.rb b/qa/qa/resource/group.rb
index 60f6cbdfc51..84416c0600d 100644
--- a/qa/qa/resource/group.rb
+++ b/qa/qa/resource/group.rb
@@ -26,6 +26,8 @@ module QA
def initialize
@description = "QA test run at #{Runtime::Namespace.time}"
@require_two_factor_authentication = false
+ # Add visibility to enable create private group
+ @visibility = 'public'
end
def fabricate!
@@ -39,6 +41,7 @@ module QA
Page::Group::New.perform do |group_new|
group_new.set_path(path)
+ group_new.set_visibility(@visibility)
group_new.create_subgroup
end
@@ -73,7 +76,7 @@ module QA
parent_id: sandbox.id,
path: path,
name: name,
- visibility: 'public',
+ visibility: @visibility.downcase,
require_two_factor_authentication: @require_two_factor_authentication,
avatar: avatar
}
diff --git a/qa/qa/resource/group_access_token.rb b/qa/qa/resource/group_access_token.rb
index 934348a1737..a1079b16971 100644
--- a/qa/qa/resource/group_access_token.rb
+++ b/qa/qa/resource/group_access_token.rb
@@ -8,30 +8,50 @@ module QA
attr_writer :name
attribute :id
+ attribute :user_id
+ attribute :expires_at
+ attribute :token
+
attribute :group do
Group.fabricate!
end
- attribute :token do
- Page::Group::Settings::AccessTokens.perform(&:created_access_token)
+ def api_get_path
+ "/groups/#{group.id}/access_tokens/#{id}"
+ rescue NoValueError
+ token = parse_body(api_get_from("/groups/#{group.id}/access_tokens")).find { |t| t[:name] == name }
+
+ raise ResourceNotFoundError unless token
+
+ @id = token[:id]
+ retry
end
- def api_get_path
- "/groups/#{group.id}/access_tokens"
+ def identifier
+ "with name '#{name}', token's bot username '#{token_user[:username]}'"
end
def api_post_path
- api_get_path
+ "/groups/#{group.id}/access_tokens"
+ end
+
+ def api_user_path
+ "/users/#{user_id}"
+ end
+
+ def token_user
+ parse_body(api_get_from(api_user_path))
end
def name
- @name || 'api-group-access-token'
+ @name ||= "api-group-access-token-#{Faker::Alphanumeric.alphanumeric(number: 8)}"
end
def api_post_body
{
name: name,
- scopes: ["api"]
+ scopes: ["api"],
+ expires_at: expires_at.to_s
}
end
@@ -51,6 +71,11 @@ module QA
end
end
+ # Expire in 2 days just in case the token is created just before midnight
+ def expires_at
+ @expires_at || Time.now.utc.to_date + 2
+ end
+
def fabricate!
Flow::Login.sign_in_unless_signed_in
@@ -59,12 +84,14 @@ module QA
Page::Group::Menu.perform(&:go_to_access_token_settings)
Page::Group::Settings::AccessTokens.perform do |token_page|
- token_page.fill_token_name(name || 'api-project-access-token')
+ token_page.fill_token_name(name)
token_page.check_api
- # Expire in 2 days just in case the token is created just before midnight
- token_page.fill_expiry_date(Time.now.utc.to_date + 2)
+ token_page.fill_expiry_date(expires_at)
token_page.click_create_token_button
+ self.token = token_page.created_access_token
end
+
+ reload!
end
end
end
diff --git a/qa/qa/resource/group_base.rb b/qa/qa/resource/group_base.rb
index bda72703906..f6d1aacca0a 100644
--- a/qa/qa/resource/group_base.rb
+++ b/qa/qa/resource/group_base.rb
@@ -14,7 +14,9 @@ module QA
attributes :id,
:runners_token,
:name,
- :full_path
+ :full_path,
+ # Add visibility to enable create private group
+ :visibility
# Get group projects
#
diff --git a/qa/qa/resource/impersonation_token.rb b/qa/qa/resource/impersonation_token.rb
index 3bd356b5e9b..99a9cd7bbab 100644
--- a/qa/qa/resource/impersonation_token.rb
+++ b/qa/qa/resource/impersonation_token.rb
@@ -22,7 +22,7 @@ module QA
end
def api_post_path
- api_get_path
+ "/users/#{user.id}/impersonation_tokens"
end
def name
diff --git a/qa/qa/resource/job.rb b/qa/qa/resource/job.rb
index 96c502e567c..5b0dac9b2df 100644
--- a/qa/qa/resource/job.rb
+++ b/qa/qa/resource/job.rb
@@ -22,6 +22,10 @@ module QA
"/projects/#{project.id}/jobs/#{id}"
end
+ def api_trace_path
+ "#{api_get_path}/trace"
+ end
+
def api_post_path
end
@@ -30,6 +34,11 @@ module QA
artifacts: artifacts
}
end
+
+ # Job log
+ def trace
+ get(request_url(api_trace_path))
+ end
end
end
end
diff --git a/qa/qa/resource/kubernetes_cluster/project_cluster.rb b/qa/qa/resource/kubernetes_cluster/project_cluster.rb
deleted file mode 100644
index 0443b26064e..00000000000
--- a/qa/qa/resource/kubernetes_cluster/project_cluster.rb
+++ /dev/null
@@ -1,50 +0,0 @@
-# frozen_string_literal: true
-
-module QA
- module Resource
- module KubernetesCluster
- # TODO: This resource is currently broken, since one-click apps have been removed.
- # See https://gitlab.com/gitlab-org/gitlab/-/issues/333818
- class ProjectCluster < Base
- attr_writer :cluster,
- :install_ingress, :install_prometheus, :install_runner, :domain
-
- attribute :project do
- Resource::Project.fabricate!
- end
-
- attribute :ingress_ip do
- @cluster.fetch_external_ip_for_ingress
- end
-
- def fabricate!
- project.visit!
-
- Page::Project::Menu.perform(
- &:go_to_infrastructure_kubernetes)
-
- Page::Project::Infrastructure::Kubernetes::Index.perform(
- &:connect_existing_cluster)
-
- Page::Project::Infrastructure::Kubernetes::AddExisting.perform do |cluster_page|
- cluster_page.set_cluster_name(@cluster.cluster_name)
- cluster_page.set_api_url(@cluster.api_url)
- cluster_page.set_ca_certificate(@cluster.ca_certificate)
- cluster_page.set_token(@cluster.token)
- cluster_page.uncheck_rbac! unless @cluster.rbac
- cluster_page.add_cluster!
- end
-
- Page::Project::Infrastructure::Kubernetes::Show.perform do |show|
- if @install_ingress
- ingress_ip
-
- show.set_domain("#{@ingress_ip}.nip.io")
- show.save_domain
- end
- end
- end
- end
- end
- end
-end
diff --git a/qa/qa/resource/personal_access_token.rb b/qa/qa/resource/personal_access_token.rb
index ad0f183c603..33044774c59 100644
--- a/qa/qa/resource/personal_access_token.rb
+++ b/qa/qa/resource/personal_access_token.rb
@@ -5,12 +5,13 @@ require 'date'
module QA
module Resource
class PersonalAccessToken < Base
- attr_accessor :name
+ attr_writer :name
# The user for which the personal access token is to be created
# This *could* be different than the api_client.user or the api_user provided by the QA::Resource::ApiFabricator
attr_writer :user
+ attribute :id
attribute :token
# Only Admins can create PAT via the API.
@@ -41,13 +42,35 @@ module QA
end
def api_get_path
- '/personal_access_tokens'
+ "/personal_access_tokens/#{id}"
+ rescue NoValueError
+ user.reload! unless user.id
+
+ api_client = Runtime::API::Client.new(:gitlab,
+ is_new_session: false,
+ user: user,
+ personal_access_token: self.token)
+ request_url = Runtime::API::Request.new(api_client,
+ "/personal_access_tokens?user_id=#{user.id}",
+ per_page: '100').url
+
+ token = auto_paginated_response(request_url).find { |t| t[:name] == name }
+
+ raise ResourceNotFoundError unless token
+
+ @id = token[:id]
+ retry
+ end
+
+ def name
+ @name ||= "api-pat-#{user.username}-#{Faker::Alphanumeric.alphanumeric(number: 8)}"
end
def api_post_body
{
- name: name || 'api-test-token',
- scopes: ["api"]
+ name: name,
+ scopes: ["api"],
+ expires_at: expires_at.to_s
}
end
@@ -59,12 +82,20 @@ module QA
def find_and_set_value
@token ||= QA::Resource::PersonalAccessTokenCache.get_token_for_username(user.username)
+ @retrieved_from_cache = true if @token
+
+ @token
end
def cache_token
QA::Resource::PersonalAccessTokenCache.set_token_for_username(user.username, self.token) if @user && self.token
end
+ # Expire in 2 days just in case the token is created just before midnight
+ def expires_at
+ @expires_at || Time.now.utc.to_date + 2
+ end
+
def fabricate!
return if find_and_set_value
@@ -76,8 +107,7 @@ module QA
Page::Profile::PersonalAccessTokens.perform do |token_page|
token_page.fill_token_name(name || 'api-test-token')
token_page.check_api
- # Expire in 2 days just in case the token is created just before midnight
- token_page.fill_expiry_date(Time.now.utc.to_date + 2)
+ token_page.fill_expiry_date(expires_at)
token_page.click_create_token_button
self.token = Page::Profile::PersonalAccessTokens.perform(&:created_access_token)
diff --git a/qa/qa/resource/project_access_token.rb b/qa/qa/resource/project_access_token.rb
index 58cb3e667c0..1ccf2103d1d 100644
--- a/qa/qa/resource/project_access_token.rb
+++ b/qa/qa/resource/project_access_token.rb
@@ -8,29 +8,50 @@ module QA
attr_writer :name
attribute :id
+ attribute :user_id
+ attribute :expires_at
+ attribute :token
+
attribute :project do
Project.fabricate!
end
- attribute :token do
- Page::Project::Settings::AccessTokens.perform(&:created_access_token)
- end
def api_get_path
- "/projects/#{project.api_resource[:id]}/access_tokens"
+ "/projects/#{project.id}/access_tokens/#{id}"
+ rescue NoValueError
+ token = parse_body(api_get_from("/projects/#{project.id}/access_tokens")).find { |t| t[:name] == name }
+
+ raise ResourceNotFoundError unless token
+
+ @id = token[:id]
+ retry
+ end
+
+ def identifier
+ "with name '#{name}', token's bot username '#{token_user[:username]}'"
end
def api_post_path
- api_get_path
+ "/projects/#{project.id}/access_tokens"
+ end
+
+ def api_user_path
+ "/users/#{user_id}"
+ end
+
+ def token_user
+ parse_body(api_get_from(api_user_path))
end
def name
- @name || 'api-project-access-token'
+ @name ||= "api-project-access-token-#{Faker::Alphanumeric.alphanumeric(number: 8)}"
end
def api_post_body
{
name: name,
- scopes: ["api"]
+ scopes: ["api"],
+ expires_at: expires_at.to_s
}
end
@@ -50,6 +71,11 @@ module QA
end
end
+ # Expire in 2 days just in case the token is created just before midnight
+ def expires_at
+ @expires_at || Time.now.utc.to_date + 2
+ end
+
def fabricate!
Flow::Login.sign_in_unless_signed_in
@@ -58,12 +84,14 @@ module QA
Page::Project::Menu.perform(&:go_to_access_token_settings)
Page::Project::Settings::AccessTokens.perform do |token_page|
- token_page.fill_token_name(name || 'api-project-access-token')
+ token_page.fill_token_name(name)
token_page.check_api
- # Expire in 2 days just in case the token is created just before midnight
- token_page.fill_expiry_date(Time.now.utc.to_date + 2)
+ token_page.fill_expiry_date(expires_at)
token_page.click_create_token_button
+ self.token = token_page.created_access_token
end
+
+ reload!
end
end
end
diff --git a/qa/qa/resource/runner.rb b/qa/qa/resource/runner.rb
index 6d5ff71b2ba..da4021f89b7 100644
--- a/qa/qa/resource/runner.rb
+++ b/qa/qa/resource/runner.rb
@@ -32,7 +32,10 @@ module QA
def fabricate_via_api!
@docker_container = Service::DockerRun::GitlabRunner.new(name).tap do |runner|
- runner.pull
+ QA::Support::Retrier.retry_on_exception(sleep_interval: 5) do
+ runner.pull
+ end
+
runner.token = @token ||= project.runners_token
runner.address = Runtime::Scenario.gitlab_address
runner.tags = @tags if @tags
diff --git a/qa/qa/resource/sandbox.rb b/qa/qa/resource/sandbox.rb
index 12ecce1ce9a..2080e279e99 100644
--- a/qa/qa/resource/sandbox.rb
+++ b/qa/qa/resource/sandbox.rb
@@ -20,6 +20,8 @@ module QA
def initialize
@path = Runtime::Namespace.sandbox_name
+ # Visibility should be public by default for backward compatibility
+ @visibility = 'public'
end
alias_method :full_path, :path
@@ -38,7 +40,7 @@ module QA
Page::Group::New.perform do |group|
group.click_create_group
group.set_path(path)
- group.set_visibility('Public')
+ group.set_visibility(@visibility)
group.create
end
@@ -68,7 +70,7 @@ module QA
{
path: path,
name: path,
- visibility: 'public',
+ visibility: @visibility.downcase,
avatar: avatar
}
end
diff --git a/qa/qa/resource/user.rb b/qa/qa/resource/user.rb
index 2fb8b18b71f..a974446b3cb 100644
--- a/qa/qa/resource/user.rb
+++ b/qa/qa/resource/user.rb
@@ -44,7 +44,7 @@ module QA
alias_method :ldap_username, :username
def password
- @password || 'password'
+ @password ||= SecureRandom.hex(8)
end
alias_method :ldap_password, :password
@@ -232,3 +232,5 @@ module QA
end
end
end
+
+QA::Resource::User.prepend_mod_with('Resource::User', namespace: QA)
diff --git a/qa/qa/runtime/browser.rb b/qa/qa/runtime/browser.rb
index f705fe54b97..0dbc3cdf09d 100644
--- a/qa/qa/runtime/browser.rb
+++ b/qa/qa/runtime/browser.rb
@@ -95,6 +95,14 @@ module QA
# Disable /dev/shm use in CI. See https://gitlab.com/gitlab-org/gitlab/issues/4252
capabilities['goog:chromeOptions'][:args] << 'disable-dev-shm-usage' if QA::Runtime::Env.disable_dev_shm?
+ # Set chrome default download path
+ if QA::Runtime::Env.chrome_default_download_path
+ capabilities['goog:chromeOptions'][:prefs] = {
+ 'download.default_directory' => File.expand_path(QA::Runtime::Env.chrome_default_download_path),
+ 'download.prompt_for_download' => false
+ }
+ end
+
# Specify the user-agent to allow challenges to be bypassed
# See https://gitlab.com/gitlab-com/gl-infra/infrastructure/-/issues/11938
if QA::Runtime::Env.user_agent
diff --git a/qa/qa/runtime/env.rb b/qa/qa/runtime/env.rb
index 1fd097d0acf..b1a912ac43e 100644
--- a/qa/qa/runtime/env.rb
+++ b/qa/qa/runtime/env.rb
@@ -263,22 +263,6 @@ module QA
ENV['GITLAB_QA_PASSWORD_6']
end
- def gitlab_qa_1p_email
- ENV['GITLAB_QA_1P_EMAIL']
- end
-
- def gitlab_qa_1p_password
- ENV['GITLAB_QA_1P_PASSWORD']
- end
-
- def gitlab_qa_1p_secret
- ENV['GITLAB_QA_1P_SECRET']
- end
-
- def gitlab_qa_1p_github_uuid
- ENV['GITLAB_QA_1P_GITHUB_UUID']
- end
-
def jira_admin_username
ENV['JIRA_ADMIN_USERNAME']
end
@@ -496,6 +480,10 @@ module QA
enabled?(ENV['QA_USE_PUBLIC_IP_API'], default: false)
end
+ def chrome_default_download_path
+ ENV['DEFAULT_CHROME_DOWNLOAD_PATH']
+ end
+
private
def remote_grid_credentials
diff --git a/qa/qa/runtime/fixtures.rb b/qa/qa/runtime/fixtures.rb
index 41d7ce5d178..d56af9b4872 100644
--- a/qa/qa/runtime/fixtures.rb
+++ b/qa/qa/runtime/fixtures.rb
@@ -49,3 +49,5 @@ module QA
end
end
end
+
+QA::Runtime::Fixtures.prepend_mod_with('Runtime::Fixtures', namespace: QA)
diff --git a/qa/qa/runtime/user.rb b/qa/qa/runtime/user.rb
index e4eeca2000f..cb2a86276f9 100644
--- a/qa/qa/runtime/user.rb
+++ b/qa/qa/runtime/user.rb
@@ -6,7 +6,10 @@ module QA
extend self
def admin
- Struct.new(:username, :password).new(admin_username, admin_password)
+ QA::Resource::User.init do |user|
+ user.username = admin_username
+ user.password = admin_password
+ end
end
def default_username
diff --git a/qa/qa/scenario/test/instance/cloud_activation.rb b/qa/qa/scenario/test/instance/cloud_activation.rb
new file mode 100644
index 00000000000..0f61cab582a
--- /dev/null
+++ b/qa/qa/scenario/test/instance/cloud_activation.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module QA
+ module Scenario
+ module Test
+ module Instance
+ class CloudActivation < All
+ tags :cloud_activation
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/scenario/test/instance/integrations.rb b/qa/qa/scenario/test/instance/integrations.rb
new file mode 100644
index 00000000000..b943b95c51e
--- /dev/null
+++ b/qa/qa/scenario/test/instance/integrations.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module QA
+ module Scenario
+ module Test
+ module Instance
+ class Integrations < All
+ tags :integrations
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/scenario/test/instance/jira.rb b/qa/qa/scenario/test/instance/jira.rb
new file mode 100644
index 00000000000..784a71515cb
--- /dev/null
+++ b/qa/qa/scenario/test/instance/jira.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module QA
+ module Scenario
+ module Test
+ module Instance
+ class Jira < All
+ tags :jira
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/scenario/test/instance/large_setup.rb b/qa/qa/scenario/test/instance/large_setup.rb
new file mode 100644
index 00000000000..934c841c619
--- /dev/null
+++ b/qa/qa/scenario/test/instance/large_setup.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module QA
+ module Scenario
+ module Test
+ module Instance
+ class LargeSetup < All
+ tags :can_use_large_setup
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/scenario/test/instance/metrics.rb b/qa/qa/scenario/test/instance/metrics.rb
new file mode 100644
index 00000000000..7643aff095b
--- /dev/null
+++ b/qa/qa/scenario/test/instance/metrics.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module QA
+ module Scenario
+ module Test
+ module Instance
+ class Metrics < All
+ tags :metrics
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/scenario/test/instance/object_storage.rb b/qa/qa/scenario/test/instance/object_storage.rb
new file mode 100644
index 00000000000..6d54b50a505
--- /dev/null
+++ b/qa/qa/scenario/test/instance/object_storage.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module QA
+ module Scenario
+ module Test
+ module Instance
+ class ObjectStorage < All
+ tags :object_storage
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/scenario/test/instance/packages.rb b/qa/qa/scenario/test/instance/packages.rb
new file mode 100644
index 00000000000..7648a2b5e80
--- /dev/null
+++ b/qa/qa/scenario/test/instance/packages.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module QA
+ module Scenario
+ module Test
+ module Instance
+ class Packages < All
+ tags :packages
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/scenario/test/instance/repository_storage.rb b/qa/qa/scenario/test/instance/repository_storage.rb
new file mode 100644
index 00000000000..c648335c353
--- /dev/null
+++ b/qa/qa/scenario/test/instance/repository_storage.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module QA
+ module Scenario
+ module Test
+ module Instance
+ class RepositoryStorage < All
+ tags :repository_storage
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/scenario/test/instance/review_blocking.rb b/qa/qa/scenario/test/instance/review_blocking.rb
new file mode 100644
index 00000000000..cb1b6c9cf10
--- /dev/null
+++ b/qa/qa/scenario/test/instance/review_blocking.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+module QA
+ module Scenario
+ module Test
+ module Instance
+ class ReviewBlocking < All
+ tags :reliable,
+ :sanity_feature_flags,
+ :"~orchestrated",
+ :"~skip_signup_disabled"
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/scenario/test/instance/review_non_blocking.rb b/qa/qa/scenario/test/instance/review_non_blocking.rb
new file mode 100644
index 00000000000..e295171eb0b
--- /dev/null
+++ b/qa/qa/scenario/test/instance/review_non_blocking.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+module QA
+ module Scenario
+ module Test
+ module Instance
+ class ReviewNonBlocking < All
+ tags :"~reliable",
+ :"~smoke",
+ :"~skip_signup_disabled",
+ *Specs::Runner::DEFAULT_SKIPPED_TAGS.map { |tag| :"~#{tag}" }
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/service/cluster_provider/gcloud.rb b/qa/qa/service/cluster_provider/gcloud.rb
index 77677745f7a..14c13eecb8d 100644
--- a/qa/qa/service/cluster_provider/gcloud.rb
+++ b/qa/qa/service/cluster_provider/gcloud.rb
@@ -33,14 +33,32 @@ module QA
delete_cluster
end
- def install_ingress
- QA::Runtime::Logger.info "Attempting to install Ingress on cluster #{cluster_name}"
- shell 'kubectl create -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-0.31.0/deploy/static/provider/cloud/deploy.yaml'
- wait_for_ingress
+ # kas is hardcoded to staging since this test should only run in staging for now
+ def install_kubernetes_agent(agent_token)
+ install_helm
+
+ shell <<~CMD.tr("\n", ' ')
+ helm repo add gitlab https://charts.gitlab.io &&
+ helm repo update &&
+ helm upgrade --install test gitlab/gitlab-agent
+ --namespace gitlab-agent
+ --create-namespace
+ --set image.tag=#{Runtime::Env.gitlab_agentk_version}
+ --set config.token=#{agent_token}
+ --set config.kasAddress=wss://kas.staging.gitlab.com
+ CMD
end
private
+ def install_helm
+ shell <<~CMD.tr("\n", ' ')
+ curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 &&
+ chmod 700 get_helm.sh &&
+ ./get_helm.sh
+ CMD
+ end
+
def login_if_not_already_logged_in
if Runtime::Env.has_gcloud_credentials?
attempt_login_with_env_vars
@@ -104,18 +122,6 @@ module QA
def get_region
Runtime::Env.gcloud_region || @available_regions.delete(@available_regions.sample)
end
-
- def wait_for_ingress
- QA::Runtime::Logger.info 'Waiting for Ingress controller pod to be initialized'
-
- Support::Retrier.retry_until(max_attempts: 60, sleep_interval: 1) do
- service_available?('kubectl get pods --all-namespaces -l app.kubernetes.io/component=controller | grep -o "ingress-nginx-controller.*1/1"')
- end
- end
-
- def service_available?(command)
- system("#{command} > /dev/null 2>&1")
- end
end
end
end
diff --git a/qa/qa/service/kubernetes_cluster.rb b/qa/qa/service/kubernetes_cluster.rb
index dafce4acc33..59bfacf9195 100644
--- a/qa/qa/service/kubernetes_cluster.rb
+++ b/qa/qa/service/kubernetes_cluster.rb
@@ -41,8 +41,8 @@ module QA
cluster_name
end
- def install_ingress
- @provider.install_ingress
+ def install_kubernetes_agent(agent_token)
+ @provider.install_kubernetes_agent(agent_token)
end
def create_secret(secret, secret_name)
@@ -73,16 +73,6 @@ module QA
shell('kubectl apply -f -', stdin_data: network_policy)
end
- def fetch_external_ip_for_ingress
- install_ingress
-
- # need to wait since the ingress-nginx service has an initial delay set of 10 seconds
- sleep 12
- ingress_ip = `kubectl get svc --all-namespaces --no-headers=true -l app.kubernetes.io/name=ingress-nginx -o custom-columns=:'status.loadBalancer.ingress[0].ip' | grep -v 'none'`
- QA::Runtime::Logger.debug "Has ingress address set to: #{ingress_ip}"
- ingress_ip
- end
-
private
def fetch_api_url
diff --git a/qa/qa/specs/features/api/3_create/gitaly/automatic_failover_and_recovery_spec.rb b/qa/qa/specs/features/api/12_systems/gitaly/automatic_failover_and_recovery_spec.rb
index 55ae0d215cf..2058d58d5d6 100644
--- a/qa/qa/specs/features/api/3_create/gitaly/automatic_failover_and_recovery_spec.rb
+++ b/qa/qa/specs/features/api/12_systems/gitaly/automatic_failover_and_recovery_spec.rb
@@ -1,8 +1,8 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Create' do
- context 'Gitaly automatic failover and recovery', :orchestrated, :gitaly_cluster do
+ RSpec.describe 'Systems' do
+ context 'with Gitaly automatic failover and recovery', :orchestrated, :gitaly_cluster do
# Variables shared between contexts. They're used and shared between
# contexts so they can't be `let` variables.
praefect_manager = Service::PraefectManager.new
@@ -47,11 +47,11 @@ module QA
commit.project = project
commit.commit_message = second_added_commit_message
commit.add_files([
- {
- file_path: "file-#{SecureRandom.hex(8)}",
- content: 'This is created on gitaly2/gitaly3 while gitaly1 is unavailable'
- }
- ])
+ {
+ file_path: "file-#{SecureRandom.hex(8)}",
+ content: 'This is created on gitaly2/gitaly3 while gitaly1 is unavailable'
+ }
+ ])
end
# Confirm that we have access to the repo after failover,
diff --git a/qa/qa/specs/features/api/12_systems/gitaly/backend_node_recovery_spec.rb b/qa/qa/specs/features/api/12_systems/gitaly/backend_node_recovery_spec.rb
new file mode 100644
index 00000000000..0b4bdf550f8
--- /dev/null
+++ b/qa/qa/specs/features/api/12_systems/gitaly/backend_node_recovery_spec.rb
@@ -0,0 +1,63 @@
+# frozen_string_literal: true
+
+module QA
+ RSpec.describe 'Systems' do
+ describe 'Gitaly backend node recovery', :orchestrated, :gitaly_cluster, :skip_live_env do
+ let(:praefect_manager) { Service::PraefectManager.new }
+ let(:project) do
+ Resource::Project.fabricate! do |project|
+ project.name = "gitaly_cluster"
+ project.initialize_with_readme = true
+ end
+ end
+
+ before do
+ # Reset the cluster in case previous tests left it in a bad state
+ praefect_manager.start_all_nodes
+ end
+
+ after do
+ # Leave the cluster in a suitable state for subsequent tests
+ praefect_manager.start_all_nodes
+ end
+
+ it 'recovers from dataloss', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347832' do
+ # Create a new project with a commit and wait for it to replicate
+ praefect_manager.wait_for_replication(project.id)
+
+ # Stop the primary node to trigger failover, and then wait
+ # for Gitaly to be ready for writes again
+ praefect_manager.stop_primary_node
+ praefect_manager.wait_for_primary_node_health_check_failure
+
+ # Push a commit to the new primary
+ Resource::Repository::ProjectPush.fabricate! do |push|
+ push.project = project
+ push.new_branch = false
+ push.commit_message = 'pushed after failover'
+ push.file_name = 'new_file'
+ push.file_content = 'new file'
+ end
+
+ # Confirm that the commit is waiting to be replicated
+ expect(praefect_manager).to be_replication_pending
+
+ # Start the old primary node again
+ praefect_manager.start_primary_node
+ praefect_manager.wait_for_health_check_all_nodes
+
+ # Wait for automatic replication
+ praefect_manager.wait_for_replication(project.id)
+
+ # Force switch to the old primary node
+ # This ensures that the commit was replicated
+ praefect_manager.stop_secondary_node
+ praefect_manager.stop_tertiary_node
+
+ # Confirm that both commits are available
+ expect(project.commits.map { |commit| commit[:message].chomp })
+ .to include("Initial commit").and include("pushed after failover")
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/api/3_create/gitaly/changing_repository_storage_spec.rb b/qa/qa/specs/features/api/12_systems/gitaly/changing_repository_storage_spec.rb
index 5ee6dfdb8d8..18ec8e0a8b4 100644
--- a/qa/qa/specs/features/api/3_create/gitaly/changing_repository_storage_spec.rb
+++ b/qa/qa/specs/features/api/12_systems/gitaly/changing_repository_storage_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Create' do
+ RSpec.describe 'Systems' do
describe 'Changing Gitaly repository storage', :requires_admin, except: { job: 'review-qa-*' } do
praefect_manager = Service::PraefectManager.new
@@ -9,7 +9,8 @@ module QA
it 'confirms a `finished` status after moving project repository storage' do
expect(project).to have_file('README.md')
expect { project.change_repository_storage(destination_storage[:name]) }.not_to raise_error
- expect { praefect_manager.verify_storage_move(source_storage, destination_storage, repo_type: :project) }.not_to raise_error
+ expect { praefect_manager.verify_storage_move(source_storage, destination_storage, repo_type: :project) }
+ .not_to raise_error
Support::Retrier.retry_on_exception(sleep_interval: 5) do
# For a short period of time after migrating, the repository can be 'read only' which may lead to errors
@@ -18,8 +19,8 @@ module QA
commit.project = project
commit.commit_message = 'Add new file'
commit.add_files([
- { file_path: 'new_file', content: '# This is a new file' }
- ])
+ { file_path: 'new_file', content: '# This is a new file' }
+ ])
end
end
@@ -28,7 +29,8 @@ module QA
end
end
- context 'when moving from one Gitaly storage to another', :orchestrated, :repository_storage, testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347827' do
+ context 'when moving from one Gitaly storage to another', :orchestrated, :repository_storage,
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347827' do
let(:source_storage) { { type: :gitaly, name: 'default' } }
let(:destination_storage) { { type: :gitaly, name: QA::Runtime::Env.additional_repository_storage } }
let(:project) do
@@ -49,7 +51,8 @@ module QA
# Note: This test doesn't have the :orchestrated tag because it runs in the Test::Integration::Praefect
# scenario with other tests that aren't considered orchestrated.
# It also runs on staging using nfs-file07 as non-cluster storage and nfs-file22 as cluster/praefect storage
- context 'when moving from Gitaly to Gitaly Cluster', :requires_praefect, testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347828' do
+ context 'when moving from Gitaly to Gitaly Cluster', :requires_praefect,
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347828' do
let(:source_storage) { { type: :gitaly, name: QA::Runtime::Env.non_cluster_repository_storage } }
let(:destination_storage) { { type: :praefect, name: QA::Runtime::Env.praefect_repository_storage } }
let(:project) do
@@ -71,7 +74,8 @@ module QA
# Note: This test doesn't have the :orchestrated tag because it runs in the Test::Integration::Praefect
# scenario with other tests that aren't considered orchestrated.
# It also runs on staging using nfs-file07 as non-cluster storage and nfs-file22 as cluster/praefect storage
- context 'when moving from Gitaly Cluster to Gitaly', :requires_praefect, testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/369204' do
+ context 'when moving from Gitaly Cluster to Gitaly', :requires_praefect,
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/369204' do
let(:source_storage) { { type: :praefect, name: QA::Runtime::Env.praefect_repository_storage } }
let(:destination_storage) { { type: :gitaly, name: QA::Runtime::Env.non_cluster_repository_storage } }
let(:project) do
diff --git a/qa/qa/specs/features/api/12_systems/gitaly/distributed_reads_spec.rb b/qa/qa/specs/features/api/12_systems/gitaly/distributed_reads_spec.rb
new file mode 100644
index 00000000000..692297e40ce
--- /dev/null
+++ b/qa/qa/specs/features/api/12_systems/gitaly/distributed_reads_spec.rb
@@ -0,0 +1,96 @@
+# frozen_string_literal: true
+
+require 'parallel'
+
+module QA
+ RSpec.describe 'Systems' do
+ describe 'Gitaly distributed reads', :orchestrated, :gitaly_cluster, :skip_live_env, :requires_admin do
+ let(:number_of_reads_per_loop) { 9 }
+ let(:praefect_manager) { Service::PraefectManager.new }
+ let(:project) do
+ Resource::Project.fabricate! do |project|
+ project.name = "gitaly_cluster"
+ project.initialize_with_readme = true
+ end
+ end
+
+ before do
+ praefect_manager.start_all_nodes
+ praefect_manager.wait_for_replication(project.id)
+ end
+
+ it 'reads from each node', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347833' do
+ pre_read_data = praefect_manager.query_read_distribution
+
+ wait_for_reads_to_increase(project, number_of_reads_per_loop, pre_read_data)
+
+ aggregate_failures "each gitaly node" do
+ praefect_manager.query_read_distribution.each_with_index do |data, index|
+ pre_read_count = praefect_manager.value_for_node(pre_read_data, data[:node])
+ QA::Runtime::Logger.debug("Node: #{data[:node]}; before: #{pre_read_count}; now: #{data[:value]}")
+ expect(data[:value]).to be > pre_read_count,
+ "Read counts did not differ for node #{data[:node]}"
+ end
+ end
+ end
+
+ context 'when a node is unhealthy' do
+ before do
+ praefect_manager.stop_secondary_node
+ end
+
+ after do
+ # Leave the cluster in a suitable state for subsequent tests
+ praefect_manager.start_secondary_node
+ end
+
+ it 'does not read from the unhealthy node',
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347834' do
+ pre_read_data = praefect_manager.query_read_distribution
+
+ read_from_project(project, number_of_reads_per_loop * 10)
+
+ praefect_manager.wait_for_read_count_change(pre_read_data)
+
+ post_read_data = praefect_manager.query_read_distribution
+
+ aggregate_failures "each gitaly node" do
+ expect(praefect_manager.value_for_node(post_read_data, 'gitaly1'))
+ .to be > praefect_manager.value_for_node(pre_read_data, 'gitaly1')
+ expect(praefect_manager.value_for_node(post_read_data, 'gitaly2'))
+ .to eq praefect_manager.value_for_node(pre_read_data, 'gitaly2')
+ expect(praefect_manager.value_for_node(post_read_data, 'gitaly3'))
+ .to be > praefect_manager.value_for_node(pre_read_data, 'gitaly3')
+ end
+ end
+ end
+
+ def read_from_project(project, number_of_reads)
+ QA::Runtime::Logger.info('Reading from the repository')
+ Parallel.each((1..number_of_reads)) do
+ Git::Repository.perform do |repository|
+ repository.uri = project.repository_http_location.uri
+ repository.use_default_credentials
+ repository.clone
+ end
+ end
+ end
+
+ def wait_for_reads_to_increase(project, number_of_reads, pre_read_data)
+ diff_found = pre_read_data
+
+ Support::Waiter.wait_until(sleep_interval: 5, raise_on_failure: false) do
+ read_from_project(project, number_of_reads)
+
+ praefect_manager.query_read_distribution.each_with_index do |data, index|
+ diff_found[index] = {} unless diff_found[index]
+ if data[:value] > praefect_manager.value_for_node(pre_read_data, data[:node])
+ diff_found[index][:diff] = true
+ end
+ end
+ diff_found.all? { |node| node.key?(:diff) && node[:diff] }
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/api/12_systems/gitaly/gitaly_mtls_spec.rb b/qa/qa/specs/features/api/12_systems/gitaly/gitaly_mtls_spec.rb
new file mode 100644
index 00000000000..a4b39554453
--- /dev/null
+++ b/qa/qa/specs/features/api/12_systems/gitaly/gitaly_mtls_spec.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+module QA
+ RSpec.describe 'Systems' do
+ describe 'Gitaly using mTLS', :orchestrated, :mtls do
+ let(:intial_commit_message) { 'Initial commit' }
+ let(:first_added_commit_message) { 'commit over git' }
+ let(:second_added_commit_message) { 'commit over api' }
+
+ it 'pushes to gitaly', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347677' do
+ project = Resource::Project.fabricate! do |project|
+ project.name = "mTLS"
+ project.initialize_with_readme = true
+ end
+
+ Resource::Repository::ProjectPush.fabricate! do |push|
+ push.project = project
+ push.new_branch = false
+ push.commit_message = first_added_commit_message
+ push.file_content = 'First commit'
+ end
+
+ Resource::Repository::Commit.fabricate_via_api! do |commit|
+ commit.project = project
+ commit.commit_message = second_added_commit_message
+ commit.add_files(
+ [{
+ file_path: "file-#{SecureRandom.hex(8)}",
+ content: 'Second commit'
+ }]
+ )
+ end
+
+ expect(project.commits.map { |commit| commit[:message].chomp })
+ .to include(intial_commit_message)
+ .and include(first_added_commit_message)
+ .and include(second_added_commit_message)
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/api/3_create/gitaly/praefect_connectivity_spec.rb b/qa/qa/specs/features/api/12_systems/gitaly/praefect_connectivity_spec.rb
index 28469b99d04..f25b50f584d 100644
--- a/qa/qa/specs/features/api/3_create/gitaly/praefect_connectivity_spec.rb
+++ b/qa/qa/specs/features/api/12_systems/gitaly/praefect_connectivity_spec.rb
@@ -1,8 +1,8 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Create' do
- context 'Praefect connectivity commands', :orchestrated, :gitaly_cluster do
+ RSpec.describe 'Systems' do
+ describe 'Praefect connectivity commands', :orchestrated, :gitaly_cluster do
praefect_manager = Service::PraefectManager.new
before do
@@ -10,22 +10,26 @@ module QA
end
context 'in a healthy environment' do
- it 'confirms healthy connection to database', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/349937' do
+ it 'confirms healthy connection to database',
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/349937' do
expect(praefect_manager.praefect_sql_ping_healthy?).to be true
end
- it 'confirms healthy connection to gitaly nodes', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/349938' do
+ it 'confirms healthy connection to gitaly nodes',
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/349938' do
expect(praefect_manager.wait_for_dial_nodes_successful).to be true
end
end
context 'in an unhealthy environment' do
- it 'diagnoses unhealthy connection to database', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/349939' do
+ it 'diagnoses unhealthy connection to database',
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/349939' do
praefect_manager.stop_node(praefect_manager.postgres)
expect(praefect_manager.praefect_sql_ping_healthy?).to be false
end
- it 'diagnoses connection issues to gitaly nodes', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/349940' do
+ it 'diagnoses connection issues to gitaly nodes',
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/349940' do
praefect_manager.stop_node(praefect_manager.primary_node)
praefect_manager.stop_node(praefect_manager.tertiary_node)
expect(praefect_manager.praefect_dial_nodes_status?(praefect_manager.primary_node, false)).to be true
diff --git a/qa/qa/specs/features/api/3_create/gitaly/praefect_dataloss_spec.rb b/qa/qa/specs/features/api/12_systems/gitaly/praefect_dataloss_spec.rb
index 5b02cc4646c..944c58ebc83 100644
--- a/qa/qa/specs/features/api/3_create/gitaly/praefect_dataloss_spec.rb
+++ b/qa/qa/specs/features/api/12_systems/gitaly/praefect_dataloss_spec.rb
@@ -1,8 +1,8 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Create' do
- context 'Praefect dataloss commands', :orchestrated, :gitaly_cluster do
+ RSpec.describe 'Systems' do
+ describe 'Praefect dataloss commands', :orchestrated, :gitaly_cluster do
let(:praefect_manager) { Service::PraefectManager.new }
let(:project) do
@@ -16,13 +16,15 @@ module QA
praefect_manager.start_all_nodes
end
- it 'confirms that changes are synced across all storages', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/352691' do
+ it 'confirms that changes are synced across all storages',
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/352691' do
expect { praefect_manager.praefect_dataloss_information(project.id) }
.to(eventually_include('All repositories are fully available on all assigned storages!')
.within(max_duration: 60))
end
- it 'identifies how many changes are not in sync across storages', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/352692' do
+ it 'identifies how many changes are not in sync across storages',
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/352692' do
# Ensure our test repository is replicated and in a consistent state prior to test
praefect_manager.wait_for_project_synced_across_all_storages(project.id)
@@ -36,9 +38,11 @@ module QA
commit.branch = "newbranch-#{SecureRandom.hex(8)}"
commit.start_branch = project.default_branch
commit.commit_message = 'Add new file'
- commit.add_files([
- { file_path: "new_file-#{SecureRandom.hex(8)}.txt", content: 'new file' }
- ])
+ commit.add_files(
+ [{
+ file_path: "new_file-#{SecureRandom.hex(8)}.txt", content: 'new file'
+ }]
+ )
end
end
@@ -48,21 +52,25 @@ module QA
project_data_loss = praefect_manager.praefect_dataloss_information(project.id)
aggregate_failures "validate dataloss identified" do
expect(project_data_loss).to include('gitaly1, assigned host')
- expect(project_data_loss).to include("gitaly2 is behind by #{number_of_changes} changes or less, assigned host, unhealthy")
+ expect(project_data_loss)
+ .to include("gitaly2 is behind by #{number_of_changes} changes or less, assigned host, unhealthy")
expect(project_data_loss).to include('gitaly3, assigned host, unhealthy')
end
end
- it 'allows admin resolve scenario where data cannot be recovered', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/352708' do
+ it 'allows admin resolve scenario where data cannot be recovered',
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/352708' do
# Ensure everything is in sync before begining test
praefect_manager.wait_for_project_synced_across_all_storages(project.id)
Resource::Repository::Commit.fabricate_via_api! do |commit|
commit.project = project
commit.commit_message = 'accept-dataloss-1'
- commit.add_files([
- { file_path: "new_file-#{SecureRandom.hex(8)}.txt", content: 'Add a commit to gitaly1,gitaly2,gitaly3' }
- ])
+ commit.add_files(
+ [{
+ file_path: "new_file-#{SecureRandom.hex(8)}.txt", content: 'Add a commit to gitaly1,gitaly2,gitaly3'
+ }]
+ )
end
praefect_manager.wait_for_replication_to_node(project.id, praefect_manager.primary_node)
@@ -70,9 +78,11 @@ module QA
Resource::Repository::Commit.fabricate_via_api! do |commit|
commit.project = project
commit.commit_message = 'accept-dataloss-2'
- commit.add_files([
- { file_path: "new_file-#{SecureRandom.hex(8)}.txt", content: 'Add a commit to gitaly2,gitaly3' }
- ])
+ commit.add_files(
+ [{
+ file_path: "new_file-#{SecureRandom.hex(8)}.txt", content: 'Add a commit to gitaly2,gitaly3'
+ }]
+ )
end
praefect_manager.wait_for_replication_to_node(project.id, praefect_manager.secondary_node)
@@ -81,8 +91,8 @@ module QA
commit.project = project
commit.commit_message = 'accept-dataloss-3'
commit.add_files([
- { file_path: "new_file-#{SecureRandom.hex(8)}.txt", content: 'Add a commit to gitaly3' }
- ])
+ { file_path: "new_file-#{SecureRandom.hex(8)}.txt", content: 'Add a commit to gitaly3' }
+ ])
end
# Confirms that they want to accept dataloss, using gitaly2 as authoritative storage to use as a base
diff --git a/qa/qa/specs/features/api/3_create/gitaly/praefect_replication_queue_spec.rb b/qa/qa/specs/features/api/12_systems/gitaly/praefect_replication_queue_spec.rb
index a53614960cd..f4efcf74956 100644
--- a/qa/qa/specs/features/api/3_create/gitaly/praefect_replication_queue_spec.rb
+++ b/qa/qa/specs/features/api/12_systems/gitaly/praefect_replication_queue_spec.rb
@@ -3,8 +3,8 @@
require 'parallel'
module QA
- RSpec.describe 'Create' do
- context 'Gitaly Cluster replication queue', :orchestrated, :gitaly_cluster, :skip_live_env do
+ RSpec.describe 'Systems' do
+ describe 'Gitaly Cluster replication queue', :orchestrated, :gitaly_cluster, :skip_live_env do
let(:praefect_manager) { Service::PraefectManager.new }
let(:project) do
Resource::Project.fabricate! do |project|
@@ -22,7 +22,8 @@ module QA
praefect_manager.clear_replication_queue
end
- it 'allows replication of different repository after interruption', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347829' do
+ it 'allows replication of different repository after interruption',
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347829' do
# We want to fill the replication queue with 10 `in_progress` jobs,
# while a lock has been acquired, which is when the problem occurred
# as reported in https://gitlab.com/gitlab-org/gitaly/-/issues/2801
diff --git a/qa/qa/specs/features/api/3_create/gitaly/praefect_repo_sync_spec.rb b/qa/qa/specs/features/api/12_systems/gitaly/praefect_repo_sync_spec.rb
index 47be7e0620b..064743ae469 100644
--- a/qa/qa/specs/features/api/3_create/gitaly/praefect_repo_sync_spec.rb
+++ b/qa/qa/specs/features/api/12_systems/gitaly/praefect_repo_sync_spec.rb
@@ -1,12 +1,17 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Create' do
- context 'Praefect repository commands', :orchestrated, :gitaly_cluster do
+ RSpec.describe 'Systems' do
+ describe 'Praefect repository commands', :orchestrated, :gitaly_cluster do
let(:praefect_manager) { Service::PraefectManager.new }
- let(:repo1) { { "relative_path" => "@hashed/repo1.git", "storage" => "gitaly1", "virtual_storage" => "default" } }
- let(:repo2) { { "relative_path" => "@hashed/path/to/repo2.git", "storage" => "gitaly3", "virtual_storage" => "default" } }
+ let(:repo1) do
+ { "relative_path" => "@hashed/repo1.git", "storage" => "gitaly1", "virtual_storage" => "default" }
+ end
+
+ let(:repo2) do
+ { "relative_path" => "@hashed/path/to/repo2.git", "storage" => "gitaly3", "virtual_storage" => "default" }
+ end
before do
praefect_manager.start_all_nodes
@@ -21,54 +26,70 @@ module QA
praefect_manager.remove_repository_from_praefect_database(repo2["relative_path"])
end
- it 'allows admin to manage difference between praefect database and disk state', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347606' do
+ it 'allows admin to manage difference between praefect database and disk state',
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347606' do
# Some repos are on disk that praefect is not aware of
untracked_repositories = praefect_manager.list_untracked_repositories
expect(untracked_repositories).to include(repo1)
expect(untracked_repositories).to include(repo2)
# admin manually adds the first repo to the praefect database
- praefect_manager.track_repository_in_praefect(repo1["relative_path"], repo1["storage"], repo1["virtual_storage"])
+ praefect_manager
+ .track_repository_in_praefect(repo1["relative_path"], repo1["storage"], repo1["virtual_storage"])
untracked_repositories = praefect_manager.list_untracked_repositories
expect(untracked_repositories).not_to include(repo1)
expect(untracked_repositories).to include(repo2)
- expect(praefect_manager.repository_exists_on_node_disk?(praefect_manager.primary_node, repo1["relative_path"])).to be true
+ expect(praefect_manager.repository_exists_on_node_disk?(praefect_manager.primary_node, repo1["relative_path"]))
+ .to be true
expect(praefect_manager.praefect_database_tracks_repo?(repo1["relative_path"])).to be true
# admin manually adds the second repo to the praefect database
- praefect_manager.track_repository_in_praefect(repo2["relative_path"], repo2["storage"], repo2["virtual_storage"])
+ praefect_manager
+ .track_repository_in_praefect(repo2["relative_path"], repo2["storage"], repo2["virtual_storage"])
untracked_repositories = praefect_manager.list_untracked_repositories
expect(untracked_repositories).not_to include(repo2)
- expect(praefect_manager.repository_exists_on_node_disk?(praefect_manager.tertiary_node, repo2["relative_path"])).to be true
+ expect(praefect_manager.repository_exists_on_node_disk?(praefect_manager.tertiary_node, repo2["relative_path"]))
+ .to be true
expect(praefect_manager.praefect_database_tracks_repo?(repo2["relative_path"])).to be true
# admin ensures replication to other nodes occurs
- expect(praefect_manager.repository_replicated_to_disk?(praefect_manager.secondary_node, repo1["relative_path"])).to be true
- expect(praefect_manager.repository_replicated_to_disk?(praefect_manager.tertiary_node, repo1["relative_path"])).to be true
- expect(praefect_manager.repository_replicated_to_disk?(praefect_manager.primary_node, repo2["relative_path"])).to be true
- expect(praefect_manager.repository_replicated_to_disk?(praefect_manager.secondary_node, repo2["relative_path"])).to be true
+ expect(praefect_manager.repository_replicated_to_disk?(praefect_manager.secondary_node, repo1["relative_path"]))
+ .to be true
+ expect(praefect_manager.repository_replicated_to_disk?(praefect_manager.tertiary_node, repo1["relative_path"]))
+ .to be true
+ expect(praefect_manager.repository_replicated_to_disk?(praefect_manager.primary_node, repo2["relative_path"]))
+ .to be true
+ expect(praefect_manager.repository_replicated_to_disk?(praefect_manager.secondary_node, repo2["relative_path"]))
+ .to be true
# admin chooses to remove the first repo completely from praefect and disk
praefect_manager.remove_tracked_praefect_repository(repo1["relative_path"], repo1["virtual_storage"])
- expect(praefect_manager.repository_exists_on_node_disk?(praefect_manager.primary_node, repo1["relative_path"])).to be false
- expect(praefect_manager.repository_exists_on_node_disk?(praefect_manager.secondary_node, repo1["relative_path"])).to be false
- expect(praefect_manager.repository_exists_on_node_disk?(praefect_manager.tertiary_node, repo1["relative_path"])).to be false
+ expect(praefect_manager.repository_exists_on_node_disk?(praefect_manager.primary_node, repo1["relative_path"]))
+ .to be false
+ expect(praefect_manager.repository_exists_on_node_disk?(praefect_manager
+ .secondary_node, repo1["relative_path"])).to be false
+ expect(praefect_manager.repository_exists_on_node_disk?(praefect_manager.tertiary_node, repo1["relative_path"]))
+ .to be false
expect(praefect_manager.praefect_database_tracks_repo?(repo1["relative_path"])).to be false
untracked_repositories = praefect_manager.list_untracked_repositories
expect(untracked_repositories).not_to include(repo1)
end
- it 'allows admin to control the number of replicas of data', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347566' do
- praefect_manager.track_repository_in_praefect(repo1['relative_path'], repo1['storage'], repo1['virtual_storage'])
+ it 'allows admin to control the number of replicas of data',
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347566' do
+ praefect_manager
+ .track_repository_in_praefect(repo1['relative_path'], repo1['storage'], repo1['virtual_storage'])
praefect_manager.set_replication_factor(repo1['relative_path'], repo1['virtual_storage'], 2)
- replication_storages = praefect_manager.get_replication_storages(repo1['relative_path'], repo1['virtual_storage'])
+ replication_storages = praefect_manager
+ .get_replication_storages(repo1['relative_path'], repo1['virtual_storage'])
expect(replication_storages).to have_attributes(size: 2)
praefect_manager.set_replication_factor(repo1['relative_path'], repo1['virtual_storage'], 3)
- replication_storages = praefect_manager.get_replication_storages(repo1['relative_path'], repo1['virtual_storage'])
- expect(replication_storages).to eq(%w(gitaly1 gitaly2 gitaly3))
+ replication_storages = praefect_manager
+ .get_replication_storages(repo1['relative_path'], repo1['virtual_storage'])
+ expect(replication_storages).to eq(%w[gitaly1 gitaly2 gitaly3])
end
end
end
diff --git a/qa/qa/specs/features/api/1_manage/import_large_github_repo_spec.rb b/qa/qa/specs/features/api/1_manage/import_large_github_repo_spec.rb
index de460a39ccf..e6b60a5b090 100644
--- a/qa/qa/specs/features/api/1_manage/import_large_github_repo_spec.rb
+++ b/qa/qa/specs/features/api/1_manage/import_large_github_repo_spec.rb
@@ -88,14 +88,19 @@ module QA
let(:gh_issue_comments) do
logger.debug("= Fetching issue comments =")
github_client.issues_comments(github_repo).each_with_object(Hash.new { |h, k| h[k] = [] }) do |c, hash|
- hash[c.html_url.gsub(/\#\S+/, "")] << c.body&.gsub(gh_link_pattern, dummy_url) # use base html url as key
+ # use base html url as key
+ hash[c.html_url.gsub(/\#\S+/, "")] << c.body&.gsub(gh_link_pattern, dummy_url)
end
end
let(:gh_pr_comments) do
logger.debug("= Fetching pr comments =")
github_client.pull_requests_comments(github_repo).each_with_object(Hash.new { |h, k| h[k] = [] }) do |c, hash|
- hash[c.html_url.gsub(/\#\S+/, "")] << c.body&.gsub(gh_link_pattern, dummy_url) # use base html url as key
+ # use base html url as key
+ hash[c.html_url.gsub(/\#\S+/, "")] << c.body
+ # some suggestions can contain extra whitespaces which gitlab will remove
+ &.gsub(/suggestion\s+\r/, "suggestion\r")
+ &.gsub(gh_link_pattern, dummy_url)
end
end
diff --git a/qa/qa/specs/features/api/1_manage/user_inherited_access_spec.rb b/qa/qa/specs/features/api/1_manage/user_inherited_access_spec.rb
index 444d86f63d3..9f0e2664213 100644
--- a/qa/qa/specs/features/api/1_manage/user_inherited_access_spec.rb
+++ b/qa/qa/specs/features/api/1_manage/user_inherited_access_spec.rb
@@ -71,7 +71,12 @@ module QA
it(
'is allowed to commit to sub-group project via the API',
:reliable,
- testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/363349'
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/363349',
+ quarantine: {
+ only: { subdomain: %i[staging staging-ref] },
+ type: :investigating,
+ issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/370282'
+ }
) do
expect do
Resource::Repository::Commit.fabricate_via_api! do |commit|
diff --git a/qa/qa/specs/features/api/3_create/gitaly/backend_node_recovery_spec.rb b/qa/qa/specs/features/api/3_create/gitaly/backend_node_recovery_spec.rb
deleted file mode 100644
index 25e860b4f6d..00000000000
--- a/qa/qa/specs/features/api/3_create/gitaly/backend_node_recovery_spec.rb
+++ /dev/null
@@ -1,65 +0,0 @@
-# frozen_string_literal: true
-
-module QA
- RSpec.describe 'Create' do
- context 'Gitaly' do
- describe 'Backend node recovery', :orchestrated, :gitaly_cluster, :skip_live_env do
- let(:praefect_manager) { Service::PraefectManager.new }
- let(:project) do
- Resource::Project.fabricate! do |project|
- project.name = "gitaly_cluster"
- project.initialize_with_readme = true
- end
- end
-
- before do
- # Reset the cluster in case previous tests left it in a bad state
- praefect_manager.start_all_nodes
- end
-
- after do
- # Leave the cluster in a suitable state for subsequent tests
- praefect_manager.start_all_nodes
- end
-
- it 'recovers from dataloss', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347832' do
- # Create a new project with a commit and wait for it to replicate
- praefect_manager.wait_for_replication(project.id)
-
- # Stop the primary node to trigger failover, and then wait
- # for Gitaly to be ready for writes again
- praefect_manager.stop_primary_node
- praefect_manager.wait_for_primary_node_health_check_failure
-
- # Push a commit to the new primary
- Resource::Repository::ProjectPush.fabricate! do |push|
- push.project = project
- push.new_branch = false
- push.commit_message = 'pushed after failover'
- push.file_name = 'new_file'
- push.file_content = 'new file'
- end
-
- # Confirm that the commit is waiting to be replicated
- expect(praefect_manager).to be_replication_pending
-
- # Start the old primary node again
- praefect_manager.start_primary_node
- praefect_manager.wait_for_health_check_all_nodes
-
- # Wait for automatic replication
- praefect_manager.wait_for_replication(project.id)
-
- # Force switch to the old primary node
- # This ensures that the commit was replicated
- praefect_manager.stop_secondary_node
- praefect_manager.stop_tertiary_node
-
- # Confirm that both commits are available
- expect(project.commits.map { |commit| commit[:message].chomp })
- .to include("Initial commit").and include("pushed after failover")
- end
- end
- end
- end
-end
diff --git a/qa/qa/specs/features/api/3_create/gitaly/distributed_reads_spec.rb b/qa/qa/specs/features/api/3_create/gitaly/distributed_reads_spec.rb
deleted file mode 100644
index 2b96c35415e..00000000000
--- a/qa/qa/specs/features/api/3_create/gitaly/distributed_reads_spec.rb
+++ /dev/null
@@ -1,92 +0,0 @@
-# frozen_string_literal: true
-
-require 'parallel'
-
-module QA
- RSpec.describe 'Create' do
- context 'Gitaly' do
- describe 'Distributed reads', :orchestrated, :gitaly_cluster, :skip_live_env, :requires_admin do
- let(:number_of_reads_per_loop) { 9 }
- let(:praefect_manager) { Service::PraefectManager.new }
- let(:project) do
- Resource::Project.fabricate! do |project|
- project.name = "gitaly_cluster"
- project.initialize_with_readme = true
- end
- end
-
- before do
- praefect_manager.start_all_nodes
- praefect_manager.wait_for_replication(project.id)
- end
-
- it 'reads from each node', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347833' do
- pre_read_data = praefect_manager.query_read_distribution
-
- wait_for_reads_to_increase(project, number_of_reads_per_loop, pre_read_data)
-
- aggregate_failures "each gitaly node" do
- praefect_manager.query_read_distribution.each_with_index do |data, index|
- pre_read_count = praefect_manager.value_for_node(pre_read_data, data[:node])
- QA::Runtime::Logger.debug("Node: #{data[:node]}; before: #{pre_read_count}; now: #{data[:value]}")
- expect(data[:value]).to be > pre_read_count,
- "Read counts did not differ for node #{data[:node]}"
- end
- end
- end
-
- context 'when a node is unhealthy' do
- before do
- praefect_manager.stop_secondary_node
- end
-
- after do
- # Leave the cluster in a suitable state for subsequent tests
- praefect_manager.start_secondary_node
- end
-
- it 'does not read from the unhealthy node', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347834' do
- pre_read_data = praefect_manager.query_read_distribution
-
- read_from_project(project, number_of_reads_per_loop * 10)
-
- praefect_manager.wait_for_read_count_change(pre_read_data)
-
- post_read_data = praefect_manager.query_read_distribution
-
- aggregate_failures "each gitaly node" do
- expect(praefect_manager.value_for_node(post_read_data, 'gitaly1')).to be > praefect_manager.value_for_node(pre_read_data, 'gitaly1')
- expect(praefect_manager.value_for_node(post_read_data, 'gitaly2')).to eq praefect_manager.value_for_node(pre_read_data, 'gitaly2')
- expect(praefect_manager.value_for_node(post_read_data, 'gitaly3')).to be > praefect_manager.value_for_node(pre_read_data, 'gitaly3')
- end
- end
- end
-
- def read_from_project(project, number_of_reads)
- QA::Runtime::Logger.info('Reading from the repository')
- Parallel.each((1..number_of_reads)) do
- Git::Repository.perform do |repository|
- repository.uri = project.repository_http_location.uri
- repository.use_default_credentials
- repository.clone
- end
- end
- end
-
- def wait_for_reads_to_increase(project, number_of_reads, pre_read_data)
- diff_found = pre_read_data
-
- Support::Waiter.wait_until(sleep_interval: 5, raise_on_failure: false) do
- read_from_project(project, number_of_reads)
-
- praefect_manager.query_read_distribution.each_with_index do |data, index|
- diff_found[index] = {} unless diff_found[index]
- diff_found[index][:diff] = true if data[:value] > praefect_manager.value_for_node(pre_read_data, data[:node])
- end
- diff_found.all? { |node| node.key?(:diff) && node[:diff] }
- end
- end
- end
- end
- end
-end
diff --git a/qa/qa/specs/features/api/3_create/gitaly/gitaly_mtls_spec.rb b/qa/qa/specs/features/api/3_create/gitaly/gitaly_mtls_spec.rb
deleted file mode 100644
index 5000c273578..00000000000
--- a/qa/qa/specs/features/api/3_create/gitaly/gitaly_mtls_spec.rb
+++ /dev/null
@@ -1,43 +0,0 @@
-# frozen_string_literal: true
-
-module QA
- RSpec.describe 'Create' do
- context 'Gitaly', :orchestrated, :mtls do
- describe 'Using mTLS' do
- let(:intial_commit_message) { 'Initial commit' }
- let(:first_added_commit_message) { 'commit over git' }
- let(:second_added_commit_message) { 'commit over api' }
-
- it 'pushes to gitaly', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347677' do
- project = Resource::Project.fabricate! do |project|
- project.name = "mTLS"
- project.initialize_with_readme = true
- end
-
- Resource::Repository::ProjectPush.fabricate! do |push|
- push.project = project
- push.new_branch = false
- push.commit_message = first_added_commit_message
- push.file_content = 'First commit'
- end
-
- Resource::Repository::Commit.fabricate_via_api! do |commit|
- commit.project = project
- commit.commit_message = second_added_commit_message
- commit.add_files([
- {
- file_path: "file-#{SecureRandom.hex(8)}",
- content: 'Second commit'
- }
- ])
- end
-
- expect(project.commits.map { |commit| commit[:message].chomp })
- .to include(intial_commit_message)
- .and include(first_added_commit_message)
- .and include(second_added_commit_message)
- end
- end
- end
- end
-end
diff --git a/qa/qa/specs/features/api/3_create/repository/commit_to_templated_project_spec.rb b/qa/qa/specs/features/api/3_create/repository/commit_to_templated_project_spec.rb
index c06912e0367..9d47872a774 100644
--- a/qa/qa/specs/features/api/3_create/repository/commit_to_templated_project_spec.rb
+++ b/qa/qa/specs/features/api/3_create/repository/commit_to_templated_project_spec.rb
@@ -2,7 +2,7 @@
module QA
RSpec.describe 'Create' do
- describe 'Create a new project from a template' do
+ describe 'Create a new project from a template', product_group: :source_code do
let(:project) do
Resource::Project.fabricate_via_api! do |project|
project.name = 'templated-project'
diff --git a/qa/qa/specs/features/api/3_create/repository/default_branch_name_setting_spec.rb b/qa/qa/specs/features/api/3_create/repository/default_branch_name_setting_spec.rb
index cba563ef85a..123a4a625ab 100644
--- a/qa/qa/specs/features/api/3_create/repository/default_branch_name_setting_spec.rb
+++ b/qa/qa/specs/features/api/3_create/repository/default_branch_name_setting_spec.rb
@@ -2,7 +2,7 @@
module QA
RSpec.describe 'Create' do
- describe 'Default branch name instance setting', :requires_admin, :skip_live_env do
+ describe 'Default branch name instance setting', :requires_admin, :skip_live_env, product_group: :source_code do
before(:context) do
Runtime::ApplicationSettings.set_application_settings(default_branch_name: 'main')
end
diff --git a/qa/qa/specs/features/api/3_create/repository/files_spec.rb b/qa/qa/specs/features/api/3_create/repository/files_spec.rb
index 151fd0fffe3..71bd03fab17 100644
--- a/qa/qa/specs/features/api/3_create/repository/files_spec.rb
+++ b/qa/qa/specs/features/api/3_create/repository/files_spec.rb
@@ -4,7 +4,7 @@ require 'airborne'
module QA
RSpec.describe 'Create' do
- describe 'API basics' do
+ describe 'API basics', product_group: :source_code do
before(:context) do
@api_client = Runtime::API::Client.new(:gitlab)
end
diff --git a/qa/qa/specs/features/api/3_create/repository/project_archive_compare_spec.rb b/qa/qa/specs/features/api/3_create/repository/project_archive_compare_spec.rb
index 6f175272d91..a211eb6042d 100644
--- a/qa/qa/specs/features/api/3_create/repository/project_archive_compare_spec.rb
+++ b/qa/qa/specs/features/api/3_create/repository/project_archive_compare_spec.rb
@@ -5,7 +5,8 @@ require 'digest'
module QA
RSpec.describe 'Create' do
- describe 'Compare archives of different user projects with the same name and check they\'re different' do
+ describe 'Compare archives of different user projects with the same name and check they\'re different',
+ product_group: :source_code do
include Support::API
let(:project_name) { "project-archive-download-#{SecureRandom.hex(8)}" }
@@ -52,12 +53,11 @@ module QA
project.api_client = api_client
end
- Resource::Repository::ProjectPush.fabricate! do |push|
- push.project = project
- push.file_name = 'README.md'
- push.file_content = '# This is a test project'
- push.commit_message = 'Add README.md'
- push.user = user
+ Resource::Repository::Commit.fabricate_via_api! do |commit|
+ commit.project = project
+ commit.add_files([{ file_path: 'README.md', content: '# This is a test project' }])
+ commit.commit_message = 'Add README.md'
+ commit.api_client = api_client
end
project
@@ -65,7 +65,7 @@ module QA
def download_project_archive_via_api(api_client, project, type = 'tar.gz')
get_project_archive_zip = Runtime::API::Request.new(api_client, project.api_get_archive_path(type))
- project_archive_download = get(get_project_archive_zip.url, raw_response: true)
+ project_archive_download = Support::API.get(get_project_archive_zip.url, raw_response: true)
expect(project_archive_download.code).to eq(200)
project_archive_download.file
diff --git a/qa/qa/specs/features/api/3_create/repository/push_postreceive_idempotent_spec.rb b/qa/qa/specs/features/api/3_create/repository/push_postreceive_idempotent_spec.rb
index 1a2a1679cca..1d41184df98 100644
--- a/qa/qa/specs/features/api/3_create/repository/push_postreceive_idempotent_spec.rb
+++ b/qa/qa/specs/features/api/3_create/repository/push_postreceive_idempotent_spec.rb
@@ -2,7 +2,7 @@
module QA
RSpec.describe 'Create' do
- describe 'PostReceive idempotent' do
+ describe 'PostReceive idempotent', product_group: :source_code do
# Tests that a push does not result in multiple changes from repeated PostReceive executions.
# One of the consequences would be duplicate push events
diff --git a/qa/qa/specs/features/api/3_create/repository/storage_size_spec.rb b/qa/qa/specs/features/api/3_create/repository/storage_size_spec.rb
index 406ff191f95..df3b5a4e7fb 100644
--- a/qa/qa/specs/features/api/3_create/repository/storage_size_spec.rb
+++ b/qa/qa/specs/features/api/3_create/repository/storage_size_spec.rb
@@ -2,7 +2,7 @@
module QA
RSpec.describe 'Create' do
- describe 'Repository Usage Quota', :skip_live_env, feature_flag: {
+ describe 'Repository Usage Quota', :skip_live_env, product_group: :source_code, feature_flag: {
name: 'gitaly_revlist_for_repo_size',
scope: :global
} do
diff --git a/qa/qa/specs/features/api/3_create/repository/tag_revision_trigger_prereceive_hook_spec.rb b/qa/qa/specs/features/api/3_create/repository/tag_revision_trigger_prereceive_hook_spec.rb
index 98612d84b21..b34b4208337 100644
--- a/qa/qa/specs/features/api/3_create/repository/tag_revision_trigger_prereceive_hook_spec.rb
+++ b/qa/qa/specs/features/api/3_create/repository/tag_revision_trigger_prereceive_hook_spec.rb
@@ -2,29 +2,32 @@
module QA
RSpec.describe 'Create' do
- let(:project) do
- Resource::Project.fabricate_via_api! do |project|
- project.initialize_with_readme = true
+ describe 'Prereceive hook', product_group: :source_code do
+ let(:project) do
+ Resource::Project.fabricate_via_api! do |project|
+ project.initialize_with_readme = true
+ end
end
- end
- context 'when creating a tag for a ref' do
- context 'when it triggers a prereceive hook configured with a custom error' do
- before do
- # The configuration test prereceive hook must match a specific naming pattern
- # In this test we create a project with a different name and then change the path.
- # Otherwise we wouldn't be able create any commits to be tagged due to the hook.
- project.change_path("project-reject-prereceive-#{SecureRandom.hex(8)}")
- end
+ context 'when creating a tag for a ref' do
+ context 'when it triggers a prereceive hook configured with a custom error' do
+ before do
+ # The configuration test prereceive hook must match a specific naming pattern
+ # In this test we create a project with a different name and then change the path.
+ # Otherwise we wouldn't be able create any commits to be tagged due to the hook.
+ project.change_path("project-reject-prereceive-#{SecureRandom.hex(8)}")
+ end
- it 'returns a custom server hook error',
- :skip_live_env,
- except: { job: 'review-qa-*' },
- testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/369053' do
- expect { project.create_repository_tag('v1.2.3') }.to raise_error
- .with_message(
- /rejecting prereceive hook for projects with GL_PROJECT_PATH matching pattern reject-prereceive/
- )
+ it 'returns a custom server hook error',
+ :skip_live_env,
+ except: { job: 'review-qa-*' },
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/369053' do
+ expect { project.create_repository_tag('v1.2.3') }
+ .to raise_error
+ .with_message(
+ /rejecting prereceive hook for projects with GL_PROJECT_PATH matching pattern reject-prereceive/
+ )
+ end
end
end
end
diff --git a/qa/qa/specs/features/api/4_verify/file_variable_spec.rb b/qa/qa/specs/features/api/4_verify/file_variable_spec.rb
new file mode 100644
index 00000000000..9722f62d5a7
--- /dev/null
+++ b/qa/qa/specs/features/api/4_verify/file_variable_spec.rb
@@ -0,0 +1,142 @@
+# frozen_string_literal: true
+
+module QA
+ RSpec.describe 'Verify', :runner, feature_flag: {
+ name: 'ci_stop_expanding_file_vars_for_runners',
+ scope: :project
+ } do
+ describe 'Pipeline with project file variables' do
+ let(:executor) { "qa-runner-#{Faker::Alphanumeric.alphanumeric(number: 8)}" }
+
+ let(:project) do
+ Resource::Project.fabricate_via_api! do |project|
+ project.name = 'project-with-file-variables'
+ end
+ end
+
+ let(:runner) do
+ Resource::Runner.fabricate! do |runner|
+ runner.project = project
+ runner.name = executor
+ runner.tags = [executor]
+ end
+ end
+
+ let(:add_ci_file) do
+ Resource::Repository::Commit.fabricate_via_api! do |commit|
+ commit.project = project
+ commit.commit_message = 'Add .gitlab-ci.yml'
+ commit.add_files(
+ [
+ {
+ file_path: '.gitlab-ci.yml',
+ content: <<~YAML
+ variables:
+ EXTRA_ARGS: "-f $TEST_FILE"
+ DOCKER_REMOTE_ARGS: --tlscacert="$DOCKER_CA_CERT"
+ EXTRACTED_CRT_FILE: ${DOCKER_CA_CERT}.crt
+ MY_FILE_VAR: $TEST_FILE
+
+ test:
+ tags: [#{executor}]
+ script:
+ - echo "run something $EXTRA_ARGS"
+ - echo "docker run $DOCKER_REMOTE_ARGS"
+ - echo "run --output=$EXTRACTED_CRT_FILE"
+ - echo "Will read private key from $MY_FILE_VAR"
+ YAML
+ }
+ ]
+ )
+ end
+ end
+
+ let(:add_file_variables) do
+ {
+ 'TEST_FILE' => 'hello, this is test',
+ 'DOCKER_CA_CERT' => 'This is secret'
+ }.each do |file_name, content|
+ add_file_variable_to_project(file_name, content)
+ end
+ end
+
+ after do
+ runner.remove_via_api!
+ end
+
+ shared_examples 'variables are read correctly' do
+ it 'shows in job log accordingly' do
+ job = Resource::Job.fabricate_via_api! do |job|
+ job.project = project
+ job.id = project.job_by_name('test')[:id]
+ end
+
+ aggregate_failures do
+ trace = job.trace
+ expect(trace).to have_content('run something -f hello, this is test')
+ expect(trace).to have_content('docker run --tlscacert="This is secret"')
+ expect(trace).to have_content('run --output=This is secret.crt')
+ expect(trace).to have_content('Will read private key from hello, this is test')
+ end
+ end
+ end
+
+ # FF does not change current behavior
+ # https://gitlab.com/gitlab-org/gitlab/-/merge_requests/94198#note_1057609893
+ #
+ # TODO: Remove when FF is removed
+ # TODO: Archive testcase issue when FF is removed
+ # Rollout issue: https://gitlab.com/gitlab-org/gitlab/-/issues/369907
+ context 'when FF is on', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/370787' do
+ before do
+ Runtime::Feature.enable(:ci_stop_expanding_file_vars_for_runners, project: project)
+
+ runner
+ add_file_variables
+ add_ci_file
+ trigger_pipeline
+ wait_for_pipeline
+ end
+
+ it_behaves_like 'variables are read correctly'
+ end
+
+ # TODO: Refactor when FF is removed
+ # TODO: Update testcase issue title and description to not refer to FF status
+ context 'when FF is off', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/370791' do
+ before do
+ runner
+ add_file_variables
+ add_ci_file
+ trigger_pipeline
+ wait_for_pipeline
+ end
+
+ it_behaves_like 'variables are read correctly'
+ end
+
+ private
+
+ def add_file_variable_to_project(key, value)
+ Resource::CiVariable.fabricate_via_api! do |ci_variable|
+ ci_variable.project = project
+ ci_variable.key = key
+ ci_variable.value = value
+ ci_variable.variable_type = 'file'
+ end
+ end
+
+ def trigger_pipeline
+ Resource::Pipeline.fabricate_via_api! do |pipeline|
+ pipeline.project = project
+ end
+ end
+
+ def wait_for_pipeline
+ Support::Waiter.wait_until do
+ project.pipelines.present? && project.pipelines.first[:status] == 'success'
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/1_manage/login/log_in_with_2fa_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/login/log_in_with_2fa_spec.rb
index c5efa833f04..ad90df4b90d 100644
--- a/qa/qa/specs/features/browser_ui/1_manage/login/log_in_with_2fa_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/login/log_in_with_2fa_spec.rb
@@ -36,7 +36,11 @@ module QA
group.add_member(developer_user, Resource::Members::AccessLevel::DEVELOPER)
end
- it 'allows enforcing 2FA via UI and logging in with 2FA', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347931' do
+ it(
+ 'allows enforcing 2FA via UI and logging in with 2FA',
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347931',
+ quarantine: { type: :flaky, issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/369516' }
+ ) do
enforce_two_factor_authentication_on_group(group)
enable_two_factor_authentication_for_user(developer_user)
diff --git a/qa/qa/specs/features/browser_ui/1_manage/login/register_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/login/register_spec.rb
index 8cc772ed022..295702aa328 100644
--- a/qa/qa/specs/features/browser_ui/1_manage/login/register_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/login/register_spec.rb
@@ -66,7 +66,7 @@ module QA
end
# TODO: Remove retry_on_exception once https://gitlab.com/gitlab-org/gitlab/-/issues/24294 is resolved
- Support::Waiter.wait_until(retry_on_exception: true, sleep_interval: 3) { !user.exists? }
+ Support::Waiter.wait_until(max_duration: 120, retry_on_exception: true, sleep_interval: 3) { !user.exists? }
end
it 'allows recreating with same credentials', :reliable, testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347868' do
diff --git a/qa/qa/specs/features/browser_ui/1_manage/project/dashboard_images_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/project/dashboard_images_spec.rb
index b1d59b90e9c..d299997dd3c 100644
--- a/qa/qa/specs/features/browser_ui/1_manage/project/dashboard_images_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/project/dashboard_images_spec.rb
@@ -20,8 +20,10 @@ module QA
Flow::Login.sign_in(as: user)
Page::Dashboard::Welcome.perform do |welcome|
- expect(welcome).to have_welcome_title("Welcome to GitLab")
-
+ Support::Waiter.wait_until(sleep_interval: 2, max_duration: 60, reload_page: page,
+ retry_on_exception: true) do
+ expect(welcome).to have_welcome_title("Welcome to GitLab")
+ end
# This would be better if it were a visual validation test
expect(welcome).to have_loaded_all_images
end
diff --git a/qa/qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb b/qa/qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb
index ed271533f87..7e4a391c390 100644
--- a/qa/qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb
+++ b/qa/qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb
@@ -64,7 +64,7 @@ module QA
it(
'comments on an issue with an attachment',
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347946',
- except: { job: 'review-qa-smoke' }
+ except: { job: 'review-qa-*' }
) do
Page::Project::Issue::Show.perform do |show|
show.comment('See attached image for scale', attachment: file_to_attach)
diff --git a/qa/qa/specs/features/browser_ui/3_create/merge_request/merge_merge_request_from_fork_spec.rb b/qa/qa/specs/features/browser_ui/3_create/merge_request/merge_merge_request_from_fork_spec.rb
index d198d79c5fe..109cf7b73c3 100644
--- a/qa/qa/specs/features/browser_ui/3_create/merge_request/merge_merge_request_from_fork_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/merge_request/merge_merge_request_from_fork_spec.rb
@@ -17,7 +17,11 @@ module QA
merge_request.fork.remove_via_api!
end
- it 'can merge feature branch fork to mainline', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347818' do
+ it 'can merge feature branch fork to mainline', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347818', quarantine: {
+ only: :production,
+ type: :investigating,
+ issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/372258'
+ } do
merge_request.visit!
Page::MergeRequest::Show.perform do |merge_request|
diff --git a/qa/qa/specs/features/browser_ui/3_create/merge_request/rebase_merge_request_spec.rb b/qa/qa/specs/features/browser_ui/3_create/merge_request/rebase_merge_request_spec.rb
index 2280cc971a7..c7296b6eea2 100644
--- a/qa/qa/specs/features/browser_ui/3_create/merge_request/rebase_merge_request_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/merge_request/rebase_merge_request_spec.rb
@@ -12,11 +12,9 @@ module QA
it 'user rebases source branch of merge request', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347735' do
merge_request.project.visit!
- Page::Project::Menu.perform(&:go_to_general_settings)
- Page::Project::Settings::Main.perform do |main|
- main.expand_merge_requests_settings do |settings|
- settings.enable_ff_only
- end
+ Page::Project::Menu.perform(&:go_to_merge_request_settings)
+ Page::Project::Settings::MergeRequest.perform do |settings|
+ settings.enable_ff_only
end
Resource::Repository::ProjectPush.fabricate! do |push|
diff --git a/qa/qa/specs/features/browser_ui/3_create/pages/pages_pipeline_spec.rb b/qa/qa/specs/features/browser_ui/3_create/pages/new_static_page_spec.rb
index 191c4a096e7..9c912080c5f 100644
--- a/qa/qa/specs/features/browser_ui/3_create/pages/pages_pipeline_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/pages/new_static_page_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
-
module QA
- RSpec.describe 'Release', :runner do
+ RSpec.describe 'Create', :runner, only: { subdomain: :staging } do
# TODO: Convert back to :smoke once proved to be stable. Related issue: https://gitlab.com/gitlab-org/gitlab/-/issues/300906
describe 'Pages' do
let!(:project) do
@@ -27,12 +26,11 @@ module QA
runner.project = project
runner.executor = :docker
end
-
pipeline.visit!
end
- it('runs a Pages-specific pipeline',
- testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347669') do
+ it 'creates a Pages website',
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347669' do
Page::Project::Pipeline::Show.perform do |show|
expect(show).to have_job(:pages)
show.click_job(:pages)
@@ -41,6 +39,15 @@ module QA
Page::Project::Job::Show.perform do |show|
expect(show).to have_passed(timeout: 300)
end
+
+ Page::Project::Show.perform(&:go_to_pages_settings)
+ QA::Page::Project::Settings::Pages.perform do |pages|
+ pages.go_to_access_page
+ Support::Waiter.wait_until(sleep_interval: 2, max_duration: 60, reload_page: page,
+ retry_on_exception: true) do
+ expect(page).to have_content('Write an awesome description for your new site here.')
+ end
+ end
end
end
end
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/add_file_template_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/add_file_template_spec.rb
index 107d72a9724..b4103bd0976 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/add_file_template_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/add_file_template_spec.rb
@@ -2,7 +2,7 @@
module QA
RSpec.describe 'Create' do
- describe 'File templates' do
+ describe 'File templates', product_group: :source_code do
include Runtime::Fixtures
let(:project) do
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/add_list_delete_branches_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/add_list_delete_branches_spec.rb
index 0bd470fcb77..849022f5a93 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/add_list_delete_branches_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/add_list_delete_branches_spec.rb
@@ -2,7 +2,7 @@
module QA
RSpec.describe 'Create' do
- describe 'Create, list, and delete branches via web', :requires_admin do
+ describe 'Create, list, and delete branches via web', :requires_admin, product_group: :source_code do
master_branch = nil
second_branch = 'second-branch'
third_branch = 'third-branch'
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/branch_with_unusual_name_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/branch_with_unusual_name_spec.rb
index 9e77fd228da..aa332a76c94 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/branch_with_unusual_name_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/branch_with_unusual_name_spec.rb
@@ -2,7 +2,7 @@
module QA
RSpec.describe 'Create' do
- describe 'Branch with unusual name' do
+ describe 'Branch with unusual name', product_group: :source_code do
let(:branch_name) { 'unUsually/named#br--anch' }
let(:project) do
Resource::Project.fabricate_via_api! do |resource|
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/clone_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/clone_spec.rb
index d12fb05af77..b7df22fc2c2 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/clone_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/clone_spec.rb
@@ -2,7 +2,7 @@
module QA
RSpec.describe 'Create' do
- describe 'Git clone over HTTP' do
+ describe 'Git clone over HTTP', product_group: :source_code do
let(:project) do
Resource::Project.fabricate_via_api! do |scenario|
scenario.name = 'project-with-code'
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/file/create_file_via_web_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/file/create_file_via_web_spec.rb
index 095444d99f1..d5f30c19738 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/file/create_file_via_web_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/file/create_file_via_web_spec.rb
@@ -1,8 +1,8 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Create', :reliable do
- context 'File management' do
+ RSpec.describe 'Create', :reliable, product_group: :source_code do
+ describe 'File management' do
file_name = 'QA Test - File name'
file_content = 'QA Test - File content'
commit_message_for_create = 'QA Test - Create new file'
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/file/delete_file_via_web_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/file/delete_file_via_web_spec.rb
index 02ecff22840..76243066476 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/file/delete_file_via_web_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/file/delete_file_via_web_spec.rb
@@ -2,7 +2,7 @@
module QA
RSpec.describe 'Create' do
- context 'File management' do
+ describe 'File management', product_group: :source_code do
let(:file) { Resource::File.fabricate_via_api! }
commit_message_for_delete = 'QA Test - Delete file'
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/file/edit_file_via_web_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/file/edit_file_via_web_spec.rb
index 95e7a2a12d0..397796b76e5 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/file/edit_file_via_web_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/file/edit_file_via_web_spec.rb
@@ -2,7 +2,7 @@
module QA
RSpec.describe 'Create', :reliable do
- context 'File management' do
+ describe 'File management', product_group: :source_code do
let(:file) { Resource::File.fabricate_via_api! }
updated_file_content = 'QA Test - Updated file content'
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/file/file_with_unusual_name_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/file/file_with_unusual_name_spec.rb
index eb6449181b5..d7da29219e6 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/file/file_with_unusual_name_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/file/file_with_unusual_name_spec.rb
@@ -2,7 +2,7 @@
module QA
RSpec.describe 'Create' do
- describe 'File with unusual name' do
+ describe 'File with unusual name', product_group: :source_code do
let(:file_name) { '-un:usually;named#file?.md' }
let(:project) do
Resource::Project.fabricate_via_api! do |resource|
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/license_detecton_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/license_detecton_spec.rb
index 3db8128bc6d..1ae1dd87c07 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/license_detecton_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/license_detecton_spec.rb
@@ -2,7 +2,7 @@
module QA
RSpec.describe 'Create' do
- describe 'Repository License Detection' do
+ describe 'Repository License Detection', product_group: :source_code do
after do
project.remove_via_api!
end
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/move_project_create_fork_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/move_project_create_fork_spec.rb
index 153bfd292aa..a391e6313a6 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/move_project_create_fork_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/move_project_create_fork_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Create', :orchestrated, :repository_storage, :requires_admin do
+ RSpec.describe 'Create', :orchestrated, :repository_storage, :requires_admin, product_group: :source_code do
describe 'Gitaly repository storage' do
let(:user) { Resource::User.fabricate_or_use(Runtime::Env.gitlab_qa_username_1, Runtime::Env.gitlab_qa_password_1) }
let(:parent_project) do
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/protected_tags_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/protected_tags_spec.rb
index fb87ca864f4..65a15ce96a5 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/protected_tags_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/protected_tags_spec.rb
@@ -2,7 +2,7 @@
module QA
RSpec.describe 'Create' do
- describe 'Repository tags', :reliable do
+ describe 'Repository tags', :reliable, product_group: :source_code do
let(:project) do
Resource::Project.fabricate_via_api! do |project|
project.name = 'project-for-tags'
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/protocol_v2_push_http_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/protocol_v2_push_http_spec.rb
index 34439042796..557a27c002d 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/protocol_v2_push_http_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/protocol_v2_push_http_spec.rb
@@ -2,7 +2,7 @@
module QA
RSpec.describe 'Create' do
- describe 'Push over HTTP using Git protocol version 2', :requires_git_protocol_v2 do
+ describe 'Push over HTTP using Git protocol version 2', :requires_git_protocol_v2, product_group: :source_code do
it 'user pushes to the repository', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347760' do
Flow::Login.sign_in
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/protocol_v2_push_ssh_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/protocol_v2_push_ssh_spec.rb
index 25d4da95dd9..2472d1cdf25 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/protocol_v2_push_ssh_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/protocol_v2_push_ssh_spec.rb
@@ -2,7 +2,7 @@
module QA
RSpec.describe 'Create' do
- describe 'Push over SSH using Git protocol version 2', :requires_git_protocol_v2 do
+ describe 'Push over SSH using Git protocol version 2', :requires_git_protocol_v2, product_group: :source_code do
# Note: If you run this test against GDK make sure you've enabled sshd and
# enabled setting the Git protocol by adding `AcceptEnv GIT_PROTOCOL` to
# `sshd_config`
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/push_http_private_token_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/push_http_private_token_spec.rb
index 042fee38188..a785e4ae764 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/push_http_private_token_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/push_http_private_token_spec.rb
@@ -2,7 +2,7 @@
module QA
RSpec.describe 'Create' do
- describe 'Git push over HTTP', :smoke do
+ describe 'Git push over HTTP', :smoke, product_group: :source_code do
it 'user using a personal access token pushes code to the repository', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347749' do
Flow::Login.sign_in
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/push_mirroring_lfs_over_http_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/push_mirroring_lfs_over_http_spec.rb
index 9ab322df824..815a8696ff7 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/push_mirroring_lfs_over_http_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/push_mirroring_lfs_over_http_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Create', quarantine: {
+ RSpec.describe 'Create', product_group: :source_code, quarantine: {
issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/352525',
type: :test_environment,
only: { job: 'review-qa-*' }
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/push_mirroring_over_http_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/push_mirroring_over_http_spec.rb
index d644a7ead1e..b87c47761bd 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/push_mirroring_over_http_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/push_mirroring_over_http_spec.rb
@@ -2,7 +2,7 @@
module QA
RSpec.describe 'Create' do
- describe 'Push mirror a repository over HTTP' do
+ describe 'Push mirror a repository over HTTP', product_group: :source_code do
it 'configures and syncs a (push) mirrored repository', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347741' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.perform(&:sign_in_using_credentials)
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/push_over_http_file_size_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/push_over_http_file_size_spec.rb
index 0e4aa67162f..324dbbc46ef 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/push_over_http_file_size_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/push_over_http_file_size_spec.rb
@@ -3,7 +3,7 @@
module QA
# This test modifies an instance level setting,
# so skipping on live envs to avoid random transient issues
- RSpec.describe 'Create', :requires_admin, :skip_live_env do
+ RSpec.describe 'Create', :requires_admin, :skip_live_env, product_group: :source_code do
describe 'push after setting the file size limit via admin/application_settings' do
include Support::API
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/push_over_http_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/push_over_http_spec.rb
index a782a50b55d..e8f7cb252a0 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/push_over_http_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/push_over_http_spec.rb
@@ -2,7 +2,7 @@
module QA
RSpec.describe 'Create' do
- describe 'Git push over HTTP' do
+ describe 'Git push over HTTP', product_group: :source_code do
it 'user pushes code to the repository', :smoke, testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347747' do
Flow::Login.sign_in
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/push_over_ssh_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/push_over_ssh_spec.rb
index 0323448878b..4fb52f1e54d 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/push_over_ssh_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/push_over_ssh_spec.rb
@@ -2,7 +2,7 @@
module QA
RSpec.describe 'Create' do
- describe 'SSH key support' do
+ describe 'SSH key support', product_group: :source_code do
# Note: If you run these tests against GDK make sure you've enabled sshd
# See: https://gitlab.com/gitlab-org/gitlab-qa/blob/master/docs/run_qa_against_gdk.md
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/push_protected_branch_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/push_protected_branch_spec.rb
index 91b0940f137..e097e1fd2bb 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/push_protected_branch_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/push_protected_branch_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Create', :reliable do
+ RSpec.describe 'Create', :reliable, product_group: :source_code do
describe 'Protected branch support' do
let(:branch_name) { 'protected-branch' }
let(:commit_message) { 'Protected push commit message' }
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/push_to_canary_gitaly_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/push_to_canary_gitaly_spec.rb
index 78abdb94dfe..d95a880c305 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/push_to_canary_gitaly_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/push_to_canary_gitaly_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Create', only: { subdomain: %i[staging staging-canary] } do
+ RSpec.describe 'Create', only: { subdomain: %i[staging staging-canary] }, product_group: :source_code do
describe 'Git push to canary Gitaly node over HTTP' do
it 'pushes to a project using a canary specific Gitaly repository storage', :smoke, :requires_admin, testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/351116' do
Flow::Login.sign_in_as_admin
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/ssh_key_support_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/ssh_key_support_spec.rb
index 55df1615f5c..64858287285 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/ssh_key_support_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/ssh_key_support_spec.rb
@@ -2,7 +2,7 @@
module QA
RSpec.describe 'Create' do
- describe 'SSH keys support', :smoke do
+ describe 'SSH keys support', :smoke, product_group: :source_code do
let(:key_title) { "key for ssh tests #{Time.now.to_f}" }
key = nil
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/user_views_commit_diff_patch_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/user_views_commit_diff_patch_spec.rb
index bbf6c3ca37a..13b42209114 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/user_views_commit_diff_patch_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/user_views_commit_diff_patch_spec.rb
@@ -2,7 +2,7 @@
module QA
RSpec.describe 'Create' do
- describe 'Commit data' do
+ describe 'Commit data', product_group: :source_code do
before(:context) do
# Get the user's details to confirm they're included in the email patch
@user = Resource::User.fabricate_via_api! do |user|
diff --git a/qa/qa/specs/features/browser_ui/4_verify/ci_variable/prefill_variables_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/ci_variable/prefill_variables_spec.rb
new file mode 100644
index 00000000000..8352ad6aa33
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/4_verify/ci_variable/prefill_variables_spec.rb
@@ -0,0 +1,72 @@
+# frozen_string_literal: true
+
+module QA
+ RSpec.describe 'Verify' do
+ describe 'Pipeline with prefill variables' do
+ let(:prefill_variable_description1) { Faker::Lorem.sentence }
+ let(:prefill_variable_value1) { Faker::Lorem.word }
+ let(:prefill_variable_description2) { Faker::Lorem.sentence }
+ let(:project) do
+ Resource::Project.fabricate_via_api! do |project|
+ project.name = 'project-with-prefill-variables'
+ end
+ end
+
+ let!(:commit) do
+ Resource::Repository::Commit.fabricate_via_api! do |commit|
+ commit.project = project
+ commit.commit_message = 'Add .gitlab-ci.yml'
+ commit.add_files(
+ [
+ {
+ file_path: '.gitlab-ci.yml',
+ content: <<~YAML
+ variables:
+ TEST1:
+ value: #{prefill_variable_value1}
+ description: #{prefill_variable_description1}
+ TEST2:
+ description: #{prefill_variable_description2}
+ TEST3:
+ value: test 3 value
+ TEST4: test 4 value
+
+ test:
+ script: echo "$FOO"
+ YAML
+ }
+ ]
+ )
+ end
+ end
+
+ before do
+ Flow::Login.sign_in
+ project.visit!
+
+ # Navigate to Run Pipeline page
+ Page::Project::Menu.perform(&:click_ci_cd_pipelines)
+ Page::Project::Pipeline::Index.perform(&:click_run_pipeline_button)
+ end
+
+ it(
+ 'shows only variables with description as prefill variables on the run pipeline page',
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/371204'
+ ) do
+ Page::Project::Pipeline::New.perform do |new|
+ aggregate_failures do
+ expect(new).to have_field('Input variable key', with: 'TEST1')
+ expect(new).to have_field('Input variable value', with: prefill_variable_value1)
+ expect(new).to have_content(prefill_variable_description1)
+
+ expect(new).to have_field('Input variable key', with: 'TEST2')
+ expect(new).to have_content(prefill_variable_description2)
+
+ expect(new).not_to have_field('Input variable key', with: 'TEST3')
+ expect(new).not_to have_field('Input variable key', with: 'TEST4')
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/4_verify/pipeline/pipeline_with_image_pull_policy_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/pipeline/pipeline_with_image_pull_policy_spec.rb
index 412498476f0..f9113573295 100644
--- a/qa/qa/specs/features/browser_ui/4_verify/pipeline/pipeline_with_image_pull_policy_spec.rb
+++ b/qa/qa/specs/features/browser_ui/4_verify/pipeline/pipeline_with_image_pull_policy_spec.rb
@@ -1,12 +1,7 @@
# frozen_string_literal: true
module QA
- # TODO: remove feature flag upon rollout completion
- # FF rollout issue: https://gitlab.com/gitlab-org/gitlab/-/issues/363186
- RSpec.describe 'Verify', :runner, feature_flag: {
- name: 'ci_docker_image_pull_policy',
- scope: :global
- } do
+ RSpec.describe 'Verify', :runner do
describe 'Pipeline with image:pull_policy' do
let(:runner_name) { "qa-runner-#{Faker::Alphanumeric.alphanumeric(number: 8)}" }
let(:job_name) { "test-job-#{pull_policies.join('-')}" }
@@ -27,10 +22,6 @@ module QA
end
before do
- Runtime::Feature.enable(:ci_docker_image_pull_policy)
- # Give the feature some time to switch
- sleep(30)
-
update_runner_policy(allowed_policies)
add_ci_file
Flow::Login.sign_in
@@ -39,12 +30,13 @@ module QA
end
after do
- Runtime::Feature.disable(:ci_docker_image_pull_policy)
-
runner.remove_via_api!
end
- context 'when policy is allowed' do
+ context(
+ 'when policy is allowed',
+ quarantine: { type: :flaky, issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/369397' }
+ ) do
let(:allowed_policies) { %w[if-not-present always never] }
where do
@@ -102,7 +94,8 @@ module QA
it(
'fails job with policy not allowed message',
- testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/368853'
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/368853',
+ quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/371420', type: :stale }
) do
visit_job
diff --git a/qa/qa/specs/features/browser_ui/5_package/infrastructure_registry/terraform_module_registry_spec.rb b/qa/qa/specs/features/browser_ui/5_package/infrastructure_registry/terraform_module_registry_spec.rb
new file mode 100644
index 00000000000..6ce395affc7
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/5_package/infrastructure_registry/terraform_module_registry_spec.rb
@@ -0,0 +1,81 @@
+# frozen_string_literal: true
+
+module QA
+ RSpec.describe 'Package', :orchestrated, :packages do
+ describe 'Terraform Module Registry' do
+ include Runtime::Fixtures
+
+ let(:group) { Resource::Group.fabricate_via_api! }
+
+ let(:imported_project) do
+ Resource::ProjectImportedFromURL.fabricate_via_browser_ui! do |project|
+ project.name = 'terraform-module-test'
+ project.group = group
+ project.gitlab_repository_path = 'https://gitlab.com/mattkasa/terraform-module-test.git'
+ end
+ end
+
+ let(:runner) do
+ Resource::Runner.fabricate! do |runner|
+ runner.name = "qa-runner-#{Time.now.to_i}"
+ runner.tags = ["runner-for-#{imported_project.name}"]
+ runner.executor = :docker
+ runner.project = imported_project
+ runner.token = group.reload!.runners_token
+ end
+ end
+
+ before do
+ Flow::Login.sign_in
+
+ imported_project
+ runner
+ end
+
+ after do
+ runner.remove_via_api!
+ end
+
+ it 'publishes a module', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/371583' do
+ Support::Retrier.retry_on_exception(max_attempts: 3, sleep_interval: 2) do
+ Resource::Repository::Commit.fabricate_via_api! do |commit|
+ terraform_module_yaml = ERB.new(
+ read_fixture('package_managers/terraform', 'module_upload.yaml.erb')
+ ).result(binding)
+ commit.project = imported_project
+ commit.commit_message = 'Add gitlab-ci.yaml file'
+ commit.update_files([
+ {
+ file_path: '.gitlab-ci.yml',
+ content: terraform_module_yaml
+ }
+ ]
+ )
+ end
+ end
+
+ Resource::Tag.fabricate_via_api! do |tag|
+ tag.project = imported_project
+ tag.ref = imported_project.default_branch
+ tag.name = "1.0.0"
+ end
+
+ Flow::Pipeline.visit_latest_pipeline
+
+ Page::Project::Pipeline::Show.perform do |pipeline|
+ pipeline.click_job('upload')
+ end
+
+ Page::Project::Job::Show.perform do |job|
+ expect(job).to be_successful(timeout: 800)
+ end
+
+ Page::Project::Menu.perform(&:go_to_infrastructure_registry)
+
+ Page::Project::Packages::Index.perform do |index|
+ expect(index).to have_module("#{imported_project.name}/local")
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_group_level_spec.rb b/qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_group_level_spec.rb
index 180910e85a0..921b36b34af 100644
--- a/qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_group_level_spec.rb
+++ b/qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_group_level_spec.rb
@@ -142,7 +142,7 @@ module QA
Page::Group::Menu.perform(&:go_to_package_settings)
end
- context 'when enabled' do
+ context 'when disabled' do
where do
{
'using a personal access token' => {
@@ -176,7 +176,7 @@ module QA
end
before do
- Page::Group::Settings::PackageRegistries.perform(&:set_reject_duplicates_enabled)
+ Page::Group::Settings::PackageRegistries.perform(&:set_allow_duplicates_disabled)
end
it 'prevents users from publishing group level Maven packages duplicates', testcase: params[:testcase] do
@@ -195,7 +195,7 @@ module QA
end
end
- context 'when disabled' do
+ context 'when enabled' do
where do
{
'using a personal access token' => {
@@ -229,7 +229,7 @@ module QA
end
before do
- Page::Group::Settings::PackageRegistries.perform(&:set_reject_duplicates_disabled)
+ Page::Group::Settings::PackageRegistries.perform(&:set_allow_duplicates_enabled)
end
it 'allows users to publish group level Maven packages duplicates', testcase: params[:testcase] do
diff --git a/qa/qa/specs/features/browser_ui/5_package/package_registry/pypi_repository_spec.rb b/qa/qa/specs/features/browser_ui/5_package/package_registry/pypi_repository_spec.rb
index 22d76d684e5..012a03ca115 100644
--- a/qa/qa/specs/features/browser_ui/5_package/package_registry/pypi_repository_spec.rb
+++ b/qa/qa/specs/features/browser_ui/5_package/package_registry/pypi_repository_spec.rb
@@ -105,10 +105,6 @@ module QA
QA::Runtime::Logger.debug('Visiting the secondary Geo site')
QA::Flow::Login.while_signed_in(address: :geo_secondary) do
- EE::Page::Main::Banner.perform do |banner|
- expect(banner).to have_secondary_read_only_banner
- end
-
Page::Main::Menu.perform(&:go_to_projects)
Page::Dashboard::Projects.perform do |dashboard|
diff --git a/qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb b/qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb
index f1a2eb71390..b839855c500 100644
--- a/qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb
+++ b/qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb
@@ -1,78 +1,67 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Configure',
- only: { subdomain: %i[staging staging-canary] },
- quarantine: {
- issue: 'https://gitlab.com/gitlab-org/quality/team-tasks/-/issues/1198',
- type: :waiting_on
- } do
- let(:project) do
- Resource::Project.fabricate_via_api! do |project|
- project.name = 'autodevops-project'
- project.auto_devops_enabled = true
+ RSpec.describe 'Configure', only: { subdomain: %i[staging staging-canary] } do
+ describe 'Auto DevOps with a Kubernetes Agent' do
+ let!(:app_project) do
+ Resource::Project.fabricate_via_api! do |project|
+ project.name = 'autodevops-app-project'
+ project.template_name = 'express'
+ project.auto_devops_enabled = true
+ end
end
- end
- before do
- set_kube_ingress_base_domain(project)
- disable_optional_jobs(project)
- end
+ let!(:cluster) { Service::KubernetesCluster.new(provider_class: Service::ClusterProvider::Gcloud).create! }
- describe 'Auto DevOps support' do
- context 'when rbac is enabled' do
- let(:cluster) { Service::KubernetesCluster.new.create! }
+ let!(:kubernetes_agent) do
+ Resource::Clusters::Agent.fabricate_via_api! do |agent|
+ agent.name = 'agent1'
+ agent.project = app_project
+ end
+ end
- after do
- cluster&.remove!
- project.remove_via_api!
+ let!(:agent_token) do
+ Resource::Clusters::AgentToken.fabricate_via_api! do |token|
+ token.agent = kubernetes_agent
end
+ end
- it 'runs auto devops', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/348061' do
- Flow::Login.sign_in
-
- Resource::KubernetesCluster::ProjectCluster.fabricate! do |k8s_cluster|
- k8s_cluster.project = project
- k8s_cluster.cluster = cluster
- k8s_cluster.install_ingress = true
- end
-
- Resource::Repository::ProjectPush.fabricate! do |push|
- push.project = project
- push.directory = Pathname
- .new(__dir__)
- .join('../../../../../fixtures/auto_devops_rack')
- push.commit_message = 'Create Auto DevOps compatible rack application'
- end
-
- Flow::Pipeline.visit_latest_pipeline
-
- Page::Project::Pipeline::Show.perform do |pipeline|
- pipeline.click_job('build')
- end
- Page::Project::Job::Show.perform do |job|
- expect(job).to be_successful(timeout: 600)
-
- job.click_element(:pipeline_path)
- end
-
- Page::Project::Pipeline::Show.perform do |pipeline|
- pipeline.click_job('test')
- end
- Page::Project::Job::Show.perform do |job|
- expect(job).to be_successful(timeout: 600)
-
- job.click_element(:pipeline_path)
- end
-
- Page::Project::Pipeline::Show.perform do |pipeline|
- pipeline.click_job('production')
- end
- Page::Project::Job::Show.perform do |job|
- expect(job).to be_successful(timeout: 1200)
-
- job.click_element(:pipeline_path)
- end
+ before do
+ cluster.install_kubernetes_agent(agent_token.token)
+ upload_agent_config(app_project, kubernetes_agent.name)
+
+ set_kube_ingress_base_domain(app_project)
+ set_kube_context(app_project)
+ disable_optional_jobs(app_project)
+ end
+
+ after do
+ cluster&.remove!
+ end
+
+ it 'runs auto devops', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/348061' do
+ Flow::Login.sign_in
+
+ app_project.visit!
+
+ Page::Project::Menu.perform(&:click_ci_cd_pipelines)
+ Page::Project::Pipeline::Index.perform(&:click_run_pipeline_button)
+ Page::Project::Pipeline::New.perform(&:click_run_pipeline_button)
+
+ Page::Project::Pipeline::Show.perform do |pipeline|
+ pipeline.click_job('build')
+ end
+ Page::Project::Job::Show.perform do |job|
+ expect(job).to be_successful(timeout: 600)
+
+ job.click_element(:pipeline_path)
+ end
+
+ Page::Project::Pipeline::Show.perform do |pipeline|
+ pipeline.click_job('production')
+ end
+ Page::Project::Job::Show.perform do |job|
+ expect(job).to be_successful(timeout: 600)
end
end
end
@@ -88,12 +77,43 @@ module QA
end
end
+ def set_kube_context(project)
+ Resource::CiVariable.fabricate_via_api! do |resource|
+ resource.project = project
+ resource.key = 'KUBE_CONTEXT'
+ resource.value = "#{project.path_with_namespace}:#{kubernetes_agent.name}"
+ resource.masked = false
+ end
+ end
+
+ def upload_agent_config(project, agent)
+ Support::Retrier.retry_on_exception(max_attempts: 3, sleep_interval: 2) do
+ Resource::Repository::Commit.fabricate_via_api! do |commit|
+ commit.project = project
+ commit.commit_message = 'Add kubernetes agent configuration'
+ commit.add_files(
+ [
+ {
+ file_path: ".gitlab/agents/#{agent}/config.yaml",
+ content: <<~YAML
+ ci_access:
+ projects:
+ - id: #{project.path_with_namespace}
+ YAML
+ }
+ ]
+ )
+ end
+ end
+ end
+
def disable_optional_jobs(project)
%w[
- CODE_QUALITY_DISABLED LICENSE_MANAGEMENT_DISABLED
- SAST_DISABLED DAST_DISABLED DEPENDENCY_SCANNING_DISABLED
- CONTAINER_SCANNING_DISABLED BROWSER_PERFORMANCE_DISABLED
- SECRET_DETECTION_DISABLED
+ TEST_DISABLED CODE_QUALITY_DISABLED LICENSE_MANAGEMENT_DISABLED
+ BROWSER_PERFORMANCE_DISABLED LOAD_PERFORMANCE_DISABLED
+ SAST_DISABLED SECRET_DETECTION_DISABLED DEPENDENCY_SCANNING_DISABLED
+ CONTAINER_SCANNING_DISABLED DAST_DISABLED REVIEW_DISABLED
+ CODE_INTELLIGENCE_DISABLED CLUSTER_IMAGE_SCANNING_DISABLED
].each do |key|
Resource::CiVariable.fabricate_via_api! do |resource|
resource.project = project
diff --git a/qa/qa/specs/features/browser_ui/7_configure/kubernetes/kubernetes_integration_spec.rb b/qa/qa/specs/features/browser_ui/7_configure/kubernetes/kubernetes_integration_spec.rb
deleted file mode 100644
index 94f9e9ec1f6..00000000000
--- a/qa/qa/specs/features/browser_ui/7_configure/kubernetes/kubernetes_integration_spec.rb
+++ /dev/null
@@ -1,38 +0,0 @@
-# frozen_string_literal: true
-
-module QA
- RSpec.describe 'Configure', except: { job: 'review-qa-*' } do
- describe 'Kubernetes Cluster Integration', :orchestrated, :requires_admin, :skip_live_env do
- context 'Project Clusters' do
- let!(:cluster) { Service::KubernetesCluster.new(provider_class: Service::ClusterProvider::K3s).create! }
- let(:project) do
- Resource::Project.fabricate_via_api! do |project|
- project.name = 'project-with-k8s'
- project.description = 'Project with Kubernetes cluster integration'
- end
- end
-
- before do
- Flow::Login.sign_in_as_admin
- end
-
- after do
- cluster.remove!
- end
-
- it 'can create and associate a project cluster', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/348062' do
- Resource::KubernetesCluster::ProjectCluster.fabricate_via_browser_ui! do |k8s_cluster|
- k8s_cluster.project = project
- k8s_cluster.cluster = cluster
- end.project.visit!
-
- Page::Project::Menu.perform(&:go_to_infrastructure_kubernetes)
-
- Page::Project::Infrastructure::Kubernetes::Index.perform do |index|
- expect(index).to have_cluster(cluster)
- end
- end
- end
- end
- end
-end
diff --git a/qa/qa/specs/features/shared_examples/merge_with_code_owner_shared_examples.rb b/qa/qa/specs/features/shared_examples/merge_with_code_owner_shared_examples.rb
index 4bbad9bf3e5..01b229192cc 100644
--- a/qa/qa/specs/features/shared_examples/merge_with_code_owner_shared_examples.rb
+++ b/qa/qa/specs/features/shared_examples/merge_with_code_owner_shared_examples.rb
@@ -8,11 +8,9 @@ module QA
# Require one approval from any eligible user on any branch
# This will confirm that this type of unrestricted approval is
# also satisfied when a code owner grants approval
- Page::Project::Menu.perform(&:go_to_general_settings)
- Page::Project::Settings::Main.perform do |main|
- main.expand_merge_request_approvals_settings do |settings|
- settings.set_default_number_of_approvals_required(1)
- end
+ Page::Project::Menu.perform(&:go_to_merge_request_settings)
+ Page::Project::Settings::MergeRequest.perform do |settings|
+ settings.set_default_number_of_approvals_required(1)
end
Resource::Repository::Commit.fabricate_via_api! do |commit|
diff --git a/qa/qa/specs/helpers/context_selector.rb b/qa/qa/specs/helpers/context_selector.rb
index 9ac79ad6196..3608fa7c581 100644
--- a/qa/qa/specs/helpers/context_selector.rb
+++ b/qa/qa/specs/helpers/context_selector.rb
@@ -30,6 +30,8 @@ module QA
next unless option.is_a?(Hash)
+ opts.merge!(option)
+
if option[:pipeline].present?
return true if Runtime::Env.ci_project_name.blank?
@@ -41,8 +43,6 @@ module QA
return job_matches?(option[:job])
elsif option[:subdomain].present?
- opts.merge!(option)
-
opts[:subdomain] = case option[:subdomain]
when Array
"(#{option[:subdomain].join("|")})\\."
diff --git a/qa/qa/specs/runner.rb b/qa/qa/specs/runner.rb
index 801b9b222a4..c46b6300200 100644
--- a/qa/qa/specs/runner.rb
+++ b/qa/qa/specs/runner.rb
@@ -86,8 +86,7 @@ module QA
File.open(filename, 'w') { |f| f.write(total_examples) } if total_examples.to_i > 0
- saved_file_msg = total_examples.to_i > 0 ? ". Saved to file: #{filename}" : ''
- $stdout.puts "Total examples in #{Runtime::Scenario.klass}: #{total_examples}#{saved_file_msg}"
+ $stdout.puts total_examples
end
def test_metadata_only(args)
diff --git a/qa/qa/specs/spec_helper.rb b/qa/qa/specs/spec_helper.rb
index e1c08515521..b9e67c2fa72 100644
--- a/qa/qa/specs/spec_helper.rb
+++ b/qa/qa/specs/spec_helper.rb
@@ -12,6 +12,9 @@ QA::Runtime::Browser.configure! unless QA::Runtime::Env.dry_run
QA::Runtime::AllureReport.configure!
QA::Runtime::Scenario.from_env(QA::Runtime::Env.runtime_scenario_attributes)
+# Enable zero monkey patching mode before loading any other RSpec code.
+RSpec.configure(&:disable_monkey_patching!)
+
Dir[::File.join(__dir__, "features/shared_examples/*.rb")].sort.each { |f| require f }
Dir[::File.join(__dir__, "features/shared_contexts/*.rb")].sort.each { |f| require f }
@@ -119,7 +122,6 @@ RSpec.configure do |config|
end
config.shared_context_metadata_behavior = :apply_to_host_groups
- config.disable_monkey_patching!
config.expose_dsl_globally = true
config.profile_examples = 10
config.order = :random
diff --git a/qa/qa/support/api.rb b/qa/qa/support/api.rb
index a1bbe9f378a..8fa6d3f23dc 100644
--- a/qa/qa/support/api.rb
+++ b/qa/qa/support/api.rb
@@ -3,10 +3,13 @@
module QA
module Support
module API
+ extend self
+
HTTP_STATUS_OK = 200
HTTP_STATUS_CREATED = 201
HTTP_STATUS_NO_CONTENT = 204
HTTP_STATUS_ACCEPTED = 202
+ HTTP_STATUS_PERMANENT_REDIRECT = 308
HTTP_STATUS_NOT_FOUND = 404
HTTP_STATUS_TOO_MANY_REQUESTS = 429
HTTP_STATUS_SERVER_ERROR = 500
@@ -21,7 +24,7 @@ module QA
}
RestClient::Request.execute(default_args.merge(args))
- rescue RestClient::ExceptionWithResponse => e
+ rescue StandardError => e
return_response_or_raise(e)
end
end
@@ -37,7 +40,7 @@ module QA
RestClient::Request.execute(
default_args.merge(args)
)
- rescue RestClient::ExceptionWithResponse => e
+ rescue StandardError => e
return_response_or_raise(e)
end
end
@@ -49,7 +52,7 @@ module QA
url: url,
payload: payload,
verify_ssl: false)
- rescue RestClient::ExceptionWithResponse => e
+ rescue StandardError => e
return_response_or_raise(e)
end
end
@@ -64,7 +67,7 @@ module QA
}
RestClient::Request.execute(default_args.merge(args))
- rescue RestClient::ExceptionWithResponse => e
+ rescue StandardError => e
return_response_or_raise(e)
end
end
@@ -75,7 +78,7 @@ module QA
method: :delete,
url: url,
verify_ssl: false)
- rescue RestClient::ExceptionWithResponse => e
+ rescue StandardError => e
return_response_or_raise(e)
end
end
@@ -86,11 +89,15 @@ module QA
method: :head,
url: url,
verify_ssl: false)
- rescue RestClient::ExceptionWithResponse => e
+ rescue StandardError => e
return_response_or_raise(e)
end
end
+ def masked_url(url)
+ url.sub(/private_token=[^&]*/, "private_token=[****]")
+ end
+
def with_retry_on_too_many_requests
response = nil
@@ -115,7 +122,7 @@ module QA
end
def return_response_or_raise(error)
- raise error unless error.respond_to?(:response) && error.response
+ raise error, masked_url(error.to_s) unless error.respond_to?(:response) && error.response
error.response
end
@@ -129,7 +136,7 @@ module QA
def with_paginated_response_body(url, attempts: 0)
not_ok_error = lambda do |resp|
- raise "Failed to GET #{QA::Runtime::API::Request.masked_url(url)} - (#{resp.code}): `#{resp}`."
+ raise "Failed to GET #{masked_url(url)} - (#{resp.code}): `#{resp}`."
end
loop do
diff --git a/qa/qa/support/json_formatter.rb b/qa/qa/support/json_formatter.rb
index 0b805cd9eec..252ccfe73d3 100644
--- a/qa/qa/support/json_formatter.rb
+++ b/qa/qa/support/json_formatter.rb
@@ -51,6 +51,7 @@ module QA
testcase: example.metadata[:testcase],
quarantine: example.metadata[:quarantine],
screenshot: example.metadata[:screenshot],
+ product_group: example.metadata[:product_group],
ci_job_url: QA::Runtime::Env.ci_job_url
}
end
diff --git a/qa/qa/support/matchers/eventually_matcher.rb b/qa/qa/support/matchers/eventually_matcher.rb
index 01d07585f57..3f451f89246 100644
--- a/qa/qa/support/matchers/eventually_matcher.rb
+++ b/qa/qa/support/matchers/eventually_matcher.rb
@@ -21,6 +21,7 @@ module QA
eq
be
include
+ match
be_truthy
be_falsey
be_empty
diff --git a/qa/qa/tools/ci/ff_changes.rb b/qa/qa/tools/ci/ff_changes.rb
new file mode 100644
index 00000000000..67e52633833
--- /dev/null
+++ b/qa/qa/tools/ci/ff_changes.rb
@@ -0,0 +1,66 @@
+# frozen_string_literal: true
+
+require "yaml"
+
+module QA
+ module Tools
+ module Ci
+ class FfChanges
+ include Helpers
+
+ def initialize(mr_diff)
+ @mr_diff = mr_diff
+ end
+
+ # Return list of feature flags changed in mr with inverse or deleted state
+ #
+ # @return [String]
+ def fetch
+ logger.info("Detecting feature flag changes")
+ ff_toggles = mr_diff.map do |change|
+ ff_yaml = ff_yaml_for_file(change)
+ next unless ff_yaml
+
+ state = if ff_yaml[:deleted]
+ "deleted"
+ else
+ ff_yaml[:default_enabled] ? 'disabled' : 'enabled'
+ end
+
+ logger.info(" found changes in feature flag '#{ff_yaml[:name]}'")
+ "#{ff_yaml[:name]}=#{state}"
+ end.compact
+
+ if ff_toggles.empty?
+ logger.info(" no changes to feature flags detected, skipping!")
+ return
+ end
+
+ logger.info(" constructed feature flag states: '#{ff_toggles}'")
+ ff_toggles.join(",")
+ end
+
+ private
+
+ attr_reader :mr_diff
+
+ # Loads the YAML feature flag definition based on changed files in merge requests.
+ # The definition is loaded from the definition file itself.
+ #
+ # @param [Hash] change mr file change
+ # @return [Hash] a hash containing the YAML data for the feature flag definition
+ def ff_yaml_for_file(change)
+ return unless change[:path] =~ %r{/feature_flags/(development|ops)/.*\.yml}
+ if change[:deleted_file]
+ return { name: change[:path].split("/").last.gsub(/\.(yml|yaml)/, ""), deleted: true }
+ end
+
+ YAML.safe_load(
+ File.read(File.expand_path("../#{change[:path]}", QA::Runtime::Path.qa_root)),
+ symbolize_names: true
+ )
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/tools/ci/helpers.rb b/qa/qa/tools/ci/helpers.rb
new file mode 100644
index 00000000000..55bb123de20
--- /dev/null
+++ b/qa/qa/tools/ci/helpers.rb
@@ -0,0 +1,50 @@
+# frozen_string_literal: true
+
+module QA
+ module Tools
+ module Ci
+ # Helpers for CI related tasks
+ #
+ module Helpers
+ include Support::API
+
+ # Logger instance
+ #
+ # @return [Logger]
+ def logger
+ @logger ||= Gitlab::QA::TestLogger.logger(
+ level: Gitlab::QA::Runtime::Env.log_level,
+ source: "CI Tools"
+ )
+ end
+
+ # Api get request
+ #
+ # @param [String] path
+ # @param [Hash] args
+ # @return [Hash, Array]
+ def api_get(path, **args)
+ response = get("#{api_url}/#{path}", { headers: { "PRIVATE-TOKEN" => access_token }, **args })
+ response = response.follow_redirection if response.code == Support::API::HTTP_STATUS_PERMANENT_REDIRECT
+ raise "Request failed: '#{response.body}'" unless response.code == Support::API::HTTP_STATUS_OK
+
+ args[:raw_response] ? response : parse_body(response)
+ end
+
+ # Gitlab api url
+ #
+ # @return [String]
+ def api_url
+ @api_url ||= ENV.fetch('CI_API_V4_URL', 'https://gitlab.com/api/v4')
+ end
+
+ # Api access token
+ #
+ # @return [String]
+ def access_token
+ @access_token ||= ENV.fetch('QA_GITLAB_CI_TOKEN') { raise('Variable QA_GITLAB_CI_TOKEN missing') }
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/tools/ci/non_empty_suites.rb b/qa/qa/tools/ci/non_empty_suites.rb
new file mode 100644
index 00000000000..687c11a3e62
--- /dev/null
+++ b/qa/qa/tools/ci/non_empty_suites.rb
@@ -0,0 +1,98 @@
+# frozen_string_literal: true
+
+require 'open3'
+
+module QA
+ module Tools
+ module Ci
+ # Run count commands for scenarios and detect which ones have more than 0 examples to run
+ #
+ class NonEmptySuites
+ include Helpers
+
+ # rubocop:disable Layout/LineLength
+ SCENARIOS = [
+ { klass: "Test::Instance::All" },
+ { klass: "Test::Instance::Smoke" },
+ { klass: "Test::Instance::Reliable" },
+ { klass: "Test::Instance::ReviewBlocking" },
+ { klass: "Test::Instance::ReviewNonBlocking" },
+ { klass: "Test::Instance::CloudActivation" },
+ { klass: "Test::Instance::Integrations" },
+ { klass: "Test::Instance::Jira" },
+ { klass: "Test::Instance::LargeSetup" },
+ { klass: "Test::Instance::Metrics" },
+ { klass: "Test::Instance::ObjectStorage" },
+ { klass: "Test::Instance::Packages" },
+ { klass: "Test::Instance::RepositoryStorage" },
+ { klass: "Test::Integration::ServicePingDisabled" },
+ { klass: "Test::Integration::LDAPNoTLS" },
+ { klass: "Test::Integration::LDAPTLS" },
+ { klass: "Test::Integration::LDAPNoServer" },
+ { klass: "Test::Integration::InstanceSAML" },
+ { klass: "Test::Integration::RegistryWithCDN" },
+ { klass: "Test::Integration::RegistryTLS" },
+ { klass: "Test::Integration::Registry" },
+ { klass: "Test::Integration::SMTP" },
+ { klass: "QA::EE::Scenario::Test::Integration::Elasticsearch" },
+ { klass: "QA::EE::Scenario::Test::Integration::GroupSAML" },
+ {
+ klass: "QA::EE::Scenario::Test::Geo",
+ args: "--primary-address http://dummy1.test --primary-name gitlab-primary --secondary-address http://dummy2.test --secondary-name gitlab-secondary --without-setup"
+ },
+ {
+ klass: "Test::Integration::Mattermost",
+ args: "--mattermost-address http://mattermost.test"
+ }
+ ].freeze
+ # rubocop:enable Layout/LineLength
+
+ def initialize(qa_tests)
+ @qa_tests = qa_tests
+ end
+
+ # Run counts and return runnable scenario list
+ #
+ # @return [String]
+ def fetch
+ logger.info("Checking for runnable suites")
+ scenarios = SCENARIOS.each_with_object([]) do |scenario, runnable_scenarios|
+ logger.info(" fetching runnable specs for '#{scenario[:klass]}'")
+
+ out, err, status = run_command(**scenario)
+
+ unless status.success?
+ logger.error(" example count failed!\n#{err}")
+ next
+ end
+
+ count = out.split("\n").last.to_i
+ logger.info(" found #{count} examples to run")
+ runnable_scenarios << scenario[:klass] if count > 0
+ end
+
+ scenarios.join(",")
+ end
+
+ private
+
+ attr_reader :qa_tests
+
+ # Run scenario count command
+ #
+ # @param [String] klass
+ # @param [String] args
+ # @return [String]
+ def run_command(klass:, args: nil)
+ cmd = ["bundle exec bin/qa"]
+ cmd << klass
+ cmd << "--count-examples-only --address http://dummy1.test"
+ cmd << args if args
+ cmd << "-- #{qa_tests}" unless qa_tests.blank?
+
+ Open3.capture3(cmd.join(" "))
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/tools/ci/qa_changes.rb b/qa/qa/tools/ci/qa_changes.rb
new file mode 100644
index 00000000000..75274961efe
--- /dev/null
+++ b/qa/qa/tools/ci/qa_changes.rb
@@ -0,0 +1,116 @@
+# frozen_string_literal: true
+
+require "pathname"
+
+module QA
+ module Tools
+ module Ci
+ # Determine specific qa specs or paths to execute based on changes
+ class QaChanges
+ include Helpers
+
+ QA_PATTERN = %r{^qa/}.freeze
+ SPEC_PATTERN = %r{^qa/qa/specs/features/}.freeze
+
+ def initialize(mr_diff, mr_labels)
+ @mr_diff = mr_diff
+ @mr_labels = mr_labels
+ end
+
+ # Specific specs to run
+ #
+ # @return [String]
+ def qa_tests
+ return if mr_diff.empty?
+ # make paths relative to qa directory
+ return changed_files&.map { |path| path.delete_prefix("qa/") }&.join(" ") if only_spec_changes?
+ return qa_spec_directories_for_devops_stage&.join(" ") if non_qa_changes? && mr_labels.any?
+ end
+
+ # Qa framework changes
+ #
+ # @return [Boolean]
+ def framework_changes?
+ return false if mr_diff.empty?
+ return false if only_spec_changes?
+
+ changed_files
+ # TODO: expand pattern to other non spec paths that shouldn't trigger full suite
+ .select { |file_path| file_path.match?(QA_PATTERN) && !file_path.match?(SPEC_PATTERN) }
+ .any?
+ end
+
+ def quarantine_changes?
+ return false if mr_diff.empty?
+ return false if mr_diff.any? { |change| change[:new_file] || change[:deleted_file] }
+
+ files_count = 0
+ specs_count = 0
+ quarantine_specs_count = 0
+
+ mr_diff.each do |change|
+ path = change[:path]
+ next if File.directory?(File.expand_path("../#{path}", QA::Runtime::Path.qa_root))
+
+ files_count += 1
+ next unless path.match?(SPEC_PATTERN) && path.end_with?('_spec.rb')
+
+ specs_count += 1
+ quarantine_specs_count += 1 if change[:diff].match?(/^\+.*,? quarantine:/)
+ end
+
+ return false if specs_count == 0
+ return true if quarantine_specs_count == specs_count && quarantine_specs_count == files_count
+
+ false
+ end
+
+ private
+
+ # @return [Array]
+ attr_reader :mr_diff
+
+ # @return [Array]
+ attr_reader :mr_labels
+
+ # Are the changed files only qa specs?
+ #
+ # @return [Boolean] whether the changes files are only qa specs
+ def only_spec_changes?
+ changed_files.all? { |file_path| file_path =~ SPEC_PATTERN }
+ end
+
+ # Are the changed files only outside the qa directory?
+ #
+ # @return [Boolean] whether the changes files are outside of qa directory
+ def non_qa_changes?
+ changed_files.none? { |file_path| file_path =~ QA_PATTERN }
+ end
+
+ # Extract devops stage from MR labels
+ #
+ # @return [String] a devops stage
+ def devops_stage_from_mr_labels
+ mr_labels.find { |label| label =~ /^devops::/ }&.delete_prefix('devops::')
+ end
+
+ # Get qa spec directories for devops stage
+ #
+ # @return [Array] qa spec directories
+ def qa_spec_directories_for_devops_stage
+ devops_stage = devops_stage_from_mr_labels
+ return unless devops_stage
+
+ Dir.glob("qa/specs/**/*/").select { |dir| dir =~ %r{\d+_#{devops_stage}/$} }
+ end
+
+ # Change files in merge request
+ #
+ # @return [Array<String>]
+ def changed_files
+ @changed_files ||= mr_diff.map { |change| change[:path] } # rubocop:disable Rails/Pluck
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/tools/ci/test_results.rb b/qa/qa/tools/ci/test_results.rb
new file mode 100644
index 00000000000..635b69f6ca0
--- /dev/null
+++ b/qa/qa/tools/ci/test_results.rb
@@ -0,0 +1,78 @@
+# frozen_string_literal: true
+
+module QA
+ module Tools
+ module Ci
+ class TestResults
+ include Helpers
+
+ def initialize(pipeline_name, test_report_job_name, report_path)
+ @pipeline_name = pipeline_name
+ @test_report_job_name = test_report_job_name
+ @report_path = report_path
+ end
+
+ # Get test report artifacts from downstream pipeline
+ #
+ # @param [String] pipeline_name
+ # @param [String] test_report_job_name
+ # @param [String] report_path
+ # @return [void]
+ def self.get(pipeline_name, test_report_job_name, report_path)
+ new(pipeline_name, test_report_job_name, report_path).download_test_results
+ end
+
+ # Download test results from child pipeline
+ #
+ # @return [void]
+ def download_test_results
+ logger.info("Fetching test results for '#{pipeline_name}'")
+
+ logger.debug(" fetching pipeline id of '#{pipeline_name}' child pipeline")
+ downstream_pipeline_id = api_get("#{pipelines_url(pipeline_id)}/bridges")
+ .find { |bridge| bridge[:name] == pipeline_name }
+ &.dig(:downstream_pipeline, :id)
+ return logger.error("Child pipeline '#{pipeline_name}' not found!") unless downstream_pipeline_id
+
+ logger.debug(" fetching job id of test report job")
+ job_id = api_get("#{pipelines_url(downstream_pipeline_id)}/jobs")
+ .find { |job| job[:name] == test_report_job_name }
+ &.fetch(:id)
+ return logger.error("Test report job '#{test_report_job_name}' not found!") unless job_id
+
+ logger.debug(" fetching test results artifact archive")
+ response = api_get("/projects/#{project_id}/jobs/#{job_id}/artifacts", raw_response: true)
+
+ logger.info("Extracting test result archive")
+ system("unzip", "-o", "-d", report_path, response.file.path)
+ end
+
+ private
+
+ attr_reader :pipeline_name, :test_report_job_name, :report_path
+
+ # Base get pipeline url
+ #
+ # @param [Integer] id
+ # @return [String]
+ def pipelines_url(id)
+ "/projects/#{project_id}/pipelines/#{id}"
+ end
+
+ # Current pipeline id
+ #
+ # @return [String]
+ def pipeline_id
+ ENV["CI_PIPELINE_ID"]
+ end
+
+ # Current project id
+ #
+ # @return [String]
+ def project_id
+ ENV["CI_PROJECT_ID"]
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/tools/revoke_all_personal_access_tokens.rb b/qa/qa/tools/revoke_all_personal_access_tokens.rb
deleted file mode 100644
index b4fa02a36d4..00000000000
--- a/qa/qa/tools/revoke_all_personal_access_tokens.rb
+++ /dev/null
@@ -1,44 +0,0 @@
-# frozen_string_literal: true
-
-require 'net/protocol'
-
-# This script revokes all personal access tokens with the name of 'api-test-token' on the host specified by GITLAB_ADDRESS
-# Required environment variables: GITLAB_USERNAME, GITLAB_PASSWORD and GITLAB_ADDRESS
-# Run `rake revoke_personal_access_tokens`
-
-module QA
- module Tools
- class RevokeAllPersonalAccessTokens
- def run
- do_run
- rescue Net::ReadTimeout
- $stdout.puts 'Net::ReadTimeout during run. Trying again'
- run
- end
-
- private
-
- def do_run
- raise ArgumentError, "Please provide GITLAB_USERNAME" unless ENV['GITLAB_USERNAME']
- raise ArgumentError, "Please provide GITLAB_PASSWORD" unless ENV['GITLAB_PASSWORD']
- raise ArgumentError, "Please provide GITLAB_ADDRESS" unless ENV['GITLAB_ADDRESS']
-
- $stdout.puts 'Running...'
-
- Runtime::Browser.visit(ENV['GITLAB_ADDRESS'], Page::Main::Login)
- Page::Main::Login.perform(&:sign_in_using_credentials)
- Page::Main::Menu.perform(&:click_edit_profile_link)
- Page::Profile::Menu.perform(&:click_access_tokens)
-
- token_name = 'api-test-token'
-
- Page::Profile::PersonalAccessTokens.perform do |tokens_page|
- while tokens_page.has_token_row_for_name?(token_name)
- tokens_page.revoke_first_token_with_name(token_name)
- print "\e[32m.\e[0m"
- end
- end
- end
- end
- end
-end
diff --git a/qa/qa/tools/revoke_user_personal_access_tokens.rb b/qa/qa/tools/revoke_user_personal_access_tokens.rb
new file mode 100644
index 00000000000..2854241f420
--- /dev/null
+++ b/qa/qa/tools/revoke_user_personal_access_tokens.rb
@@ -0,0 +1,94 @@
+# frozen_string_literal: true
+
+# This script revokes all active personal access tokens owned by a given USER_ID
+# up to a given date (Date.today - 1 by default)
+# Required environment variables: USER_ID, GITLAB_QA_ACCESS_TOKEN and GITLAB_ADDRESS
+# Run `rake revoke_user_pats`
+
+module QA
+ module Tools
+ class RevokeUserPersonalAccessTokens
+ include Support::API
+
+ def initialize(revoke_before: (Date.today - 1).to_s, dry_run: false)
+ raise ArgumentError, "Please provide GITLAB_ADDRESS environment variable" unless ENV['GITLAB_ADDRESS']
+
+ unless ENV['GITLAB_QA_ACCESS_TOKEN']
+ raise ArgumentError, "Please provide GITLAB_QA_ACCESS_TOKEN environment variable"
+ end
+
+ raise ArgumentError, "Please provide USER_ID environment variable" unless ENV['USER_ID']
+
+ @revoke_before = Date.parse(revoke_before)
+ @dry_run = dry_run
+ @api_client = Runtime::API::Client.new(ENV['GITLAB_ADDRESS'],
+ personal_access_token: ENV['GITLAB_QA_ACCESS_TOKEN'])
+ end
+
+ def run
+ $stdout.puts 'Running...'
+
+ tokens_head_response = head Runtime::API::Request.new(@api_client,
+ "/personal_access_tokens?user_id=#{ENV['USER_ID']}",
+ per_page: "100").url
+
+ total_token_pages = tokens_head_response.headers[:x_total_pages]
+ total_tokens = tokens_head_response.headers[:x_total]
+
+ $stdout.puts "Total tokens: #{total_tokens}. Total pages: #{total_token_pages}"
+
+ tokens = fetch_tokens
+
+ revoke_tokens(tokens, @api_client, @dry_run) unless tokens.empty?
+ $stdout.puts "\nDone"
+ end
+
+ private
+
+ def fetch_tokens
+ fetched_tokens = []
+
+ page_no = 1
+
+ while page_no > 0
+ tokens_response = get Runtime::API::Request.new(@api_client,
+ "/personal_access_tokens?user_id=#{ENV['USER_ID']}",
+ page: page_no.to_s, per_page: "100").url
+
+ fetched_tokens
+ .concat(JSON.parse(tokens_response.body)
+ .select { |token| Date.parse(token["created_at"]) < @revoke_before && token['active'] }
+ .map { |token| { id: token["id"], name: token["name"], created_at: token["created_at"] } }
+ )
+
+ page_no = tokens_response.headers[:x_next_page].to_i
+ end
+
+ fetched_tokens
+ end
+
+ def revoke_tokens(tokens, api_client, dry_run = false)
+ if dry_run
+ $stdout.puts "Following #{tokens.count} tokens would be revoked:"
+ else
+ $stdout.puts "Revoking #{tokens.count} tokens..."
+ end
+
+ tokens.each do |token|
+ if dry_run
+ $stdout.puts "Token name: #{token[:name]}, id: #{token[:id]}, created at: #{token[:created_at]}"
+ else
+ request_url = Runtime::API::Request.new(api_client, "/personal_access_tokens/#{token[:id]}").url
+
+ $stdout.puts "\nRevoking token with name: #{token[:name]}, " \
+ "id: #{token[:id]}, created at: #{token[:created_at]}"
+
+ delete_response = delete(request_url)
+ dot_or_f = delete_response.code == 204 ? "\e[32m.\e[0m" : "\e[31mF - #{delete_response}\e[0m"
+ print dot_or_f
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/tools/test_resources_handler.rb b/qa/qa/tools/test_resources_handler.rb
index 0030a47ed55..60c6dbfc16c 100644
--- a/qa/qa/tools/test_resources_handler.rb
+++ b/qa/qa/tools/test_resources_handler.rb
@@ -27,7 +27,6 @@ module QA
include Support::API
IGNORED_RESOURCES = [
- 'QA::Resource::PersonalAccessToken',
'QA::Resource::CiVariable',
'QA::Resource::Repository::Commit',
'QA::EE::Resource::GroupIteration',
diff --git a/qa/spec/fixtures/ff/bulk_import_projects.yml b/qa/spec/fixtures/ff/bulk_import_projects.yml
new file mode 100644
index 00000000000..853389577cf
--- /dev/null
+++ b/qa/spec/fixtures/ff/bulk_import_projects.yml
@@ -0,0 +1,8 @@
+---
+name: bulk_import_projects
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/68873
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/339941
+milestone: '14.3'
+type: development
+group: group::import
+default_enabled: false
diff --git a/qa/spec/resource/base_spec.rb b/qa/spec/resource/base_spec.rb
index f23f4aa728b..195e497f290 100644
--- a/qa/spec/resource/base_spec.rb
+++ b/qa/spec/resource/base_spec.rb
@@ -24,6 +24,10 @@ RSpec.describe QA::Resource::Base do
'block'
end
+ attribute :token do
+ 'token_value'
+ end
+
attribute :username do
'qa'
end
@@ -208,6 +212,24 @@ RSpec.describe QA::Resource::Base do
.to have_received(:debug).with(/api_with_block/)
end
end
+
+ context 'when the attribute is token and has a block' do
+ let(:api_resource) { { token: 'another_token_value' } }
+
+ before do
+ allow(QA::Runtime::Logger).to receive(:debug)
+ end
+
+ it 'emits a masked debug log entry' do
+ result = subject.fabricate!(resource: resource)
+
+ expect(result).to be_a(described_class)
+ expect(result.token).to eq('another_token_value')
+
+ expect(QA::Runtime::Logger)
+ .to have_received(:debug).with(/MASKED/)
+ end
+ end
end
context 'when the attribute is populated via direct assignment' do
diff --git a/qa/spec/resource/user_spec.rb b/qa/spec/resource/user_spec.rb
index e7397d9c0bf..c0140abf298 100644
--- a/qa/spec/resource/user_spec.rb
+++ b/qa/spec/resource/user_spec.rb
@@ -23,14 +23,15 @@ RSpec.describe QA::Resource::User do
end
describe '#password' do
- it 'generates a default password' do
- expect(subject.password).to eq('password')
+ it 'generates a random 16 character password by default' do
+ expect(subject.password).to match(/\w{16}/)
end
it 'is possible to set the password' do
- subject.password = 'secret'
+ new_password = "21c7a808"
+ subject.password = new_password
- expect(subject.password).to eq('secret')
+ expect(subject.password).to eq(new_password)
end
end
diff --git a/qa/spec/specs/helpers/context_selector_spec.rb b/qa/spec/specs/helpers/context_selector_spec.rb
index 5a320cde71f..7541bb45d82 100644
--- a/qa/spec/specs/helpers/context_selector_spec.rb
+++ b/qa/spec/specs/helpers/context_selector_spec.rb
@@ -57,6 +57,26 @@ RSpec.describe QA::Specs::Helpers::ContextSelector do
expect(described_class.context_matches?(:production)).to be_truthy
end
+ it 'matches domain' do
+ QA::Runtime::Scenario.define(:gitlab_address, 'https://jihulab.com')
+
+ aggregate_failures do
+ expect(described_class.context_matches?(:production)).to be_falsey
+ expect(described_class.context_matches?(domain: 'gitlab')).to be_falsey
+ expect(described_class.context_matches?(domain: 'jihulab')).to be_truthy
+ end
+ end
+
+ it 'matches tld' do
+ QA::Runtime::Scenario.define(:gitlab_address, 'https://gitlab.cn')
+
+ aggregate_failures do
+ expect(described_class.context_matches?).to be_falsey
+ expect(described_class.context_matches?(tld: 'net')).to be_falsey
+ expect(described_class.context_matches?(tld: 'cn')).to be_truthy
+ end
+ end
+
it 'doesnt match with mismatching switches' do
QA::Runtime::Scenario.define(:gitlab_address, 'https://gitlab.test')
diff --git a/qa/spec/tools/ci/ff_changes_spec.rb b/qa/spec/tools/ci/ff_changes_spec.rb
new file mode 100644
index 00000000000..71ca26867e0
--- /dev/null
+++ b/qa/spec/tools/ci/ff_changes_spec.rb
@@ -0,0 +1,51 @@
+# frozen_string_literal: true
+
+RSpec.describe QA::Tools::Ci::FfChanges do
+ subject(:ff_changes) { described_class.new(mr_diff) }
+
+ before do
+ allow(Gitlab::QA::TestLogger).to receive(:logger).and_return(Logger.new(StringIO.new))
+ end
+
+ context "with merge request pipeline" do
+ let(:deleted_file) { false }
+ let(:mr_diff) do
+ [
+ {
+ path: "config/feature_flags/development/bulk_import_projects.yml",
+ deleted_file: deleted_file
+ }
+ ]
+ end
+
+ before do
+ allow(File).to receive(:read)
+ .with(File.expand_path("../#{mr_diff.first[:path]}", QA::Runtime::Path.qa_root))
+ .and_return(File.read("spec/fixtures/ff/bulk_import_projects.yml"))
+ end
+
+ context "with changed feature flag" do
+ it "returns inverse ff state option" do
+ expect(ff_changes.fetch).to eq("bulk_import_projects=enabled")
+ end
+ end
+
+ context "with deleted feature flag" do
+ let(:deleted_file) { true }
+
+ it "returns deleted ff state option" do
+ expect(ff_changes.fetch).to eq("bulk_import_projects=deleted")
+ end
+ end
+ end
+
+ context "without merge request pipeline" do
+ let(:mr_diff) { [] }
+
+ context "with empty mr diff" do
+ it "doesn't return any ff options" do
+ expect(ff_changes.fetch).to be_nil
+ end
+ end
+ end
+end
diff --git a/qa/spec/tools/ci/non_empty_suites_spec.rb b/qa/spec/tools/ci/non_empty_suites_spec.rb
new file mode 100644
index 00000000000..d9bfe1eebe7
--- /dev/null
+++ b/qa/spec/tools/ci/non_empty_suites_spec.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+RSpec.describe QA::Tools::Ci::NonEmptySuites do
+ let(:non_empty_suites) { described_class.new(nil) }
+
+ let(:status) { instance_double(Process::Status, success?: true) }
+
+ before do
+ allow(Gitlab::QA::TestLogger).to receive(:logger).and_return(Logger.new(StringIO.new))
+ allow(Open3).to receive(:capture3).and_return(["output\n0", "", status])
+ allow(Open3).to receive(:capture3)
+ .with("bundle exec bin/qa Test::Instance::All --count-examples-only --address http://dummy1.test")
+ .and_return(["output\n1", "", status])
+ end
+
+ it "returns runnable test suites" do
+ expect(non_empty_suites.fetch).to eq("Test::Instance::All")
+ end
+end
diff --git a/qa/spec/tools/ci/qa_changes_spec.rb b/qa/spec/tools/ci/qa_changes_spec.rb
new file mode 100644
index 00000000000..bc98ec16d7f
--- /dev/null
+++ b/qa/spec/tools/ci/qa_changes_spec.rb
@@ -0,0 +1,87 @@
+# frozen_string_literal: true
+
+RSpec.describe QA::Tools::Ci::QaChanges do
+ subject(:qa_changes) { described_class.new(mr_diff, mr_labels) }
+
+ let(:mr_labels) { [] }
+
+ before do
+ allow(File).to receive(:directory?).and_return(false)
+ end
+
+ context "with spec only changes" do
+ let(:mr_diff) do
+ [
+ { path: "qa/qa/specs/features/test_spec.rb", diff: "" },
+ { path: "qa/qa/specs/features/another_test_spec.rb", diff: "" }
+ ]
+ end
+
+ it ".qa_tests return changed specs" do
+ expect(qa_changes.qa_tests).to eq(
+ "qa/specs/features/test_spec.rb qa/specs/features/another_test_spec.rb"
+ )
+ end
+
+ it ".framework_changes? return false" do
+ expect(qa_changes.framework_changes?).to eq(false)
+ end
+
+ it ".quarantine_changes? return false" do
+ expect(qa_changes.quarantine_changes?).to eq(false)
+ end
+ end
+
+ context "with framework changes" do
+ let(:mr_diff) { [{ path: "qa/qa.rb" }] }
+
+ it ".qa_tests do not return specifix specs" do
+ expect(qa_changes.qa_tests).to be_nil
+ end
+
+ it ".framework_changes? return true" do
+ expect(qa_changes.framework_changes?).to eq(true)
+ end
+
+ it ".quarantine_changes? return false" do
+ expect(qa_changes.quarantine_changes?).to eq(false)
+ end
+ end
+
+ context "with non qa changes" do
+ let(:mr_diff) { [{ path: "Gemfile" }] }
+
+ it ".framework_changes? return false" do
+ expect(qa_changes.framework_changes?).to eq(false)
+ end
+
+ it ".quarantine_changes? return false" do
+ expect(qa_changes.quarantine_changes?).to eq(false)
+ end
+
+ context "without mr labels" do
+ it ".qa_tests do not return any specific specs" do
+ expect(qa_changes.qa_tests).to be_nil
+ end
+ end
+
+ context "with mr label" do
+ let(:mr_labels) { ["devops::manage"] }
+
+ it ".qa_tests return specs for devops stage" do
+ expect(qa_changes.qa_tests.split(" ")).to include(
+ "qa/specs/features/browser_ui/1_manage/",
+ "qa/specs/features/api/1_manage/"
+ )
+ end
+ end
+ end
+
+ context "with quarantine changes" do
+ let(:mr_diff) { [{ path: "qa/qa/specs/features/test_spec.rb", diff: "+ , quarantine: true" }] }
+
+ it ".quarantine_changes? return true" do
+ expect(qa_changes.quarantine_changes?).to eq(true)
+ end
+ end
+end
diff --git a/qa/tasks/ci.rake b/qa/tasks/ci.rake
new file mode 100644
index 00000000000..44a794d9f94
--- /dev/null
+++ b/qa/tasks/ci.rake
@@ -0,0 +1,61 @@
+# frozen_string_literal: true
+
+require_relative "helpers/util"
+
+# rubocop:disable Rails/RakeEnvironment
+namespace :ci do
+ include Task::Helpers::Util
+
+ desc "Detect changes and populate test variables for selective test execution and feature flag testing"
+ task :detect_changes, [:env_file] do |_, args|
+ env_file = args[:env_file]
+ abort("ERROR: Path for environment file must be provided") unless env_file
+
+ diff = mr_diff
+ labels = mr_labels
+
+ qa_changes = QA::Tools::Ci::QaChanges.new(diff, labels)
+ logger = qa_changes.logger
+
+ logger.info("Analyzing merge request changes")
+ # skip running tests when only quarantine changes detected
+ if qa_changes.quarantine_changes?
+ logger.info(" merge request contains only quarantine changes, e2e test execution will be skipped!")
+ append_to_file(env_file, <<~TXT)
+ QA_SKIP_ALL_TESTS=true
+ TXT
+ next
+ end
+
+ tests = qa_changes.qa_tests
+ if qa_changes.framework_changes? # run all tests when framework changes detected
+ logger.info(" merge request contains qa framework changes, full test suite will be executed")
+ append_to_file(env_file, <<~TXT)
+ QA_FRAMEWORK_CHANGES=true
+ TXT
+ elsif tests
+ logger.info(" detected following specs to execute: '#{tests}'")
+ else
+ logger.info(" no specific specs to execute detected")
+ end
+
+ # always check all test suites in case a suite is defined but doesn't have any runnable specs
+ suites = QA::Tools::Ci::NonEmptySuites.new(tests).fetch
+ append_to_file(env_file, <<~TXT)
+ QA_TESTS='#{tests}'
+ QA_SUITES='#{suites}'
+ TXT
+
+ # check if mr contains feature flag changes
+ feature_flags = QA::Tools::Ci::FfChanges.new(diff).fetch
+ append_to_file(env_file, <<~TXT)
+ QA_FEATURE_FLAGS='#{feature_flags}'
+ TXT
+ end
+
+ desc "Download test results from downstream pipeline"
+ task :download_test_results, [:trigger_name, :test_report_job_name, :report_path] do |_, args|
+ QA::Tools::Ci::TestResults.get(args[:trigger_name], args[:test_report_job_name], args[:report_path])
+ end
+end
+# rubocop:enable Rails/RakeEnvironment
diff --git a/qa/tasks/helpers/util.rb b/qa/tasks/helpers/util.rb
new file mode 100644
index 00000000000..f8eb9b02f72
--- /dev/null
+++ b/qa/tasks/helpers/util.rb
@@ -0,0 +1,49 @@
+# frozen_string_literal: true
+
+module Task
+ module Helpers
+ module Util
+ include ::QA::Support::API
+
+ # Append text to file
+ #
+ # @param [String] path
+ # @param [String] text
+ # @return [void]
+ def append_to_file(path, text)
+ File.open(path, "a") { |f| f.write(text) }
+ end
+
+ # Merge request labels
+ #
+ # @return [Array]
+ def mr_labels
+ ENV["CI_MERGE_REQUEST_LABELS"]&.split(',') || []
+ end
+
+ # Merge request changes
+ #
+ # @return [Array<Hash>]
+ def mr_diff
+ mr_iid = ENV["CI_MERGE_REQUEST_IID"]
+ return [] unless mr_iid
+
+ gitlab_endpoint = ENV["CI_API_V4_URL"]
+ gitlab_token = ENV["PROJECT_TOKEN_FOR_CI_SCRIPTS_API_USAGE"]
+ project_id = ENV["CI_MERGE_REQUEST_PROJECT_ID"]
+
+ response = get(
+ "#{gitlab_endpoint}/projects/#{project_id}/merge_requests/#{mr_iid}/changes",
+ headers: { "PRIVATE-TOKEN" => gitlab_token }
+ )
+
+ parse_body(response).fetch(:changes, []).map do |change|
+ {
+ path: change[:new_path],
+ **change.slice(:new_file, :deleted_file, :diff)
+ }
+ end
+ end
+ end
+ end
+end