bugsnag-maze-runner 6.27.0 → 7.22.1

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.
Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/bin/download-logs +14 -16
  3. data/bin/maze-runner +53 -15
  4. data/bin/upload-app +6 -6
  5. data/lib/features/steps/breadcrumb_steps.rb +44 -14
  6. data/lib/features/steps/error_reporting_steps.rb +16 -0
  7. data/lib/features/steps/network_steps.rb +66 -6
  8. data/lib/features/steps/payload_steps.rb +23 -0
  9. data/lib/features/steps/request_assertion_steps.rb +87 -8
  10. data/lib/features/steps/runner_steps.rb +22 -0
  11. data/lib/features/steps/session_tracking_steps.rb +1 -1
  12. data/lib/features/steps/trace_steps.rb +206 -0
  13. data/lib/features/support/internal_hooks.rb +31 -84
  14. data/lib/maze/api/appium/file_manager.rb +29 -0
  15. data/lib/maze/aws_public_ip.rb +53 -0
  16. data/lib/maze/checks/assert_check.rb +9 -31
  17. data/lib/maze/client/appium/base_client.rb +131 -0
  18. data/lib/maze/client/appium/bb_client.rb +102 -0
  19. data/lib/maze/client/appium/bb_devices.rb +127 -0
  20. data/lib/maze/client/appium/bs_client.rb +91 -0
  21. data/lib/maze/client/appium/bs_devices.rb +141 -0
  22. data/lib/maze/client/appium/bs_legacy_client.rb +31 -0
  23. data/lib/maze/client/appium/local_client.rb +67 -0
  24. data/lib/maze/client/appium.rb +23 -0
  25. data/lib/maze/client/bb_api_client.rb +102 -0
  26. data/lib/maze/client/bb_client_utils.rb +181 -0
  27. data/lib/maze/client/bs_client_utils.rb +168 -0
  28. data/lib/maze/client/selenium/base_client.rb +15 -0
  29. data/lib/maze/client/selenium/bb_browsers.yml +188 -0
  30. data/lib/maze/client/selenium/bb_client.rb +38 -0
  31. data/lib/maze/client/selenium/bs_browsers.yml +257 -0
  32. data/lib/maze/client/selenium/bs_client.rb +89 -0
  33. data/lib/maze/client/selenium/local_client.rb +16 -0
  34. data/lib/maze/client/selenium.rb +16 -0
  35. data/lib/maze/configuration.rb +18 -10
  36. data/lib/maze/docker.rb +40 -1
  37. data/lib/maze/driver/appium.rb +5 -24
  38. data/lib/maze/driver/browser.rb +12 -26
  39. data/lib/maze/errors.rb +32 -0
  40. data/lib/maze/generator.rb +55 -0
  41. data/lib/maze/helper.rb +7 -3
  42. data/lib/maze/hooks/appium_hooks.rb +29 -190
  43. data/lib/maze/hooks/browser_hooks.rb +2 -55
  44. data/lib/maze/hooks/error_code_hook.rb +49 -0
  45. data/lib/maze/hooks/hooks.rb +2 -2
  46. data/lib/maze/http_request.rb +21 -0
  47. data/lib/maze/logger.rb +16 -3
  48. data/lib/maze/maze_output.rb +88 -0
  49. data/lib/maze/option/parser.rb +17 -22
  50. data/lib/maze/option/processor.rb +21 -34
  51. data/lib/maze/option/validator.rb +38 -67
  52. data/lib/maze/option.rb +16 -18
  53. data/lib/maze/plugins/cucumber_report_plugin.rb +1 -1
  54. data/lib/maze/plugins/error_code_plugin.rb +21 -0
  55. data/lib/maze/request_list.rb +10 -5
  56. data/lib/maze/request_repeater.rb +49 -0
  57. data/lib/maze/retry_handler.rb +4 -13
  58. data/lib/maze/schemas/OtelTraceSchema.json +390 -0
  59. data/lib/maze/schemas/trace_schema.rb +7 -0
  60. data/lib/maze/schemas/trace_validator.rb +98 -0
  61. data/lib/maze/server.rb +74 -30
  62. data/lib/maze/servlets/base_servlet.rb +10 -5
  63. data/lib/maze/servlets/command_servlet.rb +10 -7
  64. data/lib/maze/servlets/log_servlet.rb +2 -2
  65. data/lib/maze/servlets/reflective_servlet.rb +12 -11
  66. data/lib/maze/servlets/servlet.rb +47 -8
  67. data/lib/maze/servlets/temp.rb +0 -0
  68. data/lib/maze/servlets/trace_servlet.rb +13 -0
  69. data/lib/maze.rb +2 -2
  70. data/lib/utils/deep_merge.rb +17 -0
  71. data/lib/utils/selenium_money_patch.rb +17 -0
  72. metadata +101 -21
  73. data/lib/maze/bitbar_devices.rb +0 -84
  74. data/lib/maze/bitbar_utils.rb +0 -112
  75. data/lib/maze/browser_stack_devices.rb +0 -160
  76. data/lib/maze/browser_stack_utils.rb +0 -164
  77. data/lib/maze/browsers_bs.yml +0 -220
  78. data/lib/maze/browsers_cbt.yml +0 -100
  79. data/lib/maze/capabilities.rb +0 -126
  80. data/lib/maze/driver/resilient_appium.rb +0 -51
  81. data/lib/maze/sauce_labs_utils.rb +0 -96
  82. data/lib/maze/smart_bear_utils.rb +0 -71
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 508b197638d8f5af31b4018fecd9776cfa91071ba760c90ce6647cf720d0bfb0
4
- data.tar.gz: 3f47abb96bfa76e64a569b06c58d13b229305405e74142da6b9709a5a655d27e
3
+ metadata.gz: 25918b6a96c58be96023fec6acae1dca6463db11c722fe9ef612f1faa1137f48
4
+ data.tar.gz: 68af4db3ee29db253541685361060a0b969371752a22c7c7e7e78bfd51c7dc31
5
5
  SHA512:
