gitlab_quality-test_tooling 0.1.0 → 0.2.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.
Files changed (50) 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/README.md +150 -9
  6. data/exe/generate-test-session +50 -0
  7. data/exe/post-to-slack +58 -0
  8. data/exe/prepare-stage-reports +38 -0
  9. data/exe/relate-failure-issue +59 -0
  10. data/exe/report-results +56 -0
  11. data/exe/update-screenshot-paths +38 -0
  12. data/lib/gitlab_quality/test_tooling/gitlab_issue_client.rb +194 -0
  13. data/lib/gitlab_quality/test_tooling/gitlab_issue_dry_client.rb +26 -0
  14. data/lib/gitlab_quality/test_tooling/report/concerns/find_set_dri.rb +51 -0
  15. data/lib/gitlab_quality/test_tooling/report/concerns/results_reporter.rb +75 -0
  16. data/lib/gitlab_quality/test_tooling/report/concerns/utils.rb +49 -0
  17. data/lib/gitlab_quality/test_tooling/report/generate_test_session.rb +275 -0
  18. data/lib/gitlab_quality/test_tooling/report/prepare_stage_reports.rb +78 -0
  19. data/lib/gitlab_quality/test_tooling/report/relate_failure_issue.rb +377 -0
  20. data/lib/gitlab_quality/test_tooling/report/report_as_issue.rb +134 -0
  21. data/lib/gitlab_quality/test_tooling/report/report_results.rb +83 -0
  22. data/lib/gitlab_quality/test_tooling/report/results_in_issues.rb +130 -0
  23. data/lib/gitlab_quality/test_tooling/report/results_in_testcases.rb +113 -0
  24. data/lib/gitlab_quality/test_tooling/report/update_screenshot_path.rb +81 -0
  25. data/lib/gitlab_quality/test_tooling/runtime/env.rb +113 -0
  26. data/lib/gitlab_quality/test_tooling/runtime/logger.rb +92 -0
  27. data/lib/gitlab_quality/test_tooling/runtime/token_finder.rb +44 -0
  28. data/lib/gitlab_quality/test_tooling/slack/post_to_slack.rb +36 -0
  29. data/lib/gitlab_quality/test_tooling/summary_table.rb +41 -0
  30. data/lib/gitlab_quality/test_tooling/support/http_request.rb +34 -0
  31. data/lib/gitlab_quality/test_tooling/system_logs/finders/json_log_finder.rb +65 -0
  32. data/lib/gitlab_quality/test_tooling/system_logs/finders/rails/api_log_finder.rb +21 -0
  33. data/lib/gitlab_quality/test_tooling/system_logs/finders/rails/application_log_finder.rb +21 -0
  34. data/lib/gitlab_quality/test_tooling/system_logs/finders/rails/exception_log_finder.rb +21 -0
  35. data/lib/gitlab_quality/test_tooling/system_logs/finders/rails/graphql_log_finder.rb +21 -0
  36. data/lib/gitlab_quality/test_tooling/system_logs/log_types/log.rb +38 -0
  37. data/lib/gitlab_quality/test_tooling/system_logs/log_types/rails/api_log.rb +34 -0
  38. data/lib/gitlab_quality/test_tooling/system_logs/log_types/rails/application_log.rb +27 -0
  39. data/lib/gitlab_quality/test_tooling/system_logs/log_types/rails/exception_log.rb +23 -0
  40. data/lib/gitlab_quality/test_tooling/system_logs/log_types/rails/graphql_log.rb +30 -0
  41. data/lib/gitlab_quality/test_tooling/system_logs/shared_fields.rb +29 -0
  42. data/lib/gitlab_quality/test_tooling/system_logs/system_logs_formatter.rb +65 -0
  43. data/lib/gitlab_quality/test_tooling/test_results/base_test_results.rb +39 -0
  44. data/lib/gitlab_quality/test_tooling/test_results/builder.rb +35 -0
  45. data/lib/gitlab_quality/test_tooling/test_results/j_unit_test_results.rb +27 -0
  46. data/lib/gitlab_quality/test_tooling/test_results/json_test_results.rb +29 -0
  47. data/lib/gitlab_quality/test_tooling/test_results/test_result.rb +184 -0
  48. data/lib/gitlab_quality/test_tooling/version.rb +1 -1
  49. data/lib/gitlab_quality/test_tooling.rb +11 -2
  50. metadata +51 -3
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ module GitlabQuality
6
+ module TestTooling
7
+ module SystemLogs
8
+ module Finders
9
+ class JsonLogFinder
10
+ def initialize(base_path, file_path)
11
+ @base_path = base_path
12
+ @file_path = file_path
13
+ end
14
+
15
+ def find(correlation_id)
16
+ log_file_path = "#{@base_path}/#{@file_path}"
17
+ logs = []
18
+
19
+ if File.exist?(log_file_path) && !correlation_id.nil?
20
+ File.foreach(log_file_path) do |line|
21
+ begin
22
+ json_line = JSON.parse(line, symbolize_names: true)
23
+ rescue JSON::ParserError
24
+ Runtime::Logger.debug("JsonLogFinder#find attempted to parse invalid JSON: #{line}")
25
+
26
+ next
27
+ end
28
+
29
+ if (json_line[:correlation_id])&.casecmp?(correlation_id)
30
+ normalized_line = normalize_keys(json_line)
31
+ logs << new_log(normalized_line)
32
+ end
33
+ end
34
+ end
35
+
36
+ logs
37
+ end
38
+
39
+ def new_log(_data)
40
+ raise 'abstract method new_log must be defined!'
41
+ end
42
+
43
+ private
44
+
45
+ def normalize_keys(json_line)
46
+ normalized_hash = {}
47
+
48
+ json_line.each_key do |old_key|
49
+ key_string = old_key.to_s
50
+
51
+ if key_string.include?('.')
52
+ normalized_key = key_string.tr('.', '_').to_sym
53
+ normalized_hash[normalized_key] = json_line[old_key]
54
+ else
55
+ normalized_hash[old_key] = json_line[old_key]
56
+ end
57
+ end
58
+
59
+ normalized_hash
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GitlabQuality
4
+ module TestTooling
5
+ module SystemLogs
6
+ module Finders
7
+ module Rails
8
+ class ApiLogFinder < JsonLogFinder
9
+ def initialize(base_path, file_path = 'gitlab-rails/api_json.log')
10
+ super(base_path, file_path)
11
+ end
12
+
13
+ def new_log(data)
14
+ LogTypes::Rails::ApiLog.new(data)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GitlabQuality
4
+ module TestTooling
5
+ module SystemLogs
6
+ module Finders
7
+ module Rails
8
+ class ApplicationLogFinder < JsonLogFinder
9
+ def initialize(base_path, file_path = 'gitlab-rails/application_json.log')
10
+ super(base_path, file_path)
11
+ end
12
+
13
+ def new_log(data)
14
+ LogTypes::Rails::ApplicationLog.new(data)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GitlabQuality
4
+ module TestTooling
5
+ module SystemLogs
6
+ module Finders
7
+ module Rails
8
+ class ExceptionLogFinder < JsonLogFinder
9
+ def initialize(base_path, file_path = 'gitlab-rails/exceptions_json.log')
10
+ super(base_path, file_path)
11
+ end
12
+
13
+ def new_log(data)
14
+ LogTypes::Rails::ExceptionLog.new(data)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GitlabQuality
4
+ module TestTooling
5
+ module SystemLogs
6
+ module Finders
7
+ module Rails
8
+ class GraphqlLogFinder < JsonLogFinder
9
+ def initialize(base_path, file_path = 'gitlab-rails/graphql_json.log')
10
+ super(base_path, file_path)
11
+ end
12
+
13
+ def new_log(data)
14
+ LogTypes::Rails::GraphqlLog.new(data)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GitlabQuality
4
+ module TestTooling
5
+ module SystemLogs
6
+ module LogTypes
7
+ class Log
8
+ def initialize(name, data)
9
+ @name = name
10
+ @data = data
11
+ end
12
+
13
+ attr_reader :name, :data
14
+
15
+ def summary_fields
16
+ [
17
+ :severity,
18
+ :correlation_id,
19
+ :time,
20
+ :message
21
+ ]
22
+ end
23
+
24
+ def summary
25
+ summary = {}
26
+
27
+ summary_fields.each do |field|
28
+ value = data[field]
29
+ summary[field] = value unless value.nil?
30
+ end
31
+
32
+ summary
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GitlabQuality
4
+ module TestTooling
5
+ module SystemLogs
6
+ module LogTypes
7
+ module Rails
8
+ class ApiLog < Log
9
+ include SharedFields::Exception
10
+ include SharedFields::Meta
11
+
12
+ def initialize(data)
13
+ super('Rails API', data)
14
+ end
15
+
16
+ def summary_fields
17
+ super.concat(
18
+ [
19
+ :method,
20
+ :path,
21
+ :status,
22
+ :params,
23
+ :api_error
24
+ ],
25
+ exception_fields,
26
+ meta_fields
27
+ )
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GitlabQuality
4
+ module TestTooling
5
+ module SystemLogs
6
+ module LogTypes
7
+ module Rails
8
+ class ApplicationLog < Log
9
+ include SharedFields::Exception
10
+ include SharedFields::Meta
11
+
12
+ def initialize(data)
13
+ super('Rails Application', data)
14
+ end
15
+
16
+ def summary_fields
17
+ super.concat(
18
+ exception_fields,
19
+ meta_fields
20
+ )
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GitlabQuality
4
+ module TestTooling
5
+ module SystemLogs
6
+ module LogTypes
7
+ module Rails
8
+ class ExceptionLog < Log
9
+ include SharedFields::Exception
10
+
11
+ def initialize(data)
12
+ super('Rails Exceptions', data)
13
+ end
14
+
15
+ def summary_fields
16
+ super.concat(exception_fields)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GitlabQuality
4
+ module TestTooling
5
+ module SystemLogs
6
+ module LogTypes
7
+ module Rails
8
+ class GraphqlLog < Log
9
+ include SharedFields::Meta
10
+
11
+ def initialize(data)
12
+ super('Rails GraphQL', data)
13
+ end
14
+
15
+ def summary_fields
16
+ super.concat(
17
+ [
18
+ :operation_name,
19
+ :query_string,
20
+ :variables
21
+ ],
22
+ meta_fields
23
+ )
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GitlabQuality
4
+ module TestTooling
5
+ module SystemLogs
6
+ module SharedFields
7
+ module Meta
8
+ def meta_fields
9
+ [
10
+ :meta_user,
11
+ :meta_project,
12
+ :meta_caller_id
13
+ ]
14
+ end
15
+ end
16
+
17
+ module Exception
18
+ def exception_fields
19
+ [
20
+ :exception_class,
21
+ :exception_message,
22
+ :exception_backtrace
23
+ ]
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GitlabQuality
4
+ module TestTooling
5
+ module SystemLogs
6
+ class SystemLogsFormatter
7
+ NUM_OF_LOG_SECTIONS = 4
8
+
9
+ def initialize(base_paths, correlation_id)
10
+ @base_paths = base_paths
11
+ @correlation_id = correlation_id
12
+ end
13
+
14
+ def system_logs_summary_markdown
15
+ log_sections = Array.new(NUM_OF_LOG_SECTIONS) { [] }
16
+
17
+ @base_paths.each do |base_path|
18
+ all_logs = [
19
+ Finders::Rails::ApiLogFinder.new(base_path).find(@correlation_id),
20
+ Finders::Rails::ExceptionLogFinder.new(base_path).find(@correlation_id),
21
+ Finders::Rails::ApplicationLogFinder.new(base_path).find(@correlation_id),
22
+ Finders::Rails::GraphqlLogFinder.new(base_path).find(@correlation_id)
23
+ ]
24
+
25
+ create_log_summary_sections!(all_logs, log_sections)
26
+ end
27
+
28
+ log_sections.prepend('### System Logs') unless log_sections.all?(&:empty?)
29
+ log_sections.join("\n").rstrip
30
+ end
31
+
32
+ private
33
+
34
+ def create_log_summary_sections!(all_logs, sections)
35
+ sections.zip(all_logs) do |section, logs|
36
+ unless logs.empty?
37
+ section_title = "\n#### #{logs.first.name}"
38
+ section.append(section_title) unless section.include?(section_title)
39
+ section.append(create_log_summaries(logs))
40
+ end
41
+ end
42
+ end
43
+
44
+ def create_log_summaries(logs)
45
+ section = []
46
+
47
+ logs.each do |log|
48
+ log_summary = <<~MARKDOWN.chomp
49
+ <details><summary>Click to expand</summary>
50
+
51
+ ```json
52
+ #{JSON.pretty_generate(log.summary)}
53
+ ```
54
+ </details>
55
+ MARKDOWN
56
+
57
+ section.append(log_summary)
58
+ end
59
+
60
+ section.join("\n\n")
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GitlabQuality
4
+ module TestTooling
5
+ module TestResults
6
+ class BaseTestResults
7
+ include Enumerable
8
+
9
+ attr_reader :path
10
+
11
+ def initialize(path)
12
+ @path = path
13
+ @results = parse
14
+ @testcases = process
15
+ end
16
+
17
+ def each(&block)
18
+ testcases.each(&block)
19
+ end
20
+
21
+ def write
22
+ raise NotImplementedError
23
+ end
24
+
25
+ private
26
+
27
+ attr_reader :results, :testcases
28
+
29
+ def parse
30
+ raise NotImplementedError
31
+ end
32
+
33
+ def process
34
+ raise NotImplementedError
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GitlabQuality
4
+ module TestTooling
5
+ module TestResults
6
+ class Builder
7
+ def initialize(file_glob)
8
+ @file_glob = file_glob
9
+ end
10
+
11
+ def test_results_per_file
12
+ Dir.glob(file_glob).each do |path|
13
+ extension = File.extname(path)
14
+
15
+ test_results =
16
+ case extension
17
+ when '.json'
18
+ TestResults::JsonTestResults.new(path)
19
+ when '.xml'
20
+ TestResults::JUnitTestResults.new(path)
21
+ else
22
+ raise "Unknown extension #{extension}"
23
+ end
24
+
25
+ yield test_results
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ attr_reader :file_glob
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'nokogiri'
4
+
5
+ module GitlabQuality
6
+ module TestTooling
7
+ module TestResults
8
+ class JUnitTestResults < BaseTestResults
9
+ def write
10
+ # Ignore it for now
11
+ end
12
+
13
+ private
14
+
15
+ def parse
16
+ Nokogiri::XML.parse(File.read(path))
17
+ end
18
+
19
+ def process
20
+ results.xpath('//testcase').map do |test|
21
+ TestResult.from_junit(test)
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ module GitlabQuality
6
+ module TestTooling
7
+ module TestResults
8
+ class JsonTestResults < BaseTestResults
9
+ def write
10
+ json = results.merge('examples' => testcases.map(&:report))
11
+
12
+ File.write(path, JSON.pretty_generate(json))
13
+ end
14
+
15
+ private
16
+
17
+ def parse
18
+ JSON.parse(File.read(path))
19
+ end
20
+
21
+ def process
22
+ results['examples'].map do |test|
23
+ TestResult.from_json(test)
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end