wsdirector-core 1.0.2 → 1.1.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 +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
|