6
- metadata.gz: 5ad71748e2a4ee9a83c65b1192ebd3a5cbfe6c7ee31ef63cdff5ea78674c130315946cd784004c8a0b0f0bfaf21fbf4e71c8277ca94b9a036bf88f7b36781538
7
- data.tar.gz: bec09abd2c07c745d561a3ffc84653b07d792614a5070af2b7fbecc88f5f4a2b720e0bdae2a4e430d872a86885432e3552837b6f1542e92c9e0226949362ad7d
6
+ metadata.gz: 5734aa0594c7286ebe127cb4261b0d029f7241a88c090eea5c52a059459e3f7deed26a3fc14d754155ae8d1000b5001d65b3b903e2d98822ccc6cf7a824573e4
7
+ data.tar.gz: 91d4e5a297ec91c2f71bc2f023632eab9c328fe70bcd61bf511f8eab688b7e85c4cba8a9d5dc5ccec3017a2240daee180f6c0364db490e71376389f77cc5a45f
data/bin/download-logs CHANGED
@@ -2,7 +2,7 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require_relative '../lib/maze'
5
- require_relative '../lib/maze/browser_stack_utils'
5
+ require_relative '../lib/maze/client/bs_client_utils'
6
6
  require_relative '../lib/maze/logger'
7
7
  require 'net/http'
8
8
  require 'json'
@@ -44,9 +44,9 @@ class DownloadLogEntry
44
44
  end
45
45
 
46
46
 
47
- build_info = Maze::BrowserStackUtils.build_info username,
48
- access_key,
49
- opts[:build_id]
47
+ build_info = Maze::Client::BrowserStackClientUtils.build_info username,
48
+ access_key,
49
+ opts[:build_id]
50
50
 
51
51
  $logger.info "Getting logs for build: #{opts[:build_id]}"
52
52
 
@@ -55,20 +55,18 @@ class DownloadLogEntry
55
55
 
56
56
  log_name = "#{opts[:build_id]}-#{index + 1}"
57
57
 
58
- Maze::BrowserStackUtils.download_log username,
59
- access_key,
60
- log_name,
61
- session['automation_session']['device_logs_url'],
62
- :device
58
+ Maze::Client::BrowserStackClientUtils.download_log username,
59
+ access_key,
60
+ log_name,
61
+ session['automation_session']['device_logs_url'],
62
+ :device
63
63
 
64
64
  $logger.info "Downloading Appium Logs for Session: #{index + 1}"
65
- Maze::BrowserStackUtils.download_log username,
66
- access_key,
67
- log_name,
68
- session['automation_session']['appium_logs_url'],
69
- :appium
70
-
71
-
65
+ Maze::Client::BrowserStackClientUtils.download_log username,
66
+ access_key,
67
+ log_name,
68
+ session['automation_session']['appium_logs_url'],
69
+ :appium
72
70
  end
73
71
  end
74
72
  end
data/bin/maze-runner CHANGED
@@ -3,37 +3,60 @@
3
3
 
4
4
  require 'cucumber/cli/main'
5
5
 
6
+ require_relative '../lib/utils/deep_merge'
7
+
6
8
  require_relative '../lib/maze'
7
9
 
8
10
  require_relative '../lib/maze/appium_server'
