bugsnag-maze-runner 6.27.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 +7 -0
- data/bin/bugsnag-print-load-paths +6 -0
- data/bin/download-logs +76 -0
- data/bin/maze-runner +136 -0
- data/bin/upload-app +56 -0
- data/lib/features/scripts/await-android-emulator.sh +11 -0
- data/lib/features/scripts/clear-android-app-data.sh +8 -0
- data/lib/features/scripts/force-stop-android-app.sh +8 -0
- data/lib/features/scripts/install-android-app.sh +15 -0
- data/lib/features/scripts/launch-android-app.sh +38 -0
- data/lib/features/scripts/launch-android-emulator.sh +15 -0
- data/lib/features/steps/android_steps.rb +51 -0
- data/lib/features/steps/app_automator_steps.rb +228 -0
- data/lib/features/steps/aws_sam_steps.rb +212 -0
- data/lib/features/steps/breadcrumb_steps.rb +50 -0
- data/lib/features/steps/browser_steps.rb +93 -0
- data/lib/features/steps/build_api_steps.rb +25 -0
- data/lib/features/steps/document_server_steps.rb +7 -0
- data/lib/features/steps/error_reporting_steps.rb +342 -0
- data/lib/features/steps/feature_flag_steps.rb +190 -0
- data/lib/features/steps/header_steps.rb +72 -0
- data/lib/features/steps/log_steps.rb +29 -0
- data/lib/features/steps/multipart_request_steps.rb +142 -0
- data/lib/features/steps/network_steps.rb +75 -0
- data/lib/features/steps/payload_steps.rb +234 -0
- data/lib/features/steps/proxy_steps.rb +34 -0
- data/lib/features/steps/query_parameter_steps.rb +31 -0
- data/lib/features/steps/request_assertion_steps.rb +107 -0
- data/lib/features/steps/runner_steps.rb +406 -0
- data/lib/features/steps/session_tracking_steps.rb +116 -0
- data/lib/features/steps/value_steps.rb +119 -0
- data/lib/features/support/env.rb +7 -0
- data/lib/features/support/internal_hooks.rb +260 -0
- data/lib/maze/appium_server.rb +112 -0
- data/lib/maze/assertions/request_set_assertions.rb +97 -0
- data/lib/maze/aws/sam.rb +112 -0
- data/lib/maze/bitbar_devices.rb +84 -0
- data/lib/maze/bitbar_utils.rb +112 -0
- data/lib/maze/browser_stack_devices.rb +160 -0
- data/lib/maze/browser_stack_utils.rb +164 -0
- data/lib/maze/browsers_bs.yml +220 -0
- data/lib/maze/browsers_cbt.yml +100 -0
- data/lib/maze/bugsnag_config.rb +42 -0
- data/lib/maze/capabilities.rb +126 -0
- data/lib/maze/checks/assert_check.rb +91 -0
- data/lib/maze/checks/noop_check.rb +34 -0
- data/lib/maze/compare.rb +161 -0
- data/lib/maze/configuration.rb +174 -0
- data/lib/maze/docker.rb +108 -0
- data/lib/maze/document_server.rb +46 -0
- data/lib/maze/driver/appium.rb +217 -0
- data/lib/maze/driver/browser.rb +138 -0
- data/lib/maze/driver/resilient_appium.rb +51 -0
- data/lib/maze/errors.rb +20 -0
- data/lib/maze/helper.rb +118 -0
- data/lib/maze/hooks/appium_hooks.rb +216 -0
- data/lib/maze/hooks/browser_hooks.rb +68 -0
- data/lib/maze/hooks/command_hooks.rb +9 -0
- data/lib/maze/hooks/hooks.rb +61 -0
- data/lib/maze/interactive_cli.rb +173 -0
- data/lib/maze/logger.rb +73 -0
- data/lib/maze/macos_utils.rb +14 -0
- data/lib/maze/network.rb +49 -0
- data/lib/maze/option/parser.rb +245 -0
- data/lib/maze/option/processor.rb +143 -0
- data/lib/maze/option/validator.rb +184 -0
- data/lib/maze/option.rb +64 -0
- data/lib/maze/plugins/bugsnag_reporting_plugin.rb +49 -0
- data/lib/maze/plugins/cucumber_report_plugin.rb +101 -0
- data/lib/maze/plugins/global_retry_plugin.rb +38 -0
- data/lib/maze/proxy.rb +114 -0
- data/lib/maze/request_list.rb +82 -0
- data/lib/maze/retry_handler.rb +76 -0
- data/lib/maze/runner.rb +149 -0
- data/lib/maze/sauce_labs_utils.rb +96 -0
- data/lib/maze/server.rb +207 -0
- data/lib/maze/servlets/base_servlet.rb +22 -0
- data/lib/maze/servlets/command_servlet.rb +44 -0
- data/lib/maze/servlets/log_servlet.rb +64 -0
- data/lib/maze/servlets/reflective_servlet.rb +69 -0
- data/lib/maze/servlets/servlet.rb +160 -0
- data/lib/maze/smart_bear_utils.rb +71 -0
- data/lib/maze/store.rb +15 -0
- data/lib/maze/terminating_server.rb +129 -0
- data/lib/maze/timers.rb +51 -0
- data/lib/maze/wait.rb +35 -0
- data/lib/maze.rb +27 -0
- metadata +371 -0
@@ -0,0 +1,142 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'open-uri'
|
3
|
+
require 'json'
|
4
|
+
require 'cgi'
|
5
|
+
|
6
|
+
# @!group Multipart request assertion steps
|
7
|
+
|
8
|
+
# Verifies a request contains the correct Content-Type header and some contents
|
9
|
+
# for a multipart/form-data request
|
10
|
+
#
|
11
|
+
# @param request [Hash] The payload to test
|
12
|
+
def valid_multipart_form_data?(request)
|
13
|
+
content_regex = Regexp.new('^multipart\\/form-data; boundary=[\\h-]+$')
|
14
|
+
content_header = request[:request]['Content-Type']
|
15
|
+
Maze.check.match(content_regex, content_header)
|
16
|
+
Maze.check.true(
|
17
|
+
request[:body].size.positive?,
|
18
|
+
"Multipart request payload contained #{request[:body].size} fields"
|
19
|
+
)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Verifies that any type of request contains multipart form-data
|
23
|
+
#
|
24
|
+
# @step_input request_type [String] The type of request (error, session, build, etc)
|
25
|
+
Then('the {word} request is valid multipart form-data') do |request_type|
|
26
|
+
list = Maze::Server.list_for request_type
|
27
|
+
valid_multipart_form_data?(list.current)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Verifies all requests of a given type contain multipart form-data
|
31
|
+
#
|
32
|
+
# @step_input request_type [String] The type of request (error, session, build, etc)
|
33
|
+
Then('all {word} requests are valid multipart form-data') do |request_type|
|
34
|
+
list = Maze::Server.list_for request_type
|
35
|
+
list.all.all? { |request| valid_multipart_form_data?(request) }
|
36
|
+
end
|
37
|
+
|
38
|
+
# Tests the number of fields a given type of multipart request contains.
|
39
|
+
#
|
40
|
+
# @step_input request_type [String] The type of request (error, session, build, etc)
|
41
|
+
# @step_input part_count [Integer] The number of expected fields
|
42
|
+
Then('the {word} multipart request has {int} fields') do |request_type, part_count|
|
43
|
+
list = Maze::Server.list_for request_type
|
44
|
+
parts = list.current[:body]
|
45
|
+
Maze.check.equal(part_count, parts.size)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Tests a given type of multipart request has at least one field.
|
49
|
+
#
|
50
|
+
# @step_input request_type [String] The type of request (error, session, build, etc)
|
51
|
+
Then('the {word} multipart request has a non-empty body') do |request_type|
|
52
|
+
list = Maze::Server.list_for request_type
|
53
|
+
parts = list.current[:body]
|
54
|
+
Maze.check.true(parts.size.positive?, "Multipart request payload contained #{parts.size} fields")
|
55
|
+
end
|
56
|
+
|
57
|
+
# Takes a hashmap and parses all fields into strings or hashes depending on their format
|
58
|
+
# Used to convert a multipart/form-data request into a JSON comparable hash
|
59
|
+
#
|
60
|
+
# @param body [Hash] The multipart/form-data hash to parse
|
61
|
+
#
|
62
|
+
# @return [Hash] The result of parsing hash fields to strings/JSON hashes
|
63
|
+
def parse_multipart_body(body)
|
64
|
+
body.each_with_object({}) do |(k, v), out|
|
65
|
+
out[k] = JSON.parse(v.to_s)
|
66
|
+
rescue JSON::ParserError
|
67
|
+
out[k] = v.to_s
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# Tests that a given type of multipart payload body does not match a JSON file.
|
72
|
+
# JSON formatted multipart fields will be parsed into hashes.
|
73
|
+
#
|
74
|
+
# @step_input request_type [String] The type of request (error, session, build, etc)
|
75
|
+
# @step_input json_path [String] Path to a JSON file relative to maze-runner root
|
76
|
+
Then('the {word} multipart body does not match the JSON file in {string}') do |request_type, json_path|
|
77
|
+
Maze.check.true(File.exist?(json_path), "'#{json_path}' does not exist")
|
78
|
+
payload_list = Maze::Server.list_for request_type
|
79
|
+
raw_payload_value = payload_list.current[:body]
|
80
|
+
payload_value = parse_multipart_body(raw_payload_value)
|
81
|
+
expected_value = JSON.parse(open(json_path, &:read))
|
82
|
+
result = Maze::Compare.value(expected_value, payload_value)
|
83
|
+
Maze.check.false(result.equal?, "Payload:\n#{payload_value}\nExpected:#{expected_value}")
|
84
|
+
end
|
85
|
+
|
86
|
+
# Tests that a given type of multipart payload body matches a JSON fixture.
|
87
|
+
# JSON formatted multipart fields will be parsed into hashes.
|
88
|
+
#
|
89
|
+
# @step_input request_type [String] The type of request (error, session, build, etc)
|
90
|
+
# @step_input json_path [String] Path to a JSON file relative to maze-runner root
|
91
|
+
Then('the {word} multipart body matches the JSON file in {string}') do |request_type, json_path|
|
92
|
+
Maze.check.true(File.exist?(json_path), "'#{json_path}' does not exist")
|
93
|
+
payload_list = Maze::Server.list_for request_type
|
94
|
+
raw_payload_value = payload_list.current[:body]
|
95
|
+
payload_value = parse_multipart_body(raw_payload_value)
|
96
|
+
expected_value = JSON.parse(open(json_path, &:read))
|
97
|
+
result = Maze::Compare.value(expected_value, payload_value)
|
98
|
+
Maze.check.true(result.equal?, "The payload field '#{result.keypath}' does not match the fixture:\n #{result.reasons.join('\n')}")
|
99
|
+
end
|
100
|
+
|
101
|
+
# Tests that a given type of multipart field matches a JSON fixture.
|
102
|
+
# The field will be parsed into a hash.
|
103
|
+
#
|
104
|
+
# @step_input request_type [String] The type of request (error, session, build, etc)
|
105
|
+
# @step_input field_path [String] Path to the tested element
|
106
|
+
# @step_input json_path [String] Path to a JSON file relative to maze-runner root
|
107
|
+
Then('the {word} multipart field {string} matches the JSON file in {string}') do |request_type, field_path, json_path|
|
108
|
+
Maze.check.true(File.exist?(json_path), "'#{json_path}' does not exist")
|
109
|
+
payload_list = Maze::Server.list_for request_type
|
110
|
+
payload_value = JSON.parse(payload_list.current[:body][field_path].to_s)
|
111
|
+
expected_value = JSON.parse(open(json_path, &:read))
|
112
|
+
result = Maze::Compare.value(expected_value, payload_value)
|
113
|
+
Maze.check.true(result.equal?, "The multipart field '#{result.keypath}' does not match the fixture:\n #{result.reasons.join('\n')}")
|
114
|
+
end
|
115
|
+
|
116
|
+
# Tests that a multipart request field exists and is not null.
|
117
|
+
#
|
118
|
+
# @step_input request_type [String] The type of request (error, session, build, etc)
|
119
|
+
# @step_input part_key [String] The key to the multipart element
|
120
|
+
Then('the field {string} for multipart {word} is not null') do |part_key, request_type|
|
121
|
+
parts = Maze::Server.list_for(request_type).current[:body]
|
122
|
+
Maze.check.not_nil(parts[part_key], "The field '#{part_key}' should not be null")
|
123
|
+
end
|
124
|
+
|
125
|
+
# Tests that a multipart request field exists and is null.
|
126
|
+
#
|
127
|
+
# @step_input part_key [String] The key to the multipart element
|
128
|
+
# @step_input request_type [String] The type of request (error, session, build, etc)
|
129
|
+
Then('the field {string} for multipart {word} is null') do |part_key, request_type|
|
130
|
+
parts = Maze::Server.list_for(request_type).current[:body]
|
131
|
+
Maze.check.nil(parts[part_key], "The field '#{part_key}' should be null")
|
132
|
+
end
|
133
|
+
|
134
|
+
# Tests that a multipart request field equals a string.
|
135
|
+
#
|
136
|
+
# @step_input part_key [String] The key to the multipart element
|
137
|
+
# @step_input request_type [String] The type of request (error, session, build, etc)
|
138
|
+
# @step_input expected_value [String] The string to match against
|
139
|
+
Then('the field {string} for multipart {word} equals {string}') do |part_key, request_type, expected_value|
|
140
|
+
parts = Maze::Server.list_for(request_type).current[:body]
|
141
|
+
Maze.check.equal(parts[part_key], expected_value)
|
142
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# @!group Network steps
|
2
|
+
|
3
|
+
# Checks that a port on a given host is open and ready for connections.
|
4
|
+
#
|
5
|
+
# @step_input host [String] The host to check
|
6
|
+
# @step_input port [String] The port to check
|
7
|
+
When('I wait for the host {string} to open port {string}') do |host, port|
|
8
|
+
Maze::Network.wait_for_port(host, port)
|
9
|
+
end
|
10
|
+
|
11
|
+
# Sets the HTTP status code to be used for all subsequent requests
|
12
|
+
#
|
13
|
+
# @step_input status_code [Integer] The status code to return
|
14
|
+
When('I set the HTTP status code to {int}') do |status_code|
|
15
|
+
Maze::Server.status_code = status_code
|
16
|
+
end
|
17
|
+
|
18
|
+
# Sets the HTTP status code to be used for the next request
|
19
|
+
#
|
20
|
+
# @step_input status_code [Integer] The status code to return
|
21
|
+
When('I set the HTTP status code for the next request to {int}') do |status_code|
|
22
|
+
Maze::Server.reset_status_code = true
|
23
|
+
Maze::Server.status_code = status_code
|
24
|
+
end
|
25
|
+
|
26
|
+
# Sets the response delay to be used for all subsequent requests
|
27
|
+
#
|
28
|
+
# @step_input response_delay_ms [Integer] The delay in milliseconds
|
29
|
+
When('I set the response delay to {int} milliseconds') do |response_delay_ms|
|
30
|
+
Maze::Server.response_delay_ms = response_delay_ms
|
31
|
+
end
|
32
|
+
|
33
|
+
# Sets the response delay to be used for the next request
|
34
|
+
#
|
35
|
+
# @step_input delay [Integer] The delay in milliseconds
|
36
|
+
When('I set the response delay for the next request to {int} milliseconds') do |delay|
|
37
|
+
Maze::Server.reset_response_delay = true
|
38
|
+
Maze::Server.response_delay_ms = delay
|
39
|
+
end
|
40
|
+
|
41
|
+
# Attempts to open a URL.
|
42
|
+
#
|
43
|
+
# @step_input url [String] The URL to open.
|
44
|
+
When('I open the URL {string}') do |url|
|
45
|
+
begin
|
46
|
+
open(url, &:read)
|
47
|
+
rescue OpenURI::HTTPError
|
48
|
+
$logger.debug $!.inspect
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Starts the terminating server to cancel requests received.
|
53
|
+
When('I start the terminating server') do
|
54
|
+
Maze::TerminatingServer.start
|
55
|
+
end
|
56
|
+
|
57
|
+
# Sets the response message on the terminating server
|
58
|
+
When('I set the terminated response message to {string}') do |response_message|
|
59
|
+
Maze::TerminatingServer.response = response_message
|
60
|
+
end
|
61
|
+
|
62
|
+
# Sets the maximum allowable amount of data received to the terminating server
|
63
|
+
#
|
64
|
+
# @step_input max_length [Integer] The number of bytes receivable
|
65
|
+
When('I set the terminating server data threshold to {int} bytes') do |max_length|
|
66
|
+
Maze::TerminatingServer.max_received_size = max_length
|
67
|
+
end
|
68
|
+
|
69
|
+
# Check if a certain number of connections have been received by the terminating server
|
70
|
+
#
|
71
|
+
# @step_input request_count [Integer] The number of desired requests
|
72
|
+
Then('the terminating server has received {int} requests') do |request_count|
|
73
|
+
Maze.check.equal(request_count, Maze::TerminatingServer.received_request_count,
|
74
|
+
"#{request_count} terminated requests expected, #{Maze::TerminatingServer.received_request_count} received")
|
75
|
+
end
|
@@ -0,0 +1,234 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @!group Payload steps
|
4
|
+
|
5
|
+
# Tests the payload body does not match a JSON fixture.
|
6
|
+
#
|
7
|
+
# @step_input request_type [String] The type of request (error, session, build, etc)
|
8
|
+
# @step_input fixture_path [String] Path to a JSON fixture
|
9
|
+
Then('the {word} payload body does not match the JSON fixture in {string}') do |request_type, fixture_path|
|
10
|
+
payload_value = Maze::Server.list_for(request_type).current[:body]
|
11
|
+
expected_value = JSON.parse(open(fixture_path, &:read))
|
12
|
+
result = Maze::Compare.value(expected_value, payload_value)
|
13
|
+
Maze.check.false(result.equal?, "Payload:\n#{payload_value}\nExpected:#{expected_value}")
|
14
|
+
end
|
15
|
+
|
16
|
+
# Test the payload body matches a JSON fixture.
|
17
|
+
#
|
18
|
+
# @step_input request_type [String] The type of request (error, session, build, etc)
|
19
|
+
# @step_input fixture_path [String] Path to a JSON fixture
|
20
|
+
Then('the {word} payload body matches the JSON fixture in {string}') do |request_type, fixture_path|
|
21
|
+
payload_value = Maze::Server.list_for(request_type).current[:body]
|
22
|
+
expected_value = JSON.parse(open(fixture_path, &:read))
|
23
|
+
result = Maze::Compare.value(expected_value, payload_value)
|
24
|
+
Maze.check.true(result.equal?,
|
25
|
+
"The payload field '#{result.keypath}' does not match the fixture:\n #{result.reasons.join('\n')}")
|
26
|
+
end
|
27
|
+
|
28
|
+
# Test that a payload element matches a JSON fixture.
|
29
|
+
#
|
30
|
+
# @step_input request_type [String] The type of request (error, session, build, etc)
|
31
|
+
# @step_input field_path [String] Path to the tested element
|
32
|
+
# @step_input fixture_path [String] Path to a JSON fixture
|
33
|
+
Then('the {word} payload field {string} matches the JSON fixture in {string}') \
|
34
|
+
do |request_type, field_path, fixture_path|
|
35
|
+
list = Maze::Server.list_for(request_type)
|
36
|
+
payload_value = Maze::Helper.read_key_path(list.current[:body], field_path)
|
37
|
+
expected_value = JSON.parse(open(fixture_path, &:read))
|
38
|
+
result = Maze::Compare.value(expected_value, payload_value)
|
39
|
+
Maze.check.true(result.equal?,
|
40
|
+
"The payload field '#{result.keypath}' does not match the fixture:\n #{result.reasons.join('\n')}")
|
41
|
+
end
|
42
|
+
|
43
|
+
# Tests that a request element is true.
|
44
|
+
#
|
45
|
+
# @step_input request_type [String] The type of request (error, session, build, etc)
|
46
|
+
# @step_input field_path [String] Path to the tested element
|
47
|
+
Then('the {word} payload field {string} is true') do |request_type, field_path|
|
48
|
+
list = Maze::Server.list_for(request_type)
|
49
|
+
Maze.check.true(Maze::Helper.read_key_path(list.current[:body], field_path))
|
50
|
+
end
|
51
|
+
|
52
|
+
# Tests that a request element is false.
|
53
|
+
#
|
54
|
+
# @step_input request_type [String] The type of request (error, session, build, etc)
|
55
|
+
# @step_input field_path [String] Path to the tested element
|
56
|
+
Then('the {word} payload field {string} is false') do |request_type, field_path|
|
57
|
+
list = Maze::Server.list_for(request_type)
|
58
|
+
Maze.check.false(Maze::Helper.read_key_path(list.current[:body], field_path))
|
59
|
+
end
|
60
|
+
|
61
|
+
# Tests that a request element is null.
|
62
|
+
#
|
63
|
+
# @step_input request_type [String] The type of request (error, session, build, etc)
|
64
|
+
# @step_input field_path [String] Path to the tested element
|
65
|
+
Then('the {word} payload field {string} is null') do |request_type, field_path|
|
66
|
+
list = Maze::Server.list_for(request_type)
|
67
|
+
Maze.check.nil(Maze::Helper.read_key_path(list.current[:body], field_path))
|
68
|
+
end
|
69
|
+
|
70
|
+
# Tests that a request element is not null.
|
71
|
+
#
|
72
|
+
# @step_input request_type [String] The type of request (error, session, build, etc)
|
73
|
+
# @step_input field_path [String] Path to the tested element
|
74
|
+
Then('the {word} payload field {string} is not null') do |request_type, field_path|
|
75
|
+
list = Maze::Server.list_for(request_type)
|
76
|
+
Maze.check.not_nil(Maze::Helper.read_key_path(list.current[:body], field_path))
|
77
|
+
end
|
78
|
+
|
79
|
+
# Tests that a payload element equals an integer.
|
80
|
+
#
|
81
|
+
# @step_input request_type [String] The type of request (error, session, build, etc)
|
82
|
+
# @step_input field_path [String] Path to the tested element
|
83
|
+
# @step_input int_value [Integer] The value to test against
|
84
|
+
Then('the {word} payload field {string} equals {int}') do |request_type, field_path, int_value|
|
85
|
+
Maze.check.equal(int_value,
|
86
|
+
Maze::Helper.read_key_path(Maze::Server.list_for(request_type).current[:body], field_path))
|
87
|
+
end
|
88
|
+
|
89
|
+
# Tests the payload field value against an environment variable.
|
90
|
+
#
|
91
|
+
# @step_input request_type [String] The type of request (error, session, build, etc)
|
92
|
+
# @step_input field_path [String] The payload element to test
|
93
|
+
# @step_input env_var [String] The environment variable to test against
|
94
|
+
Then('the {word} payload field {string} equals the environment variable {string}') \
|
95
|
+
do |request_type, field_path, env_var|
|
96
|
+
environment_value = ENV[env_var]
|
97
|
+
Maze.check.false(environment_value.nil?, "The environment variable #{env_var} must not be nil")
|
98
|
+
list = Maze::Server.list_for(request_type)
|
99
|
+
value = Maze::Helper.read_key_path(list.current[:body], field_path)
|
100
|
+
|
101
|
+
Maze.check.equal(environment_value, value)
|
102
|
+
end
|
103
|
+
|
104
|
+
# Tests a payload field contains a number larger than a value.
|
105
|
+
#
|
106
|
+
# @step_input request_type [String] The type of request (error, session, build, etc)
|
107
|
+
# @step_input field_path [String] The payload element to test
|
108
|
+
# @step_input int_value [Integer] The value to compare against
|
109
|
+
Then('the {word} payload field {string} is greater than {int}') do |request_type, field_path, int_value|
|
110
|
+
list = Maze::Server.list_for(request_type)
|
111
|
+
value = Maze::Helper.read_key_path(list.current[:body], field_path)
|
112
|
+
Maze.check.kind_of Integer, value
|
113
|
+
Maze.check.operator(value, :>, int_value, "The payload field '#{field_path}' (#{value}) is not greater than '#{int_value}'")
|
114
|
+
end
|
115
|
+
|
116
|
+
# Tests a payload field contains a number smaller than a value.
|
117
|
+
#
|
118
|
+
# @step_input request_type [String] The type of request (error, session, build, etc)
|
119
|
+
# @step_input field_path [String] The payload element to test
|
120
|
+
# @step_input int_value [Integer] The value to compare against
|
121
|
+
Then('the {word} payload field {string} is less than {int}') do |request_type, field_path, int_value|
|
122
|
+
list = Maze::Server.list_for(request_type)
|
123
|
+
value = Maze::Helper.read_key_path(list.current[:body], field_path)
|
124
|
+
Maze.check.kind_of Integer, value
|
125
|
+
fail_message = "The #{request_type} payload field '#{field_path}' (#{value}) is not less than '#{int_value}'"
|
126
|
+
Maze.check.operator(value, :<, int_value, fail_message)
|
127
|
+
end
|
128
|
+
|
129
|
+
# Tests a payload field equals a string.
|
130
|
+
#
|
131
|
+
# @step_input request_type [String] The type of request (error, session, build, etc)
|
132
|
+
# @step_input field_path [String] The payload element to test
|
133
|
+
# @step_input string_value [String] The string to test against
|
134
|
+
Then('the {word} payload field {string} equals {string}') do |request_type, field_path, string_value|
|
135
|
+
list = Maze::Server.list_for(request_type)
|
136
|
+
Maze.check.equal(string_value, Maze::Helper.read_key_path(list.current[:body], field_path))
|
137
|
+
end
|
138
|
+
|
139
|
+
# Tests a payload field starts with a string.
|
140
|
+
#
|
141
|
+
# @step_input request_type [String] The type of request (error, session, build, etc)
|
142
|
+
# @step_input field_path [String] The payload element to test
|
143
|
+
# @step_input string_value [String] The string to test against
|
144
|
+
Then('the {word} payload field {string} starts with {string}') do |request_type, field_path, string_value|
|
145
|
+
list = Maze::Server.list_for(request_type)
|
146
|
+
value = Maze::Helper.read_key_path(list.current[:body], field_path)
|
147
|
+
Maze.check.kind_of String, value
|
148
|
+
Maze.check.true(
|
149
|
+
value.start_with?(string_value),
|
150
|
+
"Field '#{field_path}' value ('#{value}') does not start with '#{string_value}'"
|
151
|
+
)
|
152
|
+
end
|
153
|
+
|
154
|
+
# Tests a payload field ends with a string.
|
155
|
+
#
|
156
|
+
# @step_input request_type [String] The type of request (error, session, build, etc)
|
157
|
+
# @step_input field_path [String] The payload element to test
|
158
|
+
# @step_input string_value [String] The string to test against
|
159
|
+
Then('the {word} payload field {string} ends with {string}') do |request_type, field_path, string_value|
|
160
|
+
list = Maze::Server.list_for(request_type)
|
161
|
+
value = Maze::Helper.read_key_path(list.current[:body], field_path)
|
162
|
+
Maze.check.kind_of String, value
|
163
|
+
Maze.check.true(
|
164
|
+
value.end_with?(string_value),
|
165
|
+
"Field '#{field_path}' value ('#{value}') does not end with '#{string_value}'"
|
166
|
+
)
|
167
|
+
end
|
168
|
+
|
169
|
+
# Tests a payload field is an array with a specific element count.
|
170
|
+
#
|
171
|
+
# @step_input request_type [String] The type of request (error, session, build, etc)
|
172
|
+
# @step_input field [String] The payload element to test
|
173
|
+
# @step_input count [Integer] The value expected
|
174
|
+
Then('the {word} payload field {string} is an array with {int} elements') do |request_type, field, count|
|
175
|
+
list = Maze::Server.list_for(request_type)
|
176
|
+
value = Maze::Helper.read_key_path(list.current[:body], field)
|
177
|
+
Maze.check.kind_of Array, value
|
178
|
+
Maze.check.equal(count, value.length)
|
179
|
+
end
|
180
|
+
|
181
|
+
# Tests a payload field is an array with at least one element.
|
182
|
+
#
|
183
|
+
# @step_input request_type [String] The type of request (error, session, build, etc)
|
184
|
+
# @step_input field [String] The payload element to test
|
185
|
+
Then('the {word} payload field {string} is a non-empty array') do |request_type, field|
|
186
|
+
list = Maze::Server.list_for(request_type)
|
187
|
+
value = Maze::Helper.read_key_path(list.current[:body], field)
|
188
|
+
Maze.check.kind_of Array, value
|
189
|
+
Maze.check.true(value.length.positive?, "the field '#{field}' must be a non-empty array")
|
190
|
+
end
|
191
|
+
|
192
|
+
# Tests a payload field matches a regex.
|
193
|
+
#
|
194
|
+
# @step_input request_type [String] The type of request (error, session, build, etc)
|
195
|
+
# @step_input field [String] The payload element to test
|
196
|
+
# @step_input regex [String] The regex to test against
|
197
|
+
Then('the {word} payload field {string} matches the regex {string}') do |request_type, field, regex_string|
|
198
|
+
regex = Regexp.new(regex_string)
|
199
|
+
list = Maze::Server.list_for(request_type)
|
200
|
+
value = Maze::Helper.read_key_path(list.current[:body], field)
|
201
|
+
Maze.check.match(regex, value)
|
202
|
+
end
|
203
|
+
|
204
|
+
# Tests a payload field is a numeric timestamp.
|
205
|
+
#
|
206
|
+
# @step_input request_type [String] The type of request (error, session, build, etc)
|
207
|
+
# @step_input field [String] The payload element to test
|
208
|
+
Then('the {word} payload field {string} is a parsable timestamp in seconds') do |request_type, field|
|
209
|
+
list = Maze::Server.list_for(request_type)
|
210
|
+
value = Maze::Helper.read_key_path(list.current[:body], field)
|
211
|
+
begin
|
212
|
+
int = value.to_i
|
213
|
+
parsed_time = Time.at(int)
|
214
|
+
rescue StandardError
|
215
|
+
parsed_time = nil
|
216
|
+
end
|
217
|
+
Maze.check.not_nil(parsed_time)
|
218
|
+
end
|
219
|
+
|
220
|
+
# Tests that every element in an array contains a specified key-value pair.
|
221
|
+
#
|
222
|
+
# @step_input request_type [String] The type of request (error, session, build, etc)
|
223
|
+
# @step_input key_path [String] The path to the tested array
|
224
|
+
# @step_input element_key_path [String] The key for the expected element inside the array
|
225
|
+
Then('each element in {word} payload field {string} has {string}') do |request_type, key_path, element_key_path|
|
226
|
+
list = Maze::Server.list_for(request_type)
|
227
|
+
value = Maze::Helper.read_key_path(list.current[:body], key_path)
|
228
|
+
Maze.check.kind_of Array, value
|
229
|
+
value.each do |element|
|
230
|
+
Maze.check.not_nil(Maze::Helper.read_key_path(element, element_key_path),
|
231
|
+
"Each element in '#{key_path}' must have '#{element_key_path}'")
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# @!group Proxy steps
|
2
|
+
|
3
|
+
# Starts an HTTP proxy server.
|
4
|
+
#
|
5
|
+
Then('I start an http proxy') do
|
6
|
+
Maze::Proxy.instance.start :Http
|
7
|
+
end
|
8
|
+
|
9
|
+
# Starts an authenticated HTTP proxy server.
|
10
|
+
#
|
11
|
+
Then('I start an authenticated http proxy') do
|
12
|
+
Maze::Proxy.instance.start :Http, true
|
13
|
+
end
|
14
|
+
|
15
|
+
# Starts an HTTPS proxy server.
|
16
|
+
#
|
17
|
+
Then('I start an https proxy') do
|
18
|
+
Maze::Proxy.instance.start :Https
|
19
|
+
end
|
20
|
+
|
21
|
+
# Starts an authenticated HTTPS proxy server.
|
22
|
+
#
|
23
|
+
Then('I start an authenticated https proxy') do
|
24
|
+
Maze::Proxy.instance.start :Https, true
|
25
|
+
end
|
26
|
+
|
27
|
+
# Test the proxy server handled a request for a host.
|
28
|
+
#
|
29
|
+
# @step_input host [String] Destination host to check
|
30
|
+
Then('the proxy handled a request for {string}') do |host|
|
31
|
+
Maze.check.true(Maze::Proxy.instance.handled_host?(host), "The proxy did not handle a request for #{host}")
|
32
|
+
end
|
33
|
+
|
34
|
+
# @!endgroup
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @!group Query parameter steps
|
4
|
+
|
5
|
+
# Tests that a query parameter matches a string.
|
6
|
+
#
|
7
|
+
# @step_input request_type [String] The type of request (error, session, build, etc)
|
8
|
+
# @step_input parameter_name [String] The parameter to test
|
9
|
+
# @step_input parameter_value [String] The expected value
|
10
|
+
Then('the {word} {string} query parameter equals {string}') do |request_type, parameter_name, parameter_value|
|
11
|
+
Maze.check.equal(parameter_value,
|
12
|
+
Maze::Helper.parse_querystring(Maze::Server.list_for(request_type).current)[parameter_name][0])
|
13
|
+
end
|
14
|
+
|
15
|
+
# Tests that a query parameter is present and not null.
|
16
|
+
#
|
17
|
+
# @step_input request_type [String] The type of request (error, session, build, etc)
|
18
|
+
# @step_input parameter_name [String] The parameter to test
|
19
|
+
Then('the {word} {string} query parameter is not null') do |request_type, parameter_name|
|
20
|
+
Maze.check.not_nil(Maze::Helper.parse_querystring(Maze::Server.list_for(request_type).current)[parameter_name][0],
|
21
|
+
"The '#{parameter_name}' query parameter should not be null")
|
22
|
+
end
|
23
|
+
|
24
|
+
# Tests that a query parameter is a timestamp.
|
25
|
+
#
|
26
|
+
# @step_input request_type [String] The type of request (error, session, build, etc)
|
27
|
+
# @step_input parameter_name [String] The parameter to test
|
28
|
+
Then('the {word} {string} query parameter is a timestamp') do |request_type, parameter_name|
|
29
|
+
param = Maze::Helper.parse_querystring(Maze::Server.list_for(request_type).current)[parameter_name][0]
|
30
|
+
Maze.check.match(TIMESTAMP_REGEX, param)
|
31
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'test/unit'
|
4
|
+
require 'open-uri'
|
5
|
+
require 'json'
|
6
|
+
require 'cgi'
|
7
|
+
require_relative '../../maze/wait'
|
8
|
+
|
9
|
+
# @!group Request assertion steps
|
10
|
+
|
11
|
+
def assert_received_requests(request_count, list, list_name)
|
12
|
+
timeout = Maze.config.receive_requests_wait
|
13
|
+
wait = Maze::Wait.new(timeout: timeout)
|
14
|
+
|
15
|
+
received = wait.until { list.size >= request_count }
|
16
|
+
|
17
|
+
unless received
|
18
|
+
raise Test::Unit::AssertionFailedError.new <<-MESSAGE
|
19
|
+
Expected #{request_count} #{list_name} but received #{list.size} within the #{timeout}s timeout.
|
20
|
+
This could indicate that:
|
21
|
+
- Bugsnag crashed with a fatal error.
|
22
|
+
- Bugsnag did not make the requests that it should have done.
|
23
|
+
- The requests were made, but not deemed to be valid (e.g. missing integrity header).
|
24
|
+
- The requests made were prevented from being received due to a network or other infrastructure issue.
|
25
|
+
Please check the Maze Runner and device logs to confirm.)
|
26
|
+
MESSAGE
|
27
|
+
end
|
28
|
+
|
29
|
+
Maze.check.equal(request_count, list.size, "#{list.size} #{list_name} received")
|
30
|
+
end
|
31
|
+
|
32
|
+
#
|
33
|
+
# Error request assertions
|
34
|
+
#
|
35
|
+
# Shortcut to waiting to receive a single request of the given type
|
36
|
+
#
|
37
|
+
# @step_input request_type [String] The type of request (error, session, build, etc)
|
38
|
+
Then('I wait to receive a(n) {word}') do |request_type|
|
39
|
+
step "I wait to receive 1 #{request_type}"
|
40
|
+
end
|
41
|
+
|
42
|
+
# Continually checks to see if the required amount of requests have been received,
|
43
|
+
# timing out according to @see Maze.config.receive_requests_wait.
|
44
|
+
# If all expected requests are received and have the Bugsnag-Sent-At header, they
|
45
|
+
# will be sorted by the header.
|
46
|
+
#
|
47
|
+
# @step_input request_type [String] The type of request (error, session, build, etc)
|
48
|
+
# @step_input request_count [Integer] The amount of requests expected
|
49
|
+
Then('I wait to receive {int} {word}') do |request_count, request_type|
|
50
|
+
list = Maze::Server.list_for(request_type)
|
51
|
+
assert_received_requests request_count, list, request_type
|
52
|
+
list.sort_by_sent_at! request_count
|
53
|
+
end
|
54
|
+
|
55
|
+
# Verify that at least a certain amount of requests have been received
|
56
|
+
# This step is only intended for use in stress tests
|
57
|
+
#
|
58
|
+
# @step_input min_received [Integer] The minimum amount of requests required to pass
|
59
|
+
# @step_input request_type [String] The type of request (error, session, build, etc)
|
60
|
+
Then('I have received at least {int} {word}') do |min_received, request_type|
|
61
|
+
list = Maze::Server.list_for(request_type)
|
62
|
+
Maze.check.operator(list.size, :>=, min_received, "Actually received #{list.size} #{request_type} requests")
|
63
|
+
end
|
64
|
+
|
65
|
+
# Assert that the test Server hasn't received any requests - of a specific, or any, type.
|
66
|
+
#
|
67
|
+
# @step_input request_type [String] The type of request ('error', 'session', build, etc), or 'requests' to assert on all
|
68
|
+
# request types.
|
69
|
+
Then('I should receive no {word}') do |request_type|
|
70
|
+
sleep Maze.config.receive_no_requests_wait
|
71
|
+
if request_type == 'requests'
|
72
|
+
# Assert that the test Server hasn't received any requests at all.
|
73
|
+
Maze.check.equal(0, Maze::Server.errors.size, "#{Maze::Server.errors.size} errors received")
|
74
|
+
Maze.check.equal(0, Maze::Server.sessions.size, "#{Maze::Server.sessions.size} sessions received")
|
75
|
+
else
|
76
|
+
list = Maze::Server.list_for(request_type)
|
77
|
+
Maze.check.equal(0, list.size, "#{list.size} #{request_type} received")
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# Moves to the next request
|
82
|
+
#
|
83
|
+
# @step_input request_type [String] The type of request (error, session, build, etc)
|
84
|
+
Then('I discard the oldest {word}') do |request_type|
|
85
|
+
raise "No #{request_type} to discard" if Maze::Server.list_for(request_type).current.nil?
|
86
|
+
|
87
|
+
Maze::Server.list_for(request_type).next
|
88
|
+
end
|
89
|
+
|
90
|
+
Then('the received errors match:') do |table|
|
91
|
+
# Checks that each request matches one of the event fields
|
92
|
+
requests = Maze::Server.errors.remaining
|
93
|
+
match_count = 0
|
94
|
+
|
95
|
+
# iterate through each row in the table. exactly 1 request should match each row.
|
96
|
+
table.hashes.each do |row|
|
97
|
+
requests.each do |request|
|
98
|
+
# Skip if no body.events in this request
|
99
|
+
next if (!request.key? :body) || (!request[:body].key? 'events')
|
100
|
+
|
101
|
+
events = request[:body]['events']
|
102
|
+
Maze.check.equal(1, events.length, 'Expected exactly one event per request')
|
103
|
+
match_count += 1 if Maze::Assertions::RequestSetAssertions.request_matches_row(events[0], row)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
Maze.check.equal(requests.size, match_count, 'Unexpected number of requests matched the received payloads')
|
107
|
+
end
|