bugsnag-maze-runner 8.2.0 → 8.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/features/steps/deprecated_steps.rb +8 -0
- data/lib/features/steps/request_assertion_steps.rb +12 -1
- data/lib/features/steps/trace_steps.rb +35 -6
- data/lib/features/support/internal_hooks.rb +14 -1
- data/lib/maze/api/appium/file_manager.rb +4 -2
- data/lib/maze/client/appium/bs_devices.rb +0 -1
- data/lib/maze/client/selenium/bs_browsers.yml +1 -1
- data/lib/maze/configuration.rb +4 -0
- data/lib/maze/schemas/trace_validator.rb +55 -5
- data/lib/maze/schemas/validator.rb +1 -1
- data/lib/maze.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5f3d1e632ba5e9853751cbe8dfafd2fc77b754754ef9c3036ff9f68ccce5f983
|
4
|
+
data.tar.gz: 54a4d8d1341998c95f15af9a0a15b901eeeb9af00974dd74d196ec2779958883
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 225110ab9babc625e187c005217d0b4ea7568b1c55c137a55583dc1713b1728196ae5d3aa5c64ec51fcfb6640118e1ee7e5eb110cab753be718c9e64a1a28c6c
|
7
|
+
data.tar.gz: 3cf95b41ed9dfa2695f73563d5da3e9d7b32e1a4629bdd74b64f9132e206450a18dd09590ee16976091bb0372a945801982aa81c16535246bd9888ce477c189e
|
@@ -0,0 +1,8 @@
|
|
1
|
+
# @!group Deprecated steps
|
2
|
+
|
3
|
+
# Waits for a given number of spans to be received, which may be spread across one or more trace requests.
|
4
|
+
#
|
5
|
+
# @step_input span_count [Integer] The number of spans to wait for
|
6
|
+
When('I wait for {int} span(s)') do |span_count|
|
7
|
+
assert_received_spans Maze::Server.list_for('traces'), span_count
|
8
|
+
end
|
@@ -8,7 +8,7 @@ require_relative '../../maze/wait'
|
|
8
8
|
|
9
9
|
# @!group Request assertion steps
|
10
10
|
|
11
|
-
def assert_received_requests(request_count, list, list_name, precise = true)
|
11
|
+
def assert_received_requests(request_count, list, list_name, precise = true, maximum = nil)
|
12
12
|
timeout = Maze.config.receive_requests_wait
|
13
13
|
# Interval set to 0.5s to make it more likely to detect erroneous extra requests,
|
14
14
|
# without impacting overall speed too much
|
@@ -45,6 +45,7 @@ def assert_received_requests(request_count, list, list_name, precise = true)
|
|
45
45
|
Maze.check.equal(request_count, list.size_remaining, "#{list.size_remaining} #{list_name} received")
|
46
46
|
else
|
47
47
|
Maze.check.operator(request_count, :<=, list.size_remaining, "#{list.size_remaining} #{list_name} received")
|
48
|
+
Maze.check.operator(maximum, :>=, list.size_remaining, "#{list.size_remaining} #{list_name} received") unless maximum.nil?
|
48
49
|
end
|
49
50
|
end
|
50
51
|
|
@@ -102,6 +103,16 @@ Then('I have received at least {int} {request_type}') do |min_received, request_
|
|
102
103
|
Maze.check.operator(list.size_remaining, :>=, min_received, "Actually received #{list.size_remaining} #{request_type} requests")
|
103
104
|
end
|
104
105
|
|
106
|
+
# Verify that an amount of requests within a range have been received
|
107
|
+
#
|
108
|
+
# @step_input min_received [Integer] The minimum amount of requests required to pass
|
109
|
+
# @step_input max_received [Integer] The maximum amount of requests before failure
|
110
|
+
# @step_input request_type [String] The type of request (error, session, build, etc)
|
111
|
+
Then('I wait to receive between {int} and {int} {request_type}') do |min_received, max_received, request_type|
|
112
|
+
list = Maze::Server.list_for(request_type)
|
113
|
+
assert_received_requests min_received, list, request_type, false, max_received
|
114
|
+
end
|
115
|
+
|
105
116
|
# Assert that the test Server hasn't received any requests - of a specific, or any, type.
|
106
117
|
#
|
107
118
|
# @step_input request_type [String] The type of request ('error', 'session', 'trace', sampling request', etc)
|
@@ -3,8 +3,25 @@
|
|
3
3
|
# Waits for a given number of spans to be received, which may be spread across one or more trace requests.
|
4
4
|
#
|
5
5
|
# @step_input span_count [Integer] The number of spans to wait for
|
6
|
-
|
7
|
-
|
6
|
+
Then('I wait to receive {int} span(s)') do |span_count|
|
7
|
+
assert_received_span_count Maze::Server.list_for('traces'), span_count
|
8
|
+
end
|
9
|
+
|
10
|
+
# Waits for a minimum number of spans to be received, which may be spread across one or more trace requests.
|
11
|
+
# If more spans than requested are received, this step will still pass.
|
12
|
+
#
|
13
|
+
# @step_input span_min [Integer] The minimum number of spans to wait for
|
14
|
+
Then('I wait to receive at least {int} span(s)') do |span_min|
|
15
|
+
assert_received_minimum_span_count Maze::Server.list_for('traces'), span_min
|
16
|
+
end
|
17
|
+
|
18
|
+
# Waits for a minimum number of spans to be received, which may be spread across one or more trace requests.
|
19
|
+
# If more spans than the maximum requested number of spans are received, this step will fail.
|
20
|
+
#
|
21
|
+
# @step_input span_min [Integer] The minimum number of spans to wait for
|
22
|
+
# @step_input span_max [Integer] The maximum number of spans to receive before failure
|
23
|
+
Then('I wait to receive between {int} and {int} span(s)') do |span_min, span_max|
|
24
|
+
assert_received_ranged_span_count Maze::Server.list_for('traces'), span_min, span_max
|
8
25
|
end
|
9
26
|
|
10
27
|
Then('I should have received no spans') do
|
@@ -227,16 +244,28 @@ def attribute_value_matches?(attribute_value, expected_type, expected_value)
|
|
227
244
|
end
|
228
245
|
end
|
229
246
|
|
230
|
-
def
|
247
|
+
def assert_received_span_count(list, count)
|
248
|
+
assert_received_spans(list, count, count)
|
249
|
+
end
|
250
|
+
|
251
|
+
def assert_received_minimum_span_count(list, minimum)
|
252
|
+
assert_received_spans(list, minimum)
|
253
|
+
end
|
254
|
+
|
255
|
+
def assert_received_ranged_span_count(list, minimum, maximum)
|
256
|
+
assert_received_spans(list, minimum, maximum)
|
257
|
+
end
|
258
|
+
|
259
|
+
def assert_received_spans(list, min_received, max_received = nil)
|
231
260
|
timeout = Maze.config.receive_requests_wait
|
232
261
|
wait = Maze::Wait.new(timeout: timeout)
|
233
262
|
|
234
|
-
received = wait.until { spans_from_request_list(list).size >=
|
263
|
+
received = wait.until { spans_from_request_list(list).size >= min_received }
|
235
264
|
received_count = spans_from_request_list(list).size
|
236
265
|
|
237
266
|
unless received
|
238
267
|
raise Test::Unit::AssertionFailedError.new <<-MESSAGE
|
239
|
-
Expected #{
|
268
|
+
Expected #{min_received} spans but received #{received_count} within the #{timeout}s timeout.
|
240
269
|
This could indicate that:
|
241
270
|
- Bugsnag crashed with a fatal error.
|
242
271
|
- Bugsnag did not make the requests that it should have done.
|
@@ -246,7 +275,7 @@ def assert_received_spans(span_count, list)
|
|
246
275
|
MESSAGE
|
247
276
|
end
|
248
277
|
|
249
|
-
Maze.check.operator(
|
278
|
+
Maze.check.operator(max_received, :>=, received_count, "#{received_count} spans received") if max_received
|
250
279
|
|
251
280
|
Maze::Schemas::Validator.verify_against_schema(list, 'trace')
|
252
281
|
Maze::Schemas::Validator.validate_payload_elements(list, 'trace')
|
@@ -30,12 +30,14 @@ BeforeAll do
|
|
30
30
|
end
|
31
31
|
$logger.info "Running in #{Maze.mode.to_s} mode"
|
32
32
|
|
33
|
-
# Clear out maze_output folder
|
33
|
+
# Clear out maze_output folder and zip
|
34
34
|
maze_output = Dir.glob(File.join(Dir.pwd, 'maze_output', '*'))
|
35
35
|
if Maze.config.file_log && !maze_output.empty?
|
36
36
|
maze_output.each { |path| $logger.info "Clearing contents of #{path}" }
|
37
37
|
FileUtils.rm_rf(maze_output)
|
38
38
|
end
|
39
|
+
maze_output_zip = Dir.glob(File.join(Dir.pwd, 'maze_output.zip'))
|
40
|
+
FileUtils.rm_rf(maze_output_zip)
|
39
41
|
|
40
42
|
# Record the local server starting time
|
41
43
|
Maze.start_time = Time.now.strftime('%Y-%m-%d %H:%M:%S')
|
@@ -222,6 +224,17 @@ end
|
|
222
224
|
|
223
225
|
# After all tests
|
224
226
|
AfterAll do
|
227
|
+
maze_output = File.join(Dir.pwd, 'maze_output')
|
228
|
+
maze_output_zip = File.join(Dir.pwd, 'maze_output.zip')
|
229
|
+
# zip a folder with files and subfolders
|
230
|
+
Zip::File.open(maze_output_zip, Zip::File::CREATE) do |zipfile|
|
231
|
+
Dir["#{maze_output}/**/**"].each do |file|
|
232
|
+
zipfile.add(file.sub(Dir.pwd + '/', ''), file)
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
# Move the zip file to the maze_output folder
|
237
|
+
FileUtils.mv(maze_output_zip, maze_output)
|
225
238
|
|
226
239
|
metrics = Maze::MetricsProcessor.new(Maze::Server.metrics)
|
227
240
|
metrics.process
|
@@ -9,7 +9,8 @@ module Maze
|
|
9
9
|
end
|
10
10
|
|
11
11
|
# Creates a file with the given contents on the device (using Appium). The file will be located in the app's
|
12
|
-
#
|
12
|
+
# documents directory for iOS. On Android, it will be /sdcard/Android/data/<app-id>/files unless
|
13
|
+
# Maze.config.android_app_files_directory has been set.
|
13
14
|
# @param contents [String] Content of the file to be written
|
14
15
|
# @param filename [String] Name (with no path) of the file to be written on the device
|
15
16
|
def write_app_file(contents, filename)
|
@@ -17,7 +18,8 @@ module Maze
|
|
17
18
|
when 'ios'
|
18
19
|
"@#{@driver.app_id}/Documents/#{filename}"
|
19
20
|
when 'android'
|
20
|
-
"/sdcard/Android/data/#{@driver.app_id}/files
|
21
|
+
directory = Maze.config.android_app_files_directory || "/sdcard/Android/data/#{@driver.app_id}/files"
|
22
|
+
"#{directory}/#{filename}"
|
21
23
|
end
|
22
24
|
|
23
25
|
$logger.trace "Pushing file to '#{path}' with contents: #{contents}"
|
@@ -108,7 +108,6 @@ module Maze
|
|
108
108
|
add_android 'Samsung Galaxy A8', '7.1', hash # ANDROID_7_1_SAMSUNG_GALAXY_A8
|
109
109
|
add_android 'Samsung Galaxy Note 8', '7.1', hash # ANDROID_7_1_SAMSUNG_GALAXY_NOTE_8
|
110
110
|
add_android 'Samsung Galaxy S8', '7.0', hash # ANDROID_7_0_SAMSUNG_GALAXY_S8
|
111
|
-
add_android 'Samsung Galaxy S8 Plus', '7.0', hash # ANDROID_7_0_SAMSUNG_GALAXY_S8_PLUS
|
112
111
|
|
113
112
|
# Specific iOS devices
|
114
113
|
add_ios 'iPhone 14 Plus', '16.0', hash # IOS_16_0_IPHONE_14_PLUS
|
data/lib/maze/configuration.rb
CHANGED
@@ -11,6 +11,7 @@ module Maze
|
|
11
11
|
self.receive_requests_slow_threshold = 10
|
12
12
|
self.enforce_bugsnag_integrity = true
|
13
13
|
self.captured_invalid_requests = Set[:errors, :sessions, :builds, :uploads, :sourcemaps]
|
14
|
+
self.android_app_files_directory = nil
|
14
15
|
@legacy_driver = false
|
15
16
|
end
|
16
17
|
|
@@ -102,6 +103,9 @@ module Maze
|
|
102
103
|
# Whether the device farm secure tunnel should be started
|
103
104
|
attr_accessor :start_tunnel
|
104
105
|
|
106
|
+
# Folder to push app files to on Android
|
107
|
+
attr_accessor :android_app_files_directory
|
108
|
+
|
105
109
|
#
|
106
110
|
# Device farm specific configuration
|
107
111
|
#
|
@@ -5,6 +5,11 @@ require_relative '../helper'
|
|
5
5
|
module Maze
|
6
6
|
module Schemas
|
7
7
|
|
8
|
+
HEX_STRING_16 = '^[A-Fa-f0-9]{16}$'
|
9
|
+
HEX_STRING_32 = '^[A-Fa-f0-9]{32}$'
|
10
|
+
SAMPLING_HEADER_ENTRY = '((1(.0)?|0(\.[0-9]+)?):[0-9]+)'
|
11
|
+
SAMPLING_HEADER = "^#{SAMPLING_HEADER_ENTRY}(;#{SAMPLING_HEADER_ENTRY})*$"
|
12
|
+
|
8
13
|
# Contains a set of pre-defined validations for ensuring traces are correct
|
9
14
|
class TraceValidator
|
10
15
|
|
@@ -15,9 +20,10 @@ module Maze
|
|
15
20
|
|
16
21
|
# Creates the validator
|
17
22
|
#
|
18
|
-
# @param
|
19
|
-
def initialize(
|
20
|
-
@
|
23
|
+
# @param request [Hash] The trace request to validate
|
24
|
+
def initialize(request)
|
25
|
+
@headers = request[:request].header
|
26
|
+
@body = request[:body]
|
21
27
|
@success = nil
|
22
28
|
@errors = []
|
23
29
|
end
|
@@ -26,8 +32,9 @@ module Maze
|
|
26
32
|
def validate
|
27
33
|
@success = true
|
28
34
|
|
29
|
-
|
30
|
-
regex_comparison('resourceSpans.0.scopeSpans.0.spans.0.
|
35
|
+
validate_headers
|
36
|
+
regex_comparison('resourceSpans.0.scopeSpans.0.spans.0.spanId', HEX_STRING_16)
|
37
|
+
regex_comparison('resourceSpans.0.scopeSpans.0.spans.0.traceId', HEX_STRING_32)
|
31
38
|
element_int_in_range('resourceSpans.0.scopeSpans.0.spans.0.kind', 0..5)
|
32
39
|
regex_comparison('resourceSpans.0.scopeSpans.0.spans.0.startTimeUnixNano', '^[0-9]+$')
|
33
40
|
regex_comparison('resourceSpans.0.scopeSpans.0.spans.0.endTimeUnixNano', '^[0-9]+$')
|
@@ -40,6 +47,49 @@ module Maze
|
|
40
47
|
)
|
41
48
|
end
|
42
49
|
|
50
|
+
def validate_header(name)
|
51
|
+
value = @headers[name]
|
52
|
+
if value.nil? || value.size > 1
|
53
|
+
@errors << "Expected exactly one value for header #{name}, received #{value || 'nil'}"
|
54
|
+
else
|
55
|
+
yield value[0]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Checks that the required headers are present and correct
|
60
|
+
def validate_headers
|
61
|
+
# API key
|
62
|
+
validate_header('bugsnag-api-key') do |api_key|
|
63
|
+
expected = Regexp.new(HEX_STRING_32)
|
64
|
+
unless expected.match(api_key)
|
65
|
+
@success = false
|
66
|
+
@errors << "bugsnag-api-key header was expected to match the regex '#{HEX_STRING_32}', but was '#{api_key}'"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# Bugsnag-Sent-at
|
71
|
+
validate_header('bugsnag-sent-at') do |date|
|
72
|
+
begin
|
73
|
+
Date.iso8601(date)
|
74
|
+
rescue Date::Error
|
75
|
+
@success = false
|
76
|
+
@errors << "bugsnag-sent-at header was expected to be an IOS 8601 date, but was '#{date}'"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# Bugsnag-Span-Sampling
|
81
|
+
# of the format x:y where x is a decimal between 0 and 1 (inclusive) and y is the number of spans in the batch (if possible at this stage - we could weaken this if necessary)
|
82
|
+
validate_header('bugsnag-span-sampling') do |sampling|
|
83
|
+
begin
|
84
|
+
expected = Regexp.new(SAMPLING_HEADER)
|
85
|
+
unless expected.match(sampling)
|
86
|
+
@success = false
|
87
|
+
@errors << "bugsnag-span-sampling header was expected to match the regex '#{SAMPLING_HEADER}', but was '#{sampling}'"
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
43
93
|
def regex_comparison(path, regex)
|
44
94
|
element_value = Maze::Helper.read_key_path(@body, path)
|
45
95
|
expected = Regexp.new(regex)
|
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 = '8.
|
10
|
+
VERSION = '8.4.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: 8.
|
4
|
+
version: 8.4.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: 2023-
|
11
|
+
date: 2023-08-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: cucumber
|
@@ -347,6 +347,7 @@ files:
|
|
347
347
|
- lib/features/steps/breadcrumb_steps.rb
|
348
348
|
- lib/features/steps/browser_steps.rb
|
349
349
|
- lib/features/steps/build_api_steps.rb
|
350
|
+
- lib/features/steps/deprecated_steps.rb
|
350
351
|
- lib/features/steps/document_server_steps.rb
|
351
352
|
- lib/features/steps/error_reporting_steps.rb
|
352
353
|
- lib/features/steps/feature_flag_steps.rb
|