9
- require_relative '../lib/maze/bitbar_utils'
10
- require_relative '../lib/maze/bitbar_devices'
11
- require_relative '../lib/maze/browser_stack_devices'
12
- require_relative '../lib/maze/browser_stack_utils'
11
+ require_relative '../lib/maze/api/appium/file_manager'
13
12
  require_relative '../lib/maze/bugsnag_config'
14
- require_relative '../lib/maze/capabilities'
13
+ require_relative '../lib/maze/client/bb_api_client'
14
+ require_relative '../lib/maze/client/bb_client_utils'
15
+ require_relative '../lib/maze/client/bs_client_utils'
16
+ require_relative '../lib/maze/client/appium'
17
+ require_relative '../lib/maze/client/appium/base_client'
18
+ require_relative '../lib/maze/client/appium/bb_client'
19
+ require_relative '../lib/maze/client/appium/bb_devices'
20
+ require_relative '../lib/maze/client/appium/bs_client'
21
+ require_relative '../lib/maze/client/appium/bs_legacy_client'
22
+ require_relative '../lib/maze/client/appium/bs_devices'
23
+ require_relative '../lib/maze/client/appium/local_client'
24
+ require_relative '../lib/maze/client/selenium'
25
+ require_relative '../lib/maze/client/selenium/base_client'
26
+ require_relative '../lib/maze/client/selenium/bb_client'
27
+ require_relative '../lib/maze/client/selenium/bs_client'
28
+ require_relative '../lib/maze/client/selenium/local_client'
29
+ require_relative '../lib/maze/aws_public_ip'
15
30
  require_relative '../lib/maze/compare'
16
31
  require_relative '../lib/maze/docker'
17
32
  require_relative '../lib/maze/document_server'
18
33
  require_relative '../lib/maze/errors'
34
+ require_relative '../lib/maze/generator'
19
35
  require_relative '../lib/maze/helper'
36
+ require_relative '../lib/maze/http_request'
20
37
  require_relative '../lib/maze/logger'
21
38
  require_relative '../lib/maze/macos_utils'
39
+ require_relative '../lib/maze/maze_output'
22
40
  require_relative '../lib/maze/network'
23
41
  require_relative '../lib/maze/proxy'
24
42
  require_relative '../lib/maze/retry_handler'
43
+ require_relative '../lib/maze/request_repeater'
25
44
  require_relative '../lib/maze/runner'
26
- require_relative '../lib/maze/sauce_labs_utils'
27
- require_relative '../lib/maze/smart_bear_utils'
28
45
  require_relative '../lib/maze/terminating_server'
29
46
 
30
47
  require_relative '../lib/maze/servlets/base_servlet'
31
48
  require_relative '../lib/maze/servlets/command_servlet'
32
49
  require_relative '../lib/maze/servlets/servlet'
33
50
  require_relative '../lib/maze/servlets/log_servlet'
51
+ require_relative '../lib/maze/servlets/trace_servlet'
34
52
  require_relative '../lib/maze/servlets/reflective_servlet'
35
53
  require_relative '../lib/maze/server'
36
54
 
55
+ require_relative '../lib/maze/assertions/request_set_assertions'
56
+
57
+ require_relative '../lib/maze/schemas/trace_schema'
58
+ require_relative '../lib/maze/schemas/trace_validator'
59
+
37
60
  require_relative '../lib/maze/store'
38
61
  require_relative '../lib/maze/timers'
39
62
 
@@ -46,21 +69,23 @@ require_relative '../lib/maze/option/processor'
46
69
  require_relative '../lib/maze/option/validator'
47
70
 
48
71
 
49
- require_relative '../lib/maze/assertions/request_set_assertions'
50
-
51
72
  require_relative '../lib/maze/hooks/hooks'
52
73
  require_relative '../lib/maze/hooks/appium_hooks'
53
74
  require_relative '../lib/maze/hooks/browser_hooks'
54
75
  require_relative '../lib/maze/hooks/command_hooks'
76
+ require_relative '../lib/maze/hooks/error_code_hook'
55
77
 
56
78
  require_relative '../lib/maze/driver/appium'
57
79
  require_relative '../lib/maze/driver/browser'
58
- require_relative '../lib/maze/driver/resilient_appium'
59
80
 
60
81
  require_relative '../lib/maze/plugins/bugsnag_reporting_plugin'
61
82
  require_relative '../lib/maze/plugins/cucumber_report_plugin'
83
+ require_relative '../lib/maze/plugins/error_code_plugin'
62
84
  require_relative '../lib/maze/plugins/global_retry_plugin'
63
85
 
86
+ # Require monkey-patches after everything else
87
+ require_relative '../lib/utils/selenium_money_patch'
88
+
64
89
  # Encapsulates the MazeRunner entry point
65
90
  class MazeRunnerEntry
66
91
 
