wsdirector-core 0.0.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 582448a4834a77dbe5efc80bc149a3bef0eba766
4
- data.tar.gz: 9d5ef63857c5da95552ff953a186d1d449c1c5d0
2
+ SHA256:
3
+ metadata.gz: 950b1fe48bc078969f01feab7a589280a74cdfce92a2472b04a3af35710f634b
4
+ data.tar.gz: ec42ae09c70ab4d143f4d04506d066a6bab4aad7143246f5803e1465fbe772d4
5
5
  SHA512:
6
- metadata.gz: fd1a5cba842b179f8a8f4acb864dbefbe9789225c8b8af87833ddaf580600075a5bf3e4916aedc4b88acecf0cee0a2962d840eb8aeea44696e703de1b2ebdaa2
7
- data.tar.gz: 831e35a16c6da9f99e09e8b36918d48348503cfeb1c38c57e2d39a5ba66469d223425376ee123a91f4a0a954af267633850a82461e0c4a77845dddb9317d551b
6
+ metadata.gz: da07984b3fa426e11a49593f30d02036fe694f3519ce11aec26ff93eccdf20104461b77cc8c0c615ae733a207030c1297c5b51e2ffa70f30f15cf4b1387ff0fa
7
+ data.tar.gz: 126a6108c638b6d314bfa4581ab487e626fb8a59db8d5e5af20473330b749b671dce8a921ad545062e75c781ed76d5cfa275fd69eb18e7fd27bf150734314fea
data/CHANGELOG.md ADDED
@@ -0,0 +1,144 @@
1
+ # Change log
2
+
3
+ ## master
4
+
5
+ ## 1.0.0 (2022-09-19)
6
+
7
+ - Add Phoenix Channels protocol support. ([@palkan][])
8
+
9
+ - Make `receive` order-independent. ([@palkan][])
10
+
11
+ Using `receive` now looks up a matching message through all the mailbox (already received or newly arrived messages).
12
+ If you need strict order guarantees, add `ordered: true` to `receive`.
13
+
14
+ - Add `WSDirector::Snapshot`. ([@palkan][])
15
+
16
+ - Add `locals` support when running scenarios programmatically. ([@palkan][])
17
+
18
+ - Add partial receive data matching support (via `data>` field). ([@palkan][])
19
+
20
+ - Add `connection_options` support (headers, cookies, query). ([@palkan][])
21
+
22
+ - Add loading custom protocols support. ([@palkan][])
23
+
24
+ - Allow passing URL without a scheme part. ([@palkan][])
25
+
26
+ - Add `-vv` option to print action logs. ([@palkan][])
27
+
28
+ - Add `--subprotocol` support and `connection_options` in the scenario files. ([@palkan][])
29
+
30
+ - Add `-f/--file` option to specify scenario path. ([@palkan][])
31
+
32
+ - Drop Ruby 2.5 support.
33
+
34
+ ## 0.5.0 (2021-10-13)
35
+
36
+ - Add JSON support. ([@wazzuper][])
37
+
38
+ - Add new commands to CLI.
39
+
40
+ You can pass a JSON scenario directly to the CLI without creating a file:
41
+
42
+ ```bash
43
+ wsdirector -i '[{"receive": {"data":"welcome"}},{"send":{"data":"send message"}},{"receive":{"data":"receive message"}}]' -u ws://websocket.server:9876
44
+ ```
45
+
46
+ or you can pass it as a JSON file:
47
+
48
+ ```bash
49
+ wsdirector scenario.json -u ws://websocket.server:9876
50
+ ```
51
+
52
+ - Add loop option support. ([@last-in-japan][])
53
+
54
+ You can specify a `loop` option to perform a similar set of actions multiple times:
55
+
56
+ ```yml
57
+ # script.yml
58
+ - client:
59
+ name: "listeners"
60
+ loop:
61
+ multiplier: ":scale" # :scale take number from -s param, and run :scale number of clients in this group
62
+ actions:
63
+ - receive:
64
+ data:
65
+ type: "welcome"
66
+ - send:
67
+ data:
68
+ command: "subscribe"
69
+ identifier: "{\"channel\":\"Channel\"}"
70
+ - receive:
71
+ data:
72
+ identifier: "{\"channel\":\"Channel\"}"
73
+ type: "confirm_subscription"
74
+ - wait_all
75
+ - receive:
76
+ multiplier: ":scale + 1"
77
+ ```
78
+
79
+ Useful in combination with `:scale`.
80
+
81
+ ## 0.4.0 (2020-08-24)
82
+
83
+ - **Drop Ruby 2.4 support**. ([@palkan][])
84
+
85
+ - Add sampling support. ([@palkan][])
86
+
87
+ You can specify a `sample` option for a step to only run this step by specified number of clients from the group:
88
+
89
+ ```yml
90
+ - perform:
91
+ sample: ":scale / 2"
92
+ channel: "chat"
93
+ params:
94
+ id: 2
95
+ action: "speak"
96
+ data:
97
+ message: "Hello!"
98
+ ```
99
+
100
+ Useful in combination with `:scale`.
101
+
102
+ **NOTE:** Sample size is always greater or equal to 1.
103
+
104
+ - Add ERB support. ([@palkan][])
105
+
106
+ Now you can, for example, access `ENV` from scenarios.
107
+
108
+ ## 0.3.0 (2018-05-02)
109
+
110
+ - Add `debug` action and options. ([@palkan][])
111
+
112
+ Allows to print arbitrary messages during the execution.
113
+
114
+ - Add `sleep` action. ([@palkan][])
115
+
116
+ - Add `receive_all` action. ([@palkan][])
117
+
118
+ Allows to handle multiple messages with unspecified order:
119
+
120
+ ```yml
121
+ - receive_all:
122
+ messages:
123
+ - data:
124
+ text: "Hello!"
125
+ multiplier: ":scale + :scale"
126
+ channel: "chat"
127
+ params:
128
+ id: 2
129
+ - data:
130
+ text: "message sent"
131
+ channel: "chat"
132
+ params:
133
+ id: 2
134
+ ```
135
+
136
+ ## 0.2.0 (2017-11-05)
137
+
138
+ - Initial version. ([@palkan][], [@Kirillvs][], [@Grandman][])
139
+
140
+ [@palkan]: https://github.com/palkan
141
+ [@Kirillvs]: https://github.com/Kirillvs
142
+ [@Grandman]: https://github.com/Grandman
143
+ [@wazzuper]: https://github.com/wazzuper
144
+ [@last-in-japan]: https://github.com/last-in-japan
data/LICENSE.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2017 Vladimir Dementyev
3
+ Copyright (c) 2017-2022 palkan
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -1,38 +1,353 @@
1
- # Wsdirector::Core
1
+ [![Cult Of Martians](http://cultofmartians.com/assets/badges/badge.svg)](http://cultofmartians.com/tasks/websocket-director.html)
2
+ [![Gem Version](https://badge.fury.io/rb/wsdirector-cli.svg)](https://rubygems.org/gems/wsdirector-cli)
3
+ [![Build](https://github.com/palkan/wsdirector/workflows/Build/badge.svg)](https://github.com/palkan/wsdirector/actions)
2
4
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/wsdirector/core`. To experiment with that code, run `bin/console` for an interactive prompt.
5
+ # WebSocket Director
4
6
 
5
- TODO: Delete this and the text above, and describe your gem
7
+ Command line tool for testing WebSocket servers using scenarios.
8
+
9
+ Suitable for testing any websocket server implementation, like [Action Cable](https://github.com/rails/rails/tree/master/actioncable), [AnyCable](https://anycable.io), [Phoenix Channels](https://hexdocs.pm/phoenix/channels.html), [GraphQL WS](https://github.com/enisdenjo/graphql-ws) and so on.
6
10
 
7
11
  ## Installation
8
12
 
9
- Add this line to your application's Gemfile:
13
+ Install CLI:
14
+
15
+ ```sh
16
+ gem install wsdirector-cli
17
+ ```
18
+
19
+ Or use WebSockets Director as a library (see below for intructions):
10
20
 
11
21
  ```ruby
12
- gem 'wsdirector-core'
22
+ # Gemfile
23
+ gem "wsdirector-core", "~> 1.0"
13
24
  ```
14
25
 
15
- And then execute:
26
+ ## Usage
16
27
 
17
- $ bundle
28
+ Create YAML file with simple testing script:
18
29
 
19
- Or install it yourself as:
30
+ ```yml
31
+ # script.yml
32
+ - receive: "Welcome" # expect to receive message
33
+ - send:
34
+ data: "send message" # send message, all messages in data will be parse to json
35
+ - receive:
36
+ data: "receive message" # expect to receive json message
37
+ ```
20
38
 
21
- $ gem install wsdirector-core
39
+ and run it with this command:
22
40
 
23
- ## Usage
41
+ ```bash
42
+ wsdirector -f script.yml -u ws://websocket.server:9876/ws
43
+
44
+ #=> 1 clients, 0 failures
45
+ ```
46
+
47
+ You can also use positional arguments:
48
+
49
+ ```sh
50
+ wsdirector script.yml ws://websocket.server:9876/ws
51
+ ```
52
+
53
+ You can create more complex scenarios with multiple client groups:
54
+
55
+ ```yml
56
+ # script.yml
57
+ - client: # first clients group
58
+ name: "publisher" # optional group name
59
+ multiplier: ":scale" # :scale take number from -s param, and run :scale number of clients in this group
60
+ actions:
61
+ - receive:
62
+ data: "Welcome"
63
+ - wait_all # makes all clients in all groups wait untill every client get this point (global barrier)
64
+ - send:
65
+ data: "test message"
66
+ - client:
67
+ name: "listeners"
68
+ multiplier: ":scale * 2"
69
+ actions:
70
+ - receive:
71
+ data: "Welcome"
72
+ - wait_all
73
+ - receive:
74
+ multiplier: ":scale" # you can use multiplier with any action
75
+ data: "test message"
76
+ ```
77
+
78
+ Run with scale factor:
79
+
80
+ ```bash
81
+ wsdirector -f script.yml -u ws://websocket.server:9876 -s 10
82
+
83
+ #=> Group publisher: 10 clients, 0 failures
84
+ #=> Group listeners: 20 clients, 0 failures
85
+ ```
86
+
87
+ The simpliest scenario is just checking that socket is succesfully connected:
88
+
89
+ ```yml
90
+ - client:
91
+ name: connection check
92
+ # no actions
93
+ ```
94
+
95
+ Run with loop option:
96
+
97
+ ```yml
98
+ # script.yml
99
+ - client:
100
+ name: "listeners"
101
+ loop:
102
+ multiplier: ":scale" # :scale take number from -s param, and run :scale number of clients in this group
103
+ actions:
104
+ - receive:
105
+ data:
106
+ type: "welcome"
107
+ - send:
108
+ data:
109
+ command: "subscribe"
110
+ identifier: "{\"channel\":\"Channel\"}"
111
+ - receive:
112
+ data:
113
+ identifier: "{\"channel\":\"Channel\"}"
114
+ type: "confirm_subscription"
115
+ - wait_all
116
+ - receive:
117
+ multiplier: ":scale + 1"
118
+ ```
119
+
120
+ By default, `receive` action expects the exact `data` match. In some cases, it's useful to only match the specified keys (inclusion). For that, you can use `data>` field instead:
121
+
122
+ ```yml
123
+ - client:
124
+ actions:
125
+ - receive:
126
+ data:
127
+ type: "welcome"
128
+ - send:
129
+ data:
130
+ command: "subscribe"
131
+ identifier: "{\"channel\":\"Channel\"}"
132
+ - receive:
133
+ data>:
134
+ type: "confirm_subscription"
135
+ ```
136
+
137
+ Also you can pass a JSON file with some testing scripts:
138
+
139
+ ```bash
140
+ wsdirector -f scenario.json -u ws://websocket.server:9876
141
+ ```
142
+
143
+ or pass a JSON scenario directly to the CLI without creating a file:
144
+
145
+ ```bash
146
+ wsdirector -i '[{"receive": {"data":"welcome"}},{"send":{"data":"send message"}},{"receive":{"data":"receive message"}}]' -u ws://websocket.server:9876
147
+ ```
148
+
149
+ Type `wsdirector --help` to check all commands.
150
+
151
+ ### Receive order
152
+
153
+ By default, the `receive` action scans through all available or newly added message to find a matching one.
154
+ If you want to check the order of incoming messages, add the `ordered: true` option to the `receive` action.
155
+
156
+ ### Connection configuration
157
+
158
+ You can specify client's headers, cookies or query string params via the `connection_options` directive:
159
+
160
+ ```yml
161
+ - client:
162
+ connection_options:
163
+ headers:
164
+ "X-API-KEY": "secret"
165
+ query:
166
+ token: "123"
167
+ cookies:
168
+ session_id: "2022"
169
+ ```
170
+
171
+ **NOTE**: Query string params could also be passed as a part of the URL. Specifying them in the scenario allows you to provide values via the interpolation.
172
+
173
+ ### Using as a library
174
+
175
+ You can integrate WS Director into your library or application by using its APIs:
24
176
 
25
- TODO: Write usage instructions here
177
+ ```ruby
178
+ # Could be a file path or JSON-encoded string as well
179
+ scenario = [
180
+ {
181
+ send: {
182
+ data: "ping"
183
+ }
184
+ },
185
+ {
186
+ receive: {
187
+ data: "pong"
188
+ }
189
+ }
190
+ ]
191
+
192
+ result = WSDirector.run(scenario, url: "ws://my.ws.server:4949/live")
193
+ result.success? #=> true of false
194
+ result.groups #=> result data for each client group
195
+ ```
196
+
197
+ If you're using YAML-based scenarios, you can also pass local variables to be used with ERB via the `locals` option:
198
+
199
+ ```yml
200
+ - client:
201
+ connection_options:
202
+ headers:
203
+ "X-API-TOKEN": <%= token %>
204
+ ```
205
+
206
+ ```ruby
207
+ token = UserToken.generate
208
+ WSDirector.run(scenario, url: "ws://my.ws.server:4949/live", locals: {token:})
209
+ ```
210
+
211
+ ### Protocols
212
+
213
+ WSDirector uses protocols to handle provide convinient actions for some popular protocols.
214
+
215
+ #### ActionCable Example
216
+
217
+ Channel code:
218
+
219
+ ```ruby
220
+ class ChatChannel < ApplicationCable::Channel
221
+ def subscribed
222
+ stream_from "chat_test"
223
+ end
224
+
225
+ def echo(data)
226
+ transmit data
227
+ end
228
+
229
+ def broadcast(data)
230
+ ActionCable.server.broadcast "chat_test", data
231
+ end
232
+ end
233
+ ```
234
+
235
+ Scenario:
236
+
237
+ ```yml
238
+ - client:
239
+ multiplier: ":scale"
240
+ name: "publisher"
241
+ protocol: "action_cable"
242
+ actions:
243
+ - subscribe:
244
+ channel: "ChatChannel"
245
+ - wait_all
246
+ - perform:
247
+ channel: "ChatChannel"
248
+ action: "broadcast"
249
+ data:
250
+ text: "hello"
251
+ - client:
252
+ name: "listener"
253
+ protocol: "action_cable"
254
+ actions:
255
+ - subscribe:
256
+ channel: "ChatChannel"
257
+ - wait_all
258
+ - receive:
259
+ channel: "ChatChannel"
260
+ data:
261
+ text: "hello"
262
+ ```
263
+
264
+ #### Phoenix Channels
265
+
266
+ With "phoenix" protocol, you can use communicate with a [Phoenix Channels](https://hexdocs.pm/phoenix/channels.html) server:
267
+
268
+ ```yml
269
+ - client:
270
+ protocol: phoenix
271
+ multiplier: ":scale"
272
+ actions:
273
+ - join:
274
+ topic: room:lobby
275
+ - wait_all
276
+ - send:
277
+ topic: room:lobby
278
+ event: new_msg
279
+ data:
280
+ body: "Hey from WS director!"
281
+ - receive:
282
+ topic: room:lobby
283
+ multiplier: ":scale"
284
+ event: new_msg
285
+ data:
286
+ body: "Hey from WS director!"
287
+ ```
288
+
289
+ **IMPORTANT**: We support only v2 version of the Channels protocol.
290
+
291
+ #### Custom protocols
292
+
293
+ You can define your own protocol and load it dynamically:
294
+
295
+ ```ruby
296
+ # It's important to put a custom protocol class under WSDirector::Protocols
297
+ module WSDirector::Protocols
298
+ class CustomProtocol < Base
299
+ def send_ping_and_receive_pong
300
+ send("data" => {"type" => "ping"})
301
+ receive("data" => {"type" => "pong"})
302
+ end
303
+ end
304
+ end
305
+ ```
306
+
307
+ Now you can load it via the `-r` option:
308
+
309
+ ```sh
310
+ $ wsdirector -u localhost:3232/ws -i '["send_ping_and_receive_pong"]' -r ./path/to/custom_protocol.rb -vv
311
+
312
+ hh:mm:ss client=default_1 Connecting
313
+ hh:mm:ss client=default_1 Connected (45ms)
314
+ hh:mm:ss client=default_1 Sent message: {"type":"ping"}
315
+ hh:mm:ss client=default_1 Receive message: {"type":"pong"}
316
+ hh:mm:ss client=default_1 Received message: {"type":"pong"} (21ms)
317
+ ```
318
+
319
+ ## Testing frameworks integration
320
+
321
+ WSDirector does not provide any specific helpers for RSpec or Minitest. Instead, we provide an example setup, which you could adjust to your needs (and which is too small to be a part of the library).
322
+
323
+ The example below implies running tests against an Action Cable server with a token-based authentication
324
+
325
+ ```ruby
326
+ module WSDirectorTestHelper
327
+ def run_websocket_scenario(path, token:, url: ActionCable.server.config.url, **options)
328
+ url = "#{url}?jid=#{token}"
329
+ scenario = Rails.root.join "spec" / "fixtures" / "wsdirector" / path
330
+
331
+ WSDirector.run(scenario, url:, **options)
332
+ end
333
+ end
334
+
335
+ # In RSpec, you can include this modules via the configuration
336
+ RSpec.configure do |config|
337
+ # Here we only add this helper to system tests
338
+ config.include WSDirectorTestHelper, type: :system
339
+ end
340
+ ```
26
341
 
27
- ## Development
342
+ ## Future Ideas
28
343
 
29
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
344
+ - Report timings (per-client and aggregates)
30
345
 
31
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
346
+ - What else? [Submit an issue!](https://github.com/palkan/wsdirector/issues/new)
32
347
 
33
348
  ## Contributing
34
349
 
35
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/wsdirector-core.
350
+ Bug reports and pull requests are welcome on GitHub at https://github.com/palkan/wsdirector.
36
351
 
37
352
  ## License
38
353
 
data/bin/wsdirector ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ lib_path = File.expand_path("../lib", __dir__)
4
+ $LOAD_PATH.unshift(lib_path) unless $LOAD_PATH.include?(lib_path)
5
+
6
+ require "wsdirector-cli"
7
+
8
+ begin
9
+ WSDirector::CLI.new.run
10
+ rescue => e
11
+ raise e if $DEBUG
12
+ STDERR.puts e.message
13
+ STDERR.puts e.backtrace.take(10).join("\n") if ENV["WSDIRECTOR_DEBUG"] == "1"
14
+ exit 1
15
+ end
@@ -0,0 +1,152 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "optparse"
4
+ require "uri"
5
+
6
+ require "wsdirector"
7
+
8
+ module WSDirector
9
+ # Command line interface for WsDirector
10
+ class CLI
11
+ class Configuration
12
+ attr_accessor :ws_url, :scenario_path, :colorize, :scale,
13
+ :sync_timeout, :json_scenario, :subprotocol, :verbose
14
+
15
+ def initialize
16
+ reset!
17
+ end
18
+
19
+ def colorize?
20
+ colorize == true
21
+ end
22
+
23
+ # Restore to defaults
24
+ def reset!
25
+ @scale = 1
26
+ @colorize = $stdout.tty?
27
+ @sync_timeout = 5
28
+ end
29
+ end
30
+
31
+ attr_reader :config
32
+
33
+ def run
34
+ @config = Configuration.new
35
+
36
+ parse_args!
37
+
38
+ begin
39
+ require "colorize" if config.colorize?
40
+ rescue LoadError
41
+ config.colorize = false
42
+ warn "Install colorize to use colored output"
43
+ end
44
+
45
+ connection_options = {
46
+ subprotocol: config.subprotocol
47
+ }.compact
48
+
49
+ scenario = config.scenario_path || config.json_scenario
50
+
51
+ config.ws_url = "ws://#{config.ws_url}" unless config.ws_url.start_with?(/wss?:\/\//)
52
+
53
+ url = config.ws_url
54
+ scale = config.scale
55
+ sync_timeout = config.sync_timeout
56
+ colorize = config.colorize
57
+
58
+ logger = $stdout if config.verbose
59
+
60
+ result = WSDirector.run(
61
+ scenario,
62
+ url:,
63
+ connection_options:,
64
+ scale:,
65
+ sync_timeout:,
66
+ logger:,
67
+ colorize:
68
+ )
69
+
70
+ puts "\n\n" if config.verbose
71
+
72
+ result.print_summary(colorize: config.colorize?)
73
+ result.success? || exit(1)
74
+ end
75
+
76
+ private
77
+
78
+ FILE_FORMAT = /.+.(json|yml)\z/
79
+ private_constant :FILE_FORMAT
80
+
81
+ def parse_args!
82
+ parser = OptionParser.new do |opts|
83
+ opts.banner = "Usage: wsdirector scenario_path ws_url [options]"
84
+
85
+ opts.on("-s SCALE", "--scale=SCALE", Integer, "Scale factor") do
86
+ config.scale = _1
87
+ end
88
+
89
+ opts.on("-t TIMEOUT", "--timeout=TIMEOUT", Integer, "Synchronization (wait_all) timeout") do
90
+ config.sync_timeout = _1
91
+ end
92
+
93
+ opts.on("-i JSON", "--include=JSON", String, "Include JSON to parse") do
94
+ config.json_scenario = _1
95
+ end
96
+
97
+ opts.on("-u URL", "--url=URL", Object, "Websocket server URL") do
98
+ config.ws_url = _1
99
+ end
100
+
101
+ opts.on("-f PATH", "--file=PATH", String, "Scenario path") do
102
+ config.scenario_path = _1
103
+ end
104
+
105
+ opts.on("--subprotocol=VALUE", String, "WebSocket subprotocol") do
106
+ config.subprotocol = _1
107
+ end
108
+
109
+ opts.on("-c", "--[no-]color", "Colorize output") do
110
+ config.colorize = _1
111
+ end
112
+
113
+ opts.on("-v", "--version", "Print version") do
114
+ $stdout.puts WSDirector::VERSION
115
+ exit 0
116
+ end
117
+
118
+ opts.on("-vv", "Print verbose logs") do
119
+ config.verbose = true
120
+ end
121
+
122
+ opts.on("-r", "--require=PATH", "Load Ruby file (e.g., protocol)") do
123
+ Kernel.load(_1)
124
+ end
125
+ end
126
+
127
+ parser.parse!
128
+
129
+ unless config.scenario_path
130
+ config.scenario_path = ARGV.grep(FILE_FORMAT).last
131
+ end
132
+
133
+ unless config.ws_url
134
+ config.ws_url = ARGV.grep(URI::DEFAULT_PARSER.make_regexp).last
135
+ end
136
+
137
+ check_for_errors
138
+ end
139
+
140
+ def check_for_errors
141
+ if config.json_scenario.nil?
142
+ raise(Error, "Scenario is missing") unless config.scenario_path
143
+
144
+ unless File.file?(config.scenario_path)
145
+ raise(Error, "File doesn't exist # config.scenario_path}")
146
+ end
147
+ end
148
+
149
+ raise(Error, "Websocket server url is missing") unless config.ws_url
150
+ end
151
+ end
152
+ end