gitlab_quality-test_tooling 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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