@@ -105,13 +130,26 @@ class MazeRunnerEntry
105
130
  options = Maze::Option::Parser.parse args
106
131
 
107
132
  if options[Maze::Option::LIST_DEVICES]
108
- case options[Maze::Option::FARM]
133
+ case options[Maze::Option::FARM].to_sym
109
134
  when :bs
110
- Maze::BrowserStackDevices.list_devices('ios')
111
- Maze::BrowserStackDevices.list_devices('android')
135
+ Maze::Client::Appium::BrowserStackDevices.list_devices('ios')
136
+ Maze::Client::Appium::BrowserStackDevices.list_devices('android')
137
+ when :bb
138
+ unless options[Maze::Option::ACCESS_KEY]
139
+ puts 'Listing BitBar devices or device groups available requires a valid access key'
140
+ exit 1
141
+ end
142
+ access_key = options[Maze::Option::ACCESS_KEY]
143
+ if options[Maze::Option::DEVICE] && !options[Maze::Option::DEVICE].empty?
144
+ options[Maze::Option::DEVICE].each do |device_group|
145
+ Maze::Client::Appium::BitBarDevices.list_devices_for_group(device_group, access_key)
146
+ end
147
+ else
148
+ Maze::Client::Appium::BitBarDevices.list_device_groups(access_key)
149
+ end
112
150
  else
113
- Maze::BrowserStackDevices.list_devices('ios')
114
- Maze::BrowserStackDevices.list_devices('android')
151
+ Maze::Client::Appium::BrowserStackDevices.list_devices('ios')
152
+ Maze::Client::Appium::BrowserStackDevices.list_devices('android')
115
153
  end
116
154
  exit 0
117
155
  end
data/bin/upload-app CHANGED
@@ -2,7 +2,7 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require_relative '../lib/maze'
5
- require_relative '../lib/maze/browser_stack_utils'
5
+ require_relative '../lib/maze/client/bs_client_utils'
6
6
  require_relative '../lib/maze/logger'
7
7
  require_relative '../lib/maze/helper'
8
8
  require 'optimist'
@@ -45,12 +45,12 @@ class UploadAppEntry
45
45
  end
46
46
  end
47
47
 
48
- Maze::BrowserStackUtils.upload_app username,
49
- access_key,
50
- opts[:app],
51
- opts[:app_id_file]
48
+ Maze::Client::BrowserStackClientUtils.upload_app username,
49
+ access_key,
50
+ opts[:app],
51
+ opts[:app_id_file]
52
52
 
53
53
  end
54
54
  end
55
55
 
56
- UploadAppEntry.new.start(ARGV)
56
+ UploadAppEntry.new.start(ARGV)
@@ -1,16 +1,41 @@
1
1
  # @!group Breadcrumb steps
2
2
 
3
+ # Tests whether the first event entry contains the specified number of breadcrumbs.
4
+ #
5
+ # @step_input expected [Integer] The expected number of breadcrumbs
6
+ Then("the event has {int} breadcrumb(s)") do |expected|
7
+ breadcrumbs = Maze::Server.errors.current[:body]['events'].first['breadcrumbs']
8
+
9
+ Maze.check.equal(
10
+ expected,
11
+ breadcrumbs&.length || 0,
12
+ "Expected event to have '#{expected}' breadcrumbs, but got: #{breadcrumbs}"
13
+ )
14
+ end
15
+
16
+ # Tests whether the first event entry contains no breadcrumbs.
17
+ Then("the event has no breadcrumbs") do
18
+ breadcrumbs = Maze::Server.errors.current[:body]['events'].first['breadcrumbs']
19
+
20
+ Maze.check.true(
21
+ # some notifiers may omit breadcrumbs entirely when empty, otherwise it should
22
+ # be an empty array
23
+ breadcrumbs.nil? || breadcrumbs.empty?,
24
+ "Expected event not to have breadcrumbs, but got: #{breadcrumbs}"
25
+ )
26
+ end
27
+
3
28
  # Tests whether the first event entry contains a specific breadcrumb with a type and name.
4
29
  #
5
30
  # @step_input type [String] The expected breadcrumb's type
6
31
  # @step_input name [String] The expected breadcrumb's name
7
32
  Then('the event has a {string} breadcrumb named {string}') do |type, name|
8
- value = Maze::Server.errors.current[:body]['events'].first['breadcrumbs']
9
- found = false
10
- value.each do |crumb|
11
- found = true if crumb['type'] == type and crumb['name'] == name
12
- end
13
- raise("No breadcrumb matched: #{value}") unless found
33
+ breadcrumbs = Maze::Server.errors.current[:body]['events'].first['breadcrumbs']
34
+
35
+ Maze.check.true(
36
+ breadcrumbs.any? { |crumb| crumb['type'] == type && crumb['name'] == name },
37
+ "Expected event to have a breadcrumb with type '#{type}' and name '#{name}', but got: #{breadcrumbs}"
38
+ )
14
39
  end
