gitlab_quality-test_tooling 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +9 -4
  3. data/Gemfile.lock +1 -1
  4. data/Guardfile +0 -22
  5. data/exe/generate-test-session +49 -0
  6. data/exe/post-to-slack +57 -0
  7. data/exe/prepare-stage-reports +38 -0
  8. data/exe/relate-failure-issue +59 -0
  9. data/exe/report-results +56 -0
  10. data/exe/update-screenshot-paths +38 -0
  11. data/lib/gitlab_quality/test_tooling/gitlab_issue_client.rb +194 -0
  12. data/lib/gitlab_quality/test_tooling/gitlab_issue_dry_client.rb +26 -0
  13. data/lib/gitlab_quality/test_tooling/report/concerns/find_set_dri.rb +51 -0
  14. data/lib/gitlab_quality/test_tooling/report/concerns/results_reporter.rb +75 -0
  15. data/lib/gitlab_quality/test_tooling/report/concerns/utils.rb +49 -0
  16. data/lib/gitlab_quality/test_tooling/report/generate_test_session.rb +275 -0
  17. data/lib/gitlab_quality/test_tooling/report/prepare_stage_reports.rb +79 -0
  18. data/lib/gitlab_quality/test_tooling/report/relate_failure_issue.rb +377 -0
  19. data/lib/gitlab_quality/test_tooling/report/report_as_issue.rb +134 -0
  20. data/lib/gitlab_quality/test_tooling/report/report_results.rb +83 -0
  21. data/lib/gitlab_quality/test_tooling/report/results_in_issues.rb +130 -0
  22. data/lib/gitlab_quality/test_tooling/report/results_in_testcases.rb +113 -0
  23. data/lib/gitlab_quality/test_tooling/report/update_screenshot_path.rb +81 -0
  24. data/lib/gitlab_quality/test_tooling/runtime/env.rb +113 -0
  25. data/lib/gitlab_quality/test_tooling/runtime/logger.rb +92 -0
  26. data/lib/gitlab_quality/test_tooling/runtime/token_finder.rb +44 -0
  27. data/lib/gitlab_quality/test_tooling/slack/post_to_slack.rb +36 -0
  28. data/lib/gitlab_quality/test_tooling/summary_table.rb +41 -0
  29. data/lib/gitlab_quality/test_tooling/support/http_request.rb +34 -0
  30. data/lib/gitlab_quality/test_tooling/system_logs/finders/json_log_finder.rb +65 -0
  31. data/lib/gitlab_quality/test_tooling/system_logs/finders/rails/api_log_finder.rb +21 -0
  32. data/lib/gitlab_quality/test_tooling/system_logs/finders/rails/application_log_finder.rb +21 -0
  33. data/lib/gitlab_quality/test_tooling/system_logs/finders/rails/exception_log_finder.rb +21 -0
  34. data/lib/gitlab_quality/test_tooling/system_logs/finders/rails/graphql_log_finder.rb +21 -0
  35. data/lib/gitlab_quality/test_tooling/system_logs/log_types/log.rb +38 -0
  36. data/lib/gitlab_quality/test_tooling/system_logs/log_types/rails/api_log.rb +34 -0
  37. data/lib/gitlab_quality/test_tooling/system_logs/log_types/rails/application_log.rb +27 -0
  38. data/lib/gitlab_quality/test_tooling/system_logs/log_types/rails/exception_log.rb +23 -0
  39. data/lib/gitlab_quality/test_tooling/system_logs/log_types/rails/graphql_log.rb +30 -0
  40. data/lib/gitlab_quality/test_tooling/system_logs/shared_fields.rb +29 -0
  41. data/lib/gitlab_quality/test_tooling/system_logs/system_logs_formatter.rb +65 -0
  42. data/lib/gitlab_quality/test_tooling/test_results/base_test_results.rb +39 -0
  43. data/lib/gitlab_quality/test_tooling/test_results/builder.rb +35 -0
  44. data/lib/gitlab_quality/test_tooling/test_results/j_unit_test_results.rb +27 -0
  45. data/lib/gitlab_quality/test_tooling/test_results/json_test_results.rb +29 -0
  46. data/lib/gitlab_quality/test_tooling/test_results/test_result.rb +184 -0
  47. data/lib/gitlab_quality/test_tooling/version.rb +1 -1
  48. data/lib/gitlab_quality/test_tooling.rb +11 -2
  49. metadata +51 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 01e27d782b61b17f8388d031534df9096ed36ed3d1c2e94734fb22875f5a47f5
