bugsnag-maze-runner 6.27.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|