15
40
 
16
41
  # Tests whether the first event entry contains a specific breadcrumb with a type and message.
@@ -19,10 +44,7 @@ end
19
44
  # @step_input message [String] The expected breadcrumb's message
20
45
  Then('the event has a {string} breadcrumb with message {string}') do |type, message|
21
46
  value = Maze::Server.errors.current[:body]['events'].first['breadcrumbs']
22
- found = false
23
- value.each do |crumb|
24
- found = true if crumb['type'] == type && crumb['metaData'] && crumb['metaData']['message'] == message
25
- end
47
+ found = value.any? { |crumb| crumb['type'] == type && crumb['metaData'] && crumb['metaData']['message'] == message }
26
48
  raise("No breadcrumb matched: #{value}") unless found
27
49
  end
28
50
 
@@ -32,13 +54,21 @@ end
32
54
  # @step_input type [String] The type of breadcrumb expected to not be present
33
55
  Then('the event does not have a {string} breadcrumb') do |type|
34
56
  value = Maze::Server.errors.current[:body]['events'].first['breadcrumbs']
35
- found = false
36
- value.each do |crumb|
37
- found = true if crumb['type'] == type
38
- end
57
+ found = value.any? { |crumb| crumb['type'] == type }
39
58
  raise("Breadcrumb with type: #{type} matched") if found
40
59
  end
41
60
 
61
+ # Test whether the first event entry does not contain a breadcrumb with a specific type and message.
62
+ # Used for confirming filtering of breadcrumbs
63
+ #
64
+ # @step_input type [String] The type of the breadcrumb expected to be absent
65
+ # @step_input message [String] The message of the breadcrumb expected to be absent
66
+ Then('the event does not have a {string} breadcrumb with message {string}') do |type, message|
67
+ value = Maze::Server.errors.current[:body]['events'].first['breadcrumbs']
68
+ found = value.any? { |crumb| crumb['type'] == type && crumb['metaData'] && crumb['metaData']['message'] == message }
69
+ raise("Breadcrumb with type: #{type} and message: #{message} matched") if found
70
+ end
71
+
42
72
  # Tests whether any breadcrumb matches a given JSON fixture. This follows all the usual rules for JSON fixture matching.
43
73
  #
44
74
  # @step_input json_fixture [String] A path to the JSON fixture to compare against
@@ -120,6 +120,22 @@ Then('the event {string} equals {string}') do |field, string_value|
120
120
  step "the error payload field \"events.0.#{field}\" equals \"#{string_value}\""
121
121
  end
122
122
 
123
+ # Tests whether a value in the first event entry matches a floating point value.
124
+ #
125
+ # @step_input field [String] The relative location of the value to test
126
+ # @step_input string_value [String] The string to match against
127
+ Then('the event {string} equals {float}') do |field, float_value|
128
+ step "the error payload field \"events.0.#{field}\" equals #{float_value}"
129
+ end
130
+
131
+ # Tests whether a value in the first event entry matches a floating point value, to a given number of decimal places.
132
+ #
133
+ # @step_input field [String] The relative location of the value to test
134
+ # @step_input string_value [String] The string to match against
135
+ Then('the event {string} equals {float} to {int} decimal place(s)') do |field, float_value, places|
136
+ step "the error payload field \"events.0.#{field}\" equals #{float_value} to #{places} decimal places"
137
+ end
138
+
123
139
  # Tests whether a value in the first event entry equals an integer.
124
140
  #
125
141
  # @step_input field [String] The relative location of the value to test
@@ -12,32 +12,92 @@ end
12
12
  #
13
13
  # @step_input status_code [Integer] The status code to return
14
14
  When('I set the HTTP status code to {int}') do |status_code|
15
- Maze::Server.status_code = status_code
15
+ Maze::Server.set_status_code_generator(Maze::Generator.new [status_code].cycle)
16
16
  end
17
17
 
18
18
  # Sets the HTTP status code to be used for the next request
19
19
  #
20
20
  # @step_input status_code [Integer] The status code to return
