bugsnag-maze-runner 9.13.0 → 9.14.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0faaa4d935a7db46b9c6bd717fb7a5679e30c4add8e9a8010ae33708ee9a9cca
4
- data.tar.gz: afee3aba6ca6e9e6a917bd8c243c4e118426bfa568aa5f97d9b8d2f175c2568c
3
+ metadata.gz: eb842b24909ce9e347d04a2c37cdd108f22a5261faa41566188cb7eae9b7f8a0
4
+ data.tar.gz: 9d17559ba0c60a8707629dca8d9bf43a9d599a7f314750451c5a75beead78f5b
5
5
  SHA512:
6
- metadata.gz: 680e8b3f617e71d67fd3a8cf5fa24303cbbfcb0ea853b63bb306d133a3c7bdcc323da256892b5dcd8367d8440f4000d393e74353591831ab26b3c3f565c2e265
7
- data.tar.gz: 827d8aa95c7dc4b18ef253ae96e24e3ad7f3b0e61c0175474c86c35fa60ba9d47872e19fecfbb8167259c5b874349e5c1f470bd2c14e7aeebb0ac08e973153c0
6
+ metadata.gz: a44150cac8caac4e3e555857d7eb46749e86e56015b8a8d9404af4281b953b86c7dd55b5b54aabb8216ef2d993e91a1285f617986f75565bb486a18b376895f2
7
+ data.tar.gz: 04a683fc786c1ac1d0bebc4b0422002423545d9af567693eaa5d0e96a3b760952ed34f5ac9da7da3cb17d096c508dc236b657e16b5198db5fd2a34a6afcc6c15
data/bin/maze-runner CHANGED
@@ -65,8 +65,6 @@ require_relative '../lib/maze/server'
65
65
 
66
66
  require_relative '../lib/maze/assertions/request_set_assertions'
67
67
 
68
- require_relative '../lib/maze/schemas/trace_schema'
69
- require_relative '../lib/maze/schemas/trace_validator'
70
68
  require_relative '../lib/maze/schemas/validator'
71
69
 
72
70
  require_relative '../lib/maze/store'
@@ -103,6 +101,25 @@ require_relative '../lib/utils/selenium_money_patch'
103
101
  # Encapsulates the MazeRunner entry point
104
102
  class MazeRunnerEntry
105
103
 
104
+ def read_options_file(filename)
105
+ return unless File.exist?(filename)
106
+
107
+ $logger.info "Reading command line options from #{filename}"
108
+ File.readlines(filename).each do |line|
109
+ line.strip!
110
+ next if line.empty? || line.start_with?('#')
111
+ @args << line
112
+ end
113
+ end
114
+
115
+ # Loads options from config files
116
+ def load_options_from_files
117
+ all_file = File.join('features', 'support', 'maze.all.cfg')
118
+ buildkite_file = File.join('features', 'support', 'maze.buildkite.cfg')
119
+ read_options_file(all_file)
120
+ read_options_file(buildkite_file) if ENV['BUILDKITE']
121
+ end
122
+
106
123
  # Removes Maze Runner specific args from the array, as these will cause Cucumber to error.
107
124
  def remove_maze_runner_args
108
125
  Maze::Option.constants.each do |opt|
@@ -134,6 +151,13 @@ class MazeRunnerEntry
134
151
  @args << "--expand"
135
152
  end
136
153
 
154
+ # Check if we've set ENV['MAZE_NO_FAIL_FAST'] to override the fail fast behaviour
155
+ # And remove the --fail-fast option if it's set
156
+ if ENV['MAZE_NO_FAIL_FAST'] && @args.include?('--fail-fast')
157
+ @args = @args - ['--fail-fast']
158
+ $logger.info 'Suppressing --fail-fast option as MAZE_NO_FAIL_FAST is set.'
159
+ end
160
+
137
161
  # Load internal steps and helper functions
138
162
  load_dir = File.expand_path(File.dirname(File.dirname(__FILE__))).freeze
