gitlab_quality-test_tooling 0.4.0 → 0.4.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4e917eea87f25727b389510a47d70ec930f02c201acf889efcb43b89b3d7060e
4
- data.tar.gz: c7be66d7d4fdec18b8814c010e58a8a95474d9e6977e787c3b673d65892ea07c
3
+ metadata.gz: c7119bececf62cc7d2d1aa0a025c4ee193535d74cfe62777e2e4b2ff88590687
4
+ data.tar.gz: 3e5a13270e9718b683d9554583a4b6b3c1d5f2a3bd5109477b51cdd931610600
5
5
  SHA512:
6
- metadata.gz: 53b9854fb10540f4bff43a820f9fb4b13d6cd6f9e551390c0fba36a1a5a82db00bd701b84ffd4039b574c1d0504923232507428c2101bd61f893518ae6185fa0
7
- data.tar.gz: 0d8847faab298bf0c9ed4a3174cc8bfa6d82c1392dd87cbe7ea8f5c88d48ace766d5018c14faeb4bb936236d027eed33e82b6744daaf527850726779da3fa433
6
+ metadata.gz: c4cbc83f6bba83bd34ed42e619382778b5255dcc94df168d1cf0436c3b7f2d16c191ed9a6997db3d31e2cc030551a6359d30dc0ec380a8badc8e99d7373e070d
7
+ data.tar.gz: a87000a5dec45c065e46f836e9f1a7002d43aedf5c4eb11f59f9e69f321bad17537cc035269a7ce3adabef06e189b53cf69d46441bb7c5c245abd3b595a8df8b
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- gitlab_quality-test_tooling (0.4.0)
4
+ gitlab_quality-test_tooling (0.4.1)
5
5
  activesupport (~> 6.1)
6
6
  gitlab (~> 4.18.0)
7
7
  http (~> 5.0)
data/README.md CHANGED
@@ -82,7 +82,7 @@ Usage: exe/relate-failure-issue [options]
82
82
  --max-diff-ratio MAX_DIFF_RATO
83
83
  Max stacktrace diff ratio for QA failure issues detection
84
84
  -p, --project PROJECT Can be an integer or a group/project string
85
- -t, --token TOKEN A valid access token with `api` scope and Reporter permission in PROJECT
85
+ -t, --token TOKEN A valid access token with `api` scope and Maintainer permission in PROJECT
86
86
  --system-log-files SYSTEM_LOG_FILES
87
87
  Include errors from system logs in failure issues
