gitlab-qa 10.6.0 → 11.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +0 -12
  3. data/Gemfile.lock +1 -1
  4. data/lib/gitlab/qa/component/gitlab.rb +10 -9
  5. data/lib/gitlab/qa/runtime/env.rb +0 -40
  6. data/lib/gitlab/qa/scenario/test/integration/registry_with_cdn.rb +2 -2
  7. data/lib/gitlab/qa/version.rb +1 -1
  8. data/lib/gitlab/qa.rb +0 -1
  9. metadata +2 -37
  10. data/bin/slack +0 -14
  11. data/exe/gitlab-qa-report +0 -10
  12. data/lib/gitlab/qa/report/base_test_results.rb +0 -39
  13. data/lib/gitlab/qa/report/find_set_dri.rb +0 -43
  14. data/lib/gitlab/qa/report/generate_test_session.rb +0 -275
  15. data/lib/gitlab/qa/report/gitlab_issue_client.rb +0 -190
  16. data/lib/gitlab/qa/report/gitlab_issue_dry_client.rb +0 -28
  17. data/lib/gitlab/qa/report/j_unit_test_results.rb +0 -27
  18. data/lib/gitlab/qa/report/json_test_results.rb +0 -29
  19. data/lib/gitlab/qa/report/prepare_stage_reports.rb +0 -86
  20. data/lib/gitlab/qa/report/relate_failure_issue.rb +0 -374
  21. data/lib/gitlab/qa/report/report_as_issue.rb +0 -176
  22. data/lib/gitlab/qa/report/report_results.rb +0 -64
  23. data/lib/gitlab/qa/report/results_in_issues.rb +0 -126
  24. data/lib/gitlab/qa/report/results_in_testcases.rb +0 -111
  25. data/lib/gitlab/qa/report/results_reporter_shared.rb +0 -70
  26. data/lib/gitlab/qa/report/summary_table.rb +0 -43
  27. data/lib/gitlab/qa/report/test_result.rb +0 -184
  28. data/lib/gitlab/qa/report/update_screenshot_path.rb +0 -63
  29. data/lib/gitlab/qa/reporter.rb +0 -131
  30. data/lib/gitlab/qa/runtime/token_finder.rb +0 -44
  31. data/lib/gitlab/qa/slack/post_to_slack.rb +0 -30
  32. data/lib/gitlab/qa/system_logs/finders/json_log_finder.rb +0 -65
  33. data/lib/gitlab/qa/system_logs/finders/rails/api_log_finder.rb +0 -21
  34. data/lib/gitlab/qa/system_logs/finders/rails/application_log_finder.rb +0 -21
  35. data/lib/gitlab/qa/system_logs/finders/rails/exception_log_finder.rb +0 -21
  36. data/lib/gitlab/qa/system_logs/finders/rails/graphql_log_finder.rb +0 -21
  37. data/lib/gitlab/qa/system_logs/log_types/log.rb +0 -38
  38. data/lib/gitlab/qa/system_logs/log_types/rails/api_log.rb +0 -34
  39. data/lib/gitlab/qa/system_logs/log_types/rails/application_log.rb +0 -27
  40. data/lib/gitlab/qa/system_logs/log_types/rails/exception_log.rb +0 -23
  41. data/lib/gitlab/qa/system_logs/log_types/rails/graphql_log.rb +0 -30
  42. data/lib/gitlab/qa/system_logs/shared_fields.rb +0 -29
  43. data/lib/gitlab/qa/system_logs/system_logs_formatter.rb +0 -65