139
163
  paths = Dir.glob("#{load_dir}/lib/features/**/*.rb")
@@ -187,6 +211,7 @@ class MazeRunnerEntry
187
211
  Maze::Option::Processor.populate Maze.config, options
188
212
 
189
213
  # Adjust CL options before calling down to Cucumber
214
+ load_options_from_files
190
215
  remove_maze_runner_args
191
216
  add_cucumber_args
192
217
 
@@ -317,7 +317,6 @@ def assert_received_spans(list, min_received, max_received = nil)
317
317
 
318
318
  Maze.check.operator(max_received, :>=, received_count, "#{received_count} spans received") if max_received
319
319
 
320
- Maze::Schemas::Validator.verify_against_schema(list, 'trace')
321
320
  Maze::Schemas::Validator.validate_payload_elements(list, 'trace')
322
321
  end
323
322
 
@@ -233,7 +233,6 @@ end
233
233
  # specified for the specific endpoint
234
234
  After do |scenario|
235
235
  ['error', 'session', 'build', 'trace'].each do |endpoint|
236
- Maze::Schemas::Validator.verify_against_schema(Maze::Server.list_for(endpoint), endpoint)
237
236
  Maze::Schemas::Validator.validate_payload_elements(Maze::Server.list_for(endpoint), endpoint)
238
237
  end
239
238
  end
@@ -14,6 +14,7 @@ module Maze
14
14
  self.android_app_files_directory = nil
15
15
  self.span_timestamp_validation = true
16
16
  self.unmanaged_traces_mode = false
17
+ self.client_mode_validation = true
17
18
  @legacy_driver = false
18
19
  end
19
20
 
@@ -83,6 +84,33 @@ module Maze
83
84
  # Enables unmanaged trace mode.
84
85
  attr_accessor :unmanaged_traces_mode
85
86
 
87
+ # Custom validators to use for a given endpoint
88
+ attr_reader :custom_validators
89
+
90
+ # Consumes a block that will be triggered when a request is received for a specific endpoint
91
+ # This will prevent any existing default validation from triggering.
92
+ #
93
+ # @param endpoint [String] The endpoint to validate
94
+ # @param validator [Proc] The block to run when a request is received
95
+ def add_validator(endpoint, &validator)
96
+ @custom_validators ||= {}
97
+ @custom_validators[endpoint] = validator
98
+ end
99
+
100
+ # Whether default validation should be skipped for a given endpoint
101
+ attr_reader :skipped_validators
102
+
103
+ # Sets whether to skip default validation for a given endpoint
104
+ #
105
+ # @param endpoint [String] The endpoint to skip default validation for
106
+ def skip_default_validation(endpoint)
107
+ @skipped_validators ||= {}
108
+ @skipped_validators[endpoint] = true
109
+ end
110
+
111
+ # Sets whether validation should be run in client mode
112
+ attr_accessor :client_mode_validation
113
+
86
114
  #
87
115
  # General appium configuration
88
116
  #
@@ -20,7 +20,7 @@ module Maze
20
20
  url = case @request_type
21
21
  when :errors then 'https://notify.bugsnag.com/'
22
22
  when :sessions then 'https://sessions.bugsnag.com/'
23
- when :traces then 'https://otlp.bugsnag.com/v1/traces'
23
+ when :traces then "https://#{Maze.config.bugsnag_repeater_api_key}.otlp.bugsnag.com/v1/traces"
24
24
  else return nil
25
25
  end
