bugsnag-maze-runner 6.27.0 → 7.22.1

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