21
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
22
+ Maze::Server.set_status_code_generator(create_defaulting_generator([status_code], Maze::Server::DEFAULT_STATUS_CODE))
23
+ end
24
+
25
+ # Sets the HTTP status code to be used for the next set of requests
26
+ #
27
+ # @step_input status_codes [String] A comma separated list of status codes to return
28
+ When('I set the HTTP status code for the next requests to {string}') do |status_codes|
29
+ codes = status_codes.split(',').map(&:strip)
30
+ Maze::Server.set_status_code_generator(create_defaulting_generator(codes, Maze::Server::DEFAULT_STATUS_CODE))
31
+ end
32
+
33
+ # Steps the HTTP status code to be used for all subsequent requests for a given connection type
34
+ #
35
+ # @step_input http_verb [String] The type of request this code will be used for
36
+ # @step_input status_code [Integer] The status code to return
37
+ When('I set the HTTP status code for {string} requests to {int}') do |http_verb, status_code|
38
+ raise("Invalid HTTP verb: #{http_verb}") unless Maze::Server::ALLOWED_HTTP_VERBS.include?(http_verb)
39
+ Maze::Server.set_status_code_generator(Maze::Generator.new([status_code].cycle), http_verb)
40
+ end
41
+
42
+ # Steps the HTTP status code to be used for the next request for a given connection type
43
+ #
44
+ # @step_input http_verb [String] The type of request this code will be used for
45
+ # @step_input status_code [Integer] The status code to return
46
+ When('I set the HTTP status code for the next {string} request to {int}') do |http_verb, status_code|
47
+ raise("Invalid HTTP verb: #{http_verb}") unless Maze::Server::ALLOWED_HTTP_VERBS.include?(http_verb)
48
+ Maze::Server.set_status_code_generator(create_defaulting_generator([status_code], Maze::Server::DEFAULT_STATUS_CODE), http_verb)
49
+ end
50
+
51
+ # Sets the sampling probability to be used for all subsequent trace responses
52
+ #
53
+ # @step_input sampling_probability [String] The sampling probability to return
54
+ When('I set the sampling probability to {string}') do |sampling_probability|
55
+ Maze::Server.set_sampling_probability_generator(Maze::Generator.new [sampling_probability].cycle)
56
+ end
57
+
58
+ # Sets the sampling probability to be used for the next trace responses
59
+ #
60
+ # @step_input status_code [Integer] The status code to return
61
+ When('I set the sampling probability for the next trace to {string}') do |sampling_probability|
62
+ Maze::Server.set_sampling_probability_generator(create_defaulting_generator([sampling_probability], Maze::Server::DEFAULT_SAMPLING_PROBABILITY))
63
+ end
64
+
65
+ # Sets the sampling probability to be used for the next set of trace requests
66
+ #
67
+ # @step_input sampling_probability [String] A comma separated list of values to use, with "null" used to omit the header
68
+ When('I set the sampling probability for the next traces to {string}') do |status_codes|
69
+ codes = status_codes.split(',').map(&:strip)
70
+ Maze::Server.set_sampling_probability_generator(create_defaulting_generator(codes, Maze::Server::DEFAULT_SAMPLING_PROBABILITY))
24
71
  end
25
72
 
26
73
  # Sets the response delay to be used for all subsequent requests
27
74
  #
28
75
  # @step_input response_delay_ms [Integer] The delay in milliseconds
29
76
  When('I set the response delay to {int} milliseconds') do |response_delay_ms|
30
- Maze::Server.response_delay_ms = response_delay_ms
77
+ Maze::Server.set_response_delay_generator(Maze::Generator.new [response_delay_ms].cycle)
31
78
  end
32
79
 
33
80
  # Sets the response delay to be used for the next request
34
81
  #
35
82
  # @step_input delay [Integer] The delay in milliseconds
36
83
  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
84
+ Maze::Server.set_response_delay_generator(create_defaulting_generator([delay], Maze::Server::DEFAULT_RESPONSE_DELAY))
85
+ end
86
+
87
+ def create_defaulting_generator(codes, default)
88
+ enumerator = Enumerator.new do |yielder|
89
+ codes.each do |code|
90
+ yielder.yield code
91
+ end
92
+
93
+ loop do
94
+ yielder.yield default
95
+ end
96
+ end
97
+ Maze::Generator.new enumerator
39
98
  end
40
99
 
100
+
41
101
  # Attempts to open a URL.
42
102
  #
43
103
  # @step_input url [String] The URL to open.
@@ -86,6 +86,29 @@ Then('the {word} payload field {string} equals {int}') do |request_type, field_p
86
86
  Maze::Helper.read_key_path(Maze::Server.list_for(request_type).current[:body], field_path))
87
87
  end
88
88
 
