wsdirector-cli 0.4.0 → 0.5.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 28d6654bce4665a9d0c1434c33ba359e31adb6680c51aafc3ccb1c5a91b32d56
4
- data.tar.gz: 6e9c3fd2cfe938ba5f8a5f3e3519204d93cd56d428fed93be89b4056e53f004e
3
+ metadata.gz: b02895fab166dd2d388f48dda802e02407e5b7866eb508f1b5787e1a4e1976d6
4
+ data.tar.gz: e0d5af5ae2de5caed7ccd151538e19ad3c49e08db1217e042f36540d69468314
5
5
  SHA512:
6
- metadata.gz: 1ce990e199cd5562bb03760a8678349b74f549014cf997c821b3ad6f4518e7e45e349a26e89fcfb22762f2a775d57604bf84c35511dd5294097590e14389c47a
7
- data.tar.gz: 0f4287fedbc4f0488e2a19fe59aa6de276da620345e9024bcd7baa5f04a473c1a5fd36556f9ba49f4a82575101934737df6d1d470069c85fa751436fbea16c0f
6
+ metadata.gz: '09af449361f79d80a4848b0851aa96ada36e363e997bba177e5d96b01005d1c241abee2a1a05781f3a5277de08732f3b1d8afaeaea467d2097050347d2ca295c'
7
+ data.tar.gz: 8deb112a900d7cdbc710be8b86d57aef8aeaa87bbec78595170bf309088bca9bb88fd58de503e2bcfb32b43df5e08903c322791eb10930aa0fb25f00e26afebb
data/CHANGELOG.md CHANGED
@@ -2,6 +2,53 @@
2
2
 
3
3
  ## master
4
4
 
5
+ ## 0.5.0 (2021-10-13)
6
+
7
+ - Add JSON support. ([@wazzuper][])
8
+
9
+ - Add new commands to CLI.
10
+
11
+ You can pass a JSON scenario directly to the CLI without creating a file:
12
+
13
+ ```bash
14
+ wsdirector -i '[{"receive": {"data":"welcome"}},{"send":{"data":"send message"}},{"receive":{"data":"receive message"}}]' -u ws://websocket.server:9876
15
+ ```
16
+
17
+ or you can pass it as a JSON file:
18
+
19
+ ```bash
20
+ wsdirector scenario.json -u ws://websocket.server:9876
21
+ ```
22
+
23
+ - Add loop option support. ([@last-in-japan][])
24
+
25
+ You can specify a `loop` option to perform a similar set of actions multiple times:
26
+
27
+ ```yml
28
+ # script.yml
29
+ - client:
30
+ name: "listeners"
31
+ loop:
32
+ multiplier: ":scale" # :scale take number from -s param, and run :scale number of clients in this group
33
+ actions:
34
+ - receive:
35
+ data:
36
+ type: "welcome"
37
+ - send:
38
+ data:
39
+ command: "subscribe"
40
+ identifier: "{\"channel\":\"Channel\"}"
41
+ - receive:
42
+ data:
43
+ identifier: "{\"channel\":\"Channel\"}"
44
+ type: "confirm_subscription"
45
+ - wait_all
46
+ - receive:
47
+ multiplier: ":scale + 1"
48
+ ```
49
+
50
+ Useful in combination with `:scale`.
51
+
5
52
  ## 0.4.0 (2020-08-24)
6
53
 
7
54
  - **Drop Ruby 2.4 support**. ([@palkan][])
@@ -64,3 +111,5 @@ Allows to handle multiple messages with unspecified order:
64
111
  [@palkan]: https://github.com/palkan
65
112
  [@Kirillvs]: https://github.com/Kirillvs
66
113
  [@Grandman]: https://github.com/Grandman
114
+ [@wazzuper]: https://github.com/wazzuper
115
+ [@last-in-japan]: https://github.com/last-in-japan
data/README.md CHANGED
@@ -77,6 +77,45 @@ The simpliest scenario is just checking that socket is succesfully connected:
77
77
  # no actions