@@ -1,126 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Gitlab
4
- module QA
5
- module Report
6
- # Uses the API to create or update GitLab test result issues with the results of tests from RSpec report files.
7
- class ResultsInIssues < ReportAsIssue
8
- include ResultsReporterShared
9
-
10
- def initialize(**kwargs)
11
- super
12
- @issue_type = 'issue'
13
- end
14
-
15
- def get_related_issue(testcase, test)
16
- issue = find_linked_results_issue_by_iid(testcase, test)
17
- is_new = false
18
-
19
- if issue
20
- issue = update_issue_title(issue, test) if issue_title_needs_updating?(issue, test)
21
- else
22
- puts "No valid issue link found"
23
- issue = find_or_create_results_issue(test)
24
- is_new = true
25
- end
26
-
27
- [issue, is_new]
28
- end
29
-
30
- def update_issue(issue, test)
31
- new_labels = issue_labels(issue)
32
- new_labels |= ['Testcase Linked']
33
-
34
- labels_updated = update_labels(issue, test, new_labels)
35
- note_posted = note_status(issue, test)
36
-
37
- if labels_updated || note_posted
38
- puts "Issue updated: #{issue.web_url}"
39
- else
40
- puts "Test passed, no results issue update needed."
41
- end
42
- end
43
-
44
- private
45
-
46
- def find_linked_results_issue_by_iid(testcase, test)
47
- iid = issue_iid_from_testcase(testcase)
48
-
49
- return unless iid
50
-
51
- find_issue_by_iid(iid)
52
- end
53
-
54
- def find_or_create_results_issue(test)
55
- find_issue(test) || create_issue(test)
56
- end
57
-
58
- def issue_iid_from_testcase(testcase)
59
- results = testcase.description.partition(TEST_CASE_RESULTS_SECTION_TEMPLATE).last if testcase.description.include?(TEST_CASE_RESULTS_SECTION_TEMPLATE)
60
-
61
- return puts "No issue link found" unless results
62
-
63
- issue_iid = results.split('/').last
64
-
65
- issue_iid&.to_i
66
- end
67
-
68
- def note_status(issue, test)
69
- return false if test.skipped
70
- return false if test.failures.empty?
71
-
72
- note = note_content(test)
73
-
74
- gitlab.find_issue_discussions(iid: issue.iid).each do |discussion|
75
- return gitlab.add_note_to_issue_discussion_as_thread(iid: issue.iid, discussion_id: discussion.id, body: failure_summary) if new_note_matches_discussion?(note, discussion)
76
- end
77
-
78
- gitlab.create_issue_note(iid: issue.iid, note: note)
79
-
80
- true
81
- end
82
-
83
- def note_content(test)
84
- errors = test.failures.each_with_object([]) do |failure, text|
85
- text << <<~TEXT
86
- Error:
87
- ```
88
- #{failure['message']}
89
- ```
90
-
91
- Stacktrace:
92
- ```
93
- #{failure['stacktrace']}
94
- ```
95
- TEXT
96
- end.join("\n\n")
97
-
98
- "#{failure_summary}\n\n#{errors}"
99
- end
100
-
101
- def failure_summary
102
- summary = [":x: ~\"#{pipeline}::failed\""]
103
- summary << "in job `#{Runtime::Env.ci_job_name}` in #{Runtime::Env.ci_job_url}"
104
- summary.join(' ')
105
- end
106
-
107
- def new_note_matches_discussion?(note, discussion)
108
- note_error = error_and_stack_trace(note)
109
- discussion_error = error_and_stack_trace(discussion.notes.first['body'])
110
-
111
- return false if note_error.empty? || discussion_error.empty?
112
-
113
- note_error == discussion_error
114
- end
115
-
116
- def error_and_stack_trace(text)
117
- text.strip[/Error:(.*)/m, 1].to_s
118
- end
119
-
120
- def updated_description(issue, test)
121
- new_issue_description(test)
122
- end
123
- end
124
- end
125
- end
126
- end
@@ -1,111 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'erb'
4
-
5
- module Gitlab
6
- module QA
7
- module Report
8
- # Uses the API to create or update GitLab test cases with the results of tests from RSpec report files.
9
- class ResultsInTestCases < ReportAsIssue
10
- attr_reader :issue_type, :gitlab
11
-
12
- include ResultsReporterShared
13
-
14
- def initialize(**kwargs)
15
- super
16
- @issue_type = 'test_case'
17
- end
18
-
19
- def find_or_create_testcase(test)
20
- find_testcase(test) || create_issue(test)
21
- end
22
-
23
- def add_issue_link_to_testcase(testcase, issue, test)
24
- results_section = testcase.description.include?(TEST_CASE_RESULTS_SECTION_TEMPLATE) ? '' : TEST_CASE_RESULTS_SECTION_TEMPLATE
25
-
26
- gitlab.edit_issue(iid: testcase.iid, options: { description: (testcase.description + results_section + "\n\n#{issue.web_url}") })
27
- # We are using test.testcase for the url here instead of testcase.web_url since it has the updated test case path
28
- puts "Added results issue #{issue.web_url} link to test case #{test.testcase}"
29
- end
30
-
31
- def update_testcase(testcase, test)
32
- puts "Test case labels updated." if update_labels(testcase, test)
33
- puts "Test case quarantine section updated." if update_quarantine_link(testcase, test)
34
- end
35
-
36
- private
37
-
38
- def find_testcase(test)
39
- testcase = find_testcase_by_iid(test)
40
-
41
- if testcase
42
- testcase = update_issue_title(testcase, test) if issue_title_needs_updating?(testcase, test)
43
- else
44
- testcase = find_issue(test)
45
- end
46
-
47
- testcase
48
- end
49
-
50
- def find_testcase_by_iid(test)
51
- iid = testcase_iid_from_url(test.testcase)
52
-
53
- return unless iid
54
-
55
- find_issue_by_iid(iid)
56
- end
57
-
58
- def testcase_iid_from_url(url)
59
- return warn(%(\nPlease update #{url} to test case url")) if url&.include?('/-/issues/')
60
-
61
- url && url.split('/').last.to_i
62
- end
63
-
64
- def new_issue_description(test)
65
- quarantine_section = test.quarantine? && test.quarantine_issue ? "\n\n### Quarantine issue\n\n#{test.quarantine_issue}" : ''
66
-
67
- "#{super}#{quarantine_section}\n\n#{execution_graph_section(test)}"
68
- end
69
-
70
- def execution_graph_section(test)
71
- formatted_title = ERB::Util.url_encode(test.name)
72
-
73
- <<~MKDOWN.strip
74
- ### Executions
75
-
76
- All Environments:
77
- <img src="https://dashboards.quality.gitlab.net/render/d-solo/cW0UMgv7k/spec-health?orgId=1&var-run_type=All&var-name=#{formatted_title}&panelId=4&width=1000&height=500" />
78
- MKDOWN
79
- end
80
-
81
- def updated_description(testcase, test)
82
- historical_results_section = testcase.description.match(/### DO NOT EDIT BELOW THIS LINE[\s\S]+/)
83
-
84
- "#{new_issue_description(test)}\n\n#{historical_results_section}"
85
- end
86
-
87
- def issue_title_needs_updating?(testcase, test)
88
- super || !testcase.description.include?(execution_graph_section(test)) && !%w[canary production preprod release].include?(pipeline)
89
- end
90
-
91
- def quarantine_link_needs_updating?(testcase, test)
92
- if test.quarantine? && test.quarantine_issue
93
- return false if testcase.description.include?(test.quarantine_issue)
94
- else
95
- return false unless testcase.description.include?('Quarantine issue')
96
- end
97
-
98
- true
99
- end
100
-
101
- def update_quarantine_link(testcase, test)
102
- return unless quarantine_link_needs_updating?(testcase, test)
103
-
104
- new_description = updated_description(testcase, test)
105
-
106
- gitlab.edit_issue(iid: testcase.iid, options: { description: new_description })
107
- end
108
- end
109
- end
110
- end
111
- end
@@ -1,70 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'active_support/core_ext/enumerable'
4
-
5
- module Gitlab
6
- module QA
7
- module Report
8
- module ResultsReporterShared
9
- TEST_CASE_RESULTS_SECTION_TEMPLATE = "\n\n### DO NOT EDIT BELOW THIS LINE\n\nActive and historical test results:"
10
-
11
- def find_issue(test)
12
- issues = search_for_issues(test)
13
-
14
- warn(%(Too many #{issue_type}s found with the file path "#{test.file}" and name "#{test.name}")) if issues.many?
15
- puts "Found existing #{issue_type}: #{issues.first.web_url}" unless issues.empty?
16
-
17
- issues.first
18
- end
19
-
20
- def find_issue_by_iid(iid)
21
- issues = gitlab.find_issues(iid: iid) do |issue|
22
- issue.state == 'opened' && issue.issue_type == issue_type
23
- end
24
-
25
- warn(%(#{issue_type} iid "#{iid}" not valid)) if issues.empty?
26
-
27
- issues.first
28
- end
29
-
30
- def issue_title_needs_updating?(issue, test)
31
- issue.title.strip != title_from_test(test) && !%w[canary production preprod release].include?(pipeline)
32
- end
33
-
34
- def new_issue_labels(test)
35
- %w[Quality status::automated]
36
- end
37
-
38
- def search_term(test)
39
- %("#{partial_file_path(test.file)}" "#{search_safe(test.name)}")
40
- end
41
-
42
- def up_to_date_labels(test:, issue: nil, new_labels: Set.new)
43
- labels = super
44
- labels |= new_issue_labels(test).to_set
45
- labels.delete_if { |label| label.start_with?("#{pipeline}::") }
46
- labels << (test.failures.empty? ? "#{pipeline}::passed" : "#{pipeline}::failed")
47
- end
48
-
49
- def update_issue_title(issue, test)
50
- old_title = issue.title.strip
51
- new_title = title_from_test(test)
52
-
53
- warn(%(#{issue_type} title needs to be updated from '#{old_title}' to '#{new_title}'))
54
-
55
- new_description = updated_description(issue, test)
56
-
57
- gitlab.edit_issue(iid: issue.iid, options: { title: new_title, description: new_description })
58
- end
59
-
60
- private
61
-
62
- def search_for_issues(test)
63
- gitlab.find_issues(options: { search: search_term(test) }) do |issue|
64
- issue.state == 'opened' && issue.issue_type == issue_type && issue.title.strip == title_from_test(test)
65
- end
66
- end
67
- end
68
- end
69
- end
70
- end
@@ -1,43 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'nokogiri'
4
- require 'table_print'
5
-
6
- module Gitlab
7
- module QA
8
- module Report
9
- class SummaryTable
10
- def self.create(input_files:)
11
- "```\n#{TablePrint::Printer.table_print(collect_results(input_files))}```\n"
12
- end
13
-
14
- # rubocop:disable Metrics/AbcSize
15
- def self.collect_results(input_files)
16
- stage_wise_results = []
17
-
18
- Dir.glob(input_files).each do |report_file|
19
- stage_hash = {}
20
- stage_hash["Dev Stage"] = File.basename(report_file, ".*").capitalize
21
-
22
- report_stats = Nokogiri::XML(File.open(report_file)).children[0].attributes
23
-
24
- stage_hash["Total"] = report_stats["tests"].value
25
- stage_hash["Failures"] = report_stats["failures"].value
26
- stage_hash["Errors"] = report_stats["errors"].value
27
- stage_hash["Skipped"] = report_stats["skipped"].value
28
- stage_hash["Result"] = result_emoji(report_stats)
29
-
30
- stage_wise_results << stage_hash
31
- end
32
-
33
- stage_wise_results
34
- end
35
- # rubocop:enable Metrics/AbcSize
36
-
37
- def self.result_emoji(report_stats)
38
- report_stats["failures"].value.to_i.positive? || report_stats["errors"].value.to_i.positive? ? "❌" : "✅"
39
- end
40
- end
41
- end
42
- end
43
- end
@@ -1,184 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'active_support/core_ext/object/blank'
4
-
5
- module Gitlab
6
- module QA
7
- module Report
8
- class TestResult
9
- def self.from_json(report)
10
- JsonTestResult.new(report)
11
- end
12
-
13
- def self.from_junit(report)
14
- JUnitTestResult.new(report)
15
- end
16
-
17
- attr_accessor :report, :failures
18
-
19
- def initialize(report)
20
- self.report = report
21
- self.failures = failures_from_exceptions
22
- end
23
-
24
- def stage
25
- @stage ||= file[%r{(?:api|browser_ui)/(?:(?:\d+_)?(\w+))}, 1]
26
- end
27
-
28
- def name
29
- raise NotImplementedError
30
- end
31
-
32
- def file
33
- raise NotImplementedError
34
- end
35
-
36
- def skipped
37
- raise NotImplementedError
38
- end
39
-
40
- private
41
-
42
- def failures_from_exceptions
43
- raise NotImplementedError
44
- end
45
-
46
- class JsonTestResult < TestResult
47
- def name
48
- report['full_description']
49
- end
50
-
51
- def file
52
- report['file_path']
53
- end
54
-
55
- def status
56
- report['status']
57
- end
58
-
59
- def ci_job_url
60
- report['ci_job_url']
61
- end
62
-
63
- def skipped
64
- status == 'pending'
65
- end
66
-
67
- def testcase
68
- report['testcase']
69
- end
70
-
71
- def testcase=(new_testcase)
72
- report['testcase'] = new_testcase
73
- end
74
-
75
- def failure_issue
76
- report['failure_issue']
77
- end
78
-
79
- def failure_issue=(new_failure_issue)
80
- report['failure_issue'] = new_failure_issue
81
- end
82
-
83
- def quarantine?
84
- # The value for 'quarantine' could be nil, a hash, a string,
85
- # or true (if the test just has the :quarantine tag)
86
- # But any non-nil or false value should means the test is in quarantine
87
- report['quarantine'].present?
88
- end
89
-
90
- def quarantine_type
91
- report['quarantine']['type'] if quarantine?
92
- end
93
-
94
- def quarantine_issue
95
- report['quarantine']['issue'] if quarantine?
96
- end
97
-
98
- def screenshot?
99
- report['screenshot'].present?
100
- end
101
-
102
- def failure_screenshot
103
- report['screenshot']['image'] if screenshot?
104
- end
105
-
106
- def product_group?
107
- report['product_group'].present?
108
- end
109
-
110
- def product_group
111
- report['product_group'] if product_group?
112
- end
113
-
114
- private
115
-
116
- # rubocop:disable Metrics/AbcSize
117
- def failures_from_exceptions
118
- return [] unless report.key?('exceptions')
119
-
120
- report['exceptions'].map do |exception|
121
- spec_file_first_index = exception['backtrace'].rindex do |line|
122
- line.include?(File.basename(report['file_path']))
123
- end
124
-
125
- exception['message'].gsub!(/(private_token=)[\w-]+/, '********')
126
- Array(exception['message_lines']).each { |line| line.gsub!(/(private_token=)([\w-]+)/, '********') }
127
-
128
- {
129
- 'message' => "#{exception['class']}: #{exception['message']}",
130
- 'message_lines' => exception['message_lines'],
131
- 'stacktrace' => "#{format_message_lines(exception['message_lines'])}\n#{exception['backtrace'].slice(0..spec_file_first_index).join("\n")}",
132
- 'correlation_id' => exception['correlation_id']
133
- }
134
- end
135
- end
136
-
137
- def format_message_lines(message_lines)
138
- message_lines.is_a?(Array) ? message_lines.join("\n") : message_lines
139
- end
140
- # rubocop:enable Metrics/AbcSize
141
- end
142
-
143
- class JUnitTestResult < TestResult
144
- def name
145
- report['name']
146
- end
147
-
148
- def file
149
- report['file']
150
- end
151
-
152
- def skipped
153
- report.search('skipped').any?
154
- end
155
-
156
- attr_accessor :testcase # Ignore it for now
157
-
158
- private
159
-
160
- # rubocop:disable Metrics/AbcSize
161
- def failures_from_exceptions
162
- failures = report.search('failure')
163
- return [] if failures.empty?
164
-
165
- failures.map do |exception|
166
- trace = exception.content.split("\n").map(&:strip)
167
- spec_file_first_index = trace.rindex do |line|
168
- line.include?(File.basename(report['file']))
169
- end
170
-
171
- exception['message'].gsub!(/(private_token=)[\w-]+/, '********')
172
-
173
- {
174
- 'message' => "#{exception['type']}: #{exception['message']}",
175
- 'stacktrace' => trace.slice(0..spec_file_first_index).join("\n")
176
- }
177
- end
178
- end
179
- # rubocop:enable Metrics/AbcSize
180
- end
181
- end
182
- end
183
- end
184
- end
@@ -1,63 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'nokogiri'
4
- require 'json'
5
- require 'active_support/core_ext/object/blank'
6
-
7
- module Gitlab
8
- module QA
9
- module Report
10
- class UpdateScreenshotPath
11
- def initialize(files:)
12
- @files = files
13
- end
14
-
15
- REGEX = %r{(?<gitlab_qa_run>gitlab-qa-run-.*?(?=\/))\/(?<gitlab_ce_ee_qa>gitlab-(ee|ce)-qa-.*?(?=\/))}
16
- CONTAINER_PATH = File.join(Docker::Volumes::QA_CONTAINER_WORKDIR, 'tmp').freeze
17
-
18
- def invoke!
19
- Dir.glob(@files).each do |rspec_report_file|
20
- match_data = rspec_report_file.match(REGEX)
21
- artifact_path = "#{match_data[:gitlab_qa_run]}/#{match_data[:gitlab_ce_ee_qa]}"
22
-
23
- xml_report = rewrite_each_xml_screenshot_path(rspec_report_file, artifact_path)
24
-
25
- File.write(rspec_report_file, xml_report)
26
-
27
- puts "Saved #{rspec_report_file}"
28
-
29
- json_rspec_report_file = rspec_report_file.gsub('.xml', '.json')
30
- json_report = rewrite_each_json_screenshot_path(json_rspec_report_file, artifact_path)
31
-
32
- File.write(json_rspec_report_file, json_report)
33
-
34
- puts "Saved #{json_rspec_report_file}"
35
- end
36
- end
37
-
38
- private
39
-
40
- def rewrite_each_xml_screenshot_path(rspec_report_file, artifact_path)
41
- report = Nokogiri::XML(File.open(rspec_report_file))
42
-
43
- report.xpath('//system-out').each do |system_out|
44
- system_out.content = system_out.content.gsub(CONTAINER_PATH, artifact_path)
45
- end
46
-
47
- report.to_s
48
- end
49
-
50
- def rewrite_each_json_screenshot_path(json_rspec_report_file, artifact_path)
51
- report = JSON.parse(File.read(json_rspec_report_file))
52
- examples = report['examples']
53
-
54
- examples.each do |example|
55
- example['screenshot']['image'] = example['screenshot']['image'].gsub(CONTAINER_PATH, artifact_path) if example['screenshot'].present?
56
- end
57
-
58
- JSON.pretty_generate(report)
59
- end
60
- end
61
- end
62
- end
63
- end