wsdirector-cli 0.3.0 → 0.4.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 +4 -4
- data/CHANGELOG.md +27 -0
- data/README.md +3 -4
- data/lib/wsdirector/cli.rb +4 -3
- data/lib/wsdirector/configuration.rb +1 -1
- data/lib/wsdirector/ext/deep_dup.rb +4 -4
- data/lib/wsdirector/protocols/action_cable.rb +7 -7
- data/lib/wsdirector/protocols/base.rb +5 -2
- data/lib/wsdirector/result.rb +13 -1
- data/lib/wsdirector/runner.rb +1 -1
- data/lib/wsdirector/scenario_reader.rb +13 -8
- data/lib/wsdirector/task.rb +8 -0
- data/lib/wsdirector/utils.rb +2 -2
- data/lib/wsdirector/version.rb +1 -1
- metadata +10 -25
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 28d6654bce4665a9d0c1434c33ba359e31adb6680c51aafc3ccb1c5a91b32d56
|
4
|
+
data.tar.gz: 6e9c3fd2cfe938ba5f8a5f3e3519204d93cd56d428fed93be89b4056e53f004e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1ce990e199cd5562bb03760a8678349b74f549014cf997c821b3ad6f4518e7e45e349a26e89fcfb22762f2a775d57604bf84c35511dd5294097590e14389c47a
|
7
|
+
data.tar.gz: 0f4287fedbc4f0488e2a19fe59aa6de276da620345e9024bcd7baa5f04a473c1a5fd36556f9ba49f4a82575101934737df6d1d470069c85fa751436fbea16c0f
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,33 @@
|
|
2
2
|
|
3
3
|
## master
|
4
4
|
|
5
|
+
## 0.4.0 (2020-08-24)
|
6
|
+
|
7
|
+
- **Drop Ruby 2.4 support**. ([@palkan][])
|
8
|
+
|
9
|
+
- Add sampling support. ([@palkan][])
|
10
|
+
|
11
|
+
You can specify a `sample` option for a step to only run this step by specified number of clients from the group:
|
12
|
+
|
13
|
+
```yml
|
14
|
+
- perform:
|
15
|
+
sample: ":scale / 2"
|
16
|
+
channel: "chat"
|
17
|
+
params:
|
18
|
+
id: 2
|
19
|
+
action: "speak"
|
20
|
+
data:
|
21
|
+
message: "Hello!"
|
22
|
+
```
|
23
|
+
|
24
|
+
Useful in combination with `:scale`.
|
25
|
+
|
26
|
+
**NOTE:** Sample size is always greater or equal to 1.
|
27
|
+
|
28
|
+
- Add ERB support. ([@palkan][])
|
29
|
+
|
30
|
+
Now you can, for example, access `ENV` from scenarios.
|
31
|
+
|
5
32
|
## 0.3.0 (2018-05-02)
|
6
33
|
|
7
34
|
- Add `debug` action and options. ([@palkan][])
|
data/README.md
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
[](http://cultofmartians.com/tasks/websocket-director.html)
|
2
|
-
[](https://rubygems.org/gems/wsdirector-cli)
|
2
|
+
[](https://rubygems.org/gems/wsdirector-cli)
|
3
|
+
[](https://github.com/palkan/wsdirector/actions)
|
3
4
|
|
4
5
|
# WebSocket Director
|
5
6
|
|
@@ -10,7 +11,7 @@ Suitable for testing any websocket server implementation, like [Action Cable](ht
|
|
10
11
|
## Installation
|
11
12
|
|
12
13
|
```bash
|
13
|
-
|
14
|
+
gem install wsdirector-cli
|
14
15
|
```
|
15
16
|
|
16
17
|
## Usage
|
@@ -61,7 +62,6 @@ You can create more complex scenarios with multiple client groups:
|
|
61
62
|
|
62
63
|
Run with scale factor:
|
63
64
|
|
64
|
-
|
65
65
|
```bash
|
66
66
|
wsdirector script.yml ws://websocket.server:9876 -s 10
|
67
67
|
|
@@ -153,7 +153,6 @@ wsdirector -i '{"receive": "hello"}' localhost:9898/ws
|
|
153
153
|
|
154
154
|
Bug reports and pull requests are welcome on GitHub at https://github.com/palkan/wsdirector.
|
155
155
|
|
156
|
-
|
157
156
|
## License
|
158
157
|
|
159
158
|
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
data/lib/wsdirector/cli.rb
CHANGED
@@ -9,7 +9,8 @@ require "wsdirector/runner"
|
|
9
9
|
module WSDirector
|
10
10
|
# Command line interface for WsDirector
|
11
11
|
class CLI
|
12
|
-
def initialize
|
12
|
+
def initialize
|
13
|
+
end
|
13
14
|
|
14
15
|
def run
|
15
16
|
parse_args!
|
@@ -31,7 +32,7 @@ module WSDirector
|
|
31
32
|
exit 1
|
32
33
|
end
|
33
34
|
rescue Error => e
|
34
|
-
|
35
|
+
warn e.message
|
35
36
|
exit 1
|
36
37
|
end
|
37
38
|
|
@@ -55,7 +56,7 @@ module WSDirector
|
|
55
56
|
end
|
56
57
|
|
57
58
|
opts.on("-v", "--version", "Print versin") do
|
58
|
-
|
59
|
+
$stdout.puts WSDirector::VERSION
|
59
60
|
exit 0
|
60
61
|
end
|
61
62
|
end
|
@@ -4,7 +4,7 @@ module WSDirector
|
|
4
4
|
module Protocols
|
5
5
|
# ActionCable protocol
|
6
6
|
class ActionCable < Base
|
7
|
-
WELCOME_MSG = {
|
7
|
+
WELCOME_MSG = {type: "welcome"}.to_json
|
8
8
|
PING_IGNORE = /['"]type['"]:\s*['"]ping['"]/
|
9
9
|
|
10
10
|
# Add ping ignore and make sure that we receive Welcome message
|
@@ -19,14 +19,14 @@ module WSDirector
|
|
19
19
|
def subscribe(step)
|
20
20
|
identifier = extract_identifier(step)
|
21
21
|
|
22
|
-
client.send({
|
22
|
+
client.send({command: "subscribe", identifier: identifier}.to_json)
|
23
23
|
|
24
24
|
begin
|
25
25
|
receive(
|
26
|
-
"data" => {
|
26
|
+
"data" => {"type" => "confirm_subscription", "identifier" => identifier}
|
27
27
|
)
|
28
28
|
rescue UnmatchedExpectationError => e
|
29
|
-
raise unless e.message
|
29
|
+
raise unless /reject_subscription/.match?(e.message)
|
30
30
|
raise UnmatchedExpectationError, "Subscription rejected to #{identifier}"
|
31
31
|
end
|
32
32
|
end
|
@@ -39,7 +39,7 @@ module WSDirector
|
|
39
39
|
|
40
40
|
data = step.fetch("data", {}).merge(action: action).to_json
|
41
41
|
|
42
|
-
client.send({
|
42
|
+
client.send({command: "message", data: data, identifier: identifier}.to_json)
|
43
43
|
end
|
44
44
|
|
45
45
|
def receive(step)
|
@@ -47,7 +47,7 @@ module WSDirector
|
|
47
47
|
|
48
48
|
identifier = extract_identifier(step)
|
49
49
|
message = step.fetch("data", {})
|
50
|
-
super("data" => {
|
50
|
+
super("data" => {"identifier" => identifier, "message" => message})
|
51
51
|
end
|
52
52
|
|
53
53
|
def receive_all(step)
|
@@ -58,7 +58,7 @@ module WSDirector
|
|
58
58
|
messages.each do |msg|
|
59
59
|
next unless msg.key?("channel")
|
60
60
|
identifier = extract_identifier(msg)
|
61
|
-
msg["data"] = {
|
61
|
+
msg["data"] = {"identifier" => identifier, "message" => msg["data"]}
|
62
62
|
end
|
63
63
|
|
64
64
|
super
|
@@ -19,6 +19,9 @@ module WSDirector
|
|
19
19
|
def handle_step(step)
|
20
20
|
type = step.delete("type")
|
21
21
|
raise Error, "Unknown step: #{type}" unless respond_to?(type)
|
22
|
+
|
23
|
+
return unless task.sampled?(step)
|
24
|
+
|
22
25
|
public_send(type, step)
|
23
26
|
end
|
24
27
|
|
@@ -83,8 +86,8 @@ module WSDirector
|
|
83
86
|
end
|
84
87
|
rescue ThreadError
|
85
88
|
raise NoMessageError,
|
86
|
-
|
87
|
-
|
89
|
+
"Expected to receive #{total_expected} messages " \
|
90
|
+
"but received only #{total_received}"
|
88
91
|
end
|
89
92
|
# rubocop: enable Metrics/CyclomaticComplexity
|
90
93
|
|
data/lib/wsdirector/result.rb
CHANGED
@@ -11,6 +11,9 @@ module WSDirector
|
|
11
11
|
|
12
12
|
@all = Concurrent::AtomicFixnum.new(0)
|
13
13
|
@failures = Concurrent::AtomicFixnum.new(0)
|
14
|
+
|
15
|
+
@sampling_mutex = Mutex.new
|
16
|
+
@sampling_counter = Hash.new { |h, k| h[k] = 0 }
|
14
17
|
end
|
15
18
|
|
16
19
|
# Called when client successfully finished it's work
|
@@ -37,8 +40,17 @@ module WSDirector
|
|
37
40
|
failures.value
|
38
41
|
end
|
39
42
|
|
43
|
+
def track_sample(id, max)
|
44
|
+
sampling_mutex.synchronize do
|
45
|
+
return false if sampling_counter[id] >= max
|
46
|
+
|
47
|
+
sampling_counter[id] += 1
|
48
|
+
true
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
40
52
|
private
|
41
53
|
|
42
|
-
attr_reader :all, :success, :failures
|
54
|
+
attr_reader :all, :success, :failures, :sampling_counter, :sampling_mutex
|
43
55
|
end
|
44
56
|
end
|
data/lib/wsdirector/runner.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "erb"
|
4
|
+
|
3
5
|
module WSDirector
|
4
6
|
# Read and parse YAML scenario
|
5
7
|
class ScenarioReader
|
@@ -7,32 +9,35 @@ module WSDirector
|
|
7
9
|
include WSDirector::Utils
|
8
10
|
|
9
11
|
def parse(file_path)
|
10
|
-
contents = YAML.
|
12
|
+
contents = ::YAML.load(ERB.new(File.read(file_path)).result) # rubocop:disable Security/YAMLLoad
|
11
13
|
|
12
14
|
if contents.first.key?("client")
|
13
15
|
parse_multiple_scenarios(contents)
|
14
16
|
else
|
15
|
-
{
|
17
|
+
{"total" => 1, "clients" => [parse_simple_scenario(contents)]}
|
16
18
|
end
|
17
19
|
end
|
18
20
|
|
19
21
|
private
|
20
22
|
|
21
23
|
def handle_steps(steps)
|
22
|
-
steps.flat_map do |step|
|
24
|
+
steps.flat_map.with_index do |step, id|
|
23
25
|
if step.is_a?(Hash)
|
24
26
|
type, data = step.to_a.first
|
27
|
+
|
28
|
+
data["sample"] = [1, parse_multiplier(data["sample"])].max if data["sample"]
|
29
|
+
|
25
30
|
multiplier = parse_multiplier(data.delete("multiplier") || "1")
|
26
|
-
Array.new(multiplier) { {
|
31
|
+
Array.new(multiplier) { {"type" => type, "id" => id}.merge(data) }
|
27
32
|
else
|
28
|
-
{
|
33
|
+
{"type" => step, "id" => id}
|
29
34
|
end
|
30
35
|
end
|
31
36
|
end
|
32
37
|
|
33
38
|
def parse_simple_scenario(
|
34
|
-
|
35
|
-
|
39
|
+
steps,
|
40
|
+
multiplier: 1, name: "default", ignore: nil, protocol: "base"
|
36
41
|
)
|
37
42
|
{
|
38
43
|
"multiplier" => multiplier,
|
@@ -59,7 +64,7 @@ module WSDirector
|
|
59
64
|
protocol: client.fetch("protocol", "base")
|
60
65
|
)
|
61
66
|
end
|
62
|
-
{
|
67
|
+
{"total" => total_count, "clients" => clients}
|
63
68
|
end
|
64
69
|
|
65
70
|
def parse_ingore(str)
|
data/lib/wsdirector/task.rb
CHANGED
@@ -28,6 +28,14 @@ module WSDirector
|
|
28
28
|
result.failed(e.message)
|
29
29
|
end
|
30
30
|
|
31
|
+
def sampled?(step)
|
32
|
+
return true unless step["sample"]
|
33
|
+
|
34
|
+
id, max = step["id"], step["sample"]
|
35
|
+
|
36
|
+
result.track_sample(id, max)
|
37
|
+
end
|
38
|
+
|
31
39
|
private
|
32
40
|
|
33
41
|
attr_reader :steps, :result, :protocol
|
data/lib/wsdirector/utils.rb
CHANGED
@@ -2,12 +2,12 @@
|
|
2
2
|
|
3
3
|
module WSDirector
|
4
4
|
module Utils # :nodoc:
|
5
|
-
MULTIPLIER_FORMAT = /^[
|
5
|
+
MULTIPLIER_FORMAT = /^[-+*\/\\\d ]+$/
|
6
6
|
|
7
7
|
def parse_multiplier(str)
|
8
8
|
prepared = str.to_s.gsub(":scale", WSDirector.config.scale.to_s)
|
9
9
|
raise WSDirector::Error, "Unknown multiplier format: #{str}" unless
|
10
|
-
prepared
|
10
|
+
MULTIPLIER_FORMAT.match?(prepared)
|
11
11
|
|
12
12
|
eval(prepared) # rubocop:disable Security/Eval
|
13
13
|
end
|
data/lib/wsdirector/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: wsdirector-cli
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kirill Arkhipov
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date:
|
13
|
+
date: 2020-08-24 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: websocket-client-simple
|
@@ -114,28 +114,28 @@ dependencies:
|
|
114
114
|
name: bundler
|
115
115
|
requirement: !ruby/object:Gem::Requirement
|
116
116
|
requirements:
|
117
|
-
- - "
|
117
|
+
- - ">="
|
118
118
|
- !ruby/object:Gem::Version
|
119
|
-
version: '1.
|
119
|
+
version: '1.16'
|
120
120
|
type: :development
|
121
121
|
prerelease: false
|
122
122
|
version_requirements: !ruby/object:Gem::Requirement
|
123
123
|
requirements:
|
124
|
-
- - "
|
124
|
+
- - ">="
|
125
125
|
- !ruby/object:Gem::Version
|
126
|
-
version: '1.
|
126
|
+
version: '1.16'
|
127
127
|
- !ruby/object:Gem::Dependency
|
128
128
|
name: rake
|
129
129
|
requirement: !ruby/object:Gem::Requirement
|
130
130
|
requirements:
|
131
|
-
- - "
|
131
|
+
- - ">="
|
132
132
|
- !ruby/object:Gem::Version
|
133
133
|
version: '10.0'
|
134
134
|
type: :development
|
135
135
|
prerelease: false
|
136
136
|
version_requirements: !ruby/object:Gem::Requirement
|
137
137
|
requirements:
|
138
|
-
- - "
|
138
|
+
- - ">="
|
139
139
|
- !ruby/object:Gem::Version
|
140
140
|
version: '10.0'
|
141
141
|
- !ruby/object:Gem::Dependency
|
@@ -166,20 +166,6 @@ dependencies:
|
|
166
166
|
- - "~>"
|
167
167
|
- !ruby/object:Gem::Version
|
168
168
|
version: '5.9'
|
169
|
-
- !ruby/object:Gem::Dependency
|
170
|
-
name: rubocop
|
171
|
-
requirement: !ruby/object:Gem::Requirement
|
172
|
-
requirements:
|
173
|
-
- - "~>"
|
174
|
-
- !ruby/object:Gem::Version
|
175
|
-
version: '0.50'
|
176
|
-
type: :development
|
177
|
-
prerelease: false
|
178
|
-
version_requirements: !ruby/object:Gem::Requirement
|
179
|
-
requirements:
|
180
|
-
- - "~>"
|
181
|
-
- !ruby/object:Gem::Version
|
182
|
-
version: '0.50'
|
183
169
|
description: Command line tool for testing websocket servers using scenarios.
|
184
170
|
email:
|
185
171
|
- kirillvs@mail.ru
|
@@ -224,15 +210,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
224
210
|
requirements:
|
225
211
|
- - ">="
|
226
212
|
- !ruby/object:Gem::Version
|
227
|
-
version: 2.
|
213
|
+
version: 2.5.0
|
228
214
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
229
215
|
requirements:
|
230
216
|
- - ">="
|
231
217
|
- !ruby/object:Gem::Version
|
232
218
|
version: '0'
|
233
219
|
requirements: []
|
234
|
-
|
235
|
-
rubygems_version: 2.7.4
|
220
|
+
rubygems_version: 3.0.6
|
236
221
|
signing_key:
|
237
222
|
specification_version: 4
|
238
223
|
summary: Command line tool for testing websocket servers using scenarios.
|