89
+ # Tests that a payload element equals a float.
90
+ #
91
+ # @step_input request_type [String] The type of request (error, session, build, etc)
92
+ # @step_input field_path [String] Path to the tested element
93
+ # @step_input float_value [Float] The value to test against
94
+ Then('the {word} payload field {string} equals {float}') do |request_type, field_path, float_value|
95
+ Maze.check.equal(float_value,
96
+ Maze::Helper.read_key_path(Maze::Server.list_for(request_type).current[:body], field_path))
97
+ end
98
+
99
+ # Tests that a payload element equals a float, to a given number of decimal places.
100
+ #
101
+ # @step_input request_type [String] The type of request (error, session, build, etc)
102
+ # @step_input field_path [String] Path to the tested element
103
+ # @step_input float_value [Float] The value to test against
104
+ # @step_input places [Int] The number of decimal places to round the actual value to first
105
+ Then('the {word} payload field {string} equals {float} to {int} decimal place(s)') do |request_type, field_path, float_value, places|
106
+ body = Maze::Server.list_for(request_type).current[:body]
107
+ rounded_value = Maze::Helper.read_key_path(body, field_path).round places
108
+ Maze.check.equal(float_value,
109
+ rounded_value)
110
+ end
111
+
89
112
  # Tests the payload field value against an environment variable.
90
113
  #
91
114
  # @step_input request_type [String] The type of request (error, session, build, etc)
@@ -8,15 +8,15 @@ require_relative '../../maze/wait'
8
8
 
9
9
  # @!group Request assertion steps
10
10
 
11
- def assert_received_requests(request_count, list, list_name)
11
+ def assert_received_requests(request_count, list, list_name, precise = true)
12
12
  timeout = Maze.config.receive_requests_wait
13
13
  wait = Maze::Wait.new(timeout: timeout)
14
14
 
15
- received = wait.until { list.size >= request_count }
15
+ received = wait.until { list.size_remaining >= request_count }
16
16
 
17
17
  unless received
18
18
  raise Test::Unit::AssertionFailedError.new <<-MESSAGE
19
- Expected #{request_count} #{list_name} but received #{list.size} within the #{timeout}s timeout.
19
+ Expected #{request_count} #{list_name} but received #{list.size_remaining} within the #{timeout}s timeout.
20
20
  This could indicate that:
21
21
  - Bugsnag crashed with a fatal error.
22
22
  - Bugsnag did not make the requests that it should have done.
@@ -26,7 +26,65 @@ def assert_received_requests(request_count, list, list_name)
26
26
  MESSAGE
27
27
  end
28
28
 
29
- Maze.check.equal(request_count, list.size, "#{list.size} #{list_name} received")
29
+ if precise
30
+ Maze.check.equal(request_count, list.size_remaining, "#{list.size_remaining} #{list_name} received")
31
+ else
32
+ Maze.check.operator(request_count, :<=, list.size_remaining, "#{list.size_remaining} #{list_name} received")
33
+ end
34
+
35
+ verify_schema_matches(list, list_name)
36
+ validate_payload_elements(list, list_name)
37
+ end
38
+
39
+ def verify_schema_matches(list, list_name)
40
+ request_schema_results = list.all.map { |request| request[:schema_errors] }
41
+ passed = true
42
+ request_schema_results.each.with_index(1) do |schema_errors, index|
43
+ next if schema_errors.nil?
44
+ if schema_errors.size > 0
45
+ passed = false
46
+ $stdout.puts "\n"
47
+ $stdout.puts "\e[31m--- #{list_name} #{index} failed validation with errors at the following locations:\e[0m"
48
+ schema_errors.each do |error|
49
+ $stdout.puts "\e[31m#{error["data_pointer"]} failed to match #{error["schema_pointer"]}\e[0m"
50
+ end
51
+ $stdout.puts "\n"
52
+ end
53
+ end
54
+
55
+ unless passed
56
+ raise Test::Unit::AssertionFailedError.new 'The received payloads did not match the endpoint schema. A full list of the errors can be found above'
57
+ end
58
+ end
59
+
60
+ def validate_payload_elements(list, list_name)
61
+ validator_class = case list_name
62
+ when 'trace', 'traces'
63
+ Maze::Schemas::TraceValidator
64
+ else
65
+ nil
66
+ end
67
+
68
+ if validator_class
69
+ validators = list.all.map do |request|
70
+ validator = validator_class.new(request[:body])
71
+ validator.validate
72
+ validator
73
+ end
74
+
75
+ return if validators.all? { |validator| validator.success }
76
+ validators.each.with_index(1) do |validator, index|
77
+ unless validator.success
78
+ $stdout.puts "\n"
79
+ $stdout.puts "\e[31m--- #{list_name} #{index} failed validation with the following errors:\e[0m"
80
+ validator.errors.each do |error|
81
+ $stdout.puts "\e[31m#{error}\e[0m"
82
+ end
83
+ $stdout.puts "\n"
84
+ end
85
+ end
86
+ raise Test::Unit::AssertionFailedError.new("One or more #{list_name} payloads failed validation. A full list of the errors can be found above")
87
+ end
30
88
  end
