wsdirector-core 1.0.2 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +12 -0
- data/lib/.rbnext/2.7/wsdirector/protocols/base.rb +20 -7
- data/lib/.rbnext/2.7/wsdirector/protocols/phoenix.rb +2 -2
- data/lib/.rbnext/2.7/wsdirector/scenario_reader.rb +6 -1
- data/lib/.rbnext/3.0/wsdirector/ext/formatting.rb +1 -1
- data/lib/.rbnext/3.0/wsdirector/protocols/base.rb +20 -7
- data/lib/.rbnext/3.1/wsdirector/protocols/action_cable.rb +8 -4
- data/lib/.rbnext/3.1/wsdirector/protocols/phoenix.rb +2 -2
- data/lib/.rbnext/3.1/wsdirector/scenario_reader.rb +6 -1
- data/lib/.rbnext/3.2/wsdirector/ext/formatting.rb +38 -0
- data/lib/wsdirector/protocols/action_cable.rb +8 -4
- data/lib/wsdirector/protocols/base.rb +17 -4
- data/lib/wsdirector/protocols/phoenix.rb +1 -1
- data/lib/wsdirector/scenario_reader.rb +6 -1
- data/lib/wsdirector/version.rb +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e8f7c80b878147f8ceba30cff3c94398e7cf8196794d3b4b857ade3702b8a97f
|
4
|
+
data.tar.gz: dc767a46dc089ee5cbe7ff90227ddaa2bb23eee835264a436084dd5cf910e77a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c2a1d4aa0e1134344f2e4d94fb132153d90f071e48a541bef57e4ab26bf9d49227a9b646a6e3a226ff86d84995e54297933c9aa755db6374c5a8b107d741d4b8
|
7
|
+
data.tar.gz: cd9e6f0d9f2804fca5c3fff3dbccbcd6cc7cc3f1f4eafb9eba7846bf4c8b81cc96246ed1c0fc9be4721267406a97e6bb3e3173d6f7b0b8995f88a30b21dcb4ee
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,18 @@
|
|
2
2
|
|
3
3
|
## master
|
4
4
|
|
5
|
+
## 1.1.0 (2024-10-04)
|
6
|
+
|
7
|
+
- Support `stream_id`, `offset`, and `epoch` fields in Action Cable `receive`. ([@palkan][])
|
8
|
+
|
9
|
+
- Add `print` option to `receive` action to print the received message. ([@palkan][])
|
10
|
+
|
11
|
+
## 1.0.3 (2023-10-03)
|
12
|
+
|
13
|
+
- Add `timeout` option to receive to limit the amount of time we wait for the expected message. ([@palkan][])
|
14
|
+
|
15
|
+
- Add `loop` action to multiply several actions. ([@palkan][])
|
16
|
+
|
5
17
|
## 1.0.2 (2023-01-12)
|
6
18
|
|
7
19
|
- Fix adding transpiled files to releases. ([@palkan][])
|
@@ -94,11 +94,14 @@ module WSDirector
|
|
94
94
|
"an object including #{obj.inspect}"
|
95
95
|
end
|
96
96
|
|
97
|
-
def truncate(*__rest__, &__block__) ; obj.truncate(*__rest__, &__block__); end
|
97
|
+
def truncate(*__rest__, &__block__) ; obj.truncate(*__rest__, &__block__); end; respond_to?(:ruby2_keywords, true) && (ruby2_keywords :truncate)
|
98
98
|
end
|
99
99
|
|
100
100
|
# Base protocol describes basic actions
|
101
101
|
class Base
|
102
|
+
class ReceiveTimeoutError < StandardError
|
103
|
+
end
|
104
|
+
|
102
105
|
include WSDirector::Utils
|
103
106
|
|
104
107
|
def initialize(task, scale: 1, logger: nil, id: nil, color: nil)
|
@@ -115,7 +118,7 @@ module WSDirector
|
|
115
118
|
@client = build_client(*__rest__, &__block__)
|
116
119
|
|
117
120
|
log(:done) { "Connected" }
|
118
|
-
end
|
121
|
+
end; respond_to?(:ruby2_keywords, true) && (ruby2_keywords :init_client)
|
119
122
|
|
120
123
|
def handle_step(step)
|
121
124
|
type = step.delete("type")
|
@@ -155,9 +158,11 @@ module WSDirector
|
|
155
158
|
def receive(step)
|
156
159
|
expected = step["data"] || PartialMatcher.new(step["data>"])
|
157
160
|
ordered = step["ordered"]
|
161
|
+
timeout = step.fetch("timeout", 5).to_f
|
158
162
|
|
159
|
-
log { "Receive a message: #{expected.truncate(
|
163
|
+
log { "Receive a message in #{timeout}s: #{expected.inspect.truncate(100)}" }
|
160
164
|
|
165
|
+
start = Time.now.to_f
|
161
166
|
received = nil
|
162
167
|
|
163
168
|
client.each_message do |msg, id|
|
@@ -170,14 +175,22 @@ module WSDirector
|
|
170
175
|
if ordered
|
171
176
|
raise UnmatchedExpectationError, prepare_receive_error(expected, received)
|
172
177
|
end
|
178
|
+
|
179
|
+
if Time.now.to_f - start > timeout
|
180
|
+
raise ReceiveTimeoutError
|
181
|
+
end
|
173
182
|
end
|
174
183
|
|
175
|
-
|
176
|
-
|
184
|
+
if step["print"]
|
185
|
+
debug({"message" => received})
|
186
|
+
end
|
187
|
+
|
188
|
+
log(:done) { "Received a message: #{received&.truncate(100)}" }
|
189
|
+
rescue ThreadError, ReceiveTimeoutError
|
177
190
|
if received
|
178
191
|
raise UnmatchedExpectationError, prepare_receive_error(expected, received)
|
179
192
|
else
|
180
|
-
raise NoMessageError, "Expected to receive #{expected} but nothing has been received"
|
193
|
+
raise NoMessageError, "Expected to receive #{expected.inspect} but nothing has been received"
|
181
194
|
end
|
182
195
|
end
|
183
196
|
|
@@ -244,7 +257,7 @@ module WSDirector
|
|
244
257
|
|
245
258
|
def build_client(*__rest__, &__block__)
|
246
259
|
Client.new(*__rest__, &__block__)
|
247
|
-
end
|
260
|
+
end; respond_to?(:ruby2_keywords, true) && (ruby2_keywords :build_client)
|
248
261
|
|
249
262
|
def prepare_receive_error(expected, received)
|
250
263
|
<<~MSG
|
@@ -14,14 +14,14 @@ module WSDirector
|
|
14
14
|
@refs_counter = 3
|
15
15
|
@join_refs_counter = 3
|
16
16
|
@topics_to_join_ref = {}
|
17
|
-
end
|
17
|
+
end; respond_to?(:ruby2_keywords, true) && (ruby2_keywords :initialize)
|
18
18
|
|
19
19
|
def init_client(**options)
|
20
20
|
options[:query] ||= {}
|
21
21
|
# Make sure we use the v2 of the protocol
|
22
22
|
options[:query][:vsn] = "2.0.0"
|
23
23
|
|
24
|
-
super
|
24
|
+
super
|
25
25
|
end
|
26
26
|
|
27
27
|
def join(step)
|
@@ -92,7 +92,12 @@ module WSDirector
|
|
92
92
|
data["sample"] = [1, parse_multiplier(data["sample"])].max if data["sample"]
|
93
93
|
|
94
94
|
multiplier = parse_multiplier(data.delete("multiplier") || "1")
|
95
|
-
|
95
|
+
|
96
|
+
if type == "loop"
|
97
|
+
handle_steps(data.fetch("actions")) * multiplier
|
98
|
+
else
|
99
|
+
Array.new(multiplier) { {"type" => type, "id" => id}.merge(data) }
|
100
|
+
end
|
96
101
|
else
|
97
102
|
{"type" => step, "id" => id}
|
98
103
|
end
|
@@ -94,11 +94,14 @@ module WSDirector
|
|
94
94
|
"an object including #{obj.inspect}"
|
95
95
|
end
|
96
96
|
|
97
|
-
def truncate(...) ; obj.truncate(...); end
|
97
|
+
def truncate(...) ; obj.truncate(...); end; respond_to?(:ruby2_keywords, true) && (ruby2_keywords :truncate)
|
98
98
|
end
|
99
99
|
|
100
100
|
# Base protocol describes basic actions
|
101
101
|
class Base
|
102
|
+
class ReceiveTimeoutError < StandardError
|
103
|
+
end
|
104
|
+
|
102
105
|
include WSDirector::Utils
|
103
106
|
|
104
107
|
def initialize(task, scale: 1, logger: nil, id: nil, color: nil)
|
@@ -115,7 +118,7 @@ module WSDirector
|
|
115
118
|
@client = build_client(...)
|
116
119
|
|
117
120
|
log(:done) { "Connected" }
|
118
|
-
end
|
121
|
+
end; respond_to?(:ruby2_keywords, true) && (ruby2_keywords :init_client)
|
119
122
|
|
120
123
|
def handle_step(step)
|
121
124
|
type = step.delete("type")
|
@@ -155,9 +158,11 @@ module WSDirector
|
|
155
158
|
def receive(step)
|
156
159
|
expected = step["data"] || PartialMatcher.new(step["data>"])
|
157
160
|
ordered = step["ordered"]
|
161
|
+
timeout = step.fetch("timeout", 5).to_f
|
158
162
|
|
159
|
-
log { "Receive a message: #{expected.truncate(
|
163
|
+
log { "Receive a message in #{timeout}s: #{expected.inspect.truncate(100)}" }
|
160
164
|
|
165
|
+
start = Time.now.to_f
|
161
166
|
received = nil
|
162
167
|
|
163
168
|
client.each_message do |msg, id|
|
@@ -170,14 +175,22 @@ module WSDirector
|
|
170
175
|
if ordered
|
171
176
|
raise UnmatchedExpectationError, prepare_receive_error(expected, received)
|
172
177
|
end
|
178
|
+
|
179
|
+
if Time.now.to_f - start > timeout
|
180
|
+
raise ReceiveTimeoutError
|
181
|
+
end
|
173
182
|
end
|
174
183
|
|
175
|
-
|
176
|
-
|
184
|
+
if step["print"]
|
185
|
+
debug({"message" => received})
|
186
|
+
end
|
187
|
+
|
188
|
+
log(:done) { "Received a message: #{received&.truncate(100)}" }
|
189
|
+
rescue ThreadError, ReceiveTimeoutError
|
177
190
|
if received
|
178
191
|
raise UnmatchedExpectationError, prepare_receive_error(expected, received)
|
179
192
|
else
|
180
|
-
raise NoMessageError, "Expected to receive #{expected} but nothing has been received"
|
193
|
+
raise NoMessageError, "Expected to receive #{expected.inspect} but nothing has been received"
|
181
194
|
end
|
182
195
|
end
|
183
196
|
|
@@ -244,7 +257,7 @@ module WSDirector
|
|
244
257
|
|
245
258
|
def build_client(...)
|
246
259
|
Client.new(...)
|
247
|
-
end
|
260
|
+
end; respond_to?(:ruby2_keywords, true) && (ruby2_keywords :build_client)
|
248
261
|
|
249
262
|
def prepare_receive_error(expected, received)
|
250
263
|
<<~MSG
|
@@ -11,7 +11,7 @@ module WSDirector
|
|
11
11
|
options[:ignore] ||= [PING_IGNORE]
|
12
12
|
options[:subprotocol] ||= "actioncable-v1-json"
|
13
13
|
|
14
|
-
super
|
14
|
+
super
|
15
15
|
|
16
16
|
receive("data>" => {"type" => "welcome"})
|
17
17
|
log(:done) { "Welcomed" }
|
@@ -64,7 +64,11 @@ module WSDirector
|
|
64
64
|
key = step.key?("data") ? "data" : "data>"
|
65
65
|
|
66
66
|
message = step.fetch(key, {})
|
67
|
-
|
67
|
+
|
68
|
+
# Move all protocol-level fields to data
|
69
|
+
step[key] = {"identifier" => identifier, "message" => message}.merge(step.slice("offset", "stream_id", "epoch"))
|
70
|
+
|
71
|
+
super
|
68
72
|
end
|
69
73
|
|
70
74
|
def receive_all(step)
|
@@ -78,7 +82,7 @@ module WSDirector
|
|
78
82
|
|
79
83
|
key = msg.key?("data") ? "data" : "data>"
|
80
84
|
|
81
|
-
msg[key] = {"identifier" => identifier, "message" => msg[key]}
|
85
|
+
msg[key] = {"identifier" => identifier, "message" => msg[key]}.merge(step.slice("offset", "stream_id", "epoch"))
|
82
86
|
end
|
83
87
|
|
84
88
|
super
|
@@ -88,7 +92,7 @@ module WSDirector
|
|
88
92
|
|
89
93
|
def extract_identifier(step)
|
90
94
|
channel = step.delete("channel")
|
91
|
-
step.
|
95
|
+
(step.delete("params") || {}).merge(channel: channel).to_json
|
92
96
|
end
|
93
97
|
end
|
94
98
|
end
|
@@ -14,14 +14,14 @@ module WSDirector
|
|
14
14
|
@refs_counter = 3
|
15
15
|
@join_refs_counter = 3
|
16
16
|
@topics_to_join_ref = {}
|
17
|
-
end
|
17
|
+
end; respond_to?(:ruby2_keywords, true) && (ruby2_keywords :initialize)
|
18
18
|
|
19
19
|
def init_client(**options)
|
20
20
|
options[:query] ||= {}
|
21
21
|
# Make sure we use the v2 of the protocol
|
22
22
|
options[:query][:vsn] = "2.0.0"
|
23
23
|
|
24
|
-
super
|
24
|
+
super
|
25
25
|
end
|
26
26
|
|
27
27
|
def join(step)
|
@@ -92,7 +92,12 @@ module WSDirector
|
|
92
92
|
data["sample"] = [1, parse_multiplier(data["sample"])].max if data["sample"]
|
93
93
|
|
94
94
|
multiplier = parse_multiplier(data.delete("multiplier") || "1")
|
95
|
-
|
95
|
+
|
96
|
+
if type == "loop"
|
97
|
+
handle_steps(data.fetch("actions")) * multiplier
|
98
|
+
else
|
99
|
+
Array.new(multiplier) { {"type" => type, "id" => id}.merge(data) }
|
100
|
+
end
|
96
101
|
else
|
97
102
|
{"type" => step, "id" => id}
|
98
103
|
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module WSDirector
|
4
|
+
module Ext
|
5
|
+
# Extend Object through refinements
|
6
|
+
module Formatting
|
7
|
+
refine ::Object do
|
8
|
+
def truncate(*__rest__) = itself
|
9
|
+
end
|
10
|
+
|
11
|
+
refine ::String do
|
12
|
+
def truncate(limit)
|
13
|
+
return self if size <= limit
|
14
|
+
|
15
|
+
"#{self[0..(limit - 3)]}..."
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
refine ::Hash do
|
20
|
+
def truncate(limit)
|
21
|
+
str = to_json
|
22
|
+
|
23
|
+
str.truncate(limit)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
refine ::Float do
|
28
|
+
def duration
|
29
|
+
if self > 1
|
30
|
+
"#{truncate(2)}s"
|
31
|
+
else
|
32
|
+
"#{(self * 1000).to_i}ms"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -11,7 +11,7 @@ module WSDirector
|
|
11
11
|
options[:ignore] ||= [PING_IGNORE]
|
12
12
|
options[:subprotocol] ||= "actioncable-v1-json"
|
13
13
|
|
14
|
-
super
|
14
|
+
super
|
15
15
|
|
16
16
|
receive("data>" => {"type" => "welcome"})
|
17
17
|
log(:done) { "Welcomed" }
|
@@ -64,7 +64,11 @@ module WSDirector
|
|
64
64
|
key = step.key?("data") ? "data" : "data>"
|
65
65
|
|
66
66
|
message = step.fetch(key, {})
|
67
|
-
|
67
|
+
|
68
|
+
# Move all protocol-level fields to data
|
69
|
+
step[key] = {"identifier" => identifier, "message" => message}.merge(step.slice("offset", "stream_id", "epoch"))
|
70
|
+
|
71
|
+
super
|
68
72
|
end
|
69
73
|
|
70
74
|
def receive_all(step)
|
@@ -78,7 +82,7 @@ module WSDirector
|
|
78
82
|
|
79
83
|
key = msg.key?("data") ? "data" : "data>"
|
80
84
|
|
81
|
-
msg[key] = {"identifier" => identifier, "message" => msg[key]}
|
85
|
+
msg[key] = {"identifier" => identifier, "message" => msg[key]}.merge(step.slice("offset", "stream_id", "epoch"))
|
82
86
|
end
|
83
87
|
|
84
88
|
super
|
@@ -88,7 +92,7 @@ module WSDirector
|
|
88
92
|
|
89
93
|
def extract_identifier(step)
|
90
94
|
channel = step.delete("channel")
|
91
|
-
step.
|
95
|
+
(step.delete("params") || {}).merge(channel: channel).to_json
|
92
96
|
end
|
93
97
|
end
|
94
98
|
end
|
@@ -99,6 +99,9 @@ module WSDirector
|
|
99
99
|
|
100
100
|
# Base protocol describes basic actions
|
101
101
|
class Base
|
102
|
+
class ReceiveTimeoutError < StandardError
|
103
|
+
end
|
104
|
+
|
102
105
|
include WSDirector::Utils
|
103
106
|
|
104
107
|
def initialize(task, scale: 1, logger: nil, id: nil, color: nil)
|
@@ -155,9 +158,11 @@ module WSDirector
|
|
155
158
|
def receive(step)
|
156
159
|
expected = step["data"] || PartialMatcher.new(step["data>"])
|
157
160
|
ordered = step["ordered"]
|
161
|
+
timeout = step.fetch("timeout", 5).to_f
|
158
162
|
|
159
|
-
log { "Receive a message: #{expected.truncate(
|
163
|
+
log { "Receive a message in #{timeout}s: #{expected.inspect.truncate(100)}" }
|
160
164
|
|
165
|
+
start = Time.now.to_f
|
161
166
|
received = nil
|
162
167
|
|
163
168
|
client.each_message do |msg, id|
|
@@ -170,14 +175,22 @@ module WSDirector
|
|
170
175
|
if ordered
|
171
176
|
raise UnmatchedExpectationError, prepare_receive_error(expected, received)
|
172
177
|
end
|
178
|
+
|
179
|
+
if Time.now.to_f - start > timeout
|
180
|
+
raise ReceiveTimeoutError
|
181
|
+
end
|
173
182
|
end
|
174
183
|
|
175
|
-
|
176
|
-
|
184
|
+
if step["print"]
|
185
|
+
debug({"message" => received})
|
186
|
+
end
|
187
|
+
|
188
|
+
log(:done) { "Received a message: #{received&.truncate(100)}" }
|
189
|
+
rescue ThreadError, ReceiveTimeoutError
|
177
190
|
if received
|
178
191
|
raise UnmatchedExpectationError, prepare_receive_error(expected, received)
|
179
192
|
else
|
180
|
-
raise NoMessageError, "Expected to receive #{expected} but nothing has been received"
|
193
|
+
raise NoMessageError, "Expected to receive #{expected.inspect} but nothing has been received"
|
181
194
|
end
|
182
195
|
end
|
183
196
|
|
@@ -92,7 +92,12 @@ module WSDirector
|
|
92
92
|
data["sample"] = [1, parse_multiplier(data["sample"])].max if data["sample"]
|
93
93
|
|
94
94
|
multiplier = parse_multiplier(data.delete("multiplier") || "1")
|
95
|
-
|
95
|
+
|
96
|
+
if type == "loop"
|
97
|
+
handle_steps(data.fetch("actions")) * multiplier
|
98
|
+
else
|
99
|
+
Array.new(multiplier) { {"type" => type, "id" => id}.merge(data) }
|
100
|
+
end
|
96
101
|
else
|
97
102
|
{"type" => step, "id" => id}
|
98
103
|
end
|
data/lib/wsdirector/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: wsdirector-core
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Vladimir Dementyev
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date:
|
13
|
+
date: 2024-10-04 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: websocket-client-simple
|
@@ -207,6 +207,7 @@ files:
|
|
207
207
|
- lib/.rbnext/3.1/wsdirector/runner.rb
|
208
208
|
- lib/.rbnext/3.1/wsdirector/scenario_reader.rb
|
209
209
|
- lib/.rbnext/3.1/wsdirector/task.rb
|
210
|
+
- lib/.rbnext/3.2/wsdirector/ext/formatting.rb
|
210
211
|
- lib/wsdirector-cli.rb
|
211
212
|
- lib/wsdirector.rb
|
212
213
|
- lib/wsdirector/cli.rb
|
@@ -245,7 +246,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
245
246
|
- !ruby/object:Gem::Version
|
246
247
|
version: '0'
|
247
248
|
requirements: []
|
248
|
-
rubygems_version: 3.
|
249
|
+
rubygems_version: 3.5.18
|
249
250
|
signing_key:
|
250
251
|
specification_version: 4
|
251
252
|
summary: Scenario-based WebSocket black-box testing
|