diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-09-28 18:09:17 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-09-28 18:09:17 +0300 |
commit | 1bb7f81e238569fd0fe2b0c4385f1015407a2d59 (patch) | |
tree | 768c7d44fa3ed641a7e26fdf9db61422902e8294 /qa | |
parent | eb3a23aaaa99ef8ae08c7b440fad676e3c71a1af (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'qa')
-rw-r--r-- | qa/Gemfile | 6 | ||||
-rw-r--r-- | qa/Gemfile.lock | 84 | ||||
-rw-r--r-- | qa/lib/gitlab/page/group/settings/usage_quotas.rb | 2 | ||||
-rw-r--r-- | qa/qa/resource/issue.rb | 44 | ||||
-rw-r--r-- | qa/qa/resource/merge_request.rb | 42 | ||||
-rw-r--r-- | qa/qa/resource/project.rb | 7 | ||||
-rw-r--r-- | qa/qa/specs/features/api/1_manage/import_large_github_repo_spec.rb | 206 | ||||
-rw-r--r-- | qa/qa/specs/features/sanity/framework_spec.rb | 2 |
8 files changed, 292 insertions, 101 deletions
diff --git a/qa/Gemfile b/qa/Gemfile index cf939f8e301..53d05b1ce89 100644 --- a/qa/Gemfile +++ b/qa/Gemfile @@ -22,13 +22,15 @@ gem 'timecop', '~> 0.9.1' gem 'parallel', '~> 1.19' gem 'rainbow', '~> 3.0.0' gem 'rspec-parameterized', '~> 0.4.2' -gem 'octokit', '~> 4.21' +gem 'octokit', '~> 5.6.1' +gem "faraday-retry", "~> 2.0" gem 'webdrivers', '~> 5.0' gem 'zeitwerk', '~> 2.4' gem 'influxdb-client', '~> 1.17' gem 'terminal-table', '~> 3.0.0', require: false gem 'slack-notifier', '~> 2.4', require: false -gem 'fog-google', '~> 1.17', require: false +gem 'fog-google', '~> 1.19', require: false +gem 'fog-core', '2.1.0', require: false # fog-google generates a ton of warnings with latest core gem "warning", "~> 1.3" gem 'confiner', '~> 0.3' diff --git a/qa/Gemfile.lock b/qa/Gemfile.lock index dd14b675769..0773aae706a 100644 --- a/qa/Gemfile.lock +++ b/qa/Gemfile.lock @@ -67,26 +67,15 @@ GEM domain_name (0.5.20190701) unf (>= 0.0.5, < 1.0.0) equalizer (0.0.11) - excon (0.88.0) + excon (0.92.4) faker (2.19.0) i18n (>= 1.6, < 2) - faraday (1.5.1) - faraday-em_http (~> 1.0) - faraday-em_synchrony (~> 1.0) - faraday-excon (~> 1.1) - faraday-httpclient (~> 1.0.1) - faraday-net_http (~> 1.0) - faraday-net_http_persistent (~> 1.1) - faraday-patron (~> 1.0) - multipart-post (>= 1.2, < 3) + faraday (2.5.2) + faraday-net_http (>= 2.0, < 3.1) ruby2_keywords (>= 0.0.4) - faraday-em_http (1.0.0) - faraday-em_synchrony (1.0.0) - faraday-excon (1.1.0) - faraday-httpclient (1.0.1) - faraday-net_http (1.0.1) - faraday-net_http_persistent (1.2.0) - faraday-patron (1.0.0) + faraday-net_http (3.0.0) + faraday-retry (2.0.0) + faraday (~> 2.0) ffi (1.15.5) ffi-compiler (1.0.1) ffi (>= 1.0.0) @@ -96,8 +85,8 @@ GEM excon (~> 0.58) formatador (~> 0.2) mime-types - fog-google (1.17.0) - fog-core (<= 2.1.0) + fog-google (1.19.0) + fog-core (< 2.3) fog-json (~> 1.2) fog-xml (~> 0.1.0) google-apis-compute_v1 (~> 0.14) @@ -118,7 +107,7 @@ GEM gitlab (4.18.0) httparty (~> 0.18) terminal-table (>= 1.5.1) - gitlab-qa (8.4.2) + gitlab-qa (8.5.0) activesupport (~> 6.1) gitlab (~> 4.18.0) http (~> 5.0) @@ -126,9 +115,9 @@ GEM 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) + google-apis-compute_v1 (0.51.0) + google-apis-core (>= 0.7.2, < 2.a) + google-apis-core (0.9.0) addressable (~> 2.5, >= 2.5.1) googleauth (>= 0.16.2, < 2.a) httpclient (>= 2.8.1, < 3.a) @@ -137,22 +126,22 @@ GEM retriable (>= 2.0, < 4.a) rexml webrick - google-apis-dns_v1 (0.16.0) - google-apis-core (>= 0.4, < 2.a) - google-apis-iamcredentials_v1 (0.8.0) - google-apis-core (>= 0.4, < 2.a) - google-apis-monitoring_v3 (0.18.0) - google-apis-core (>= 0.4, < 2.a) - google-apis-pubsub_v1 (0.10.0) - google-apis-core (>= 0.4, < 2.a) - google-apis-sqladmin_v1beta4 (0.21.0) - google-apis-core (>= 0.4, < 2.a) - google-apis-storage_v1 (0.9.0) - google-apis-core (>= 0.4, < 2.a) + google-apis-dns_v1 (0.27.0) + google-apis-core (>= 0.7.2, < 2.a) + google-apis-iamcredentials_v1 (0.14.0) + google-apis-core (>= 0.7.2, < 2.a) + google-apis-monitoring_v3 (0.33.0) + google-apis-core (>= 0.7, < 2.a) + google-apis-pubsub_v1 (0.28.0) + google-apis-core (>= 0.7.2, < 2.a) + google-apis-sqladmin_v1beta4 (0.36.0) + google-apis-core (>= 0.7.2, < 2.a) + google-apis-storage_v1 (0.18.0) + google-apis-core (>= 0.7, < 2.a) google-cloud-env (1.6.0) faraday (>= 0.17.3, < 3.0) - googleauth (1.1.0) - faraday (>= 0.17.3, < 2.0) + googleauth (1.2.0) + faraday (>= 0.17.3, < 3.a) jwt (>= 1.4, < 3.0) memoist (~> 0.16) multi_json (~> 1.11) @@ -175,7 +164,7 @@ GEM concurrent-ruby (~> 1.0) ice_nine (0.11.2) influxdb-client (1.17.0) - jwt (2.3.0) + jwt (2.5.0) knapsack (4.0.0) rake launchy (2.4.3) @@ -197,12 +186,11 @@ GEM minitest (5.16.3) multi_json (1.15.0) multi_xml (0.6.0) - multipart-post (2.1.1) netrc (0.11.0) nokogiri (1.13.8) mini_portile2 (~> 2.8.0) racc (~> 1.4) - octokit (4.25.1) + octokit (5.6.1) faraday (>= 1, < 3) sawyer (~> 0.9) oj (3.13.11) @@ -231,7 +219,7 @@ GEM rainbow (3.0.0) rake (13.0.6) regexp_parser (2.1.1) - representable (3.1.1) + representable (3.2.0) declarative (< 0.1.0) trailblazer-option (>= 0.1.1, < 0.2.0) uber (< 0.2.0) @@ -269,7 +257,7 @@ GEM rspec-core (>= 2, < 4, != 2.12.0) ruby-debug-ide (0.7.2) rake (>= 0.8.1) - ruby2_keywords (0.0.4) + ruby2_keywords (0.0.5) rubyzip (2.3.2) sawyer (0.9.2) addressable (>= 2.3.5) @@ -278,9 +266,9 @@ GEM childprocess (>= 0.5, < 5.0) rexml (~> 3.2, >= 3.2.5) rubyzip (>= 1.2.2) - signet (0.16.0) + signet (0.17.0) addressable (~> 2.8) - faraday (>= 0.17.3, < 2.0) + faraday (>= 0.17.5, < 3.a) jwt (>= 1.5, < 3.0) multi_json (~> 1.10) slack-notifier (2.4.0) @@ -297,7 +285,7 @@ GEM unf (0.1.4) unf_ext unf_ext (0.0.8.2) - unicode-display_width (2.2.0) + unicode-display_width (2.3.0) unparser (0.4.7) abstract_type (~> 0.0.7) adamantium (~> 0.2.0) @@ -335,12 +323,14 @@ DEPENDENCIES confiner (~> 0.3) deprecation_toolkit (~> 1.5.1) faker (~> 2.19, >= 2.19.0) - fog-google (~> 1.17) + faraday-retry (~> 2.0) + fog-core (= 2.1.0) + fog-google (~> 1.19) gitlab-qa (~> 8) influxdb-client (~> 1.17) knapsack (~> 4.0) nokogiri (~> 1.12) - octokit (~> 4.21) + octokit (~> 5.6.1) parallel (~> 1.19) parallel_tests (~> 2.29) pry-byebug (~> 3.5.1) diff --git a/qa/lib/gitlab/page/group/settings/usage_quotas.rb b/qa/lib/gitlab/page/group/settings/usage_quotas.rb index 2b491188595..9f34f48fee0 100644 --- a/qa/lib/gitlab/page/group/settings/usage_quotas.rb +++ b/qa/lib/gitlab/page/group/settings/usage_quotas.rb @@ -9,7 +9,7 @@ module Gitlab link :pipelines_tab link :storage_tab link :buy_ci_minutes - link :buy_storage + link :purchase_more_storage div :plan_ci_minutes div :additional_ci_minutes span :purchased_usage_total diff --git a/qa/qa/resource/issue.rb b/qa/qa/resource/issue.rb index 1e38de97c1e..40e2e819c99 100644 --- a/qa/qa/resource/issue.rb +++ b/qa/qa/resource/issue.rb @@ -104,6 +104,33 @@ module QA api_post_to(api_comments_path, body: body, confidential: confidential) end + # Issue label events + # + # @param [Boolean] auto_paginate + # @param [Integer] attempts + # @return [Array<Hash>] + def label_events(auto_paginate: false, attempts: 0) + events("label", auto_paginate: auto_paginate, attempts: attempts) + end + + # Issue state events + # + # @param [Boolean] auto_paginate + # @param [Integer] attempts + # @return [Array<Hash>] + def state_events(auto_paginate: false, attempts: 0) + events("state", auto_paginate: auto_paginate, attempts: attempts) + end + + # Issue milestone events + # + # @param [Boolean] auto_paginate + # @param [Integer] attempts + # @return [Array<Hash>] + def milestone_events(auto_paginate: false, attempts: 0) + events("milestone", auto_paginate: auto_paginate, attempts: attempts) + end + protected # Return subset of fields for comparing issues @@ -134,6 +161,23 @@ module QA :created_at ) end + + private + + # Issue events + # + # @param [String] name event name + # @param [Boolean] auto_paginate + # @param [Integer] attempts + # @return [Array<Hash>] + def events(name, auto_paginate:, attempts:) + return parse_body(api_get_from("#{api_get_path}/resource_#{name}_events")) unless auto_paginate + + auto_paginated_response( + Runtime::API::Request.new(api_client, "#{api_get_path}/resource_#{name}_events", per_page: '100').url, + attempts: attempts + ) + end end end end diff --git a/qa/qa/resource/merge_request.rb b/qa/qa/resource/merge_request.rb index 0a92553690f..e4314c4dae7 100644 --- a/qa/qa/resource/merge_request.rb +++ b/qa/qa/resource/merge_request.rb @@ -186,6 +186,33 @@ module QA api_post_to(api_comments_path, body: body) end + # Merge request label events + # + # @param [Boolean] auto_paginate + # @param [Integer] attempts + # @return [Array<Hash>] + def label_events(auto_paginate: false, attempts: 0) + events("label", auto_paginate: auto_paginate, attempts: attempts) + end + + # Merge request state events + # + # @param [Boolean] auto_paginate + # @param [Integer] attempts + # @return [Array<Hash>] + def state_events(auto_paginate: false, attempts: 0) + events("state", auto_paginate: auto_paginate, attempts: attempts) + end + + # Merge request milestone events + # + # @param [Boolean] auto_paginate + # @param [Integer] attempts + # @return [Array<Hash>] + def milestone_events(auto_paginate: false, attempts: 0) + events("milestone", auto_paginate: auto_paginate, attempts: attempts) + end + # Return subset of fields for comparing merge requests # # @return [Hash] @@ -239,6 +266,21 @@ module QA def create_target? !(project.initialize_with_readme && target_branch == project.default_branch) && target_new_branch end + + # Merge request events + # + # @param [String] name event name + # @param [Boolean] auto_paginate + # @param [Integer] attempts + # @return [Array<Hash>] + def events(name, auto_paginate:, attempts:) + return parse_body(api_get_from("#{api_get_path}/resource_#{name}_events")) unless auto_paginate + + auto_paginated_response( + Runtime::API::Request.new(api_client, "#{api_get_path}/resource_#{name}_events", per_page: '100').url, + attempts: attempts + ) + end end end end diff --git a/qa/qa/resource/project.rb b/qa/qa/resource/project.rb index 13c6f285259..b188e432679 100644 --- a/qa/qa/resource/project.rb +++ b/qa/qa/resource/project.rb @@ -324,8 +324,11 @@ module QA result = parse_body(response) if result[:import_status] == "failed" - Runtime::Logger.error("Import failed: #{result[:import_error]}") - Runtime::Logger.error("Failed relations: #{result[:failed_relations]}") + Runtime::Logger.error(<<~ERR) + Import of project '#{full_path}' failed! + error: '#{result[:import_error]}' + failed relations: '#{result[:failed_relations]}' + ERR end result 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 e6b60a5b090..58820a7e477 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 @@ -1,21 +1,76 @@ # frozen_string_literal: true +require "etc" + # Lifesize project import test executed from https://gitlab.com/gitlab-org/manage/import/import-metrics # rubocop:disable Rails/Pluck module QA RSpec.describe 'Manage', :github, requires_admin: 'creates users', only: { job: 'large-github-import' } do - describe 'Project import' do + describe 'Project import' do # rubocop:disable RSpec/MultipleMemoizedHelpers + let(:github_repo) { ENV['QA_LARGE_IMPORT_REPO'] || 'rspec/rspec-core' } + let(:import_max_duration) { ENV['QA_LARGE_IMPORT_DURATION']&.to_i || 7200 } let(:logger) { Runtime::Logger.logger } let(:differ) { RSpec::Support::Differ.new(color: true) } let(:gitlab_address) { QA::Runtime::Scenario.gitlab_address } let(:dummy_url) { "https://example.com" } + let(:api_request_params) { { auto_paginate: true, attempts: 2 } } let(:created_by_pattern) { /\*Created by: \S+\*\n\n/ } let(:suggestion_pattern) { /suggestion:-\d+\+\d+/ } let(:gh_link_pattern) { %r{https://github.com/#{github_repo}/(issues|pull)} } let(:gl_link_pattern) { %r{#{gitlab_address}/#{imported_project.path_with_namespace}/-/(issues|merge_requests)} } - let(:event_pattern) { %r{(un)?assigned( to)? @\S+|mentioned in (issue|merge request) [!#]\d+|changed title from \*\*.*\*\* to \*\*.*\*\*} } # rubocop:disable Layout/LineLength + # rubocop:disable Lint/MixedRegexpCaptureTypes + let(:event_pattern) do + Regexp.union( + [ + /(?<event>(un)?assigned)( to)? @\S+/, + /(?<event>mentioned) in (issue|merge request) [!#]\d+/, + /(?<event>changed title) from \*\*.*\*\* to \*\*.*\*\*/, + /(?<event>requested review) from @\w+/, + /\*(?<event>Merged) by:/, + /\*\*(Review):\*\*/ + ] + ) + end + # rubocop:enable Lint/MixedRegexpCaptureTypes + + # mapping from gitlab to github names + let(:event_mapping) do + { + "label_add" => "labeled", + "label_remove" => "unlabeled", + "milestone_add" => "milestoned", + "milestone_remove" => "demilestoned", + "assigned" => "assigned", + "unassigned" => "unassigned", + "changed title" => "renamed", + "requested review" => "review_requested", + "Merged" => "merged" + } + end + + # github events that are not migrated or are not correctly mapable in gitlab + let(:unsupported_events) do + [ + "head_ref_deleted", + "head_ref_force_pushed", + "head_ref_restored", + "auto_squash_enabled", + "auto_merge_disabled", + "comment_deleted", + "convert_to_draft", + "ready_for_review", + "subscribed", + "unsubscribed", + "transferred", + # mentions are supported but they can be reported differently on gitlab's side + # for example mention of issue creation in pr will be reported in the issue on gitlab side + # or referenced in github will still create a 'mentioned in' comment in gitlab + "referenced", + "mentioned" + ] + end let(:api_client) { Runtime::API::Client.as_admin } @@ -25,79 +80,105 @@ module QA end end - let(:github_repo) { ENV['QA_LARGE_IMPORT_REPO'] || 'rspec/rspec-core' } - let(:import_max_duration) { ENV['QA_LARGE_IMPORT_DURATION'] ? ENV['QA_LARGE_IMPORT_DURATION'].to_i : 7200 } let(:github_client) do Octokit::Client.new( access_token: ENV['QA_LARGE_IMPORT_GH_TOKEN'] || Runtime::Env.github_access_token, - auto_paginate: true + auto_paginate: true, + middleware: Faraday::RackBuilder.new do |builder| + builder.use(Faraday::Retry::Middleware, exceptions: [Octokit::InternalServerError, Octokit::ServerError]) + end ) end let(:gh_repo) { github_client.repository(github_repo) } let(:gh_branches) do - logger.debug("= Fetching branches =") + logger.info("= Fetching branches =") github_client.branches(github_repo).map(&:name) end let(:gh_commits) do - logger.debug("= Fetching commits =") + logger.info("= Fetching commits =") github_client.commits(github_repo).map(&:sha) end let(:gh_labels) do - logger.debug("= Fetching labels =") + logger.info("= Fetching labels =") github_client.labels(github_repo).map { |label| { name: label.name, color: "##{label.color}" } } end let(:gh_milestones) do - logger.debug("= Fetching milestones =") + logger.info("= Fetching milestones =") github_client .list_milestones(github_repo, state: 'all') .map { |ms| { title: ms.title, description: ms.description } } end - let(:gh_all_issues) do - logger.debug("= Fetching issues and prs =") - github_client.list_issues(github_repo, state: 'all') - end - let(:gh_prs) do gh_all_issues.select(&:pull_request).each_with_object({}) do |pr, hash| - hash[pr.number] = { + id = pr.number + hash[id] = { url: pr.html_url, title: pr.title, body: pr.body || '', - comments: [*gh_pr_comments[pr.html_url], *gh_issue_comments[pr.html_url]].compact + comments: [*gh_pr_comments[id], *gh_issue_comments[id]].compact, + events: gh_pr_events[id].reject { |event| unsupported_events.include?(event) } } end end let(:gh_issues) do gh_all_issues.reject(&:pull_request).each_with_object({}) do |issue, hash| - hash[issue.number] = { + id = issue.number + hash[id] = { url: issue.html_url, title: issue.title, body: issue.body || '', - comments: gh_issue_comments[issue.html_url] + comments: gh_issue_comments[id], + events: gh_issue_events[id].reject { |event| unsupported_events.include?(event) } } end end + let(:gh_all_issues) do + logger.info("= Fetching issues and prs =") + github_client.list_issues(github_repo, state: 'all') + end + + let(:gh_all_events) do + logger.info("- Fetching issue and pr events -") + github_client.repository_issue_events(github_repo).map do |event| + { name: event[:event], **(event[:issue] || {}) } # some events don't have issue object at all + end + end + + let(:gh_issue_events) do + gh_all_events.each_with_object(Hash.new { |h, k| h[k] = [] }) do |event, hash| + next if event[:pull_request] || !event[:number] + + hash[event[:number]] << event[:name] + end + end + + let(:gh_pr_events) do + gh_all_events.each_with_object(Hash.new { |h, k| h[k] = [] }) do |event, hash| + next unless event[:pull_request] + + hash[event[:number]] << event[:name] + end + end + let(:gh_issue_comments) do - logger.debug("= Fetching issue comments =") + logger.info("- Fetching issue comments -") github_client.issues_comments(github_repo).each_with_object(Hash.new { |h, k| h[k] = [] }) do |c, hash| - # use base html url as key - hash[c.html_url.gsub(/\#\S+/, "")] << c.body&.gsub(gh_link_pattern, dummy_url) + hash[id_from_url(c.html_url)] << c.body&.gsub(gh_link_pattern, dummy_url) end end let(:gh_pr_comments) do - logger.debug("= Fetching pr comments =") + logger.info("- Fetching pr comments -") github_client.pull_requests_comments(github_repo).each_with_object(Hash.new { |h, k| h[k] = [] }) do |c, hash| - # use base html url as key - hash[c.html_url.gsub(/\#\S+/, "")] << c.body + hash[id_from_url(c.html_url)] << c.body # some suggestions can contain extra whitespaces which gitlab will remove &.gsub(/suggestion\s+\r/, "suggestion\r") &.gsub(gh_link_pattern, dummy_url) @@ -115,7 +196,6 @@ module QA end end - # rubocop:disable RSpec/InstanceVariable after do |example| next unless defined?(@import_time) @@ -164,7 +244,6 @@ module QA } ) end - # rubocop:enable RSpec/InstanceVariable it( 'imports large Github repo via api', @@ -172,17 +251,18 @@ module QA ) do start = Time.now - # import the project and log gitlab path - logger.info("== Importing project '#{github_repo}' in to '#{imported_project.reload!.full_path}' ==") # fetch all objects right after import has started fetch_github_objects + # import the project and log gitlab path + logger.info("== Importing project '#{github_repo}' in to '#{imported_project.reload!.full_path}' ==") + import_status = lambda do imported_project.project_import_status.yield_self do |status| @stats = status.dig(:stats, :imported) # fail fast if import explicitly failed - raise "Import of '#{imported_project.name}' failed!" if status[:import_status] == 'failed' + raise "Import of '#{imported_project.full_path}' failed!" if status[:import_status] == 'failed' status[:import_status] end @@ -294,7 +374,7 @@ module QA actual.each_with_object([]) do |(key, actual_item), missing_comments| expected_item = expected[key] title = actual_item[:title] - msg = "expected #{type} with title '#{title}' to have" + msg = "expected #{type} with iid '#{key}' to have" # Print title in the error message to see which object is missing # @@ -320,6 +400,14 @@ module QA expect(expected_comments.length).to eq(actual_comments.length), comment_count_msg expect(expected_comments).to match_array(actual_comments) + expected_events = expected_item[:events] + actual_events = actual_item[:events] + event_count_msg = <<~MSG + #{msg} same amount of events. Gitlab: #{expected_events.length}, Github: #{actual_events.length} + MSG + expect(expected_events.length).to eq(actual_events.length), event_count_msg + expect(expected_events).to match_array(actual_events) + # Save missing comments # comment_diff = actual_comments - expected_comments @@ -380,26 +468,27 @@ module QA def mrs @mrs ||= begin logger.debug("= Fetching merge requests =") - imported_mrs = imported_project.merge_requests(auto_paginate: true, attempts: 2) + imported_mrs = imported_project.merge_requests(**api_request_params) logger.debug("= Fetching merge request comments =") - Parallel.map(imported_mrs, in_threads: 4) do |mr| + Parallel.map(imported_mrs, in_threads: Etc.nprocessors) do |mr| resource = Resource::MergeRequest.init do |resource| resource.project = imported_project resource.iid = mr[:iid] resource.api_client = api_client end - logger.debug("Fetching comments for mr '#{mr[:title]}'") - comments = resource - .comments(auto_paginate: true, attempts: 2) - .reject { |c| c[:system] || c[:body].match?(/^(\*\*Review:\*\*)|(\*Merged by:).*/) } + logger.debug("Fetching events and comments for mr '!#{mr[:iid]}'") + comments = resource.comments(**api_request_params) + label_events = resource.label_events(**api_request_params) + state_events = resource.state_events(**api_request_params) + milestone_events = resource.milestone_events(**api_request_params) [mr[:iid], { url: mr[:web_url], title: mr[:title], body: sanitize_description(mr[:description]) || '', - events: events(comments), + events: events(comments, label_events, state_events, milestone_events), comments: non_event_comments(comments) }] end.to_h @@ -412,48 +501,59 @@ module QA def gl_issues @gl_issues ||= begin logger.debug("= Fetching issues =") - imported_issues = imported_project.issues(auto_paginate: true, attempts: 2) + imported_issues = imported_project.issues(**api_request_params) logger.debug("= Fetching issue comments =") - Parallel.map(imported_issues, in_threads: 4) do |issue| + Parallel.map(imported_issues, in_threads: Etc.nprocessors) do |issue| resource = Resource::Issue.init do |issue_resource| issue_resource.project = imported_project issue_resource.iid = issue[:iid] issue_resource.api_client = api_client end - logger.debug("Fetching comments for issue '#{issue[:title]}'") - comments = resource.comments(auto_paginate: true, attempts: 2) + logger.debug("Fetching events and comments for issue '!#{issue[:iid]}'") + comments = resource.comments(**api_request_params) + label_events = resource.label_events(**api_request_params) + state_events = resource.state_events(**api_request_params) + milestone_events = resource.milestone_events(**api_request_params) [issue[:iid], { url: issue[:web_url], title: issue[:title], body: sanitize_description(issue[:description]) || '', - events: events(comments), + events: events(comments, label_events, state_events, milestone_events), comments: non_event_comments(comments) }] end.to_h end end - # Fetch comments without events + # Filter out event comments # # @param [Array] comments # @return [Array] def non_event_comments(comments) comments - .reject { |c| c[:body].match?(event_pattern) } + .reject { |c| c[:system] || c[:body].match?(event_pattern) } .map { |c| sanitize_comment(c[:body]) } end # Events # # @param [Array] comments + # @param [Array] label_events + # @param [Array] state_events + # @param [Array] milestone_events # @return [Array] - def events(comments) - comments - .select { |c| c[:body].match?(event_pattern) } - .map { |c| c[:body] } + def events(comments, label_events, state_events, milestone_events) + mapped_label_events = label_events.map { |event| event_mapping["label_#{event[:action]}"] } + mapped_milestone_events = milestone_events.map { |event| event_mapping["milestone_#{event[:action]}"] } + mapped_state_event = state_events.map { |event| event[:state] } + mapped_comment_events = comments.map do |c| + event_mapping[c[:body].match(event_pattern)&.named_captures&.fetch("event", nil)] + end + + [*mapped_label_events, *mapped_milestone_events, *mapped_state_event, *mapped_comment_events].compact end # Normalize comments and make them directly comparable @@ -489,6 +589,16 @@ module QA def save_json(name, json) File.open("tmp/#{name}.json", "w") { |file| file.write(JSON.pretty_generate(json)) } end + + # Extract id number from web url of issue or pull request + # + # Some endpoints don't return object id as separate parameter so web url can be used as a workaround + # + # @param [String] url + # @return [Integer] + def id_from_url(url) + url.match(%r{(?<type>issues|pull)/(?<id>\d+)})&.named_captures&.fetch(:id, nil).to_i + end end end end diff --git a/qa/qa/specs/features/sanity/framework_spec.rb b/qa/qa/specs/features/sanity/framework_spec.rb index feec56478c0..fa34f525a85 100644 --- a/qa/qa/specs/features/sanity/framework_spec.rb +++ b/qa/qa/specs/features/sanity/framework_spec.rb @@ -6,7 +6,7 @@ module QA it 'succeeds' do Runtime::Browser.visit(:gitlab, Page::Main::Login) - expect(page).to have_text('A complete DevOps platform') + expect(page).to have_text('GitLab') end end |