26
26
  URI.parse(url)
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../helper'
4
+ require_relative 'validator_base'
5
+
6
+ module Maze
7
+ module Schemas
8
+ class ConfigValidator < ValidatorBase
9
+
10
+ attr_accessor :success
11
+ attr_accessor :errors
12
+ attr_reader :headers
13
+ attr_reader :body
14
+
15
+ def initialize(request, validation_block)
16
+ super(request)
17
+ @validation_block = validation_block
18
+ end
19
+
20
+ def validate
21
+ @success = true
22
+ @validation_block.call(self)
23
+ rescue => exception
24
+ @success = false
25
+ @errors << "A #{exception.class} occurred while running validation: #{exception.message}, \n #{exception.backtrace.join("\n")}"
26
+
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../helper'
4
+ require_relative 'validator_base'
5
+
6
+ module Maze
7
+ module Schemas
8
+
9
+ # Contains a set of pre-defined validations for ensuring errors are correct
10
+ class ErrorValidator < ValidatorBase
11
+ end
12
+ end
13
+ end
@@ -1,84 +1,50 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative '../helper'
4
+ require_relative 'trace_schema'
5
+ require_relative 'validator_base'
4
6
 
5
7
  module Maze
6
8
  module Schemas
7
9
 
8
- HEX_STRING_16 = '^[A-Fa-f0-9]{16}$'
9
- HEX_STRING_32 = '^[A-Fa-f0-9]{32}$'
10
10
  SAMPLING_HEADER_ENTRY = '((1(.0)?|0(\.[0-9]+)?):[0-9]+)'
11
11
  SAMPLING_HEADER = "^#{SAMPLING_HEADER_ENTRY}(;#{SAMPLING_HEADER_ENTRY})*$"
12
- HOUR_TOLERANCE = 60 * 60 * 1000 * 1000 * 1000 # 1 hour in nanoseconds
13
12
 
14
13
  # Contains a set of pre-defined validations for ensuring traces are correct
15
- class TraceValidator
16
-
17
- # Whether the trace passed the validation, one of true, false, or nil (not run)
18
- # @returns [Boolean|nil] Whether the validation was successful
19
- attr_reader :success
20
- attr_reader :errors
21
-
22
- # Creates the validator
23
- #
24
- # @param request [Hash] The trace request to validate
25
- def initialize(request)
26
- @headers = request[:request].header
27
- @body = request[:body]
28
- @success = nil
29
- @errors = []
30
- end
31
-
14
+ class TraceValidator < ValidatorBase
32
15
  # Runs the validation against the trace given
33
16
  def validate
17
+ # The tests are being run
34
18
  @success = true
35
-
19
+ verify_against_schema
36
20
  validate_headers
37
21
  regex_comparison('resourceSpans.0.scopeSpans.0.spans.0.spanId', HEX_STRING_16)
38
22
  regex_comparison('resourceSpans.0.scopeSpans.0.spans.0.traceId', HEX_STRING_32)
39
23
  element_int_in_range('resourceSpans.0.scopeSpans.0.spans.0.kind', 0..5)
40
24
  regex_comparison('resourceSpans.0.scopeSpans.0.spans.0.startTimeUnixNano', '^[0-9]+$')
41
25
  regex_comparison('resourceSpans.0.scopeSpans.0.spans.0.endTimeUnixNano', '^[0-9]+$')
42
- element_contains('resourceSpans.0.resource.attributes', 'device.id')
43
- each_element_contains('resourceSpans.0.scopeSpans.0.spans', 'attributes', 'bugsnag.sampling.p')
44
- element_contains('resourceSpans.0.resource.attributes', 'deployment.environment')
45
- element_contains('resourceSpans.0.resource.attributes', 'telemetry.sdk.name')
46
- element_contains('resourceSpans.0.resource.attributes', 'telemetry.sdk.version')
26
+ each_span_element_contains('resourceSpans.0.scopeSpans.0.spans', 'attributes', 'bugsnag.sampling.p')
27
+ span_element_contains('resourceSpans.0.resource.attributes', 'deployment.environment')
28
+ span_element_contains('resourceSpans.0.resource.attributes', 'telemetry.sdk.name')
29
+ span_element_contains('resourceSpans.0.resource.attributes', 'telemetry.sdk.version')
47
30
  validate_timestamp('resourceSpans.0.scopeSpans.0.spans.0.startTimeUnixNano', HOUR_TOLERANCE)
48
31
  validate_timestamp('resourceSpans.0.scopeSpans.0.spans.0.endTimeUnixNano', HOUR_TOLERANCE)