31
89
 
32
90
  #
@@ -52,6 +110,27 @@ Then('I wait to receive {int} {word}') do |request_count, request_type|
52
110
  list.sort_by_sent_at! request_count
53
111
  end
54
112
 
113
+ # Continually checks to see if at least the number requests given has been received,
114
+ # timing out according to @see Maze.config.receive_requests_wait.
115
+ #
116
+ # This step can tolerate receiving more than the expected number of requests.
117
+ #
118
+ # @step_input request_type [String] The type of request (error, session, build, etc)
119
+ # @step_input request_count [Integer] The amount of requests expected
120
+ Then('I wait to receive at least {int} {word}') do |request_count, request_type|
121
+ list = Maze::Server.list_for(request_type)
122
+ assert_received_requests request_count, list, request_type, false
123
+ end
124
+
125
+ # Sorts the remaining requests in a list by the field path given.
126
+ #
127
+ # @step_input request_type [String] The type of request (error, session, build, etc)
128
+ # @step_input field_path [String] The field to sort by
129
+ Then('I sort the {word} by the payload field {string}') do |request_type, field_path|
130
+ list = Maze::Server.list_for(request_type)
131
+ list.sort_by! field_path
132
+ end
133
+
55
134
  # Verify that at least a certain amount of requests have been received
56
135
  # This step is only intended for use in stress tests
57
136
  #
@@ -59,7 +138,7 @@ end
59
138
  # @step_input request_type [String] The type of request (error, session, build, etc)
60
139
  Then('I have received at least {int} {word}') do |min_received, request_type|
61
140
  list = Maze::Server.list_for(request_type)
62
- Maze.check.operator(list.size, :>=, min_received, "Actually received #{list.size} #{request_type} requests")
141
+ Maze.check.operator(list.size_remaining, :>=, min_received, "Actually received #{list.size_remaining} #{request_type} requests")
63
142
  end
64
143
 
65
144
  # Assert that the test Server hasn't received any requests - of a specific, or any, type.
@@ -70,11 +149,11 @@ Then('I should receive no {word}') do |request_type|
70
149
  sleep Maze.config.receive_no_requests_wait
71
150
  if request_type == 'requests'
72
151
  # 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")
152
+ Maze.check.equal(0, Maze::Server.errors.size_remaining, "#{Maze::Server.errors.size_remaining} errors received")
153
+ Maze.check.equal(0, Maze::Server.sessions.size_remaining, "#{Maze::Server.sessions.size_remaining} sessions received")
75
154
  else
76
155
  list = Maze::Server.list_for(request_type)
77
- Maze.check.equal(0, list.size, "#{list.size} #{request_type} received")
156
+ Maze.check.equal(0, list.size_remaining, "#{list.size_remaining} #{request_type} received")
78
157
  end
79
158
  end
80
159
 
@@ -97,6 +97,28 @@ When('I run the service {string} with the command') do |service, command|
97
97
  Maze::Docker.start_service(service, command: one_line_cmd)
98
98
  end
99
99
 
100
+ # Executes a command in the given docker compose service.
101
+ #
102
+ # The service must already be running for this to succeed as it uses 'docker
103
+ # compose exec'.
104
+ #
105
+ # @step_input command [String] The command to run inside the service
106
+ # @step_input service [String] The name of the service
107
+ When('I execute the command {string} in the service {string}') do |command, service|
108
+ Maze::Docker.exec(service, command)
109
+ end
110
+
111
+ # Executes a command in the given docker compose service in the background.
112
+ #
113
+ # The service must already be running for this to succeed as it uses 'docker
114
+ # compose exec --detach'.
115
+ #
116
+ # @step_input command [String] The command to run inside the service
117
+ # @step_input service [String] The name of the service
118
+ When('I execute the command {string} in the service {string} in the background') do |command, service|
119
+ Maze::Docker.exec(service, command, detach: true)
120
+ end
121
+
100
122
  # Allows validation of the last exit code of the last run docker-compose command.
101
123
  # Will fail if no commands have been run.
102
124
  #
@@ -36,7 +36,7 @@ Then('the session is valid for the session reporting API version {string} for th
36
36
  And the session "Content-Type" header equals "application/json"
37
37
  And the session "Bugsnag-Sent-At" header is a timestamp
38
38
 
39
- And the session payload field "notifier.name" matches the regex "(Android|iOS) Bugsnag Notifier"
39
+ And the session payload field "notifier.name" matches the regex "(Bugsnag React Native|(Android|iOS) Bugsnag Notifier)"
40
40
  And the session payload field "notifier.url" is not null
41
41
  And the session payload field "notifier.version" is not null
42
42