78
78
  ```
79
79
 
80
+ Run with loop option:
81
+
82
+ ```yml
83
+ # script.yml
84
+ - client:
85
+ name: "listeners"
86
+ loop:
87
+ multiplier: ":scale" # :scale take number from -s param, and run :scale number of clients in this group
88
+ actions:
89
+ - receive:
90
+ data:
91
+ type: "welcome"
92
+ - send:
93
+ data:
94
+ command: "subscribe"
95
+ identifier: "{\"channel\":\"Channel\"}"
96
+ - receive:
97
+ data:
98
+ identifier: "{\"channel\":\"Channel\"}"
99
+ type: "confirm_subscription"
100
+ - wait_all
101
+ - receive:
102
+ multiplier: ":scale + 1"
103
+ ```
104
+
105
+ Also you can pass a JSON file with some testing scripts:
106
+
107
+ ```bash
108
+ wsdirector scenario.json ws://websocket.server:9876
109
+ ```
110
+
111
+ or pass a JSON scenario directly to the CLI without creating a file:
112
+
113
+ ```bash
114
+ wsdirector -i '[{"receive": {"data":"welcome"}},{"send":{"data":"send message"}},{"receive":{"data":"receive message"}}]' ws://websocket.server:9876
115
+ ```
116
+
117
+ Type `wsdirector --help` to check all commands.
118
+
80
119
  ### Protocols
81
120
 
82
121
  WSDirector uses protocols to handle different actions.
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "optparse"
4
+ require "uri"
4
5
 
5
6
  require "wsdirector"
6
7
  require "wsdirector/scenario_reader"
@@ -23,7 +24,7 @@ module WSDirector
23
24
  end
24
25
 
25
26
  scenario = WSDirector::ScenarioReader.parse(
26
- WSDirector.config.scenario_path
27
+ WSDirector.config.scenario_path || WSDirector.config.json_scenario
27
28
  )
28
29
 
29
30
  if WSDirector::Runner.new(scenario).start
@@ -38,6 +39,9 @@ module WSDirector
38
39
 
39
40
  private
40
41
 
42
+ FILE_FORMAT = /.+.(json|yml)\z/.freeze
43
+ private_constant :FILE_FORMAT
44
+
41
45
  def parse_args!
42
46
  # rubocop: disable Metrics/LineLength
43
47
  parser = OptionParser.new do |opts|
@@ -51,6 +55,14 @@ module WSDirector
51
55
  WSDirector.config.sync_timeout = v
52
56
  end
53
57
 
58
+ opts.on("-i JSON", "--include=JSON", String, "Include JSON to parse") do |v|
59
+ WSDirector.config.json_scenario = v
60
+ end
61
+
62
+ opts.on("-u URL", "--url=URL", Object, "Websocket server URL") do |v|
63
+ WSDirector.config.ws_url = v
64
+ end
65
+
54
66
  opts.on("-c", "--[no-]color", "Colorize output") do |v|
55
67
  WSDirector.config.colorize = v
56
68
  end
@@ -64,15 +76,25 @@ module WSDirector
64
76
 
65
77
  parser.parse!
66
78
 
67
- WSDirector.config.scenario_path = ARGV[0]
68
- WSDirector.config.ws_url = ARGV[1]
79
+ WSDirector.config.scenario_path = ARGV.grep(FILE_FORMAT).last
69
80
 
70
- raise(Error, "Scenario path is missing") if WSDirector.config.scenario_path.nil?
81
+ unless WSDirector.config.ws_url
82
+ WSDirector.config.ws_url = ARGV.grep(URI::DEFAULT_PARSER.make_regexp).last
83
+ end
71
84
 
72
- raise(Error, "File doesn't exist #{WSDirector.config.scenario_path}") unless
73
- File.file?(WSDirector.config.scenario_path)
85
+ check_for_errors
86
+ end
87
+
88
+ def check_for_errors
89
+ if WSDirector.config.json_scenario.nil?
90
+ raise(Error, "Scenario is missing") unless WSDirector.config.scenario_path
91
+
92
+ unless File.file?(WSDirector.config.scenario_path)
93
+ raise(Error, "File doesn't exist #{WSDirector.config.scenario_path}")
94
+ end
95
+ end
74
96
 
75
- raise(Error, "Websocket server url is missing") if WSDirector.config.ws_url.nil?
97
+ raise(Error, "Websocket server url is missing") unless WSDirector.config.ws_url
76
98
  end
77
99
  end
78
100
  end
@@ -60,7 +60,7 @@ module WSDirector
60
60
 
61
61
  def ignored?(msg)
62
62
  return false unless @ignore
63
- @ignore.any? { |pattern| msg =~ pattern }
63
+ @ignore.any? { |pattern| msg =~ Regexp.new(pattern) }
64
64
  end
65
65
  end
66
66
  end
@@ -3,8 +3,7 @@
3
3
  module WSDirector
4
4
  # WSDirector configuration
5
5
  class Configuration
6
- attr_accessor :ws_url, :scenario_path, :colorize, :scale,
7
- :sync_timeout
6
+ attr_accessor :ws_url, :scenario_path, :colorize, :scale, :sync_timeout, :json_scenario
8
7
 
9
8
  def initialize
10
9
  reset!
@@ -29,6 +29,12 @@ module WSDirector
29
29
  end
30
30
  end
31
31
  end
32
+
33
+ refine ::Object do
34
+ def deep_dup
35
+ dup
36
+ end
37
+ end
32
38
  end
33
39
  end
34
40
  end
@@ -63,10 +63,10 @@ module WSDirector
63
63
  messages.nil? || messages.empty?
64
64
 
65
65
  expected =
66
- Hash[messages.map do |msg|
66
+ messages.map do |msg|
67
67
  multiplier = parse_multiplier(msg.delete("multiplier") || "1")
68
68
  [msg["data"], multiplier]
69
- end]
69
+ end.to_h
70
70
 
71
71
  total_expected = expected.values.sum
72
72
  total_received = 0
@@ -12,8 +12,10 @@ module WSDirector
12
12
  module Protocols # :nodoc:
13
13
  # Raised when received not expected message
14
14
  class UnmatchedExpectationError < WSDirector::Error; end
15
+
15
16
  # Raised when received message is unexpected
16
17
  class UnexpectedMessageError < WSDirector::Error; end
18
+
17
19
  # Raised when nothing has been received
18
20
  class NoMessageError < WSDirector::Error; end
19
21
 
@@ -1,25 +1,47 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "erb"
4
+ require "json"
5
+ require "wsdirector/ext/deep_dup"
4
6
 
5
7
  module WSDirector
6
- # Read and parse YAML scenario
8
+ # Read and parse different scenarios
7
9
  class ScenarioReader
10
+ using WSDirector::Ext::DeepDup
11
+
8
12
  class << self
9
13
  include WSDirector::Utils
10
14
 
11
- def parse(file_path)
12
- contents = ::YAML.load(ERB.new(File.read(file_path)).result) # rubocop:disable Security/YAMLLoad
15
+ def parse(scenario)
16
+ contents =
17
+ if File.file?(scenario)
18
+ parse_file(scenario)
19
+ else
20
+ [JSON.parse(scenario)]
21
+ end.flatten
13
22
 
14
23
  if contents.first.key?("client")
24
+ contents = transform_with_loop(contents, multiple: true)
15
25
  parse_multiple_scenarios(contents)
16
26
  else
27
+ contents = transform_with_loop(contents)
17
28
  {"total" => 1, "clients" => [parse_simple_scenario(contents)]}
18
29
  end
19
30
  end
20
31
 
21
32
  private
22
33
 
34
+ JSON_FILE_FORMAT = /.+.(json)\z/.freeze
35
+ private_constant :JSON_FILE_FORMAT
36
+
37
+ def parse_file(file)
38
+ if file.match?(JSON_FILE_FORMAT)
39
+ JSON.parse(File.read(file))
40
+ else
41
+ ::YAML.load(ERB.new(File.read(file)).result) # rubocop:disable Security/YAMLLoad
42
+ end
43
+ end
44
+
23
45
  def handle_steps(steps)
24
46
  steps.flat_map.with_index do |step, id|
25
47
  if step.is_a?(Hash)
@@ -67,6 +89,22 @@ module WSDirector
67
89
  {"total" => total_count, "clients" => clients}
68
90
  end
69
91
 
92
+ def transform_with_loop(contents, multiple: false)
93
+ contents.flat_map do |content|
94
+ loop_data = content.dig("client", "loop") || content.dig("loop")
95
+ next content unless loop_data
96
+
97
+ loop_multiplier = parse_multiplier(loop_data["multiplier"] || "1")
98
+
99
+ if multiple
100
+ content["client"]["actions"] = (loop_data["actions"] * loop_multiplier).map(&:deep_dup)
101
+ content
102
+ else
103
+ loop_data["actions"] * loop_multiplier
104
+ end
105
+ end
106
+ end
107
+
70
108
  def parse_ingore(str)
71
109
  return unless str
72
110
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module WSDirector
4
- VERSION = "0.4.0"
4
+ VERSION = "0.5.0"
5
5
  end
metadata CHANGED
@@ -1,16 +1,16 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: wsdirector-cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kirill Arkhipov
8
8
  - Grandman
9
9
  - palkan
10
- autorequire:
10
+ autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2020-08-24 00:00:00.000000000 Z
13
+ date: 2021-10-13 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: websocket-client-simple
@@ -202,7 +202,7 @@ homepage: https://github.com/palkan/wsdirector
202
202
  licenses:
203
203
  - MIT
204
204
  metadata: {}
205
- post_install_message:
205
+ post_install_message:
206
206
  rdoc_options: []
207
207
  require_paths:
208
208
  - lib
@@ -217,8 +217,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
217
217
  - !ruby/object:Gem::Version
218
218
  version: '0'
219
219
  requirements: []
220
- rubygems_version: 3.0.6
221
- signing_key:
220
+ rubygems_version: 3.2.22
221
+ signing_key:
222
222
  specification_version: 4
223
223
  summary: Command line tool for testing websocket servers using scenarios.
224
224
  test_files: []