49
32
  element_a_greater_or_equal_element_b(
50
33
  'resourceSpans.0.scopeSpans.0.spans.0.endTimeUnixNano',
51
34
  'resourceSpans.0.scopeSpans.0.spans.0.startTimeUnixNano'
52
35
  )
53
- end
54
36
 
55
- def validate_timestamp(path, tolerance)
56
- return unless Maze.config.span_timestamp_validation
57
- timestamp = Maze::Helper.read_key_path(@body, path)
58
- unless timestamp.kind_of?(String)
59
- @success = false
60
- @errors << "Timestamp was expected to be a string, was '#{timestamp.class.name}'"
61
- return
62
- end
63
- parsed_timestamp = timestamp.to_i
64
- unless parsed_timestamp > 0
65
- @success = false
66
- @errors << "Timestamp was expected to be a positive integer, was '#{parsed_timestamp}'"
67
- return
68
- end
69
- time_in_nanos = Time.now.to_i * 1000000000
70
- unless (time_in_nanos - parsed_timestamp).abs < tolerance
71
- @success = false
72
- @errors << "Timestamp was expected to be within #{tolerance} nanoseconds of the current time (#{time_in_nanos}), was '#{parsed_timestamp}'"
37
+ if Maze.config.client_mode_validation
38
+ span_element_contains('resourceSpans.0.resource.attributes', 'device.id')
73
39
  end
74
40
  end
75
41
 
76
- def validate_header(name)
77
- value = @headers[name]
78
- if value.nil? || value.size > 1
79
- @errors << "Expected exactly one value for header #{name}, received #{value || 'nil'}"
80
- else
81
- yield value[0]
42
+ def verify_against_schema
43
+ if !@schema_errors.nil? && @schema_errors.size > 0
44
+ @success = false
45
+ @schema_errors.each do |error|
46
+ @errors << "#{JSONSchemer::Errors.pretty(error)}"
47
+ end
82
48
  end
83
49
  end
84
50
 
@@ -118,29 +84,7 @@ module Maze
118
84
  end
119
85
  end
120
86
 
121
- def regex_comparison(path, regex)
122
- element_value = Maze::Helper.read_key_path(@body, path)
123
- expected = Regexp.new(regex)
124
- unless expected.match(element_value)
125
- @success = false
126
- @errors << "Element '#{path}' was expected to match the regex '#{regex}', but was '#{element_value}'"
127
- end
128
- end
129
-
130
- def element_int_in_range(path, range)
131
- element_value = Maze::Helper.read_key_path(@body, path)
132
- if element_value.nil? || !element_value.kind_of?(Integer)
133
- @success = false
134
- @errors << "Element '#{path}' was expected to be an integer, was '#{element_value}'"
135
- return
136
- end
137
- unless range.include?(element_value)
138
- @success = false
139
- @errors << "Element '#{path}':'#{element_value}' was expected to be in the range '#{range}'"
140
- end
141
- end
142
-
143
- def element_contains(path, key_value, value_type=nil, possible_values=nil)
87
+ def span_element_contains(path, key_value, value_type=nil, possible_values=nil)
144
88
  container = Maze::Helper.read_key_path(@body, path)
145
89
  if container.nil? || !container.kind_of?(Array)
146
90
  @success = false
@@ -161,7 +105,7 @@ module Maze
161
105
  end
162
106
  end
163
107
 
164
- def each_element_contains(container_path, attribute_path, key_value)
108
+ def each_span_element_contains(container_path, attribute_path, key_value)
165
109
  container = Maze::Helper.read_key_path(@body, container_path)
166
110
  if container.nil? || !container.kind_of?(Array)
167
111
  @success = false
@@ -169,16 +113,7 @@ module Maze
169
113
  return
170
114
  end
171
115
  container.each_with_index do |_item, index|