4
- data.tar.gz: a6df70390360e39d3b33df544c7e39d9838f16682763ed620120285886439d84
3
+ metadata.gz: 1826436013184c9312e5ba0bf45ed49eec2380743d81fa1afb31342559a799a2
4
+ data.tar.gz: d7fa98724fcf619d807ff4c61756ff119af3ad04c6b9f21306aaab59b872bcbe
5
5
  SHA512:
6
- metadata.gz: 58640b757e621d232e5951cde8fe76fa8f3df7dfd0d5dcae326e0e1546fba6337218176ba1af0ec451a8dc2ea6e0616fd5f8ce66e8c9ab4645889c7768cc0bcd
7
- data.tar.gz: e9a55097a4a8deb1a65f181e6674d89517a2a625012c0967ced81564a2cf52eff1529cdc58372a631f0d5bbb0b8684cb17b4518b22b92c77351a98e4d349deef
6
+ metadata.gz: 8c3bb098432bd3992ad6654c6540f75521e74648dd68c48be4b14962624ad835c199872cc1edf2d7a5173ec358ea3a629d612533853c2ad57a106c830b4c9ff6
7
+ data.tar.gz: 1d5adf1e7cc98306bfc3d1477aee4310ac6e267da9ed73a26203d9cdba45dd0a8b927c4ecd49f37543a8771c661eaeaa035105393957e288a62cd82165955be6
data/.rubocop.yml CHANGED
@@ -19,6 +19,12 @@ AllCops:
19
19
  MaxFilesInCache: 1_000_000
20
20
  NewCops: enable
21
21
 
22
+ CodeReuse/ActiveRecord:
23
+ Enabled: false
24
+
25
+ Gemspec/RequiredRubyVersion:
26
+ Enabled: false
27
+
22
28
  Gemspec/RequireMFA:
23
29
  Enabled: false
24
30
 
@@ -49,12 +55,11 @@ Style/ModuleFunction:
49
55
  Style/SignalException:
50
56
  Enabled: false
51
57
 
58
+ Layout/LineLength:
59
+ Max: 180
60
+
52
61
  Layout/SpaceBeforeFirstArg:
53
62
  Enabled: false
54
63
 
55
64
  RSpec/MultipleMemoizedHelpers:
56
65
  Enabled: false
57
-
58
- CodeReuse/ActiveRecord:
59
- Exclude:
60
- - 'spec/**/*_spec.rb'
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- gitlab_quality-test_tooling (0.1.0)
4
+ gitlab_quality-test_tooling (0.2.0)
5
5
  activesupport (~> 6.1)
6
6
  gitlab (~> 4.18.0)
7
7
  http (~> 5.0)
data/Guardfile CHANGED
@@ -42,28 +42,6 @@ guard :rspec, cmd: "bundle exec rspec" do
42
42
  ruby = dsl.ruby
43
43
  dsl.watch_spec_files_for(ruby.lib_files)
44
44
 
45
- # Rails files
46
- rails = dsl.rails(view_extensions: %w[erb haml slim])
47
- dsl.watch_spec_files_for(rails.app_files)
48
- dsl.watch_spec_files_for(rails.views)
49
-
50
- watch(rails.controllers) do |m|
51
- [
52
- rspec.spec.call("routing/#{m[1]}_routing"),
53
- rspec.spec.call("controllers/#{m[1]}_controller"),
54
- rspec.spec.call("acceptance/#{m[1]}")
55
- ]
56
- end
57
-
58
- # Rails config changes
59
- watch(rails.spec_helper) { rspec.spec_dir }
60
- watch(rails.routes) { "#{rspec.spec_dir}/routing" }
61
- watch(rails.app_controller) { "#{rspec.spec_dir}/controllers" }
62
-
63
- # Capybara features specs
64
- watch(rails.view_dirs) { |m| rspec.spec.call("features/#{m[1]}") }
65
- watch(rails.layouts) { |m| rspec.spec.call("features/#{m[1]}") }
66
-
67
45
  # Turnip features and steps
