bugsnag-maze-runner 10.4.1 → 10.6.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.
- checksums.yaml +4 -4
- data/bin/maze-runner +1 -0
- data/lib/features/steps/span_steps.rb +175 -0
- data/lib/features/steps/trace_steps.rb +0 -219
- data/lib/features/steps/value_steps.rb +5 -0
- data/lib/features/support/internal_hooks.rb +1 -1
- data/lib/features/support/span_support.rb +63 -0
- data/lib/maze/client/selenium/bs_browsers.yml +6 -48
- data/lib/maze/request_list.rb +3 -1
- data/lib/maze/server.rb +1 -0
- data/lib/maze/servlets/idempotent_command_servlet.rb +86 -0
- data/lib/maze.rb +1 -1
- metadata +5 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 12e2b7e1d8f5c4a28703fdb5840d5761303008e4afb80d6653c91001e44139a8
|
|
4
|
+
data.tar.gz: ddaa085ac42386037eaa09c665a9a7b27746921769718ab707c2ef1b4d943c02
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: fec79817d83e81db975f98a8e01cb759179d3bdd2052058aaabfdeb078a21b95f47e9dfe0d724bf4ec33390cc2cc83a2c888470357ac7f3d441e4768a2482c32
|
|
7
|
+
data.tar.gz: 18c1eadc34738d6cf0aebc27191303d74e87d3f6a76e7e10365291c948aaf7847214e2bb81b8d7e31ea6dd306c1eccd80cc11662a6a24c75e4b4e32bbe7ab9b7
|
data/bin/maze-runner
CHANGED
|
@@ -62,6 +62,7 @@ require_relative '../lib/maze/loggers/logger'
|
|
|
62
62
|
require_relative '../lib/maze/servlets/base_servlet'
|
|
63
63
|
require_relative '../lib/maze/servlets/all_commands_servlet'
|
|
64
64
|
require_relative '../lib/maze/servlets/command_servlet'
|
|
65
|
+
require_relative '../lib/maze/servlets/idempotent_command_servlet'
|
|
65
66
|
require_relative '../lib/maze/servlets/error_config_servlet'
|
|
66
67
|
require_relative '../lib/maze/servlets/servlet'
|
|
67
68
|
require_relative '../lib/maze/servlets/log_servlet'
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# @!group Span steps
|
|
2
|
+
|
|
3
|
+
# Disable timestamp validation for spans
|
|
4
|
+
#
|
|
5
|
+
When('I disable timestamp validation for spans') do
|
|
6
|
+
Maze.config.span_timestamp_validation = false
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
# Waits for a given number of spans to be received, which may be spread across one or more trace requests.
|
|
10
|
+
#
|
|
11
|
+
# @step_input span_count [Integer] The number of spans to wait for
|
|
12
|
+
Then('I wait to receive {int} span(s)') do |span_count|
|
|
13
|
+
SpanSupport.assert_received_span_count Maze::Server.traces, span_count
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Waits for a minimum number of spans to be received, which may be spread across one or more trace requests.
|
|
17
|
+
# If more spans than requested are received, this step will still pass.
|
|
18
|
+
#
|
|
19
|
+
# @step_input span_min [Integer] The minimum number of spans to wait for
|
|
20
|
+
Then('I wait to receive at least {int} span(s)') do |span_min|
|
|
21
|
+
SpanSupport.assert_received_minimum_span_count Maze::Server.traces, span_min
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Waits for a minimum number of spans to be received, which may be spread across one or more trace requests.
|
|
25
|
+
# If more spans than the maximum requested number of spans are received, this step will fail.
|
|
26
|
+
#
|
|
27
|
+
# @step_input span_min [Integer] The minimum number of spans to wait for
|
|
28
|
+
# @step_input span_max [Integer] The maximum number of spans to receive before failure
|
|
29
|
+
Then('I wait to receive between {int} and {int} span(s)') do |span_min, span_max|
|
|
30
|
+
SpanSupport.assert_received_ranged_span_count Maze::Server.traces, span_min, span_max
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
Then('I should have received no spans') do
|
|
34
|
+
sleep Maze.config.receive_no_requests_wait
|
|
35
|
+
Maze.check.equal SpanSupport.spans_from_request_list(Maze::Server.traces).size, 0
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
Then('a span {word} equals {string}') do |attribute, expected|
|
|
39
|
+
spans = SpanSupport.spans_from_request_list(Maze::Server.traces)
|
|
40
|
+
selected_attributes = spans.map { |span| span[attribute] }
|
|
41
|
+
Maze.check.includes selected_attributes, expected
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
Then('every span field {string} equals {string}') do |key, expected|
|
|
45
|
+
spans = SpanSupport.spans_from_request_list(Maze::Server.traces)
|
|
46
|
+
selected_keys = spans.map { |span| span[key] == expected }
|
|
47
|
+
Maze.check.not_includes selected_keys, false
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
Then('every span field {string} matches the regex {string}') do |key, pattern|
|
|
51
|
+
spans = SpanSupport.spans_from_request_list(Maze::Server.traces)
|
|
52
|
+
spans.map { |span| Maze.check.match pattern, span[key] }
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
Then('every span string attribute {string} exists') do |attribute|
|
|
56
|
+
spans = SpanSupport.spans_from_request_list(Maze::Server.traces)
|
|
57
|
+
spans.map { |span| Maze.check.not_nil span['attributes'].find { |a| a['key'] == attribute }['value']['stringValue'] }
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
Then('every span string attribute {string} equals {string}') do |attribute, expected|
|
|
61
|
+
spans = SpanSupport.spans_from_request_list(Maze::Server.traces)
|
|
62
|
+
spans.map { |span| Maze.check.equal expected, span['attributes'].find { |a| a['key'] == attribute }['value']['stringValue'] }
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
Then('every span string attribute {string} matches the regex {string}') do |attribute, pattern|
|
|
66
|
+
spans = SpanSupport.spans_from_request_list(Maze::Server.traces)
|
|
67
|
+
spans.map { |span| Maze.check.match pattern, span['attributes'].find { |a| a['key'] == attribute }['value']['stringValue'] }
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
Then('every span integer attribute {string} is greater than {int}') do |attribute, expected|
|
|
71
|
+
spans = SpanSupport.spans_from_request_list(Maze::Server.traces)
|
|
72
|
+
spans.map { |span| Maze::check.true span['attributes'].find { |a| a['key'] == attribute }['value']['intValue'].to_i > expected }
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
Then('every span bool attribute {string} is true') do |attribute|
|
|
76
|
+
spans = SpanSupport.spans_from_request_list(Maze::Server.traces)
|
|
77
|
+
spans.map { |span| Maze::check.true span['attributes'].find { |a| a['key'] == attribute }['value']['boolValue'] }
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
Then('a span string attribute {string} exists') do |attribute|
|
|
81
|
+
spans = SpanSupport.spans_from_request_list(Maze::Server.traces)
|
|
82
|
+
selected_attributes = spans.map { |span| span['attributes'].find { |a| a['key'].eql?(attribute) && a['value'].has_key?('stringValue') } }.compact
|
|
83
|
+
Maze.check.false(selected_attributes.empty?)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
Then('a span string attribute {string} equals {string}') do |attribute, expected|
|
|
87
|
+
spans = SpanSupport.spans_from_request_list(Maze::Server.traces)
|
|
88
|
+
selected_attributes = spans.map { |span| span['attributes'].find { |a| a['key'].eql?(attribute) && a['value'].has_key?('stringValue') } }.compact
|
|
89
|
+
attribute_values = selected_attributes.map { |a| a['value']['stringValue'] }
|
|
90
|
+
Maze.check.includes attribute_values, expected
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
Then('a span field {string} equals {string}') do |key, expected|
|
|
94
|
+
spans = SpanSupport.spans_from_request_list(Maze::Server.traces)
|
|
95
|
+
selected_keys = spans.map { |span| span[key] }
|
|
96
|
+
Maze.check.includes selected_keys, expected
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
Then('a span field {string} equals {int}') do |key, expected|
|
|
100
|
+
spans = SpanSupport.spans_from_request_list(Maze::Server.traces)
|
|
101
|
+
selected_keys = spans.map { |span| span[key] }
|
|
102
|
+
Maze.check.includes selected_keys, expected
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
Then('a span field {string} matches the regex {string}') do |attribute, pattern|
|
|
106
|
+
regex = Regexp.new pattern
|
|
107
|
+
spans = SpanSupport.spans_from_request_list(Maze::Server.traces)
|
|
108
|
+
selected_attributes = spans.select { |span| regex.match? span[attribute] }
|
|
109
|
+
|
|
110
|
+
Maze.check.false(selected_attributes.empty?)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
Then('a span named {string} contains the attributes:') do |span_name, table|
|
|
114
|
+
named_spans = SpanSupport.get_named_spans(span_name)
|
|
115
|
+
expected_attributes = table.hashes
|
|
116
|
+
|
|
117
|
+
match = false
|
|
118
|
+
named_spans.each do |span|
|
|
119
|
+
matches = expected_attributes.map do |expected_attribute|
|
|
120
|
+
span['attributes'].find_all { |attribute| attribute['key'].eql?(expected_attribute['attribute']) }
|
|
121
|
+
.any? { |attribute| attribute_value_matches?(attribute['value'], expected_attribute['type'], expected_attribute['value']) }
|
|
122
|
+
end
|
|
123
|
+
if matches.all? && !matches.empty?
|
|
124
|
+
match = true
|
|
125
|
+
break
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
unless match
|
|
130
|
+
raise Test::Unit::AssertionFailedError.new "No spans were found containing all of the given attributes"
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
Then('a span named {string} has a parent named {string}') do |child_name, parent_name|
|
|
135
|
+
spans = SpanSupport.spans_from_request_list(Maze::Server.traces)
|
|
136
|
+
child_spans = spans.find_all { |span| span['name'].eql?(child_name) }
|
|
137
|
+
raise Test::Unit::AssertionFailedError.new "No spans were found with the name #{child_name}" if child_spans.empty?
|
|
138
|
+
parent_spans = spans.find_all { |span| span['name'].eql?(parent_name) }
|
|
139
|
+
raise Test::Unit::AssertionFailedError.new "No spans were found with the name #{parent_name}" if parent_spans.empty?
|
|
140
|
+
|
|
141
|
+
expected_parent_ids = child_spans.map { |span| span['parentSpanId'] }
|
|
142
|
+
parent_ids = parent_spans.map { |span| span['spanId'] }
|
|
143
|
+
match = expected_parent_ids.any? { |expected_id| parent_ids.include?(expected_id) }
|
|
144
|
+
|
|
145
|
+
unless match
|
|
146
|
+
raise Test::Unit::AssertionFailedError.new "No child span named #{child_name} was found with a parent named #{parent_name}"
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
Then('a span named {string} has the following properties:') do |span_name, table|
|
|
151
|
+
named_spans = SpanSupport.get_named_spans(span_name)
|
|
152
|
+
expected_properties = table.hashes
|
|
153
|
+
|
|
154
|
+
match = false
|
|
155
|
+
named_spans.each do |span|
|
|
156
|
+
matches = expected_properties.map do |expected_property|
|
|
157
|
+
property = Maze::Helper.read_key_path(span, expected_property['property'])
|
|
158
|
+
expected_property['value'].eql?(property.to_s)
|
|
159
|
+
end
|
|
160
|
+
if matches.all? && !matches.empty?
|
|
161
|
+
match = true
|
|
162
|
+
break
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
unless match
|
|
167
|
+
raise Test::Unit::AssertionFailedError.new "No spans were found containing all of the given properties"
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
Then('the {string} field of the span named {string} is stored as the value {string}') do |field, span_name, store_key|
|
|
172
|
+
SpanSupport.store_named_span_field(span_name, field, store_key)
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
|
|
@@ -1,40 +1,5 @@
|
|
|
1
1
|
# @!group Trace steps
|
|
2
2
|
|
|
3
|
-
# Disable timestamp validation for spans
|
|
4
|
-
#
|
|
5
|
-
When('I disable timestamp validation for spans') do
|
|
6
|
-
Maze.config.span_timestamp_validation = false
|
|
7
|
-
end
|
|
8
|
-
|
|
9
|
-
# Waits for a given number of spans to be received, which may be spread across one or more trace requests.
|
|
10
|
-
#
|
|
11
|
-
# @step_input span_count [Integer] The number of spans to wait for
|
|
12
|
-
Then('I wait to receive {int} span(s)') do |span_count|
|
|
13
|
-
assert_received_span_count Maze::Server.list_for('traces'), span_count
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
# Waits for a minimum number of spans to be received, which may be spread across one or more trace requests.
|
|
17
|
-
# If more spans than requested are received, this step will still pass.
|
|
18
|
-
#
|
|
19
|
-
# @step_input span_min [Integer] The minimum number of spans to wait for
|
|
20
|
-
Then('I wait to receive at least {int} span(s)') do |span_min|
|
|
21
|
-
assert_received_minimum_span_count Maze::Server.list_for('traces'), span_min
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
# Waits for a minimum number of spans to be received, which may be spread across one or more trace requests.
|
|
25
|
-
# If more spans than the maximum requested number of spans are received, this step will fail.
|
|
26
|
-
#
|
|
27
|
-
# @step_input span_min [Integer] The minimum number of spans to wait for
|
|
28
|
-
# @step_input span_max [Integer] The maximum number of spans to receive before failure
|
|
29
|
-
Then('I wait to receive between {int} and {int} span(s)') do |span_min, span_max|
|
|
30
|
-
assert_received_ranged_span_count Maze::Server.list_for('traces'), span_min, span_max
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
Then('I should have received no spans') do
|
|
34
|
-
sleep Maze.config.receive_no_requests_wait
|
|
35
|
-
Maze.check.equal spans_from_request_list(Maze::Server.list_for('traces')).size, 0
|
|
36
|
-
end
|
|
37
|
-
|
|
38
3
|
Then('I enter unmanaged traces mode') do
|
|
39
4
|
Maze.config.unmanaged_traces_mode = true
|
|
40
5
|
end
|
|
@@ -110,154 +75,6 @@ Then('the trace payload field {string} double attribute {string} equals {float}'
|
|
|
110
75
|
check_attribute_equal field, attribute, 'doubleValue', expected
|
|
111
76
|
end
|
|
112
77
|
|
|
113
|
-
# @!group Span steps
|
|
114
|
-
Then('a span {word} equals {string}') do |attribute, expected|
|
|
115
|
-
spans = spans_from_request_list(Maze::Server.list_for('traces'))
|
|
116
|
-
selected_attributes = spans.map { |span| span[attribute] }
|
|
117
|
-
Maze.check.includes selected_attributes, expected
|
|
118
|
-
end
|
|
119
|
-
|
|
120
|
-
Then('every span field {string} equals {string}') do |key, expected|
|
|
121
|
-
spans = spans_from_request_list(Maze::Server.list_for('traces'))
|
|
122
|
-
selected_keys = spans.map { |span| span[key] == expected }
|
|
123
|
-
Maze.check.not_includes selected_keys, false
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
Then('every span field {string} matches the regex {string}') do |key, pattern|
|
|
127
|
-
spans = spans_from_request_list(Maze::Server.list_for('traces'))
|
|
128
|
-
spans.map { |span| Maze.check.match pattern, span[key] }
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
Then('every span string attribute {string} exists') do |attribute|
|
|
132
|
-
spans = spans_from_request_list(Maze::Server.list_for('traces'))
|
|
133
|
-
spans.map { |span| Maze.check.not_nil span['attributes'].find { |a| a['key'] == attribute }['value']['stringValue'] }
|
|
134
|
-
end
|
|
135
|
-
|
|
136
|
-
Then('every span string attribute {string} equals {string}') do |attribute, expected|
|
|
137
|
-
spans = spans_from_request_list(Maze::Server.list_for('traces'))
|
|
138
|
-
spans.map { |span| Maze.check.equal expected, span['attributes'].find { |a| a['key'] == attribute }['value']['stringValue'] }
|
|
139
|
-
end
|
|
140
|
-
|
|
141
|
-
Then('every span string attribute {string} matches the regex {string}') do |attribute, pattern|
|
|
142
|
-
spans = spans_from_request_list(Maze::Server.list_for('traces'))
|
|
143
|
-
spans.map { |span| Maze.check.match pattern, span['attributes'].find { |a| a['key'] == attribute }['value']['stringValue'] }
|
|
144
|
-
end
|
|
145
|
-
|
|
146
|
-
Then('every span integer attribute {string} is greater than {int}') do |attribute, expected|
|
|
147
|
-
spans = spans_from_request_list(Maze::Server.list_for('traces'))
|
|
148
|
-
spans.map { |span| Maze::check.true span['attributes'].find { |a| a['key'] == attribute }['value']['intValue'].to_i > expected }
|
|
149
|
-
end
|
|
150
|
-
|
|
151
|
-
Then('every span bool attribute {string} is true') do |attribute|
|
|
152
|
-
spans = spans_from_request_list(Maze::Server.list_for('traces'))
|
|
153
|
-
spans.map { |span| Maze::check.true span['attributes'].find { |a| a['key'] == attribute }['value']['boolValue'] }
|
|
154
|
-
end
|
|
155
|
-
|
|
156
|
-
Then('a span string attribute {string} exists') do |attribute|
|
|
157
|
-
spans = spans_from_request_list(Maze::Server.list_for('traces'))
|
|
158
|
-
selected_attributes = spans.map { |span| span['attributes'].find { |a| a['key'].eql?(attribute) && a['value'].has_key?('stringValue') } }.compact
|
|
159
|
-
Maze.check.false(selected_attributes.empty?)
|
|
160
|
-
end
|
|
161
|
-
|
|
162
|
-
Then('a span string attribute {string} equals {string}') do |attribute, expected|
|
|
163
|
-
spans = spans_from_request_list(Maze::Server.list_for('traces'))
|
|
164
|
-
selected_attributes = spans.map { |span| span['attributes'].find { |a| a['key'].eql?(attribute) && a['value'].has_key?('stringValue') } }.compact
|
|
165
|
-
attribute_values = selected_attributes.map { |a| a['value']['stringValue'] }
|
|
166
|
-
Maze.check.includes attribute_values, expected
|
|
167
|
-
end
|
|
168
|
-
|
|
169
|
-
Then('a span field {string} equals {string}') do |key, expected|
|
|
170
|
-
spans = spans_from_request_list(Maze::Server.list_for('traces'))
|
|
171
|
-
selected_keys = spans.map { |span| span[key] }
|
|
172
|
-
Maze.check.includes selected_keys, expected
|
|
173
|
-
end
|
|
174
|
-
|
|
175
|
-
Then('a span field {string} equals {int}') do |key, expected|
|
|
176
|
-
spans = spans_from_request_list(Maze::Server.list_for('traces'))
|
|
177
|
-
selected_keys = spans.map { |span| span[key] }
|
|
178
|
-
Maze.check.includes selected_keys, expected
|
|
179
|
-
end
|
|
180
|
-
|
|
181
|
-
Then('a span field {string} matches the regex {string}') do |attribute, pattern|
|
|
182
|
-
regex = Regexp.new pattern
|
|
183
|
-
spans = spans_from_request_list(Maze::Server.list_for('traces'))
|
|
184
|
-
selected_attributes = spans.select { |span| regex.match? span[attribute] }
|
|
185
|
-
|
|
186
|
-
Maze.check.false(selected_attributes.empty?)
|
|
187
|
-
end
|
|
188
|
-
|
|
189
|
-
Then('a span named {string} contains the attributes:') do |span_name, table|
|
|
190
|
-
spans = spans_from_request_list(Maze::Server.list_for('traces'))
|
|
191
|
-
named_spans = spans.find_all { |span| span['name'].eql?(span_name) }
|
|
192
|
-
raise Test::Unit::AssertionFailedError.new "No spans were found with the name #{span_name}" if named_spans.empty?
|
|
193
|
-
|
|
194
|
-
expected_attributes = table.hashes
|
|
195
|
-
|
|
196
|
-
match = false
|
|
197
|
-
named_spans.each do |span|
|
|
198
|
-
matches = expected_attributes.map do |expected_attribute|
|
|
199
|
-
span['attributes'].find_all { |attribute| attribute['key'].eql?(expected_attribute['attribute']) }
|
|
200
|
-
.any? { |attribute| attribute_value_matches?(attribute['value'], expected_attribute['type'], expected_attribute['value']) }
|
|
201
|
-
end
|
|
202
|
-
if matches.all? && !matches.empty?
|
|
203
|
-
match = true
|
|
204
|
-
break
|
|
205
|
-
end
|
|
206
|
-
end
|
|
207
|
-
|
|
208
|
-
unless match
|
|
209
|
-
raise Test::Unit::AssertionFailedError.new "No spans were found containing all of the given attributes"
|
|
210
|
-
end
|
|
211
|
-
end
|
|
212
|
-
|
|
213
|
-
Then('a span named {string} has a parent named {string}') do |child_name, parent_name|
|
|
214
|
-
spans = spans_from_request_list(Maze::Server.list_for('traces'))
|
|
215
|
-
child_spans = spans.find_all { |span| span['name'].eql?(child_name) }
|
|
216
|
-
raise Test::Unit::AssertionFailedError.new "No spans were found with the name #{child_name}" if child_spans.empty?
|
|
217
|
-
parent_spans = spans.find_all { |span| span['name'].eql?(parent_name) }
|
|
218
|
-
raise Test::Unit::AssertionFailedError.new "No spans were found with the name #{parent_name}" if parent_spans.empty?
|
|
219
|
-
|
|
220
|
-
expected_parent_ids = child_spans.map { |span| span['parentSpanId'] }
|
|
221
|
-
parent_ids = parent_spans.map { |span| span['spanId'] }
|
|
222
|
-
match = expected_parent_ids.any? { |expected_id| parent_ids.include?(expected_id) }
|
|
223
|
-
|
|
224
|
-
unless match
|
|
225
|
-
raise Test::Unit::AssertionFailedError.new "No child span named #{child_name} was found with a parent named #{parent_name}"
|
|
226
|
-
end
|
|
227
|
-
end
|
|
228
|
-
|
|
229
|
-
Then('a span named {string} has the following properties:') do |span_name, table|
|
|
230
|
-
spans = spans_from_request_list(Maze::Server.list_for('traces'))
|
|
231
|
-
found_spans = spans.find_all { |span| span['name'].eql?(span_name) }
|
|
232
|
-
raise Test::Unit::AssertionFailedError.new "No spans were found with the name #{span_name}" if found_spans.empty?
|
|
233
|
-
|
|
234
|
-
expected_properties = table.hashes
|
|
235
|
-
|
|
236
|
-
match = false
|
|
237
|
-
found_spans.each do |span|
|
|
238
|
-
matches = expected_properties.map do |expected_property|
|
|
239
|
-
property = Maze::Helper.read_key_path(span, expected_property['property'])
|
|
240
|
-
expected_property['value'].eql?(property.to_s)
|
|
241
|
-
end
|
|
242
|
-
if matches.all? && !matches.empty?
|
|
243
|
-
match = true
|
|
244
|
-
break
|
|
245
|
-
end
|
|
246
|
-
end
|
|
247
|
-
|
|
248
|
-
unless match
|
|
249
|
-
raise Test::Unit::AssertionFailedError.new "No spans were found containing all of the given properties"
|
|
250
|
-
end
|
|
251
|
-
end
|
|
252
|
-
|
|
253
|
-
def spans_from_request_list list
|
|
254
|
-
return list.remaining
|
|
255
|
-
.flat_map { |req| req[:body]['resourceSpans'] }
|
|
256
|
-
.flat_map { |r| r['scopeSpans'] }
|
|
257
|
-
.flat_map { |s| s['spans'] }
|
|
258
|
-
.select { |s| !s.nil? }
|
|
259
|
-
end
|
|
260
|
-
|
|
261
78
|
def attribute_value_matches?(attribute_value, expected_type, expected_value)
|
|
262
79
|
# Check that the required value type key is present
|
|
263
80
|
unless attribute_value.keys.include?(expected_type)
|
|
@@ -284,42 +101,6 @@ def attribute_value_matches?(attribute_value, expected_type, expected_value)
|
|
|
284
101
|
end
|
|
285
102
|
end
|
|
286
103
|
|
|
287
|
-
def assert_received_span_count(list, count)
|
|
288
|
-
assert_received_spans(list, count, count)
|
|
289
|
-
end
|
|
290
|
-
|
|
291
|
-
def assert_received_minimum_span_count(list, minimum)
|
|
292
|
-
assert_received_spans(list, minimum)
|
|
293
|
-
end
|
|
294
|
-
|
|
295
|
-
def assert_received_ranged_span_count(list, minimum, maximum)
|
|
296
|
-
assert_received_spans(list, minimum, maximum)
|
|
297
|
-
end
|
|
298
|
-
|
|
299
|
-
def assert_received_spans(list, min_received, max_received = nil)
|
|
300
|
-
timeout = Maze.config.receive_requests_wait
|
|
301
|
-
wait = Maze::Wait.new(timeout: timeout)
|
|
302
|
-
|
|
303
|
-
received = wait.until { spans_from_request_list(list).size >= min_received }
|
|
304
|
-
received_count = spans_from_request_list(list).size
|
|
305
|
-
|
|
306
|
-
unless received
|
|
307
|
-
raise Test::Unit::AssertionFailedError.new <<-MESSAGE
|
|
308
|
-
Expected #{min_received} spans but received #{received_count} within the #{timeout}s timeout.
|
|
309
|
-
This could indicate that:
|
|
310
|
-
- Bugsnag crashed with a fatal error.
|
|
311
|
-
- Bugsnag did not make the requests that it should have done.
|
|
312
|
-
- The requests were made, but not deemed to be valid (e.g. missing integrity header).
|
|
313
|
-
- The requests made were prevented from being received due to a network or other infrastructure issue.
|
|
314
|
-
Please check the Maze Runner and device logs to confirm.)
|
|
315
|
-
MESSAGE
|
|
316
|
-
end
|
|
317
|
-
|
|
318
|
-
Maze.check.operator(max_received, :>=, received_count, "#{received_count} spans received") if max_received
|
|
319
|
-
|
|
320
|
-
Maze::Schemas::Validator.validate_payload_elements(list, 'trace')
|
|
321
|
-
end
|
|
322
|
-
|
|
323
104
|
def get_attribute_value(field, attribute, attr_type)
|
|
324
105
|
list = Maze::Server.list_for 'trace'
|
|
325
106
|
attributes = Maze::Helper.read_key_path list.current[:body], "#{field}.attributes"
|
|
@@ -69,6 +69,11 @@ Then('the {request_type} payload field {string} does not equal the stored value
|
|
|
69
69
|
Maze.check.false(result.equal?, "Payload value: #{payload_value} equals stored value: #{stored_value}")
|
|
70
70
|
end
|
|
71
71
|
|
|
72
|
+
Then('the stored value {string} equals {string}') do |key, value|
|
|
73
|
+
stored_value = Maze::Store.values[key]
|
|
74
|
+
Maze.check.equal(value, stored_value)
|
|
75
|
+
end
|
|
76
|
+
|
|
72
77
|
# Tests whether a payload field is a number (Numeric according to Ruby)
|
|
73
78
|
#
|
|
74
79
|
# @step_input request_type [String] The type of request (error, session, build, etc)
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
class SpanSupport
|
|
2
|
+
class << self
|
|
3
|
+
def spans_from_request_list(list)
|
|
4
|
+
list.remaining
|
|
5
|
+
.flat_map { |req| req[:body]['resourceSpans'] }
|
|
6
|
+
.flat_map { |r| r['scopeSpans'] }
|
|
7
|
+
.flat_map { |s| s['spans'] }
|
|
8
|
+
.select { |s| !s.nil? }
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def get_named_spans(span_name)
|
|
12
|
+
spans = spans_from_request_list(Maze::Server.traces)
|
|
13
|
+
named_spans = spans.find_all { |span| span['name'].eql?(span_name) }
|
|
14
|
+
raise Test::Unit::AssertionFailedError.new "No spans were found with the name #{span_name}" if named_spans.empty?
|
|
15
|
+
named_spans
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def assert_received_span_count(list, count)
|
|
19
|
+
assert_received_spans(list, count, count)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def assert_received_minimum_span_count(list, minimum)
|
|
23
|
+
assert_received_spans(list, minimum)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def assert_received_ranged_span_count(list, minimum, maximum)
|
|
27
|
+
assert_received_spans(list, minimum, maximum)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def assert_received_spans(list, min_received, max_received = nil)
|
|
31
|
+
timeout = Maze.config.receive_requests_wait
|
|
32
|
+
wait = Maze::Wait.new(timeout: timeout)
|
|
33
|
+
|
|
34
|
+
received = wait.until { SpanSupport.spans_from_request_list(list).size >= min_received }
|
|
35
|
+
received_count = SpanSupport.spans_from_request_list(list).size
|
|
36
|
+
|
|
37
|
+
unless received
|
|
38
|
+
raise Test::Unit::AssertionFailedError.new <<-MESSAGE
|
|
39
|
+
Expected #{min_received} spans but received #{received_count} within the #{timeout}s timeout.
|
|
40
|
+
This could indicate that:
|
|
41
|
+
- Bugsnag crashed with a fatal error.
|
|
42
|
+
- Bugsnag did not make the requests that it should have done.
|
|
43
|
+
- The requests were made, but not deemed to be valid (e.g. missing integrity header).
|
|
44
|
+
- The requests made were prevented from being received due to a network or other infrastructure issue.
|
|
45
|
+
Please check the Maze Runner and device logs to confirm.)
|
|
46
|
+
MESSAGE
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
Maze.check.operator(max_received, :>=, received_count, "#{received_count} spans received") if max_received
|
|
50
|
+
|
|
51
|
+
Maze::Schemas::Validator.validate_payload_elements(list, 'trace')
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def store_named_span_field(span_name, field, store_key)
|
|
55
|
+
spans = SpanSupport.get_named_spans(span_name)
|
|
56
|
+
values = spans.map { |span| span[field] }.compact
|
|
57
|
+
raise Test::Unit::AssertionFailedError.new "Expected 1 span named #{span_name}, found #{values.size}" unless values.size == 1
|
|
58
|
+
|
|
59
|
+
value = Maze::Helper.read_key_path(spans[0], field)
|
|
60
|
+
Maze::Store.values[store_key] = value.dup
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -24,12 +24,6 @@ ie_11:
|
|
|
24
24
|
os: "windows"
|
|
25
25
|
osVersion: "10"
|
|
26
26
|
|
|
27
|
-
edge_14:
|
|
28
|
-
browserName: "edge"
|
|
29
|
-
browserVersion: "14"
|
|
30
|
-
os: "windows"
|
|
31
|
-
osVersion: "10"
|
|
32
|
-
|
|
33
27
|
edge_15:
|
|
34
28
|
browserName: "edge"
|
|
35
29
|
browserVersion: "15"
|
|
@@ -68,7 +62,7 @@ safari_6:
|
|
|
68
62
|
|
|
69
63
|
safari_10:
|
|
70
64
|
browserName: "safari"
|
|
71
|
-
browserVersion: "10.
|
|
65
|
+
browserVersion: "10.1"
|
|
72
66
|
os: "OS X"
|
|
73
67
|
osVersion: "sierra"
|
|
74
68
|
|
|
@@ -86,29 +80,22 @@ safari_13:
|
|
|
86
80
|
|
|
87
81
|
safari_14:
|
|
88
82
|
browserName: "Safari"
|
|
89
|
-
browserVersion: "14.
|
|
83
|
+
browserVersion: "14.1"
|
|
90
84
|
os: "OS X"
|
|
91
85
|
osVersion: "Big Sur"
|
|
92
86
|
|
|
93
87
|
safari_15:
|
|
94
88
|
browserName: "Safari"
|
|
95
|
-
browserVersion: "15.
|
|
89
|
+
browserVersion: "15.6"
|
|
96
90
|
os: "OS X"
|
|
97
91
|
osVersion: "Monterey"
|
|
98
92
|
|
|
99
93
|
safari_16:
|
|
100
94
|
browserName: "Safari"
|
|
101
|
-
browserVersion: "16.
|
|
95
|
+
browserVersion: "16.5"
|
|
102
96
|
os: "OS X"
|
|
103
97
|
osVersion: "Ventura"
|
|
104
98
|
|
|
105
|
-
ios_9:
|
|
106
|
-
browserName: "iphone"
|
|
107
|
-
device: "iPhone 6S"
|
|
108
|
-
os: "ios"
|
|
109
|
-
osVersion: "9.0"
|
|
110
|
-
realMobile: true
|
|
111
|
-
|
|
112
99
|
ios_10:
|
|
113
100
|
browserName: "iphone"
|
|
114
101
|
device: "iPhone 7"
|
|
@@ -123,14 +110,6 @@ ios_11:
|
|
|
123
110
|
osVersion: "11"
|
|
124
111
|
realMobile: true
|
|
125
112
|
|
|
126
|
-
# Using iPad Pro here because no iPhone is available on BrowserStack with iOS 12
|
|
127
|
-
ios_12:
|
|
128
|
-
browserName: "iphone"
|
|
129
|
-
device: "iPad Pro 12.9 2018"
|
|
130
|
-
os: "ios"
|
|
131
|
-
osVersion: "12"
|
|
132
|
-
realMobile: true
|
|
133
|
-
|
|
134
113
|
ios_13:
|
|
135
114
|
browserName: "iphone"
|
|
136
115
|
device: "iPhone 11"
|
|
@@ -172,32 +151,11 @@ ios_18:
|
|
|
172
151
|
osVersion: "18"
|
|
173
152
|
realMobile: true
|
|
174
153
|
|
|
175
|
-
android_4:
|
|
176
|
-
browserName: "Android Browser"
|
|
177
|
-
device: "Google Nexus 5"
|
|
178
|
-
os: "android"
|
|
179
|
-
osVersion: "4.4"
|
|
180
|
-
realMobile: true
|
|
181
|
-
|
|
182
|
-
android_5:
|
|
183
|
-
browserName: "Android Browser"
|
|
184
|
-
device: "Samsung Galaxy S6"
|
|
185
|
-
os: "android"
|
|
186
|
-
osVersion: "5.0"
|
|
187
|
-
realMobile: true
|
|
188
|
-
|
|
189
|
-
android_6:
|
|
190
|
-
browserName: "Android Browser"
|
|
191
|
-
device: "Samsung Galaxy S7"
|
|
192
|
-
os: "android"
|
|
193
|
-
osVersion: "6.0"
|
|
194
|
-
realMobile: true
|
|
195
|
-
|
|
196
154
|
android_8:
|
|
197
155
|
browserName: "Android Browser"
|
|
198
|
-
device: "Samsung Galaxy
|
|
156
|
+
device: "Samsung Galaxy Note 9"
|
|
199
157
|
os: "android"
|
|
200
|
-
osVersion: "8.
|
|
158
|
+
osVersion: "8.1"
|
|
201
159
|
realMobile: true
|
|
202
160
|
|
|
203
161
|
android_9:
|
data/lib/maze/request_list.rb
CHANGED
|
@@ -26,8 +26,10 @@ module Maze
|
|
|
26
26
|
# @param request The new request, from which a clone is made
|
|
27
27
|
def add(request)
|
|
28
28
|
clone = request.clone
|
|
29
|
-
# UUID primarily used for commands, but no harm to set on everything
|
|
29
|
+
# UUID and other metadata primarily used for commands, but no harm to set on everything
|
|
30
30
|
clone[:uuid] = SecureRandom.uuid
|
|
31
|
+
clone[:cucumber_scenario_name] = Maze.scenario.name
|
|
32
|
+
clone[:cucumber_scenario_location] = Maze.scenario.location
|
|
31
33
|
clone[:run_uuid] = Maze.run_uuid
|
|
32
34
|
@requests.append clone
|
|
33
35
|
@count += 1
|
data/lib/maze/server.rb
CHANGED
|
@@ -272,6 +272,7 @@ module Maze
|
|
|
272
272
|
server.mount '/breakpad-symbol', Servlets::Servlet, :sourcemaps
|
|
273
273
|
server.mount '/unity-line-mappings', Servlets::Servlet, :sourcemaps
|
|
274
274
|
server.mount '/command', Servlets::CommandServlet
|
|
275
|
+
server.mount '/idem-command', Servlets::IdempotentCommandServlet
|
|
275
276
|
server.mount '/commands', Servlets::AllCommandsServlet
|
|
276
277
|
server.mount '/error-config', Servlets::ErrorConfigServlet
|
|
277
278
|
server.mount '/logs', Servlets::LogServlet
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'json'
|
|
4
|
+
|
|
5
|
+
module Maze
|
|
6
|
+
module Servlets
|
|
7
|
+
|
|
8
|
+
# Allows clients to queue up "commands", in the form of Ruby hashes, using Maze::Server.commands.add. GET
|
|
9
|
+
# requests made to the /command endpoint will then respond with each queued command in turn.
|
|
10
|
+
class IdempotentCommandServlet < BaseServlet
|
|
11
|
+
|
|
12
|
+
NOOP_COMMAND = '{"action": "noop", "message": "No commands queued"}'
|
|
13
|
+
RESET_COMMAND = '{"action": "reset_uuid", "message": "The UUID given was unknown - client must reset its last known UUID"}'
|
|
14
|
+
|
|
15
|
+
# Serves the next command, if there is one.
|
|
16
|
+
#
|
|
17
|
+
# @param request [HTTPRequest] The incoming GET request
|
|
18
|
+
# @param response [HTTPResponse] The response to return
|
|
19
|
+
def do_GET(request, response)
|
|
20
|
+
after_uuid = request.query['after']
|
|
21
|
+
if after_uuid.nil?
|
|
22
|
+
response.body = "The 'after' query parameter must be provided, but may be an empty string"
|
|
23
|
+
response.status = 400
|
|
24
|
+
else
|
|
25
|
+
command_after(after_uuid, response)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
response.header['Access-Control-Allow-Origin'] = '*'
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def send_current_command(response)
|
|
32
|
+
commands = Maze::Server.commands
|
|
33
|
+
|
|
34
|
+
if commands.size_remaining == 0
|
|
35
|
+
response.body = NOOP_COMMAND
|
|
36
|
+
response.status = 200
|
|
37
|
+
else
|
|
38
|
+
command = commands.current
|
|
39
|
+
response.body = JSON.pretty_generate(command)
|
|
40
|
+
response.status = 200
|
|
41
|
+
commands.next
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def command_after(uuid, response)
|
|
46
|
+
commands = Maze::Server.commands
|
|
47
|
+
if uuid.empty?
|
|
48
|
+
# Return the first command in the list, if there is one
|
|
49
|
+
index = -1
|
|
50
|
+
else
|
|
51
|
+
# Find the matching command
|
|
52
|
+
index = commands.all.find_index {|command| command[:uuid] == uuid }
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
if index.nil?
|
|
56
|
+
# No matching command - client must reset its UUID
|
|
57
|
+
response.body = RESET_COMMAND
|
|
58
|
+
response.status = 200
|
|
59
|
+
else
|
|
60
|
+
if index + 1 < commands.size_all
|
|
61
|
+
# Respond with the next command in the queue
|
|
62
|
+
command = commands.get(index + 1)
|
|
63
|
+
command_json = JSON.pretty_generate(command)
|
|
64
|
+
response.body = command_json
|
|
65
|
+
response.status = 200
|
|
66
|
+
else
|
|
67
|
+
# The UUID given was for the last command in the list
|
|
68
|
+
response.body = NOOP_COMMAND
|
|
69
|
+
response.status = 200
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Logs and returns a set of valid headers for this servlet.
|
|
75
|
+
#
|
|
76
|
+
# @param request [HTTPRequest] The incoming GET request
|
|
77
|
+
# @param response [HTTPResponse] The response to return
|
|
78
|
+
def do_OPTIONS(request, response)
|
|
79
|
+
super
|
|
80
|
+
|
|
81
|
+
response.header['Access-Control-Allow-Methods'] = 'POST, OPTIONS'
|
|
82
|
+
response.status = Server.status_code('OPTIONS')
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
data/lib/maze.rb
CHANGED
|
@@ -8,7 +8,7 @@ require_relative 'maze/timers'
|
|
|
8
8
|
# providing an alternative to the proliferation of global variables or singletons.
|
|
9
9
|
module Maze
|
|
10
10
|
|
|
11
|
-
VERSION = '10.
|
|
11
|
+
VERSION = '10.6.0'
|
|
12
12
|
|
|
13
13
|
class << self
|
|
14
14
|
attr_accessor :check, :driver, :internal_hooks, :mode, :start_time, :dynamic_retry, :public_address,
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: bugsnag-maze-runner
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 10.
|
|
4
|
+
version: 10.6.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Steve Kirkland
|
|
@@ -9,7 +9,7 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date: 2025-10-
|
|
12
|
+
date: 2025-10-24 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: cucumber
|
|
@@ -422,12 +422,14 @@ files:
|
|
|
422
422
|
- lib/features/steps/request_assertion_steps.rb
|
|
423
423
|
- lib/features/steps/runner_steps.rb
|
|
424
424
|
- lib/features/steps/session_tracking_steps.rb
|
|
425
|
+
- lib/features/steps/span_steps.rb
|
|
425
426
|
- lib/features/steps/trace_steps.rb
|
|
426
427
|
- lib/features/steps/value_steps.rb
|
|
427
428
|
- lib/features/support/cucumber_types.rb
|
|
428
429
|
- lib/features/support/env.rb
|
|
429
430
|
- lib/features/support/error_config_support.rb
|
|
430
431
|
- lib/features/support/internal_hooks.rb
|
|
432
|
+
- lib/features/support/span_support.rb
|
|
431
433
|
- lib/maze.rb
|
|
432
434
|
- lib/maze/api/appium/app_manager.rb
|
|
433
435
|
- lib/maze/api/appium/device_manager.rb
|
|
@@ -514,6 +516,7 @@ files:
|
|
|
514
516
|
- lib/maze/servlets/base_servlet.rb
|
|
515
517
|
- lib/maze/servlets/command_servlet.rb
|
|
516
518
|
- lib/maze/servlets/error_config_servlet.rb
|
|
519
|
+
- lib/maze/servlets/idempotent_command_servlet.rb
|
|
517
520
|
- lib/maze/servlets/log_servlet.rb
|
|
518
521
|
- lib/maze/servlets/reflective_servlet.rb
|
|
519
522
|
- lib/maze/servlets/servlet.rb
|