172
- element_contains("#{container_path}.#{index}.#{attribute_path}", key_value)
173
- end
174
- end
175
-
176
- def element_a_greater_or_equal_element_b(path_a, path_b)
177
- element_a = Maze::Helper.read_key_path(@body, path_a)
178
- element_b = Maze::Helper.read_key_path(@body, path_b)
179
- unless element_a && element_b && element_a >= element_b
180
- @success = false
181
- @errors << "Element '#{path_a}':'#{element_a}' was expected to be greater than or equal to '#{path_b}':'#{element_b}'"
116
+ span_element_contains("#{container_path}.#{index}.#{attribute_path}", key_value)
182
117
  end
183
118
  end
184
119
  end
@@ -1,5 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'config_validator'
4
+ require_relative 'error_validator'
5
+ require_relative 'trace_validator'
6
+
3
7
  module Maze
4
8
  module Schemas
5
9
 
@@ -7,56 +11,42 @@ module Maze
7
11
  class Validator
8
12
 
9
13
  class << self
10
-
11
- # Tests that payloads for a specific path have passed any schema checks implemented on receipt
12
- # Throws an AssertionFailedError with a list of issues on failure
13
- #
14
- # @param list [Array] An array of received requests
15
- # @param list_name [String] The name of the payload list for received requests
16
- def verify_against_schema(list, list_name)
17
- request_schema_results = list.all.map { |request| request[:schema_errors] }
18
- passed = true
19
- request_schema_results.each.with_index(1) do |schema_errors, index|
20
- next if schema_errors.nil?
21
- if schema_errors.size > 0
22
- passed = false
23
- $stdout.puts "\n"
24
- $stdout.puts "\e[31m--- #{list_name} #{index} failed validation:\e[0m"
25
- schema_errors.each do |error|
26
- $stdout.puts "\e[31m#{JSONSchemer::Errors.pretty(error)}\e[0m"
27
- end
28
- $stdout.puts "\n"
29
- end
30
- end
31
-
32
- unless passed
33
- raise Test::Unit::AssertionFailedError.new 'The received payloads did not match the endpoint schema. A full list of the errors can be found above'
34
- end
35
- end
36
-
37
14
  # Tests that payloads for a specific path pass any additional validation checks
38
15
  # Throws an AssertionFailedError with a list of issues on failure
39
16
  #
40
17
  # @param list [Array] An array of received requests
41
18
  # @param list_name [String] The name of the payload list for received requests
42
19
  def validate_payload_elements(list, list_name)
43
- validator_class = case list_name
44
- when 'trace', 'traces'
45
- Maze::Schemas::TraceValidator
20
+ # Test to see if a custom validator exists for the list
21
+ custom_validator = Maze.config.custom_validators&.key?(list_name)
22
+
23
+ if Maze.config.skipped_validators && Maze.config.skipped_validators[list_name]
24
+ validator_class = false
46
25
  else
47
- nil
26
+ validator_class = case list_name
27
+ when 'trace', 'traces'
28
+ Maze::Schemas::TraceValidator
29
+ when 'error', 'errors'
30
+ Maze::Schemas::ErrorValidator
31
+ else
32
+ nil
33
+ end
48
34
  end
49
35
 
50
- if validator_class
51
- validators = list.all.map do |request|
52
- validator = validator_class.new(request)
53
- validator.validate
54
- validator
55
- end
36
+ list_validators = list.all.map do |request|
37
+ payload_validators = []
38
+ payload_validators << Maze::Schemas::ConfigValidator.new(request, Maze.config.custom_validators[list_name]) if custom_validator
39
+ payload_validators << validator_class.new(request) if validator_class
40
+
41
+ payload_validators.each { |validator| validator.validate }
42
+ payload_validators
43
+ end
56
44
 
57
- return if validators.all? { |validator| validator.success }
58
- validators.each.with_index(1) do |validator, index|
45
+ failing = false
46
+ list_validators.each.with_index(1) do |validators, index|
47
+ validators.each do |validator|
59
48
  unless validator.success
49
+ failing = true
60
50
  $stdout.puts "\n"
