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

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'qa/qa/specs/features/api/1_manage/import_large_github_repo_spec.rb')
-rw-r--r--qa/qa/specs/features/api/1_manage/import_large_github_repo_spec.rb495
1 files changed, 0 insertions, 495 deletions
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
deleted file mode 100644
index e6b60a5b090..00000000000
--- a/qa/qa/specs/features/api/1_manage/import_large_github_repo_spec.rb
+++ /dev/null
@@ -1,495 +0,0 @@
-# frozen_string_literal: true
-
-# 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
- 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(: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
-
- let(:api_client) { Runtime::API::Client.as_admin }
-
- let(:user) do
- Resource::User.fabricate_via_api! do |resource|
- resource.api_client = api_client
- 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
- )
- end
-
- let(:gh_repo) { github_client.repository(github_repo) }
-
- let(:gh_branches) do
- logger.debug("= Fetching branches =")
- github_client.branches(github_repo).map(&:name)
- end
-
- let(:gh_commits) do
- logger.debug("= Fetching commits =")
- github_client.commits(github_repo).map(&:sha)
- end
-
- let(:gh_labels) do
- logger.debug("= Fetching labels =")
- github_client.labels(github_repo).map { |label| { name: label.name, color: "##{label.color}" } }
- end
-
- let(:gh_milestones) do
- logger.debug("= 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] = {
- url: pr.html_url,
- title: pr.title,
- body: pr.body || '',
- comments: [*gh_pr_comments[pr.html_url], *gh_issue_comments[pr.html_url]].compact
- }
- end
- end
-
- let(:gh_issues) do
- gh_all_issues.reject(&:pull_request).each_with_object({}) do |issue, hash|
- hash[issue.number] = {
- url: issue.html_url,
- title: issue.title,
- body: issue.body || '',
- comments: gh_issue_comments[issue.html_url]
- }
- end
- end
-
- 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|
- # 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|
- # 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
-
- let(:imported_project) do
- Resource::ProjectImportedFromGithub.fabricate_via_api! do |project|
- project.add_name_uuid = false
- project.name = 'imported-project'
- project.github_personal_access_token = Runtime::Env.github_access_token
- project.github_repository_path = github_repo
- project.personal_namespace = user.username
- project.api_client = api_client
- end
- end
-
- # rubocop:disable RSpec/InstanceVariable
- after do |example|
- next unless defined?(@import_time)
-
- # save data for comparison notification creation
- save_json(
- "data",
- {
- importer: :github,
- import_time: @import_time,
- errors: imported_project.project_import_status[:failed_relations],
- reported_stats: @stats,
- source: {
- name: "GitHub",
- project_name: github_repo,
- address: "https://github.com",
- data: {
- branches: gh_branches.length,
- commits: gh_commits.length,
- labels: gh_labels.length,
- milestones: gh_milestones.length,
- mrs: gh_prs.length,
- mr_comments: gh_prs.sum { |_k, v| v[:comments].length },
- issues: gh_issues.length,
- issue_comments: gh_issues.sum { |_k, v| v[:comments].length }
- }
- },
- target: {
- name: "GitLab",
- project_name: imported_project.path_with_namespace,
- address: gitlab_address,
- data: {
- branches: gl_branches.length,
- commits: gl_commits.length,
- labels: gl_labels.length,
- milestones: gl_milestones.length,
- mrs: mrs.length,
- mr_comments: mrs.sum { |_k, v| v[:comments].length },
- issues: gl_issues.length,
- issue_comments: gl_issues.sum { |_k, v| v[:comments].length }
- }
- },
- not_imported: {
- mrs: @mr_diff,
- issues: @issue_diff
- }
- }
- )
- end
- # rubocop:enable RSpec/InstanceVariable
-
- it(
- 'imports large Github repo via api',
- testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347668'
- ) 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_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'
-
- status[:import_status]
- end
- end
-
- logger.info("== Waiting for import to be finished ==")
- expect(import_status).to eventually_eq('finished').within(max_duration: import_max_duration, sleep_interval: 30)
-
- @import_time = Time.now - start
-
- aggregate_failures do
- verify_repository_import
- verify_labels_import
- verify_milestones_import
- verify_merge_requests_import
- verify_issues_import
- end
- end
-
- # Persist all objects from repository being imported
- #
- # @return [void]
- def fetch_github_objects
- logger.info("== Fetching github repo objects ==")
-
- gh_repo
- gh_branches
- gh_commits
- gh_labels
- gh_milestones
- gh_prs
- gh_issues
- end
-
- # Verify repository imported correctly
- #
- # @return [void]
- def verify_repository_import
- logger.info("== Verifying repository import ==")
- expect(imported_project.description).to eq(gh_repo.description)
- # check via include, importer creates more branches
- # https://gitlab.com/gitlab-org/gitlab/-/issues/332711
- expect(gl_branches).to include(*gh_branches)
- expect(gl_commits).to match_array(gh_commits)
- end
-
- # Verify imported labels
- #
- # @return [void]
- def verify_labels_import
- logger.info("== Verifying label import ==")
- # check via include, additional labels can be inherited from parent group
- expect(gl_labels).to include(*gh_labels)
- end
-
- # Verify milestones import
- #
- # @return [void]
- def verify_milestones_import
- logger.info("== Verifying milestones import ==")
- expect(gl_milestones).to match_array(gh_milestones)
- end
-
- # Verify imported merge requests and mr issues
- #
- # @return [void]
- def verify_merge_requests_import
- logger.info("== Verifying merge request import ==")
- @mr_diff = verify_mrs_or_issues('mr')
- end
-
- # Verify imported issues and issue comments
- #
- # @return [void]
- def verify_issues_import
- logger.info("== Verifying issue import ==")
- @issue_diff = verify_mrs_or_issues('issue')
- end
-
- private
-
- # Verify imported mrs or issues and return missing items
- #
- # @param [String] type verification object, 'mrs' or 'issues'
- # @return [Hash]
- def verify_mrs_or_issues(type)
- # Compare length to have easy to read overview how many objects are missing
- #
- expected = type == 'mr' ? mrs : gl_issues
- actual = type == 'mr' ? gh_prs : gh_issues
- count_msg = "Expected to contain same amount of #{type}s. Gitlab: #{expected.length}, Github: #{actual.length}"
- expect(expected.length).to eq(actual.length), count_msg
-
- missing_comments = verify_comments(type, actual, expected)
-
- {
- "#{type}s": (actual.keys - expected.keys).map { |it| actual[it].slice(:title, :url) },
- "#{type}_comments": missing_comments
- }
- end
-
- # Verify imported comments
- #
- # @param [String] type verification object, 'mrs' or 'issues'
- # @param [Hash] actual
- # @param [Hash] expected
- # @return [Hash]
- def verify_comments(type, actual, expected)
- 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"
-
- # Print title in the error message to see which object is missing
- #
- expect(expected_item).to be_truthy, "#{msg} been imported"
- next unless expected_item
-
- # Print difference in the description
- #
- expected_body = expected_item[:body]
- actual_body = actual_item[:body]
- body_msg = <<~MSG
- #{msg} same description. diff:\n#{differ.diff(expected_body, actual_body)}
- MSG
- expect(expected_body).to eq(actual_body), body_msg
-
- # Print amount difference first
- #
- expected_comments = expected_item[:comments]
- actual_comments = actual_item[:comments]
- comment_count_msg = <<~MSG
- #{msg} same amount of comments. Gitlab: #{expected_comments.length}, Github: #{actual_comments.length}
- MSG
- expect(expected_comments.length).to eq(actual_comments.length), comment_count_msg
- expect(expected_comments).to match_array(actual_comments)
-
- # Save missing comments
- #
- comment_diff = actual_comments - expected_comments
- next if comment_diff.empty?
-
- missing_comments << {
- title: title,
- github_url: actual_item[:url],
- gitlab_url: expected_item[:url],
- missing_comments: comment_diff
- }
- end
- end
-
- # Imported project branches
- #
- # @return [Array]
- def gl_branches
- @gl_branches ||= begin
- logger.debug("= Fetching branches =")
- imported_project.repository_branches(auto_paginate: true).map { |b| b[:name] }
- end
- end
-
- # Imported project commits
- #
- # @return [Array]
- def gl_commits
- @gl_commits ||= begin
- logger.debug("= Fetching commits =")
- imported_project.commits(auto_paginate: true, attempts: 2).map { |c| c[:id] }
- end
- end
-
- # Imported project labels
- #
- # @return [Array]
- def gl_labels
- @gl_labels ||= begin
- logger.debug("= Fetching labels =")
- imported_project.labels(auto_paginate: true).map { |label| label.slice(:name, :color) }
- end
- end
-
- # Imported project milestones
- #
- # @return [<Type>] <description>
- def gl_milestones
- @gl_milestones ||= begin
- logger.debug("= Fetching milestones =")
- imported_project.milestones(auto_paginate: true).map { |ms| ms.slice(:title, :description) }
- end
- end
-
- # Imported project merge requests
- #
- # @return [Hash]
- def mrs
- @mrs ||= begin
- logger.debug("= Fetching merge requests =")
- imported_mrs = imported_project.merge_requests(auto_paginate: true, attempts: 2)
-
- logger.debug("= Fetching merge request comments =")
- Parallel.map(imported_mrs, in_threads: 4) 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:).*/) }
-
- [mr[:iid], {
- url: mr[:web_url],
- title: mr[:title],
- body: sanitize_description(mr[:description]) || '',
- events: events(comments),
- comments: non_event_comments(comments)
- }]
- end.to_h
- end
- end
-
- # Imported project issues
- #
- # @return [Hash]
- def gl_issues
- @gl_issues ||= begin
- logger.debug("= Fetching issues =")
- imported_issues = imported_project.issues(auto_paginate: true, attempts: 2)
-
- logger.debug("= Fetching issue comments =")
- Parallel.map(imported_issues, in_threads: 4) 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)
-
- [issue[:iid], {
- url: issue[:web_url],
- title: issue[:title],
- body: sanitize_description(issue[:description]) || '',
- events: events(comments),
- comments: non_event_comments(comments)
- }]
- end.to_h
- end
- end
-
- # Fetch comments without events
- #
- # @param [Array] comments
- # @return [Array]
- def non_event_comments(comments)
- comments
- .reject { |c| c[:body].match?(event_pattern) }
- .map { |c| sanitize_comment(c[:body]) }
- end
-
- # Events
- #
- # @param [Array] comments
- # @return [Array]
- def events(comments)
- comments
- .select { |c| c[:body].match?(event_pattern) }
- .map { |c| c[:body] }
- end
-
- # Normalize comments and make them directly comparable
- #
- # * remove created by prefixes
- # * unify suggestion format
- # * replace github and gitlab urls - some of the links to objects get transformed to gitlab entities, some don't,
- # update all links to example.com for now
- #
- # @param [String] body
- # @return [String]
- def sanitize_comment(body)
- body
- .gsub(created_by_pattern, "")
- .gsub(suggestion_pattern, "suggestion\r")
- .gsub(gl_link_pattern, dummy_url)
- .gsub(gh_link_pattern, dummy_url)
- end
-
- # Remove created by prefix from descripion
- #
- # @param [String] body
- # @return [String]
- def sanitize_description(body)
- body&.gsub(created_by_pattern, "")
- end
-
- # Save json as file
- #
- # @param [String] name
- # @param [Hash] json
- # @return [void]
- def save_json(name, json)
- File.open("tmp/#{name}.json", "w") { |file| file.write(JSON.pretty_generate(json)) }
- end
- end
- end
-end
-# rubocop:enable Rails/Pluck