88
88
  --dry-run Perform a dry-run (don't create or update issues)
@@ -23,7 +23,7 @@ options = OptionParser.new do |opts|
23
23
  params[:project] = project
24
24
  end
25
25
 
26
- opts.on('-t', '--token TOKEN', String, 'A valid access token with `api` scope and Reporter permission in PROJECT') do |token|
26
+ opts.on('-t', '--token TOKEN', String, 'A valid access token with `api` scope and Maintainer permission in PROJECT') do |token|
27
27
  params[:token] = token
28
28
  end
29
29
 
@@ -31,28 +31,25 @@ module GitlabQuality
31
31
  @token = token
32
32
  @project = project
33
33
  @retry_backoff = 0
34
-
35
- configure_gitlab_client
36
34
  end
37
35
 
38
36
  def assert_user_permission!
39
37
  handle_gitlab_client_exceptions do
40
- user = Gitlab.user
41
- member = Gitlab.team_member(project, user.id)
38
+ member = client.team_member(project, user.id)
42
39
 
43
- abort_not_permitted if member.access_level < REPORTER_ACCESS_LEVEL
40
+ abort_not_permitted(member.access_level) if member.access_level < REPORTER_ACCESS_LEVEL
44
41
  end
45
42
  rescue Gitlab::Error::NotFound
46
- abort_not_permitted
43
+ abort_member_not_found(user)
47
44
  end
48
45
 
49
46
  def find_issues(iid: nil, options: {}, &select)
50
47
  select ||= :itself
51
48
 
52
49
  handle_gitlab_client_exceptions do
53
- break [Gitlab.issue(project, iid)].select(&select) if iid
50
+ break [client.issue(project, iid)].select(&select) if iid
54
51
 
55
- Gitlab.issues(project, options)
52
+ client.issues(project, options)
56
53
  .auto_paginate
57
54
  .select(&select)
58
55
  end
@@ -60,7 +57,7 @@ module GitlabQuality
60
57
 
61
58
  def find_issue_discussions(iid:)
62
59
  handle_gitlab_client_exceptions do
63
- Gitlab.issue_discussions(project, iid, order_by: 'created_at', sort: 'asc').auto_paginate
60
+ client.issue_discussions(project, iid, order_by: 'created_at', sort: 'asc').auto_paginate
64
61
  end
65
62
  end
66
63
 
@@ -68,50 +65,50 @@ module GitlabQuality
68
65
  attrs = { issue_type: issue_type, description: description, labels: labels }
69
66
 
70
67
  handle_gitlab_client_exceptions do
71
- Gitlab.create_issue(project, title, attrs)
68
+ client.create_issue(project, title, attrs)
72
69
  end
73
70
  end
74
71
 
75
72
  def edit_issue(iid:, options: {})
76
73
  handle_gitlab_client_exceptions do
77
- Gitlab.edit_issue(project, iid, options)
74
+ client.edit_issue(project, iid, options)
78
75
  end
79
76
  end
80
77
 
81
78
  def find_issue_notes(iid:)
82
79
  handle_gitlab_client_exceptions do
83
- Gitlab.issue_notes(project, iid, order_by: 'created_at', sort: 'asc')&.auto_paginate
80
+ client.issue_notes(project, iid, order_by: 'created_at', sort: 'asc')&.auto_paginate
84
81
  end
85
82
  end
86
83
 
87
84
  def create_issue_note(iid:, note:)
88
85
  handle_gitlab_client_exceptions do
89
- Gitlab.create_issue_note(project, iid, note)
86
+ client.create_issue_note(project, iid, note)
90
87
  end
91
88
  end
92
89
 
93
90
  def edit_issue_note(issue_iid:, note_id:, note:)
94
91
  handle_gitlab_client_exceptions do
95
- Gitlab.edit_issue_note(project, issue_iid, note_id, note)
92
+ client.edit_issue_note(project, issue_iid, note_id, note)
96
93
  end
97
94
  end
98
95
 
99
96
  def add_note_to_issue_discussion_as_thread(iid:, discussion_id:, body:)
100
97
  handle_gitlab_client_exceptions do
101
- Gitlab.add_note_to_issue_discussion_as_thread(project, iid, discussion_id, body: body)
98
+ client.add_note_to_issue_discussion_as_thread(project, iid, discussion_id, body: body)
102
99
  end
103
100
  end
104
101
 
105
102
  def find_user_id(username:)
106
103
  handle_gitlab_client_exceptions do
107
- user = Gitlab.users(username: username)&.first
104
+ user = client.users(username: username)&.first
108
105
  user['id'] unless user.nil?
109
106
  end
110
107
  end
111
108
 
112
109
  def upload_file(file_fullpath:)
113
110
  ignore_gitlab_client_exceptions do
114
- Gitlab.upload_file(project, file_fullpath)
111
+ client.upload_file(project, file_fullpath)
115
112
  end
116
113
  end
117
114
 
@@ -173,15 +170,33 @@ module GitlabQuality
173
170
 
174
171
  attr_reader :token, :project
175
172
 
176
- def configure_gitlab_client
177
- Gitlab.configure do |config|
178
- config.endpoint = Runtime::Env.gitlab_api_base
179
- config.private_token = token
173
+ def client
174
+ @client ||= Gitlab.client(
175
+ endpoint: Runtime::Env.gitlab_api_base,
176
+ private_token: token
177
+ )
178
+ end
179
+
180
+ def user
181
+ return @user if defined?(@user)
182
+
183
+ @user ||= begin
184
+ client.user
185
+ rescue Gitlab::Error::NotFound
186
+ abort_user_not_found
180
187
  end
181
188
  end
182
189
 
183
- def abort_not_permitted
184
- abort "You must have at least Reporter access to the project to use this feature."
190
+ def abort_not_permitted(access_level)
191
+ abort "#{user.username} must have at least Reporter access to the project '#{project}' to use this feature. Current access level: #{access_level}"
192
+ end
193
+
194
+ def abort_user_not_found
195
+ abort "User not found for given token."
196
+ end
197
+
198
+ def abort_member_not_found(user)
199
+ abort "#{user.username} must be a member of the '#{project}' project."
185
200
  end
186
201
 
187
202
  def warn_exception(error)
@@ -46,15 +46,28 @@ module GitlabQuality
46
46
  labels << (test.failures.empty? ? "#{pipeline}::passed" : "#{pipeline}::failed")
47
47
  end
48
48
 
49
- def update_issue_title(issue, test)
49
+ def update_issue(issue, test)
50
+ update_params = {}
51
+
50
52
  old_title = issue.title.strip
51
53
  new_title = title_from_test(test)
52
54
 
53
- warn(%(#{issue_type} title needs to be updated from '#{old_title}' to '#{new_title}'))
55
+ if old_title != new_title
56
+ update_params[:title] = new_title
57
+ warn(%(#{issue_type} title needs to be updated from '#{old_title}' to '#{new_title}'))
58
+ end
54
59
 
60
+ old_description = issue.description
55
61
  new_description = updated_description(issue, test)
56
62
 
57
- gitlab.edit_issue(iid: issue.iid, options: { title: new_title, description: new_description })
63
+ if old_description != new_description
64
+ warn(%(#{issue_type} description needs to be updated from '#{old_description}' to '#{new_description}'))
65
+ update_params[:description] = new_description
66
+ end
67
+
68
+ return if update_params.empty?
69
+
70
+ gitlab.edit_issue(iid: issue.iid, options: update_params)
58
71
  end
59
72
 
60
73
  private
@@ -7,14 +7,6 @@ module GitlabQuality
7
7
  module Utils
8
8
  MAX_TITLE_LENGTH = 255
9
9
 
10
- def partial_file_path(path)
11
- path.match(/((ee|api|browser_ui).*)/i)[1]
12
- end
13
-
14
- def new_issue_title(test)
15
- "#{partial_file_path(test.file)} | #{search_safe(test.name)}".strip
16
- end
17
-
18
10
  def title_from_test(test)
19
11
  title = new_issue_title(test)
20
12
 
@@ -23,6 +15,14 @@ module GitlabQuality
23
15
  "#{title[0...MAX_TITLE_LENGTH - 3]}..."
24
16
  end
25
17
 
18
+ def new_issue_title(test)
19
+ "#{partial_file_path(test.file)} | #{search_safe(test.name)}".strip
20
+ end
21
+
22
+ def partial_file_path(path)
23
+ path.match(/((ee|api|browser_ui).*)/i)[1]
24
+ end
25
+
26
26
  def search_safe(value)
27
27
  value.delete('"')
28
28
  end
@@ -24,22 +24,12 @@ module GitlabQuality
24
24
  @gitlab = testcase_project_reporter.gitlab
25
25
  end
26
26
 
27
- def invoke!
28
- validate_input!
29
-
30
- run!
31
- end
32
-
33
27
  def validate_input!
34
28
  assert_input_files!(files)
35
29
  gitlab.assert_user_permission!
36
30
  end
37
31
 
38
- def assert_input_files!(files)
39
- return if Dir.glob(files).any?
40
-
41
- abort "Please provide valid JUnit report files. No files were found matching `#{files.join(',')}`"
42
- end
32
+ private
43
33
 
44
34
  # rubocop:disable Metrics/AbcSize
45
35
  def run!
@@ -60,9 +50,6 @@ module GitlabQuality
60
50
  test_results.write
61
51
  end
62
52
  end
63
- # rubocop:enable Metrics/AbcSize
64
-
65
- private
66
53
 
67
54
  def report_test(test)
68
55
  testcase = testcase_project_reporter.find_or_create_testcase(test)
@@ -70,13 +57,22 @@ module GitlabQuality
70
57
  # This updates the URL to a valid test case link.
71
58
  test.testcase = testcase.web_url.sub('/issues/', '/quality/test_cases/')
72
59
 
73
- issue, is_new = results_issue_project_reporter.get_related_issue(testcase, test)
60
+ result_issue, is_new = results_issue_project_reporter.get_related_issue(testcase, test)
74
61
 
75
- testcase_project_reporter.add_issue_link_to_testcase(testcase, issue, test) if is_new
62
+ testcase_project_reporter.add_result_issue_link_to_testcase(testcase, result_issue, test) if is_new
76
63
 
77
64
  testcase_project_reporter.update_testcase(testcase, test)
78
- results_issue_project_reporter.update_issue(issue, test)
65
+
66
+ labels_updated = results_issue_project_reporter.update_issue_labels(result_issue, test)
67
+ note_posted = results_issue_project_reporter.post_note(result_issue, test)
68
+
69
+ if labels_updated || note_posted
70
+ puts "Issue updated: #{result_issue.web_url}"
71
+ else
72
+ puts "Test passed, no results issue update needed."
73
+ end
79
74
  end
75
+ # rubocop:enable Metrics/AbcSize
80
76
  end
81
77
  end
82
78
  end
@@ -17,9 +17,9 @@ module GitlabQuality
17
17
  is_new = false
18
18
 
19
19
  if issue
20
- issue = update_issue_title(issue, test) if issue_title_needs_updating?(issue, test)
20
+ issue = update_issue(issue, test) if issue_title_needs_updating?(issue, test)
21
21
  else
22
- puts "No valid issue link found"
22
+ puts "No valid issue link found."
23
23
  issue = find_or_create_results_issue(test)
24
24
  is_new = true
25
25
  end
@@ -27,18 +27,30 @@ module GitlabQuality
27
27
  [issue, is_new]
28
28
  end
29
29
 
30
- def update_issue(issue, test)
30
+ def update_issue_labels(issue, test)
31
31
  new_labels = issue_labels(issue)
32
32
  new_labels |= ['Testcase Linked']
33
33
 
34
- labels_updated = update_labels(issue, test, new_labels)
35
- note_posted = note_status(issue, test)
34
+ update_labels(issue, test, new_labels)
35
+ end
36
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."
37
+ def post_note(issue, test)
38
+ return false if test.skipped
39
+ return false if test.failures.empty?
40
+
41
+ note = note_content(test)
42
+
43
+ gitlab.find_issue_discussions(iid: issue.iid).each do |discussion|
44
+ next unless new_note_matches_discussion?(note, discussion)
45
+
46
+ gitlab.add_note_to_issue_discussion_as_thread(
47
+ iid: issue.iid,
48
+ discussion_id: discussion.id,
49
+ body: failure_summary)
50
+ return true
41
51
  end
52
+
53
+ gitlab.create_issue_note(iid: issue.iid, note: note)
42
54
  end
43
55
 
44
56
  private
@@ -58,32 +70,13 @@ module GitlabQuality
58
70
  def issue_iid_from_testcase(testcase)
59
71
  results = testcase.description.partition(TEST_CASE_RESULTS_SECTION_TEMPLATE).last if testcase.description.include?(TEST_CASE_RESULTS_SECTION_TEMPLATE)
60
72
 
61
- return puts "No issue link found" unless results
73
+ return unless results
62
74
 
63
75
  issue_iid = results.split('/').last
64
76
 
65
77
  issue_iid&.to_i
66
78
  end
67
79
 
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
- if new_note_matches_discussion?(
76
- note, discussion)
77
- return gitlab.add_note_to_issue_discussion_as_thread(iid: issue.iid, discussion_id: discussion.id,
78
- body: failure_summary)
79
- end
80
- end
81
-
82
- gitlab.create_issue_note(iid: issue.iid, note: note)
83
-
84
- true
85
- end
86
-
87
80
  def note_content(test)
88
81
  errors = test.failures.each_with_object([]) do |failure, text|
89
82
  text << <<~TEXT
@@ -103,7 +96,7 @@ module GitlabQuality
103
96
  end
104
97
 
105
98
  def failure_summary
106
- summary = [":x: ~\"#{pipeline}::failed\""]
99
+ summary = [%(:x: ~"#{pipeline}::failed")]
107
100
  summary << "in job `#{Runtime::Env.ci_job_name}` in #{Runtime::Env.ci_job_url}"
108
101
  summary.join(' ')
109
102
  end
@@ -20,32 +20,31 @@ module GitlabQuality
20
20
  find_testcase(test) || create_issue(test)
21
21
  end
22
22
 
23
- def add_issue_link_to_testcase(testcase, issue, test)
23
+ def add_result_issue_link_to_testcase(testcase, result_issue, test)
24
24
  results_section = testcase.description.include?(TEST_CASE_RESULTS_SECTION_TEMPLATE) ? '' : TEST_CASE_RESULTS_SECTION_TEMPLATE
25
25
 
26
26
  gitlab.edit_issue(iid: testcase.iid,
27
- options: { description: (testcase.description + results_section + "\n\n#{issue.web_url}") })
27
+ options: { description: (testcase.description + results_section + "\n\n#{result_issue.web_url}") })
28
28
  # We are using test.testcase for the url here instead of testcase.web_url since it has the updated test case path
29
- puts "Added results issue #{issue.web_url} link to test case #{test.testcase}"
29
+ puts "Added results issue #{result_issue.web_url} link to test case #{test.testcase}"
30
30
  end
31
31
 
32
32
  def update_testcase(testcase, test)
33
- puts "Test case labels updated." if update_labels(testcase, test)
34
- puts "Test case quarantine section updated." if update_quarantine_link(testcase, test)
33
+ puts "Labels updated for test case #{test.testcase}." if update_labels(testcase, test)
34
+ puts "Quarantine section updated for test case #{test.testcase}." if update_quarantine_section(testcase, test)
35
35
  end
36
36
 
37
37
  private
38
38
 
39
39
  def find_testcase(test)
40
- testcase = find_testcase_by_iid(test)
40
+ testcase = find_testcase_by_iid(test) || find_issue(test)
41
+ return unless testcase
41
42
 
42
- if testcase
43
- testcase = update_issue_title(testcase, test) if issue_title_needs_updating?(testcase, test)
43
+ if testcase_needs_updating?(testcase, test)
44
+ update_issue(testcase, test)
44
45
  else
45
- testcase = find_issue(test)
46
+ testcase
46
47
  end
47
-
48
- testcase
49
48
  end
50
49
 
51
50
  def find_testcase_by_iid(test)
@@ -57,7 +56,7 @@ module GitlabQuality
57
56
  end
58
57
 
59
58
  def testcase_iid_from_url(url)
60
- return warn(%(\nPlease update #{url} to test case url")) if url&.include?('/-/issues/')
59
+ return warn(%(\nPlease update #{url} to test case url)) if url&.include?('/-/issues/')
61
60
 
62
61
  url && url.split('/').last.to_i
63
62
  end
@@ -85,12 +84,14 @@ module GitlabQuality
85
84
  "#{new_issue_description(test)}\n\n#{historical_results_section}"
86
85
  end
87
86
 
88
- def issue_title_needs_updating?(testcase, test)
89
- super || (!testcase.description.include?(execution_graph_section(test)) && !%w[canary production preprod
90
- release].include?(pipeline))
87
+ def testcase_needs_updating?(testcase, test)
88
+ return false if %w[canary production preprod release].include?(pipeline)
89
+ return true if issue_title_needs_updating?(testcase, test)
90
+
91
+ !testcase.description.include?(execution_graph_section(test))
91
92
  end
92
93
 
93
- def quarantine_link_needs_updating?(testcase, test)
94
+ def quarantine_section_needs_updating?(testcase, test)
94
95
  if test.quarantine? && test.quarantine_issue
95
96
  return false if testcase.description.include?(test.quarantine_issue)
96
97
  else
@@ -100,8 +101,8 @@ module GitlabQuality
100
101
  true
101
102
  end
102
103
 
103
- def update_quarantine_link(testcase, test)
104
- return unless quarantine_link_needs_updating?(testcase, test)
104
+ def update_quarantine_section(testcase, test)
105
+ return unless quarantine_section_needs_updating?(testcase, test)
105
106
 
106
107
  new_description = updated_description(testcase, test)
107
108
 
@@ -2,6 +2,6 @@
2
2
 
3
3
  module GitlabQuality
4
4
  module TestTooling
5
- VERSION = "0.4.0"
5
+ VERSION = "0.4.1"
6
6
  end
7
7
  end
@@ -10,10 +10,6 @@ module GitlabQuality
10
10
  loader.push_dir("#{__dir__}/test_tooling", namespace: GitlabQuality::TestTooling)
11
11
  loader.ignore("#{__dir__}/test_tooling/version.rb")
12
12
 
13
- loader.inflector.inflect(
14
- 'results_in_testcases' => 'ResultsInTestCases'
15
- )
16
-
17
13
  loader.setup
18
14
  end
19
15
  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: 0.4.0
4
+ version: 0.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - GitLab Quality
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-05-12 00:00:00.000000000 Z
11
+ date: 2023-05-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: climate_control
@@ -353,7 +353,7 @@ files:
353
353
  - lib/gitlab_quality/test_tooling/report/report_as_issue.rb
354
354
  - lib/gitlab_quality/test_tooling/report/report_results.rb
355
355
  - lib/gitlab_quality/test_tooling/report/results_in_issues.rb
356
- - lib/gitlab_quality/test_tooling/report/results_in_testcases.rb
356
+ - lib/gitlab_quality/test_tooling/report/results_in_test_cases.rb
357
357
  - lib/gitlab_quality/test_tooling/report/update_screenshot_path.rb
358
358
  - lib/gitlab_quality/test_tooling/runtime/env.rb
359
359
  - lib/gitlab_quality/test_tooling/runtime/logger.rb