61
51
  $stdout.puts "\e[31m--- #{list_name} #{index} failed validation with the following errors:\e[0m"
62
52
  validator.errors.each do |error|
@@ -65,8 +55,8 @@ module Maze
65
55
  $stdout.puts "\n"
66
56
  end
67
57
  end
68
- raise Test::Unit::AssertionFailedError.new("One or more #{list_name} payloads failed validation. A full list of the errors can be found above")
69
58
  end
59
+ raise Test::Unit::AssertionFailedError.new("One or more #{list_name} payloads failed validation. A full list of the errors can be found above") if failing
70
60
  end
71
61
  end
72
62
  end
@@ -0,0 +1,152 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Maze
4
+ module Schemas
5
+ class ValidatorBase
6
+
7
+ HEX_STRING_16 = '^[A-Fa-f0-9]{16}$'
8
+ HEX_STRING_32 = '^[A-Fa-f0-9]{32}$'
9
+ HOUR_TOLERANCE = 60 * 60 * 1000 * 1000 * 1000 # 1 hour in nanoseconds
10
+
11
+ # Whether the payloads passed the validation, one of true, false, or nil (not run)
12
+ # @returns [Boolean|nil] Whether the validation was successful
13
+ attr_reader :success
14
+
15
+ # An array of error messages if the validation failed
16
+ # @returns [Array] The error messages
17
+ attr_reader :errors
18
+
19
+ # Creates the validator
20
+ #
21
+ # @param request [Hash] The trace request to validate
22
+ def initialize(request)
23
+ @headers = request[:request].header
24
+ @body = request[:body]
25
+ @schema_errors = request[:schema_errors]
26
+ @success = nil
27
+ @errors = []
28
+ end
29
+
30
+ def validate
31
+ # By default the validation will pass
32
+ @success = true
33
+ end
34
+
35
+ def element_has_value(path, value)
36
+ element = Maze::Helper.read_key_path(@body, path)
37
+ if element.nil? || element != value
38
+ @success = false
39
+ @errors << "Element '#{path}' was expected to be '#{value}', was '#{element}'"
40
+ end
41
+ end
42
+
43
+ def element_exists(path)
44
+ element = Maze::Helper.read_key_path(@body, path)
45
+ if element.nil?
46
+ @success = false
47
+ @errors << "Element '#{path}' was not found"
48
+ end
49
+ end
50
+
51
+ def each_element_exists(paths)
52
+ if paths.kind_of?(Array)
53
+ paths.each {|path| element_exists(path)}
54
+ else
55
+ $logger.warn("each_element_exists was called with a non-array value: '#{paths}'. Use element_exists instead.")
56
+ element_exists(paths)
57
+ end
58
+
59
+ end
60
+
61
+ def each_element_contains(container_path, path)
62
+ containers = Maze::Helper.read_key_path(@body, container_path)
63
+ containers.each_with_index do |container, index|
64
+ element = Maze::Helper.read_key_path(container, path)
65
+ if element.nil?
66
+ @success = false
67
+ @errors << "Required #{container_path} element #{path} was not present at index #{index}"
68
+ end
69
+ end
70
+ end
71
+
72
+ def each_event_contains(path)
73
+ each_element_contains('events', path)
74
+ end
75
+
76
+ def each_element_contains_each(container_path, paths)
77
+ paths.each { |path| each_element_contains(container_path, path) }
78
+ end
79
+
80
+ def each_event_contains_each(paths)
81
+ paths.each { |path| each_event_contains(path) }
82
+ end
83
+
84
+ def regex_comparison(path, regex)
85
+ element_value = Maze::Helper.read_key_path(@body, path)
86
+ expected = Regexp.new(regex)
87
+ unless expected.match(element_value)
88
+ @success = false
89
+ @errors << "Element '#{path}' was expected to match the regex '#{regex}', but was '#{element_value}'"
90
+ end
91
+ end
92
+
93
+ def element_int_in_range(path, range)
94
+ element_value = Maze::Helper.read_key_path(@body, path)
95
+ if element_value.nil? || !element_value.kind_of?(Integer)
96
+ @success = false
97
+ @errors << "Element '#{path}' was expected to be an integer, was '#{element_value}'"
98
+ return
99
+ end
100
+ unless range.include?(element_value)
101
+ @success = false
102
+ @errors << "Element '#{path}':'#{element_value}' was expected to be in the range '#{range}'"
103
+ end
104
+ end
105
+
106
+ def element_a_greater_or_equal_element_b(path_a, path_b)
107
+ element_a = Maze::Helper.read_key_path(@body, path_a)
108
+ element_b = Maze::Helper.read_key_path(@body, path_b)
109
+ unless element_a && element_b && element_a >= element_b
110
+ @success = false
111
+ @errors << "Element '#{path_a}':'#{element_a}' was expected to be greater than or equal to '#{path_b}':'#{element_b}'"
112
+ end
113
+ end
114
+
115
+ def validate_timestamp(path, tolerance)
116
+ return unless Maze.config.span_timestamp_validation
117
+ timestamp = Maze::Helper.read_key_path(@body, path)
118
+ unless timestamp.kind_of?(String)
119
+ @success = false
120
+ @errors << "Timestamp was expected to be a string, was '#{timestamp.class.name}'"
121
+ return
122
+ end
123
+ parsed_timestamp = timestamp.to_i
124
+ unless parsed_timestamp > 0
125
+ @success = false
126
+ @errors << "Timestamp was expected to be a positive integer, was '#{parsed_timestamp}'"
127
+ return
128
+ end
129
+ time_in_nanos = Time.now.to_i * 1000000000
130
+ unless (time_in_nanos - parsed_timestamp).abs < tolerance
131
+ @success = false
132
+ @errors << "Timestamp was expected to be within #{tolerance} nanoseconds of the current time (#{time_in_nanos}), was '#{parsed_timestamp}'"
133
+ end
134
+ end
135
+
136
+ def validate_header(name)
137
+ begin
138
+ value = @headers[name]
139
+ if value.nil? || value.size > 1
140
+ @success = false
141
+ @errors << "Expected exactly one value for header #{name}, received #{value || 'nil'}"
142
+ else
143
+ yield value[0]
144
+ end
145
+ rescue => e
146
+ @success = false
147
+ @errors << "Error validating header #{name} with value #{value}: #{e.message}"
148
+ end
149
+ end
150
+ end
151
+ end
152
+ end
data/lib/maze.rb CHANGED
@@ -7,7 +7,7 @@ require_relative 'maze/timers'
7
7
  # Glues the various parts of MazeRunner together that need to be accessed globally,
