bugsnag-maze-runner 6.27.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (88) hide show
  1. checksums.yaml +7 -0
  2. data/bin/bugsnag-print-load-paths +6 -0
  3. data/bin/download-logs +76 -0
  4. data/bin/maze-runner +136 -0
  5. data/bin/upload-app +56 -0
  6. data/lib/features/scripts/await-android-emulator.sh +11 -0
  7. data/lib/features/scripts/clear-android-app-data.sh +8 -0
  8. data/lib/features/scripts/force-stop-android-app.sh +8 -0
  9. data/lib/features/scripts/install-android-app.sh +15 -0
  10. data/lib/features/scripts/launch-android-app.sh +38 -0
  11. data/lib/features/scripts/launch-android-emulator.sh +15 -0
  12. data/lib/features/steps/android_steps.rb +51 -0
  13. data/lib/features/steps/app_automator_steps.rb +228 -0
  14. data/lib/features/steps/aws_sam_steps.rb +212 -0
  15. data/lib/features/steps/breadcrumb_steps.rb +50 -0
  16. data/lib/features/steps/browser_steps.rb +93 -0
  17. data/lib/features/steps/build_api_steps.rb +25 -0
  18. data/lib/features/steps/document_server_steps.rb +7 -0
  19. data/lib/features/steps/error_reporting_steps.rb +342 -0
  20. data/lib/features/steps/feature_flag_steps.rb +190 -0
  21. data/lib/features/steps/header_steps.rb +72 -0
  22. data/lib/features/steps/log_steps.rb +29 -0
  23. data/lib/features/steps/multipart_request_steps.rb +142 -0
  24. data/lib/features/steps/network_steps.rb +75 -0
  25. data/lib/features/steps/payload_steps.rb +234 -0
  26. data/lib/features/steps/proxy_steps.rb +34 -0
  27. data/lib/features/steps/query_parameter_steps.rb +31 -0
  28. data/lib/features/steps/request_assertion_steps.rb +107 -0
  29. data/lib/features/steps/runner_steps.rb +406 -0
  30. data/lib/features/steps/session_tracking_steps.rb +116 -0
  31. data/lib/features/steps/value_steps.rb +119 -0
  32. data/lib/features/support/env.rb +7 -0
  33. data/lib/features/support/internal_hooks.rb +260 -0
  34. data/lib/maze/appium_server.rb +112 -0
  35. data/lib/maze/assertions/request_set_assertions.rb +97 -0
  36. data/lib/maze/aws/sam.rb +112 -0
  37. data/lib/maze/bitbar_devices.rb +84 -0
  38. data/lib/maze/bitbar_utils.rb +112 -0
  39. data/lib/maze/browser_stack_devices.rb +160 -0
  40. data/lib/maze/browser_stack_utils.rb +164 -0
  41. data/lib/maze/browsers_bs.yml +220 -0
  42. data/lib/maze/browsers_cbt.yml +100 -0
  43. data/lib/maze/bugsnag_config.rb +42 -0
  44. data/lib/maze/capabilities.rb +126 -0
  45. data/lib/maze/checks/assert_check.rb +91 -0
  46. data/lib/maze/checks/noop_check.rb +34 -0
  47. data/lib/maze/compare.rb +161 -0
  48. data/lib/maze/configuration.rb +174 -0
  49. data/lib/maze/docker.rb +108 -0
  50. data/lib/maze/document_server.rb +46 -0
  51. data/lib/maze/driver/appium.rb +217 -0
  52. data/lib/maze/driver/browser.rb +138 -0
  53. data/lib/maze/driver/resilient_appium.rb +51 -0
  54. data/lib/maze/errors.rb +20 -0
  55. data/lib/maze/helper.rb +118 -0
  56. data/lib/maze/hooks/appium_hooks.rb +216 -0
  57. data/lib/maze/hooks/browser_hooks.rb +68 -0
  58. data/lib/maze/hooks/command_hooks.rb +9 -0
  59. data/lib/maze/hooks/hooks.rb +61 -0
  60. data/lib/maze/interactive_cli.rb +173 -0
  61. data/lib/maze/logger.rb +73 -0
  62. data/lib/maze/macos_utils.rb +14 -0
  63. data/lib/maze/network.rb +49 -0
  64. data/lib/maze/option/parser.rb +245 -0
  65. data/lib/maze/option/processor.rb +143 -0
  66. data/lib/maze/option/validator.rb +184 -0
  67. data/lib/maze/option.rb +64 -0
  68. data/lib/maze/plugins/bugsnag_reporting_plugin.rb +49 -0
  69. data/lib/maze/plugins/cucumber_report_plugin.rb +101 -0
  70. data/lib/maze/plugins/global_retry_plugin.rb +38 -0
  71. data/lib/maze/proxy.rb +114 -0
  72. data/lib/maze/request_list.rb +82 -0
  73. data/lib/maze/retry_handler.rb +76 -0
  74. data/lib/maze/runner.rb +149 -0
  75. data/lib/maze/sauce_labs_utils.rb +96 -0
  76. data/lib/maze/server.rb +207 -0
  77. data/lib/maze/servlets/base_servlet.rb +22 -0
  78. data/lib/maze/servlets/command_servlet.rb +44 -0
  79. data/lib/maze/servlets/log_servlet.rb +64 -0
  80. data/lib/maze/servlets/reflective_servlet.rb +69 -0
  81. data/lib/maze/servlets/servlet.rb +160 -0
  82. data/lib/maze/smart_bear_utils.rb +71 -0
  83. data/lib/maze/store.rb +15 -0
  84. data/lib/maze/terminating_server.rb +129 -0
  85. data/lib/maze/timers.rb +51 -0
  86. data/lib/maze/wait.rb +35 -0
  87. data/lib/maze.rb +27 -0
  88. 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