68
46
  watch(%r{^spec/acceptance/(.+)\.feature$})
69
47
  watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) do |m|
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "optparse"
6
+
7
+ require_relative "../lib/gitlab_quality/test_tooling"
8
+
9
+ params = {}
10
+
11
+ options = OptionParser.new do |opts|
12
+ opts.banner = "Usage: #{$PROGRAM_NAME} [options]"
13
+
14
+ opts.on('-i', '--input-files FILES', String, 'Generate test session report') do |input_files|
15
+ params[:input_files] = input_files
16
+ end
17
+
18
+ opts.on('-p', '--project PROJECT', String, 'Can be an integer or a group/project string') do |project|
19
+ params[:project] = project
20
+ end
21
+
22
+ opts.on('-t', '--token ACCESS_TOKEN', String, 'A valid access token with Reporter permission in PROJECT') do |token|
23
+ params[:token] = GitlabQuality::TestTooling::Runtime::TokenFinder.find_token!(token)
24
+ end
25
+
26
+ opts.on('--dry-run', "Perform a dry-run (don't create or update issues or test cases)") do
27
+ params[:dry_run] = true
28
+ end
29
+
30
+ opts.on_tail('-v', '--version', 'Show the version') do
31
+ require_relative "../lib/gitlab_quality/test_tooling/version"
32
+ puts "#{$PROGRAM_NAME} : #{GitlabQuality::TestTooling::VERSION}"
33
+ exit
34
+ end
35
+
36
+ opts.on_tail('-h', '--help', 'Show the usage') do
37
+ puts opts
38
+ exit
39
+ end
40
+
41
+ opts.parse(ARGV)
42
+ end
43
+
44
+ if params.any?
45
+ GitlabQuality::TestTooling::Report::GenerateTestSession.new(**params).invoke!
46
+ else
47
+ puts options
48
+ exit 1
49
+ end
data/exe/post-to-slack ADDED
@@ -0,0 +1,57 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "optparse"
6
+
7
+ require_relative "../lib/gitlab_quality/test_tooling"
8
+
9
+ params = {}
10
+
11
+ options = OptionParser.new do |opts|
12
+ opts.banner = "Usage: #{$PROGRAM_NAME} [options]"
13
+
14
+ opts.on('-w', '--slack-webhook-url SLACK_WEBHOOK_URL', String, 'Slack webhook URL') do |slack_webhook_url|
15
+ params[:slack_webhook_url] = slack_webhook_url
16
+ end
17
+
18
+ opts.on('-c', '--channel CHANNEL', String, 'Slack channel to post the message to') do |channel|
19
+ params[:channel] = channel
20
+ end
21
+
22
+ opts.on('-m', '--message MESSAGE', String, 'Post message to Slack') do |message|
23
+ params[:message] = message
24
+ end
25
+
26
+ opts.on('-t', '--include-summary-table FILES', String, 'Create a results summary table to post to Slack') do |files|
27
+ params[:message] += "\n\n#{GitlabQuality::TestTooling::SummaryTable.create(input_files: files)}"
28
+ end
29
+
30
+ opts.on('-u', '--username USERNAME', String, 'Username to use for the Slack message') do |username|
31
+ params[:username] = username
32
+ end
33
+
34
+ opts.on('-i', '--icon-emoji ICON_EMOJI', String, 'Icon emoji to use for the Slack message') do |icon_emoji|
35
+ params[:icon_emoji] = icon_emoji
36
+ end
37
+
38
+ opts.on_tail('-v', '--version', 'Show the version') do
39
+ require_relative "../lib/gitlab_quality/test_tooling/version"
40
+ puts "#{$PROGRAM_NAME} : #{GitlabQuality::TestTooling::VERSION}"
41
+ exit
42
+ end
43
+
44
+ opts.on_tail('-h', '--help', 'Show the usage') do
45
+ puts opts
46
+ exit
47
+ end
48
+
49
+ opts.parse(ARGV)
50
+ end
51
+
52
+ if params.any?
53
+ GitlabQuality::TestTooling::Slack::PostToSlack.new(**params).invoke!
54
+ else
55
+ puts options
56
+ exit 1
57
+ end
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "optparse"
6
+
7
+ require_relative "../lib/gitlab_quality/test_tooling"
8
+
9
+ params = {}
10
+
11
+ options = OptionParser.new do |opts|
12
+ opts.banner = "Usage: #{$PROGRAM_NAME} [options]"
13
+
14
+ opts.on('-j', '--junit-files FILES', String,
15
+ 'Prepare separate reports for each Stage from the provided JUnit XML files') do |junit_files|
16
+ params[:junit_files] = junit_files
17
+ end
18
+
19
+ opts.on_tail('-v', '--version', 'Show the version') do
20
+ require_relative "../lib/gitlab_quality/test_tooling/version"
21
+ puts "#{$PROGRAM_NAME} : #{GitlabQuality::TestTooling::VERSION}"
22
+ exit
23
+ end
24
+
25
+ opts.on_tail('-h', '--help', 'Show the usage') do
26
+ puts opts
27
+ exit
28
+ end
29
+
30
+ opts.parse(ARGV)
31
+ end
32
+
33
+ if params.any?
34
+ GitlabQuality::TestTooling::Report::PrepareStageReports.new(**params).invoke!
35
+ else
36
+ puts options
37
+ exit 1
38
+ end
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "optparse"
6
+
7
+ require_relative "../lib/gitlab_quality/test_tooling"
8
+
9
+ params = {}
10
+
11
+ options = OptionParser.new do |opts|
12
+ opts.banner = "Usage: #{$PROGRAM_NAME} [options]"
13
+
14
+ opts.on('-i', '--input-files FILES', String,
15
+ 'Relate test failures to failure issues from RSpec JSON files') do |input_files|
16
+ params[:input_files] = input_files
17
+ end
18
+
19
+ opts.on('--max-diff-ratio DIFF_RATO', Float, 'Max stacktrace diff ratio for QA failure issues detection') do |value|
20
+ params[:max_diff_ratio] = value
21
+ end
22
+
23
+ opts.on('-p', '--project PROJECT', String, 'Can be an integer or a group/project string') do |project|
24
+ params[:project] = project
25
+ end
26
+
27
+ opts.on('-t', '--token ACCESS_TOKEN', String, 'A valid access token with Reporter permission in PROJECT') do |token|
28
+ params[:token] = GitlabQuality::TestTooling::Runtime::TokenFinder.find_token!(token)
29
+ end
30
+
31
+ opts.on('--system-log-files SYSTEM_LOG_FILES', String,
32
+ 'Include errors from system logs in failure issues') do |system_log_files|
33
+ params[:system_logs] = system_log_files
34
+ end
35
+
36
+ opts.on('--dry-run', "Perform a dry-run (don't create or update issues or test cases)") do
37
+ params[:dry_run] = true
38
+ end
39
+
40
+ opts.on_tail('-v', '--version', 'Show the version') do
41
+ require_relative "../lib/gitlab_quality/test_tooling/version"
42
+ puts "#{$PROGRAM_NAME} : #{GitlabQuality::TestTooling::VERSION}"
43
+ exit
44
+ end
45
+
46
+ opts.on_tail('-h', '--help', 'Show the usage') do
47
+ puts opts
48
+ exit
49
+ end
50
+
51
+ opts.parse(ARGV)
52
+ end
53
+
54
+ if params.any?
55
+ GitlabQuality::TestTooling::Report::RelateFailureIssue.new(**params).invoke!
56
+ else
57
+ puts options
58
+ exit 1
59
+ end
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "optparse"
6
+
7
+ require_relative "../lib/gitlab_quality/test_tooling"
8
+
9
+ params = {}
10
+
11
+ options = OptionParser.new do |opts|
12
+ opts.banner = "Usage: #{$PROGRAM_NAME} [options]"
13
+
14
+ opts.on('-i', '--input-files FILES', String,
15
+ 'Report test results from JUnit or JSON files in GitLab test cases and results issues') do |input_files|
16
+ params[:input_files] = input_files
17
+ end
18
+
19
+ opts.on('--test-case-project TEST_CASE_PROJECT', String,
20
+ 'Can be an integer or a group/project string') do |test_case_project|
21
+ params[:test_case_project] = test_case_project
22
+ end
23
+
24
+ opts.on('--results-issue-project RESULTS_ISSUE_PROJECT', String,
25
+ 'Can be an integer or a group/project string') do |results_issue_project|
26
+ params[:results_issue_project] = results_issue_project
27
+ end
28
+
29
+ opts.on('-t', '--token ACCESS_TOKEN', String, 'A valid access token') do |token|
30
+ params[:token] = GitlabQuality::TestTooling::Runtime::TokenFinder.find_token!(token)
31
+ end
32
+
33
+ opts.on('--dry-run', "Perform a dry-run (don't create or update issues or test cases)") do
34
+ params[:dry_run] = true
35
+ end
36
+
37
+ opts.on_tail('-v', '--version', 'Show the version') do
38
+ require_relative "../lib/gitlab_quality/test_tooling/version"
39
+ puts "#{$PROGRAM_NAME} : #{GitlabQuality::TestTooling::VERSION}"
40
+ exit
41
+ end
42
+
43
+ opts.on_tail('-h', '--help', 'Show the usage') do
44
+ puts opts
45
+ exit
46
+ end
47
+
48
+ opts.parse(ARGV)
49
+ end
50
+
51
+ if params.any?
52
+ GitlabQuality::TestTooling::Report::ReportResults.new(**params).invoke!
53
+ else
54
+ puts options
55
+ exit 1
56
+ end
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "optparse"
6
+
7
+ require_relative "../lib/gitlab_quality/test_tooling"
8
+
9
+ params = {}
10
+
11
+ options = OptionParser.new do |opts|
12
+ opts.banner = "Usage: #{$PROGRAM_NAME} [options]"
13
+
14
+ opts.on('--junit-files FILES', String,
15
+ "Update the path to screenshots to container's host from the provided JUnit XML files") do |_files|
16
+ params[:junit_files] = junit_files
17
+ end
18
+
19
+ opts.on_tail('-v', '--version', 'Show the version') do
20
+ require_relative "../lib/gitlab_quality/test_tooling/version"
21
+ puts "#{$PROGRAM_NAME} : #{GitlabQuality::TestTooling::VERSION}"
22
+ exit
23
+ end
24
+
25
+ opts.on_tail('-h', '--help', 'Show the usage') do
26
+ puts opts
27
+ exit
28
+ end
29
+
30
+ opts.parse(ARGV)
31
+ end
32
+
33
+ if params.any?
34
+ GitlabQuality::TestTooling::Report::UpdateScreenshotPath.new(**params).invoke!
35
+ else
36
+ puts options
37
+ exit 1
38
+ end
@@ -0,0 +1,194 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'gitlab'
4
+
5
+ module Gitlab
6
+ # Monkey patch the Gitlab client to use the correct API path and add required methods
7
+ class Client
8
+ def team_member(project, id)
9
+ get("/projects/#{url_encode(project)}/members/all/#{id}")
10
+ end
11
+
12
+ def issue_discussions(project, issue_id, options = {})
13
+ get("/projects/#{url_encode(project)}/issues/#{issue_id}/discussions", query: options)
14
+ end
15
+
16
+ def add_note_to_issue_discussion_as_thread(project, issue_id, discussion_id, options = {})
17
+ post("/projects/#{url_encode(project)}/issues/#{issue_id}/discussions/#{discussion_id}/notes", query: options)
18
+ end
19
+ end
20
+ end
21
+
22
+ module GitlabQuality
23
+ module TestTooling
24
+ # The GitLab client is used for API access: https://github.com/NARKOZ/gitlab
25
+ class GitlabIssueClient
26
+ MAINTAINER_ACCESS_LEVEL = 40
27
+ RETRY_BACK_OFF_DELAY = 60
28
+ MAX_RETRY_ATTEMPTS = 3
29
+
30
+ def initialize(token:, project:)
31
+ @token = token
32
+ @project = project
33
+ @retry_backoff = 0
34
+
35
+ configure_gitlab_client
36
+ end
37
+
38
+ def assert_user_permission!
39
+ handle_gitlab_client_exceptions do
40
+ user = Gitlab.user
41
+ member = Gitlab.team_member(project, user.id)
42
+
43
+ abort_not_permitted if member.access_level < MAINTAINER_ACCESS_LEVEL
44
+ end
45
+ rescue Gitlab::Error::NotFound
46
+ abort_not_permitted
47
+ end
48
+
49
+ def find_issues(iid: nil, options: {}, &select)
50
+ select ||= :itself
51
+
52
+ handle_gitlab_client_exceptions do
53
+ break [Gitlab.issue(project, iid)].select(&select) if iid
54
+
55
+ Gitlab.issues(project, options)
56
+ .auto_paginate
57
+ .select(&select)
58
+ end
59
+ end
60
+
61
+ def find_issue_discussions(iid:)
62
+ handle_gitlab_client_exceptions do
63
+ Gitlab.issue_discussions(project, iid, order_by: 'created_at', sort: 'asc').auto_paginate
64
+ end
65
+ end
66
+
67
+ def create_issue(title:, description:, labels:, issue_type: 'issue')
68
+ attrs = { issue_type: issue_type, description: description, labels: labels }
69
+
70
+ handle_gitlab_client_exceptions do
71
+ Gitlab.create_issue(project, title, attrs)
72
+ end
73
+ end
74
+
75
+ def edit_issue(iid:, options: {})
76
+ handle_gitlab_client_exceptions do
77
+ Gitlab.edit_issue(project, iid, options)
78
+ end
79
+ end
80
+
81
+ def find_issue_notes(iid:)
82
+ handle_gitlab_client_exceptions do
83
+ Gitlab.issue_notes(project, iid, order_by: 'created_at', sort: 'asc')&.auto_paginate
84
+ end
85
+ end
86
+
87
+ def create_issue_note(iid:, note:)
88
+ handle_gitlab_client_exceptions do
89
+ Gitlab.create_issue_note(project, iid, note)
90
+ end
91
+ end
92
+
93
+ def edit_issue_note(issue_iid:, note_id:, note:)
94
+ handle_gitlab_client_exceptions do
95
+ Gitlab.edit_issue_note(project, issue_iid, note_id, note)
96
+ end
97
+ end
98
+
99
+ def add_note_to_issue_discussion_as_thread(iid:, discussion_id:, body:)
100
+ handle_gitlab_client_exceptions do
101
+ Gitlab.add_note_to_issue_discussion_as_thread(project, iid, discussion_id, body: body)
102
+ end
103
+ end
104
+
105
+ def find_user_id(username:)
106
+ handle_gitlab_client_exceptions do
107
+ user = Gitlab.users(username: username)&.first
108
+ user['id'] unless user.nil?
109
+ end
110
+ end
111
+
112
+ def upload_file(file_fullpath:)
113
+ ignore_gitlab_client_exceptions do
114
+ Gitlab.upload_file(project, file_fullpath)
115
+ end
116
+ end
117
+
118
+ def ignore_gitlab_client_exceptions
119
+ yield
120
+ rescue StandardError, SystemCallError, OpenSSL::SSL::SSLError, Net::OpenTimeout, Net::ReadTimeout,
121
+ Gitlab::Error::Error => e
122
+ puts "Ignoring the following error: #{e}"
123
+ end
124
+
125
+ def handle_gitlab_client_exceptions # rubocop:disable Metrics/AbcSize
126
+ yield
127
+ rescue Gitlab::Error::NotFound
128
+ # This error could be raised in assert_user_permission!
129
+ # If so, we want it to terminate at that point
130
+ raise
131
+ rescue SystemCallError, OpenSSL::SSL::SSLError, Net::OpenTimeout, Net::ReadTimeout,
132
+ Gitlab::Error::InternalServerError, Gitlab::Error::Parsing => e
133
+ @retry_backoff += RETRY_BACK_OFF_DELAY
134
+
135
+ raise if @retry_backoff > RETRY_BACK_OFF_DELAY * MAX_RETRY_ATTEMPTS
136
+
137
+ warn_exception(e)
138
+ warn("Sleeping for #{@retry_backoff} seconds before retrying...")
139
+ sleep @retry_backoff
140
+
141
+ retry
142
+ rescue StandardError => e
143
+ pipeline = Runtime::Env.pipeline_from_project_name
144
+ channel = case pipeline
145
+ when "canary"
146
+ "qa-production"
147
+ when "staging-canary"
148
+ "qa-staging"
149
+ else
150
+ "qa-#{pipeline}"
151
+ end
152
+ error_msg = warn_exception(e)
153
+
154
+ return unless Runtime::Env.ci_commit_ref_name == Runtime::Env.default_branch
155
+
156
+ slack_options = {
157
+ slack_webhook_url: ENV.fetch('CI_SLACK_WEBHOOK_URL', nil),
158
+ channel: channel,
159
+ username: "GitLab QA Bot",
160
+ icon_emoji: ':ci_failing:',
161
+ message: <<~MSG
162
+ An unexpected error occurred while reporting test results in issues.
163
+ The error occurred in job: #{Runtime::Env.ci_job_url}
164
+ `#{error_msg}`
165
+ MSG
166
+ }
167
+ puts "Posting Slack message to channel: #{channel}"
168
+
169
+ GitlabQuality::TestTooling::Slack::PostToSlack.new(**slack_options).invoke!
170
+ end
171
+
172
+ private
173
+
174
+ attr_reader :token, :project
175
+
176
+ def configure_gitlab_client
177
+ Gitlab.configure do |config|
178
+ config.endpoint = Runtime::Env.gitlab_api_base
179
+ config.private_token = token
180
+ end
181
+ end
182
+
183
+ def abort_not_permitted
184
+ abort "You must have at least Reporter access to the project to use this feature."
185
+ end
186
+
187
+ def warn_exception(error)
188
+ error_msg = "#{error.class.name} #{error.message}"
189
+ warn(error_msg)
190
+ error_msg
191
+ end
192
+ end
193
+ end
194
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GitlabQuality
4
+ module TestTooling
5
+ class GitlabIssueDryClient < GitlabIssueClient
6
+ def create_issue(title:, description:, labels:, issue_type: 'issue')
7
+ attrs = { description: description, labels: labels }
8
+
9
+ puts "The following #{issue_type} would have been created:"
10
+ puts "project: #{project}, title: #{title}, attrs: #{attrs}"
11
+ end
12
+
13
+ def edit_issue(iid:, options: {})
14
+ puts "The #{project}##{iid} issue would have been updated with: #{options}"
15
+ end
16
+
17
+ def create_issue_note(iid:, note:)
18
+ puts "The following note would have been posted on #{project}##{iid} issue: #{note}"
19
+ end
20
+
21
+ def add_note_to_issue_discussion_as_thread(iid:, discussion_id:, body:)
22
+ puts "The following discussion note would have been posted on #{project}##{iid} (discussion #{discussion_id}) issue: #{body}"
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ module GitlabQuality
6
+ module TestTooling
7
+ module Report
8
+ module Concerns
9
+ module FindSetDri
10
+ def set_dri_via_group(product_group, test)
11
+ parse_json_with_sets
12
+ fetch_stage_sets(test)
13
+
14
+ return @sets.sample['username'] if @stage_sets.empty?
15
+
16
+ fetch_group_sets(product_group)
17
+
18
+ if @group_sets.empty?
19
+ @stage_sets.sample['username']
20
+ else
21
+ @group_sets.sample['username']
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def parse_json_with_sets
28
+ response = Support::HttpRequest.make_http_request(
29
+ url: 'https://gitlab-org.gitlab.io/gitlab-roulette/roulette.json'
30
+ )
31
+ @sets = JSON.parse(response.body).select { |user| user['role'].include?('software-engineer-in-test') }
32
+ end
33
+
34
+ def fetch_stage_sets(test)
35
+ @stage_sets = @sets.select do |user|
36
+ user['role'].include?(test.stage.split("_").map(&:capitalize).join(" "))
37
+ end
38
+ end
39
+
40
+ def fetch_group_sets(product_group)
41
+ @group_sets = @stage_sets.select do |user|
42
+ user['role'].include?(product_group.split("_").map do |word|
43
+ word == 'and' ? word : word.capitalize
44
+ end.join(" "))
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end