8
8
  # providing an alternative to the proliferation of global variables or singletons.
9
9
  module Maze
10
- VERSION = '9.13.0'
10
+ VERSION = '9.14.0'
11
11
 
12
12
  class << self
13
13
  attr_accessor :check, :driver, :internal_hooks, :mode, :start_time, :dynamic_retry, :public_address,
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bugsnag-maze-runner
3
3
  version: !ruby/object:Gem::Version
4
- version: 9.13.0
4
+ version: 9.14.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Steve Kirkland
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-08-14 00:00:00.000000000 Z
11
+ date: 2024-09-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: cucumber
@@ -440,9 +440,12 @@ files:
440
440
  - lib/maze/retry_handler.rb
441
441
  - lib/maze/runner.rb
442
442
  - lib/maze/schemas/OtelTraceSchema.json
443
+ - lib/maze/schemas/config_validator.rb
444
+ - lib/maze/schemas/error_validator.rb
443
445
  - lib/maze/schemas/trace_schema.rb
444
446
  - lib/maze/schemas/trace_validator.rb
445
447
  - lib/maze/schemas/validator.rb
448
+ - lib/maze/schemas/validator_base.rb
446
449
  - lib/maze/server.rb
447
450
  - lib/maze/servlets/all_commands_servlet.rb
448
451
  - lib/maze/servlets/base_servlet.rb