Commit 0fad83a2 authored by Mark Lapierre's avatar Mark Lapierre

Merge branch 'acunskis-large-import-improvements' into 'master'

E2E: large github import spec logging improvements

See merge request gitlab-org/gitlab!66810
parents 86d5d922 539f3e51
...@@ -22,8 +22,8 @@ gem 'rotp', '~> 3.1.0' ...@@ -22,8 +22,8 @@ gem 'rotp', '~> 3.1.0'
gem 'timecop', '~> 0.9.1' gem 'timecop', '~> 0.9.1'
gem 'parallel', '~> 1.19' gem 'parallel', '~> 1.19'
gem 'rspec-parameterized', '~> 0.4.2' gem 'rspec-parameterized', '~> 0.4.2'
gem "octokit", "~> 4.21" gem 'octokit', '~> 4.21'
gem "webdrivers", "~> 4.6" gem 'webdrivers', '~> 4.6'
gem 'chemlab', '~> 0.7' gem 'chemlab', '~> 0.7'
gem 'chemlab-library-www-gitlab-com', '~> 0.1' gem 'chemlab-library-www-gitlab-com', '~> 0.1'
......
...@@ -8,6 +8,9 @@ module QA ...@@ -8,6 +8,9 @@ module QA
# Only executes in custom job/pipeline # Only executes in custom job/pipeline
RSpec.describe 'Manage', :github, :requires_admin, only: { job: 'large-github-import' } do RSpec.describe 'Manage', :github, :requires_admin, only: { job: 'large-github-import' } do
describe 'Project import' do describe 'Project import' do
let(:logger) { Runtime::Logger.logger }
let(:differ) { RSpec::Support::Differ.new(color: true) }
let(:api_client) { Runtime::API::Client.as_admin } let(:api_client) { Runtime::API::Client.as_admin }
let(:group) do let(:group) do
Resource::Group.fabricate_via_api! do |resource| Resource::Group.fabricate_via_api! do |resource|
...@@ -22,12 +25,10 @@ module QA ...@@ -22,12 +25,10 @@ module QA
end end
end end
let(:differ) { RSpec::Support::Differ.new(color: true) } let(:github_repo) { 'rspec/rspec-core' }
let(:github_repo) { 'allure-framework/allure-ruby' }
let(:github_client) do let(:github_client) do
Octokit.middleware = Faraday::RackBuilder.new do |builder| Octokit.middleware = Faraday::RackBuilder.new do |builder|
builder.response(:logger, Runtime::Logger.logger, headers: false, bodies: false) builder.response(:logger, logger, headers: false, bodies: false)
end end
Octokit::Client.new(access_token: Runtime::Env.github_access_token, auto_paginate: true) Octokit::Client.new(access_token: Runtime::Env.github_access_token, auto_paginate: true)
...@@ -36,8 +37,16 @@ module QA ...@@ -36,8 +37,16 @@ module QA
let(:gh_branches) { github_client.branches(github_repo).map(&:name) } let(:gh_branches) { github_client.branches(github_repo).map(&:name) }
let(:gh_commits) { github_client.commits(github_repo).map(&:sha) } let(:gh_commits) { github_client.commits(github_repo).map(&:sha) }
let(:gh_repo) { github_client.repository(github_repo) } let(:gh_repo) { github_client.repository(github_repo) }
let(:gh_labels) { github_client.labels(github_repo) }
let(:gh_milestones) { github_client.list_milestones(github_repo, state: 'all') } let(:gh_labels) do
github_client.labels(github_repo).map { |label| { name: label.name, color: "##{label.color}" } }
end
let(:gh_milestones) do
github_client
.list_milestones(github_repo, state: 'all')
.map { |ms| { title: ms.title, description: ms.description } }
end
let(:gh_all_issues) do let(:gh_all_issues) do
github_client.list_issues(github_repo, state: 'all') github_client.list_issues(github_repo, state: 'all')
...@@ -88,6 +97,31 @@ module QA ...@@ -88,6 +97,31 @@ module QA
group.add_member(user, Resource::Members::AccessLevel::MAINTAINER) group.add_member(user, Resource::Members::AccessLevel::MAINTAINER)
end end
after do
# save data for comparison after run finished
save_json(
"data",
{
github: {
branches: gh_branches,
commits: gh_commits,
labels: gh_labels,
milestones: gh_milestones,
prs: gh_prs,
issues: gh_issues
},
gitlab: {
branches: gl_branches,
commits: gl_commits,
labels: gl_labels,
milestones: gl_milestones,
mrs: mrs,
issues: gl_issues
}
}.to_json
)
end
it 'imports large Github repo via api' do it 'imports large Github repo via api' do
imported_project # import the project imported_project # import the project
fetch_github_objects # fetch all objects right after import has started fetch_github_objects # fetch all objects right after import has started
...@@ -99,10 +133,10 @@ module QA ...@@ -99,10 +133,10 @@ module QA
aggregate_failures do aggregate_failures do
verify_repository_import verify_repository_import
verify_merge_requests_import
verify_issues_import
verify_labels_import verify_labels_import
verify_milestones_import verify_milestones_import
verify_merge_requests_import
verify_issues_import
end end
end end
...@@ -110,7 +144,7 @@ module QA ...@@ -110,7 +144,7 @@ module QA
# #
# @return [void] # @return [void]
def fetch_github_objects def fetch_github_objects
Runtime::Logger.debug("Fetching objects for github repo: '#{github_repo}'") logger.debug("Fetching objects for github repo: '#{github_repo}'")
gh_repo gh_repo
gh_branches gh_branches
...@@ -125,50 +159,44 @@ module QA ...@@ -125,50 +159,44 @@ module QA
# #
# @return [void] # @return [void]
def verify_repository_import def verify_repository_import
branches = imported_project.repository_branches(auto_paginate: true).map { |b| b[:name] } logger.debug("Verifying repository import")
commits = imported_project.commits(auto_paginate: true).map { |c| c[:id] }
expect(imported_project.description).to eq(gh_repo.description) expect(imported_project.description).to eq(gh_repo.description)
# check via include, importer creates more branches # check via include, importer creates more branches
# https://gitlab.com/gitlab-org/gitlab/-/issues/332711 # https://gitlab.com/gitlab-org/gitlab/-/issues/332711
expect(branches).to include(*gh_branches) expect(gl_branches).to include(*gh_branches)
expect(commits).to match_array(gh_commits) expect(gl_commits).to match_array(gh_commits)
end end
# Verify imported merge requests and mr issues # Verify imported merge requests and mr issues
# #
# @return [void] # @return [void]
def verify_merge_requests_import def verify_merge_requests_import
verify_mrs_or_issues('mrs') logger.debug("Verifying merge request import")
verify_mrs_or_issues('mr')
end end
# Verify imported issues and issue comments # Verify imported issues and issue comments
# #
# @return [void] # @return [void]
def verify_issues_import def verify_issues_import
verify_mrs_or_issues('issues') logger.debug("Verifying issue import")
verify_mrs_or_issues('issue')
end end
# Verify imported labels # Verify imported labels
# #
# @return [void] # @return [void]
def verify_labels_import def verify_labels_import
labels = imported_project.labels(auto_paginate: true).map { |label| label.slice(:name, :color) } logger.debug("Verifying label import")
actual_labels = gh_labels.map { |label| { name: label.name, color: "##{label.color}" } } expect(gl_labels).to match_array(gh_labels)
expect(labels.length).to eq(actual_labels.length)
expect(labels).to match_array(actual_labels)
end end
# Verify milestones import # Verify milestones import
# #
# @return [void] # @return [void]
def verify_milestones_import def verify_milestones_import
milestones = imported_project.milestones(auto_paginate: true).map { |ms| ms.slice(:title, :description) } logger.debug("Verifying milestones import")
actual_milestones = gh_milestones.map { |ms| { title: ms.title, description: ms.description } } expect(gl_milestones).to match_array(gh_milestones)
expect(milestones.length).to eq(actual_milestones.length)
expect(milestones).to match_array(actual_milestones)
end end
private private
...@@ -179,11 +207,17 @@ module QA ...@@ -179,11 +207,17 @@ module QA
# @return [void] # @return [void]
def verify_mrs_or_issues(type) def verify_mrs_or_issues(type)
msg = ->(title) { "expected #{type} with title '#{title}' to have" } msg = ->(title) { "expected #{type} with title '#{title}' to have" }
expected = type == 'mrs' ? mrs : gl_issues expected = type == 'mr' ? mrs : gl_issues
actual = type == 'mrs' ? gh_prs : gh_issues actual = type == 'mr' ? gh_prs : gh_issues
expect(expected.keys).to match_array(actual.keys) # Compare length to have easy to read overview how many objects are missing
expect(expected.length).to(
eq(actual.length),
"Expected to contain same amount of #{type}s. Expected: #{expected.length}, actual: #{actual.length}"
)
actual.each do |title, actual_item| actual.each do |title, actual_item|
logger.debug("Comparing #{type} with title '#{title}'")
expected_item = expected[title] expected_item = expected[title]
expect(expected_item).to be_truthy, "#{msg.call(title)} been imported" expect(expected_item).to be_truthy, "#{msg.call(title)} been imported"
...@@ -191,7 +225,7 @@ module QA ...@@ -191,7 +225,7 @@ module QA
expect(expected_item[:body]).to( expect(expected_item[:body]).to(
include(actual_item[:body]), include(actual_item[:body]),
"#{msg.call(title)} same description. #{diff(expected_item[:body], actual_item[:body])}" "#{msg.call(title)} same description. diff:\n#{differ.diff(expected_item[:body], actual_item[:body])}"
) )
expect(expected_item[:comments].length).to( expect(expected_item[:comments].length).to(
eq(actual_item[:comments].length), eq(actual_item[:comments].length),
...@@ -201,14 +235,44 @@ module QA ...@@ -201,14 +235,44 @@ module QA
end end
end end
# Imported project branches
#
# @return [Array]
def gl_branches
@gl_branches ||= imported_project.repository_branches(auto_paginate: true).map { |b| b[:name] }
end
# Imported project commits
#
# @return [Array]
def gl_commits
@gl_commits ||= imported_project.commits(auto_paginate: true).map { |c| c[:id] }
end
# Imported project labels
#
# @return [Array]
def gl_labels
@gl_labels ||= imported_project.labels(auto_paginate: true).map { |label| label.slice(:name, :color) }
end
# Imported project milestones
#
# @return [<Type>] <description>
def gl_milestones
@gl_milestones ||= imported_project.milestones(auto_paginate: true).map { |ms| ms.slice(:title, :description) }
end
# Imported project merge requests # Imported project merge requests
# #
# @return [Hash] # @return [Hash]
def mrs def mrs
@mrs ||= begin @mrs ||= begin
logger.debug("Fetching merge requests")
imported_mrs = imported_project.merge_requests(auto_paginate: true) imported_mrs = imported_project.merge_requests(auto_paginate: true)
# fetch comments in parallel since we need to do it for each mr separately # fetch comments in parallel since we need to do it for each mr separately
mrs_hashes = Parallel.map(imported_mrs, in_processes: 5) do |mr| logger.debug("Transforming merge request objects for comparison")
mrs_hashes = Parallel.map(imported_mrs) do |mr|
resource = Resource::MergeRequest.init do |resource| resource = Resource::MergeRequest.init do |resource|
resource.project = imported_project resource.project = imported_project
resource.iid = mr[:iid] resource.iid = mr[:iid]
...@@ -239,9 +303,11 @@ module QA ...@@ -239,9 +303,11 @@ module QA
# @return [Hash] # @return [Hash]
def gl_issues def gl_issues
@gl_issues ||= begin @gl_issues ||= begin
logger.debug("Fetching issues")
imported_issues = imported_project.issues(auto_paginate: true) imported_issues = imported_project.issues(auto_paginate: true)
# fetch comments in parallel since we need to do it for each mr separately # fetch comments in parallel since we need to do it for each mr separately
issue_hashes = Parallel.map(imported_issues, in_processes: 5) do |issue| logger.debug("Transforming issue objects for comparison")
issue_hashes = Parallel.map(imported_issues) do |issue|
resource = Resource::Issue.init do |issue_resource| resource = Resource::Issue.init do |issue_resource|
issue_resource.project = imported_project issue_resource.project = imported_project
issue_resource.iid = issue[:iid] issue_resource.iid = issue[:iid]
...@@ -272,13 +338,13 @@ module QA ...@@ -272,13 +338,13 @@ module QA
body.gsub(/\*Created by: \S+\*\n\n/, "") body.gsub(/\*Created by: \S+\*\n\n/, "")
end end
# Diff of 2 objects # Save json as file
# #
# @param [Object] actual # @param [String] name
# @param [Object] expected # @param [String] json
# @return [String] # @return [void]
def diff(actual, expected) def save_json(name, json)
"diff:\n#{differ.diff(actual, expected)}" File.open("tmp/#{name}.json", "w") { |file| file.write(json) }
end end
end end
end end
......
...@@ -53,11 +53,15 @@ module Matchers ...@@ -53,11 +53,15 @@ module Matchers
# @param [Symbol] expectation_name # @param [Symbol] expectation_name
# @return [Boolean] # @return [Boolean]
def wait_and_check(actual, expectation_name) def wait_and_check(actual, expectation_name)
attempt = 0
QA::Support::Retrier.retry_until( QA::Support::Retrier.retry_until(
max_attempts: @attempts, max_attempts: @attempts,
max_duration: @duration, max_duration: @duration,
sleep_interval: @interval || 0.5 sleep_interval: @interval || 0.5
) do ) do
QA::Runtime::Logger.debug("Evaluating expectation '#{operator_msg}', attempt: #{attempt += 1}")
public_send(expectation_name, actual) public_send(expectation_name, actual)
rescue RSpec::Expectations::ExpectationNotMetError, QA::Resource::ApiFabricator::ResourceNotFoundError rescue RSpec::Expectations::ExpectationNotMetError, QA::Resource::ApiFabricator::ResourceNotFoundError
false false
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment