gitlab_quality-test_tooling 2.9.0 → 2.11.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 (33) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +2 -1
  3. data/Gemfile.lock +9 -1
  4. data/README.md +44 -3
  5. data/exe/failed-test-issues +53 -8
  6. data/exe/feature-readiness-checklist +61 -0
  7. data/exe/feature-readiness-evaluation +62 -0
  8. data/lib/gitlab_quality/test_tooling/feature_readiness/analyzed_items/analyzed_epic.rb +94 -0
  9. data/lib/gitlab_quality/test_tooling/feature_readiness/analyzed_items/analyzed_issue.rb +92 -0
  10. data/lib/gitlab_quality/test_tooling/feature_readiness/analyzed_items/analyzed_merge_request.rb +139 -0
  11. data/lib/gitlab_quality/test_tooling/feature_readiness/concerns/issue_concern.rb +34 -0
  12. data/lib/gitlab_quality/test_tooling/feature_readiness/concerns/work_item_concern.rb +128 -0
  13. data/lib/gitlab_quality/test_tooling/feature_readiness/evaluation.rb +82 -0
  14. data/lib/gitlab_quality/test_tooling/feature_readiness/operational_readiness_check.rb +82 -0
  15. data/lib/gitlab_quality/test_tooling/gitlab_client/gitlab_graphql_client.rb +54 -0
  16. data/lib/gitlab_quality/test_tooling/gitlab_client/issues_client.rb +38 -0
  17. data/lib/gitlab_quality/test_tooling/gitlab_client/issues_dry_client.rb +3 -3
  18. data/lib/gitlab_quality/test_tooling/gitlab_client/labels_client.rb +13 -0
  19. data/lib/gitlab_quality/test_tooling/gitlab_client/merge_requests_client.rb +21 -0
  20. data/lib/gitlab_quality/test_tooling/gitlab_client/merge_requests_dry_client.rb +0 -10
  21. data/lib/gitlab_quality/test_tooling/gitlab_client/work_items_client.rb +277 -0
  22. data/lib/gitlab_quality/test_tooling/gitlab_client/work_items_dry_client.rb +25 -0
  23. data/lib/gitlab_quality/test_tooling/labels_inference.rb +4 -0
  24. data/lib/gitlab_quality/test_tooling/report/failed_test_issue.rb +6 -6
  25. data/lib/gitlab_quality/test_tooling/report/feature_readiness/report_on_epic.rb +174 -0
  26. data/lib/gitlab_quality/test_tooling/report/health_problem_reporter.rb +120 -20
  27. data/lib/gitlab_quality/test_tooling/report/knapsack_report_issue.rb +1 -1
  28. data/lib/gitlab_quality/test_tooling/report/prepare_stage_reports.rb +1 -1
  29. data/lib/gitlab_quality/test_tooling/report/relate_failure_issue.rb +1 -1
  30. data/lib/gitlab_quality/test_tooling/runtime/env.rb +11 -6
  31. data/lib/gitlab_quality/test_tooling/test_metrics_exporter/support/gcs_tools.rb +18 -5
  32. data/lib/gitlab_quality/test_tooling/version.rb +1 -1
  33. metadata +32 -2
@@ -15,13 +15,29 @@ module GitlabQuality
15
15
  class HealthProblemReporter < ReportAsIssue
16
16
  include Concerns::GroupAndCategoryLabels
17
17
  include Concerns::IssueReports
18
+ include TestMetricsExporter::Support::GcsTools
18
19
 
19
20
  BASE_SEARCH_LABELS = ['test'].freeze
20
21
  FOUND_IN_MR_LABEL = '~"found:in MR"'
21
22
  FOUND_IN_MASTER_LABEL = '~"found:master"'
22
23
 
23
- def initialize(input_files: [], **kwargs)
24
- super(input_files: input_files, **kwargs)
24
+ def initialize(
25
+ input_files: [],
26
+ dry_run: false,
27
+ issue_update_enabled: true,
28
+ gcs_enabled: false,
29
+ gcs_project_id: nil,
30
+ gcs_bucket: nil,
31
+ gcs_credentials: nil,
32
+ **kwargs)
33
+ super(input_files: input_files, dry_run: dry_run, **kwargs)
34
+
35
+ @dry_run = dry_run
36
+ @issue_update_enabled = issue_update_enabled
37
+ @gcs_enabled = gcs_enabled
38
+ @gcs_project_id = gcs_project_id
39
+ @gcs_bucket = gcs_bucket
40
+ @gcs_credentials = gcs_credentials
25
41
  end
26
42
 
27
43
  def most_recent_report_date_for_issue(issue_iid:)
@@ -33,6 +49,8 @@ module GitlabQuality
33
49
 
34
50
  private
35
51
 
52
+ attr_reader :dry_run, :issue_update_enabled, :gcs_enabled, :gcs_project_id, :gcs_bucket, :gcs_credentials
53
+
36
54
  def problem_type
37
55
  'unhealthy'
38
56
  end
@@ -70,43 +88,89 @@ module GitlabQuality
70
88
  end
71
89
 
72
90
  def run!
73
- puts "Reporting tests in `#{files.join(',')}` as issues in project `#{project}` via the API at `#{Runtime::Env.gitlab_api_base}`."
91
+ Runtime::Logger.info "issue creation/update is disabled." unless issue_update_enabled
92
+ Runtime::Logger.info "push to GCS is disabled." unless gcs_enabled
93
+ return unless test_reporting_enabled?
74
94
 
95
+ Runtime::Logger.info "Reporting tests in `#{files.join(',')}`."
75
96
  TestResults::Builder.new(file_glob: files, token: token, project: project).test_results_per_file do |test_results|
76
- puts "=> Processing #{test_results.count} tests in #{test_results.path}"
97
+ Runtime::Logger.info "=> Processing #{test_results.count} tests in #{test_results.path}"
77
98
 
78
99
  process_test_results(test_results)
79
100
  end
80
101
  end
81
102
 
82
103
  def process_test_results(test_results)
83
- reported_test_count = 0
104
+ applicable_tests = test_results.select { |test| test_is_applicable?(test) }
105
+ return if applicable_tests.empty?
106
+
107
+ process_issues_for_tests(applicable_tests) if issue_update_enabled
84
108
 
85
- test_results.each do |test|
86
- next unless test_is_applicable?(test)
109
+ if gcs_enabled
110
+ tests_data = build_tests_data(applicable_tests)
111
+ test_results_filename = File.basename(test_results.path)
112
+
113
+ push_test_to_gcs(tests_data, test_results_filename)
114
+ end
87
115
 
88
- puts " => Reporting #{problem_type} test '#{test.name}'..."
116
+ Runtime::Logger.info " => Reported #{applicable_tests.size} #{problem_type} tests."
117
+ end
118
+
119
+ def process_issues_for_tests(tests)
120
+ tests.each do |test|
121
+ Runtime::Logger.info " => Processing issues for #{problem_type} test '#{test.name}'..."
122
+ issues = existing_test_health_issues_for_test(test)
123
+ create_or_update_test_health_issues(issues, test)
124
+ end
125
+ end
89
126
 
90
- issues = find_issues_by_hash(test_hash(test), state: 'opened', labels: search_labels)
127
+ def existing_test_health_issues_for_test(test)
128
+ find_issues_by_hash(test_hash(test), state: 'opened', labels: search_labels)
129
+ end
91
130
 
92
- if issues.empty?
93
- issues << create_issue(test)
94
- else
95
- # Keep issues description up-to-date
96
- update_issues(issues, test)
97
- end
131
+ def build_tests_data(tests)
132
+ tests.map do |test|
133
+ Runtime::Logger.info " => Building data for #{problem_type} test '#{test.name}'..."
134
+ issues = existing_test_health_issues_for_test(test)
135
+ test_datum(test, issues)
136
+ end
137
+ end
98
138
 
99
- update_reports(issues, test)
100
- collect_issues(test, issues)
101
- reported_test_count += 1
139
+ def create_or_update_test_health_issues(issues, test)
140
+ if issues.empty?
141
+ issues << create_issue(test)
142
+ else
143
+ # Keep issues description up-to-date
144
+ update_issues(issues, test)
102
145
  end
103
146
 
104
- puts " => Reported #{reported_test_count} #{problem_type} tests."
147
+ update_reports(issues, test)
148
+ collect_issues(test, issues)
149
+ end
150
+
151
+ def push_test_to_gcs(tests_data, test_results_filename)
152
+ Runtime::Logger.info "will push the test data to GCS"
153
+
154
+ gcs_client(project_id: gcs_project_id, credentials: gcs_credentials, dry_run: dry_run).put_object(
155
+ gcs_bucket,
156
+ gcs_metrics_file_name(test_results_filename),
157
+ tests_data.to_json,
158
+ force: true,
159
+ content_type: 'application/json'
160
+ )
161
+
162
+ Runtime::Logger.info "Successfully pushed the #{gcs_metrics_file_name(test_results_filename)} file to the GCS bucket."
163
+ end
164
+
165
+ def gcs_metrics_file_name(test_results_filename)
166
+ today = Time.now.strftime('%Y-%m-%d')
167
+
168
+ "#{today}-#{test_results_filename}"
105
169
  end
106
170
 
107
171
  def update_reports(issues, test)
108
172
  issues.each do |issue|
109
- puts " => Reporting #{problem_type} test to existing issue: #{issue.web_url}"
173
+ Runtime::Logger.info " => Reporting #{problem_type} test to existing issue: #{issue.web_url}"
110
174
  add_report_to_issue(issue: issue, test: test, related_issues: (issues - [issue]))
111
175
  end
112
176
  end
@@ -162,6 +226,42 @@ module GitlabQuality
162
226
  end
163
227
  end
164
228
 
229
+ # We report a test if we have issue update and/or push to GCS enabled.
230
+ def test_reporting_enabled?
231
+ issue_update_enabled || gcs_enabled
232
+ end
233
+
234
+ # rubocop:disable Metrics/AbcSize
235
+ def test_datum(test, issues)
236
+ feature_category = test.feature_category.to_s.strip.empty? ? nil : test.feature_category
237
+ product_group = test.product_group.to_s.strip.empty? ? nil : test.product_group
238
+
239
+ if !product_group && feature_category
240
+ labels_inference = GitlabQuality::TestTooling::LabelsInference
241
+ product_group = labels_inference.new.product_group_from_feature_category(feature_category)
242
+ end
243
+
244
+ {
245
+ feature_category: feature_category,
246
+ created_at: Time.now.utc.iso8601,
247
+ description: test.name,
248
+ filename: test.file,
249
+ gitlab_project_id: Runtime::Env.ci_project_id,
250
+ product_group: product_group,
251
+ id: test.example_id,
252
+ hash: test_hash(test),
253
+ issue_url: issues.first&.web_url,
254
+ job_id: Runtime::Env.ci_job_id,
255
+ job_web_url: test.ci_job_url,
256
+ pipeline_id: Runtime::Env.ci_pipeline_id,
257
+ pipeline_ref: Runtime::Env.ci_commit_ref_name,
258
+ pipeline_web_url: Runtime::Env.ci_pipeline_url,
259
+ stacktrace: test.full_stacktrace,
260
+ test_level: test.level
261
+ }
262
+ end
263
+ # rubocop:enable Metrics/AbcSize
264
+
165
265
  def found_label
166
266
  if ENV.key?('CI_MERGE_REQUEST_IID')
167
267
  FOUND_IN_MR_LABEL
@@ -17,7 +17,7 @@ module GitlabQuality
17
17
  include Concerns::GroupAndCategoryLabels
18
18
 
19
19
  NEW_ISSUE_LABELS = Set.new([
20
- 'test', 'automation::bot-authored', 'type::maintenance', 'maintenance::performance',
20
+ 'test', 'automation:bot-authored', 'type::maintenance', 'maintenance::performance',
21
21
  'priority::3', 'severity::3', 'knapsack_report'
22
22
  ]).freeze
23
23
  SEARCH_LABELS = %w[test maintenance::performance knapsack_report].freeze
@@ -6,7 +6,7 @@ module GitlabQuality
6
6
  module TestTooling
7
7
  module Report
8
8
  class PrepareStageReports
9
- EXTRACT_STAGE_FROM_TEST_FILE_REGEX = %r{(?:api|browser_ui)/(?:[0-9]+_)?(?<stage>[_\w]+)/}i
9
+ EXTRACT_STAGE_FROM_TEST_FILE_REGEX = %r{(?:api|browser_ui)/(?:[0-9]+_)?(?<stage>[\w]+)/}i
10
10
 
11
11
  def initialize(input_files:)
12
12
  @input_files = input_files
@@ -318,7 +318,7 @@ module GitlabQuality
318
318
  initialize_gitlab_ops_client
319
319
 
320
320
  if Runtime::Env.ci_pipeline_url.include?('ops.gitlab.net')
321
- pipeline = ops_gitlab_client.find_pipeline(project, Runtime::Env.ci_pipeline_id.to_i)
321
+ pipeline = ops_gitlab_client.find_pipeline(Runtime::Env.ci_project_path, Runtime::Env.ci_pipeline_id.to_i)
322
322
  generate_ops_gitlab_diff(pipeline)
323
323
  else
324
324
  pipeline = gitlab.find_pipeline(project, Runtime::Env.ci_pipeline_id.to_i)
@@ -11,19 +11,20 @@ module GitlabQuality
11
11
  using Rainbow
12
12
 
13
13
  ENV_VARIABLES = {
14
- 'GITLAB_QA_ISSUE_URL' => :qa_issue_url,
15
14
  'CI_COMMIT_REF_NAME' => :ci_commit_ref_name,
15
+ 'CI_JOB_ID' => :ci_job_id,
16
16
  'CI_JOB_NAME' => :ci_job_name,
17
17
  'CI_JOB_URL' => :ci_job_url,
18
- 'CI_PROJECT_ID' => :ci_project_id,
19
- 'CI_PROJECT_NAME' => :ci_project_name,
20
- 'CI_PROJECT_PATH' => :ci_project_path,
21
18
  'CI_PIPELINE_ID' => :ci_pipeline_id,
22
19
  'CI_PIPELINE_NAME' => :ci_pipeline_name,
23
20
  'CI_PIPELINE_URL' => :ci_pipeline_url,
24
- 'SLACK_QA_CHANNEL' => :slack_qa_channel,
21
+ 'CI_PROJECT_ID' => :ci_project_id,
22
+ 'CI_PROJECT_NAME' => :ci_project_name,
23
+ 'CI_PROJECT_PATH' => :ci_project_path,
25
24
  'DEPLOY_VERSION' => :deploy_version,
26
- 'QA_GITLAB_CI_TOKEN' => :gitlab_ci_token
25
+ 'GITLAB_QA_ISSUE_URL' => :qa_issue_url,
26
+ 'QA_GITLAB_CI_TOKEN' => :gitlab_ci_token,
27
+ 'SLACK_QA_CHANNEL' => :slack_qa_channel
27
28
  }.freeze
28
29
 
29
30
  ENV_VARIABLES.each do |env_name, method_name|
@@ -56,6 +57,10 @@ module GitlabQuality
56
57
  env_var_value_if_defined('GITLAB_API_BASE') || ci_api_v4_url
57
58
  end
58
59
 
60
+ def gitlab_graphql_api_base
61
+ env_var_value_if_defined('GITLAB_GRAPHQL_API_BASE')
62
+ end
63
+
59
64
  def pipeline_from_project_name
60
65
  %w[gitlab gitaly].any? { |str| ci_project_name.to_s.start_with?(str) } ? default_branch : ci_project_name
61
66
  end
@@ -12,11 +12,15 @@ module GitlabQuality
12
12
  # @param project_id [String]
13
13
  # @param credentials [String]
14
14
  # @return [Fog::Storage::Google]
15
- def gcs_client(project_id:, credentials:)
16
- Fog::Storage::Google.new(
17
- google_project: project_id || raise("Missing Google project_id"),
18
- **gcs_creds(credentials)
19
- )
15
+ def gcs_client(project_id:, credentials:, dry_run: false)
16
+ if dry_run
17
+ GCSMockClient.new
18
+ else
19
+ Fog::Storage::Google.new(
20
+ google_project: project_id || raise("Missing Google project_id"),
21
+ **gcs_creds(credentials)
22
+ )
23
+ end
20
24
  end
21
25
 
22
26
  # GCS Credentials
@@ -29,6 +33,15 @@ module GitlabQuality
29
33
 
30
34
  { google_json_key_string: json_key }
31
35
  end
36
+
37
+ class GCSMockClient
38
+ def initialize(*_args, **_kwargs); end
39
+
40
+ def put_object(gcs_bucket, filename, data, **_kwargs)
41
+ Runtime::Logger.info "The #{filename} file would have been pushed to the #{gcs_bucket} bucket, with this content:"
42
+ Runtime::Logger.info data
43
+ end
44
+ end
32
45
  end
33
46
  end
34
47
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module GitlabQuality
4
4
  module TestTooling
5
- VERSION = "2.9.0"
5
+ VERSION = "2.11.0"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gitlab_quality-test_tooling
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.9.0
4
+ version: 2.11.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - GitLab Quality
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-04-08 00:00:00.000000000 Z
11
+ date: 2025-05-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: climate_control
@@ -206,6 +206,20 @@ dependencies:
206
206
  - - "~>"
207
207
  - !ruby/object:Gem::Version
208
208
  version: 0.6.0
209
+ - !ruby/object:Gem::Dependency
210
+ name: rest-client
211
+ requirement: !ruby/object:Gem::Requirement
212
+ requirements:
213
+ - - "~>"
214
+ - !ruby/object:Gem::Version
215
+ version: 2.1.0
216
+ type: :development
217
+ prerelease: false
218
+ version_requirements: !ruby/object:Gem::Requirement
219
+ requirements:
220
+ - - "~>"
221
+ - !ruby/object:Gem::Version
222
+ version: 2.1.0
209
223
  - !ruby/object:Gem::Dependency
210
224
  name: activesupport
211
225
  requirement: !ruby/object:Gem::Requirement
@@ -417,6 +431,8 @@ executables:
417
431
  - detect-infrastructure-failures
418
432
  - existing-test-health-issue
419
433
  - failed-test-issues
434
+ - feature-readiness-checklist
435
+ - feature-readiness-evaluation
420
436
  - flaky-test-issues
421
437
  - generate-test-session
422
438
  - knapsack-report-issues
@@ -448,6 +464,8 @@ files:
448
464
  - exe/detect-infrastructure-failures
449
465
  - exe/existing-test-health-issue
450
466
  - exe/failed-test-issues
467
+ - exe/feature-readiness-checklist
468
+ - exe/feature-readiness-evaluation
451
469
  - exe/flaky-test-issues
452
470
  - exe/generate-test-session
453
471
  - exe/knapsack-report-issues
@@ -463,18 +481,29 @@ files:
463
481
  - lib/gitlab_quality/test_tooling.rb
464
482
  - lib/gitlab_quality/test_tooling/concerns/find_set_dri.rb
465
483
  - lib/gitlab_quality/test_tooling/failed_jobs_table.rb
484
+ - lib/gitlab_quality/test_tooling/feature_readiness/analyzed_items/analyzed_epic.rb
485
+ - lib/gitlab_quality/test_tooling/feature_readiness/analyzed_items/analyzed_issue.rb
486
+ - lib/gitlab_quality/test_tooling/feature_readiness/analyzed_items/analyzed_merge_request.rb
487
+ - lib/gitlab_quality/test_tooling/feature_readiness/concerns/issue_concern.rb
488
+ - lib/gitlab_quality/test_tooling/feature_readiness/concerns/work_item_concern.rb
489
+ - lib/gitlab_quality/test_tooling/feature_readiness/evaluation.rb
490
+ - lib/gitlab_quality/test_tooling/feature_readiness/operational_readiness_check.rb
466
491
  - lib/gitlab_quality/test_tooling/gitlab_client/branches_client.rb
467
492
  - lib/gitlab_quality/test_tooling/gitlab_client/branches_dry_client.rb
468
493
  - lib/gitlab_quality/test_tooling/gitlab_client/commits_client.rb
469
494
  - lib/gitlab_quality/test_tooling/gitlab_client/commits_dry_client.rb
470
495
  - lib/gitlab_quality/test_tooling/gitlab_client/gitlab_client.rb
496
+ - lib/gitlab_quality/test_tooling/gitlab_client/gitlab_graphql_client.rb
471
497
  - lib/gitlab_quality/test_tooling/gitlab_client/issues_client.rb
472
498
  - lib/gitlab_quality/test_tooling/gitlab_client/issues_dry_client.rb
473
499
  - lib/gitlab_quality/test_tooling/gitlab_client/job_client.rb
474
500
  - lib/gitlab_quality/test_tooling/gitlab_client/jobs_client.rb
501
+ - lib/gitlab_quality/test_tooling/gitlab_client/labels_client.rb
475
502
  - lib/gitlab_quality/test_tooling/gitlab_client/merge_requests_client.rb
476
503
  - lib/gitlab_quality/test_tooling/gitlab_client/merge_requests_dry_client.rb
477
504
  - lib/gitlab_quality/test_tooling/gitlab_client/repository_files_client.rb
505
+ - lib/gitlab_quality/test_tooling/gitlab_client/work_items_client.rb
506
+ - lib/gitlab_quality/test_tooling/gitlab_client/work_items_dry_client.rb
478
507
  - lib/gitlab_quality/test_tooling/job_trace_analyzer.rb
479
508
  - lib/gitlab_quality/test_tooling/knapsack_reports/spec_run_time.rb
480
509
  - lib/gitlab_quality/test_tooling/knapsack_reports/spec_run_time_report.rb
@@ -484,6 +513,7 @@ files:
484
513
  - lib/gitlab_quality/test_tooling/report/concerns/results_reporter.rb
485
514
  - lib/gitlab_quality/test_tooling/report/concerns/utils.rb
486
515
  - lib/gitlab_quality/test_tooling/report/failed_test_issue.rb
516
+ - lib/gitlab_quality/test_tooling/report/feature_readiness/report_on_epic.rb
487
517
  - lib/gitlab_quality/test_tooling/report/flaky_test_issue.rb
488
518
  - lib/gitlab_quality/test_tooling/report/generate_test_session.rb
489
519
  - lib/gitlab_quality/test_tooling/report/health_problem_reporter.rb