wsdirector-cli 0.2.1 → 0.3.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
- SHA1:
3
- metadata.gz: 12469915784ca778073f50ab6b6157c73c014bee
4
- data.tar.gz: '09f32425b8005d8cade46678259222286f4eff8b'
2
+ SHA256:
3
+ metadata.gz: 2baebc5541a7fa47137932138d2605c7d7c7666542ef1967a056b884660d06df
4
+ data.tar.gz: b1179e52ccc50773d88fae780ce58d6a7038b45ad3ae29ac52427f594301de05
5
5
  SHA512:
6
- metadata.gz: 1985ee2404b98a54e9a63f40b35db59e139738f9d4d3f3eeb79bc451533f97f3a85c0f2abebdb5aa473e8a7f836a4597bdf058d6bd8cf8c016d85226545606b4
7
- data.tar.gz: 55994d15c789a8f8c3bad3b2847a9d274a7ed581081e5928c071c6daeb6f4a1e2d225e844e7499364bf051982dca87cd28e4e9f4f563deb52c22bbe9087e1be6
6
+ metadata.gz: 79a5b74d539c88e58932f67e91730652a13f3051d753cd14539c5b55a8a410134e9b16bca26debff21aa51db7943a0263bb859b1ff68c0444d2e7ba0d617312c
7
+ data.tar.gz: 505976c437539b628b9325a0f3b7f423e55c4df33f29fc00476aaf2bbaf9f5e786cb6dc5bca9fb1cb10e6aca92dec9265e6a8fbfde704ecfc5be2734e6b4ec73
@@ -1,5 +1,35 @@
1
1
  # Change log
2
2
 
3
+ ## master
4
+
5
+ ## 0.3.0 (2018-05-02)
6
+
7
+ - Add `debug` action and options. ([@palkan][])
8
+
9
+ Allows to print arbitrary messages during the execution.
10
+
11
+ - Add `sleep` action. ([@palkan][])
12
+
13
+ - Add `receive_all` action. ([@palkan][])
14
+
15
+ Allows to handle multiple messages with unspecified order:
16
+
17
+ ```yml
18
+ - receive_all:
19
+ messages:
20
+ - data:
21
+ text: "Hello!"
22
+ multiplier: ":scale + :scale"
23
+ channel: "chat"
24
+ params:
25
+ id: 2
26
+ - data:
27
+ text: "message sent"
28
+ channel: "chat"
29
+ params:
30
+ id: 2
31
+ ```
32
+
3
33
  ## 0.2.0 (2017-11-05)
4
34
 
5
35
  - Initial version. ([@palkan][], [@Kirillvs][], [@Grandman][])
data/README.md CHANGED
@@ -1,4 +1,5 @@
1
- [![Gem Version](https://badge.fury.io/rb/wsdirector-cli.svg)](https://rubygems.org/gems/wsdirector-cli) [![CircleCI](https://circleci.com/gh/palkan/wsdirector.svg?style=svg)](https://circleci.com/gh/palkan/wsdirector)
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) [![Build Status](https://travis-ci.org/palkan/wsdirector.svg?branch=master)](https://travis-ci.org/palkan/wsdirector) [![CircleCI](https://circleci.com/gh/palkan/wsdirector.svg?style=svg)](https://circleci.com/gh/palkan/wsdirector)
2
3
 
3
4
  # WebSocket Director
4
5
 
@@ -6,9 +7,6 @@ Command line tool for testing websocket servers using scenarios.
6
7
 
7
8
  Suitable for testing any websocket server implementation, like [Action Cable](https://github.com/rails/rails/tree/master/actioncable), [Websocket Eventmachine Server](https://github.com/imanel/websocket-eventmachine-server), [Litecable](https://github.com/palkan/litecable) and so on.
8
9
 
9
- <a href="https://evilmartians.com/">
10
- <img src="https://evilmartians.com/badges/sponsored-by-evil-martians.svg" alt="Sponsored by Evil Martians" width="236" height="54"></a>
11
-
12
10
  ## Installation
13
11
 
14
12
  ```bash
@@ -17,7 +15,7 @@ Suitable for testing any websocket server implementation, like [Action Cable](ht
17
15
 
18
16
  ## Usage
19
17
 
20
- Create YAML file with simle testing script:
18
+ Create YAML file with simple testing script:
21
19
 
22
20
  ```yml
23
21
  # script.yml
@@ -43,7 +41,7 @@ You can create more complex scenarios with multiple client groups:
43
41
  - client: # first clients group
44
42
  name: "publisher" # optional group name
45
43
  multiplier: ":scale" # :scale take number from -s param, and run :scale number of clients in this group
46
- actions: #
44
+ actions:
47
45
  - receive:
48
46
  data: "Welcome"
49
47
  - wait_all # makes all clients in all groups wait untill every client get this point (global barrier)
@@ -133,6 +131,24 @@ Scenario:
133
131
  text: "hello"
134
132
  ```
135
133
 
134
+ ## Future Ideas
135
+
136
+ - Report timings (per-client and aggregates)
137
+
138
+ - File-less scenarios (JSON-encoded?), e.g.
139
+
140
+ ```shell
141
+ wsdirector -i '{"receive": "hello"}' localhost:9898/ws
142
+ ```
143
+
144
+ - Connection parameters (headers, query params, etc)
145
+
146
+ - Testing frameworks integrations
147
+
148
+ - Loading protocols dynamically
149
+
150
+ - What else? [Submit an issue!](https://github.com/palkan/wsdirector/issues/new)
151
+
136
152
  ## Contributing
137
153
 
138
154
  Bug reports and pull requests are welcome on GitHub at https://github.com/palkan/wsdirector.
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "wsdirector/cli"
@@ -6,6 +6,7 @@ require "json"
6
6
 
7
7
  require "wsdirector/version"
8
8
  require "wsdirector/configuration"
9
+ require "wsdirector/utils"
9
10
 
10
11
  # Command line tool for testing websocket servers using scenarios.
11
12
  module WSDirector
@@ -1,13 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "websocket-client-simple"
4
+ require "securerandom"
4
5
 
5
6
  module WSDirector
6
7
  # WebSocket client
7
8
  class Client
8
9
  WAIT_WHEN_EXPECTING_EVENT = 5
9
10
 
10
- attr_reader :ws
11
+ attr_reader :ws, :id
11
12
 
12
13
  # Create new WebSocket client and connect to WSDirector
13
14
  # ws URL.
@@ -22,6 +23,7 @@ module WSDirector
22
23
  open = Concurrent::Promise.new
23
24
  client = self
24
25
 
26
+ @id = SecureRandom.hex(6)
25
27
  @ws = WebSocket::Client::Simple.connect(path) do |ws|
26
28
  ws.on(:open) do |_event|
27
29
  open.set(true)
@@ -12,6 +12,8 @@ module WSDirector
12
12
  module Protocols # :nodoc:
13
13
  # Raised when received not expected message
14
14
  class UnmatchedExpectationError < WSDirector::Error; end
15
+ # Raised when received message is unexpected
16
+ class UnexpectedMessageError < WSDirector::Error; end
15
17
  # Raised when nothing has been received
16
18
  class NoMessageError < WSDirector::Error; end
17
19
 
@@ -50,6 +50,20 @@ module WSDirector
50
50
  super("data" => { "identifier" => identifier, "message" => message })
51
51
  end
52
52
 
53
+ def receive_all(step)
54
+ messages = step["messages"]
55
+
56
+ return super if messages.nil? || messages.empty?
57
+
58
+ messages.each do |msg|
59
+ next unless msg.key?("channel")
60
+ identifier = extract_identifier(msg)
61
+ msg["data"] = { "identifier" => identifier, "message" => msg["data"] }
62
+ end
63
+
64
+ super
65
+ end
66
+
53
67
  private
54
68
 
55
69
  def extract_identifier(step)
@@ -1,9 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "time"
4
+
3
5
  module WSDirector
4
6
  module Protocols
5
7
  # Base protocol describes basic actions
6
8
  class Base
9
+ include WSDirector::Utils
10
+
7
11
  def initialize(task)
8
12
  @task = task
9
13
  end
@@ -18,14 +22,72 @@ module WSDirector
18
22
  public_send(type, step)
19
23
  end
20
24
 
25
+ # Sleeps for a specified number of seconds.
26
+ #
27
+ # If "shift" is provided than the initial value is
28
+ # shifted by random number from (-shift, shift).
29
+ #
30
+ # Set "debug" to true to print the delay time.
31
+ def sleep(step)
32
+ delay = step.fetch("time").to_f
33
+ shift = step.fetch("shift", 0).to_f
34
+
35
+ delay = delay - shift * rand + shift * rand
36
+
37
+ print("Sleep for #{delay}s") if step.fetch("debug", false)
38
+
39
+ Kernel.sleep delay if delay > 0
40
+ end
41
+
42
+ # Prints provided message
43
+ def debug(step)
44
+ print(step.fetch("message"))
45
+ end
46
+
21
47
  def receive(step)
22
48
  expected = step.fetch("data")
23
49
  received = client.receive
24
- receive_matches?(expected, received)
50
+ raise UnmatchedExpectationError, prepare_receive_error(expected, received) unless
51
+ receive_matches?(expected, received)
25
52
  rescue ThreadError
26
53
  raise NoMessageError, "Expected to receive #{expected} but nothing has been received"
27
54
  end
28
55
 
56
+ # rubocop: disable Metrics/CyclomaticComplexity
57
+ def receive_all(step)
58
+ messages = step.delete("messages")
59
+ raise ArgumentError, "Messages array must be specified" if
60
+ messages.nil? || messages.empty?
61
+
62
+ expected =
63
+ Hash[messages.map do |msg|
64
+ multiplier = parse_multiplier(msg.delete("multiplier") || "1")
65
+ [msg["data"], multiplier]
66
+ end]
67
+
68
+ total_expected = expected.values.sum
69
+ total_received = 0
70
+
71
+ total_expected.times do
72
+ received = client.receive
73
+
74
+ total_received += 1
75
+
76
+ match = expected.find { |k, _| receive_matches?(k, received) }
77
+
78
+ raise UnexpectedMessageError, "Unexpected message received: #{received}" if
79
+ match.nil?
80
+
81
+ expected[match.first] -= 1
82
+ expected.delete(match.first) if expected[match.first].zero?
83
+ end
84
+ rescue ThreadError
85
+ raise NoMessageError,
86
+ "Expected to receive #{total_expected} messages " \
87
+ "but received only #{total_received}"
88
+ end
89
+ # rubocop: enable Metrics/CyclomaticComplexity
90
+
29
91
  def send(step)
30
92
  data = step.fetch("data")
31
93
  data = JSON.generate(data) if data.is_a?(Hash)
@@ -51,8 +113,7 @@ module WSDirector
51
113
  def receive_matches?(expected, received)
52
114
  received = JSON.parse(received) if expected.is_a?(Hash)
53
115
 
54
- raise UnmatchedExpectationError, prepare_receive_error(expected, received) if
55
- received != expected
116
+ received == expected
56
117
  end
57
118
 
58
119
  def prepare_receive_error(expected, received)
@@ -62,6 +123,10 @@ module WSDirector
62
123
  ++ got: #{received}
63
124
  MSG
64
125
  end
126
+
127
+ def print(msg)
128
+ $stdout.puts "DEBUG #{Time.now.iso8601} client=#{client.id} #{msg}\n"
129
+ end
65
130
  end
66
131
  end
67
132
  end
@@ -3,9 +3,9 @@
3
3
  module WSDirector
4
4
  # Read and parse YAML scenario
5
5
  class ScenarioReader
6
- MULTIPLIER_FORMAT = /^[-+*\\\d ]+$/
7
-
8
6
  class << self
7
+ include WSDirector::Utils
8
+
9
9
  def parse(file_path)
10
10
  contents = YAML.load_file(file_path)
11
11
 
@@ -62,14 +62,6 @@ module WSDirector
62
62
  { "total" => total_count, "clients" => clients }
63
63
  end
64
64
 
65
- def parse_multiplier(str)
66
- prepared = str.to_s.gsub(":scale", WSDirector.config.scale.to_s)
67
- raise WSDirector::Error, "Unknown multiplier format: #{str}" unless
68
- prepared =~ MULTIPLIER_FORMAT
69
-
70
- eval(prepared) # rubocop:disable Security/Eval
71
- end
72
-
73
65
  def parse_ingore(str)
74
66
  return unless str
75
67
 
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WSDirector
4
+ module Utils # :nodoc:
5
+ MULTIPLIER_FORMAT = /^[-+*\\\d ]+$/
6
+
7
+ def parse_multiplier(str)
8
+ prepared = str.to_s.gsub(":scale", WSDirector.config.scale.to_s)
9
+ raise WSDirector::Error, "Unknown multiplier format: #{str}" unless
10
+ prepared =~ MULTIPLIER_FORMAT
11
+
12
+ eval(prepared) # rubocop:disable Security/Eval
13
+ end
14
+ end
15
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module WSDirector
4
- VERSION = "0.2.1"
4
+ VERSION = "0.3.0"
5
5
  end
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.2.1
4
+ version: 0.3.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: 2017-11-05 00:00:00.000000000 Z
13
+ date: 2018-05-02 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: websocket-client-simple
@@ -194,6 +194,7 @@ files:
194
194
  - LICENSE.txt
195
195
  - README.md
196
196
  - bin/wsdirector
197
+ - lib/wsdirector-cli.rb
197
198
  - lib/wsdirector.rb
198
199
  - lib/wsdirector/cli.rb
199
200
  - lib/wsdirector/client.rb
@@ -209,6 +210,7 @@ files:
209
210
  - lib/wsdirector/runner.rb
210
211
  - lib/wsdirector/scenario_reader.rb
211
212
  - lib/wsdirector/task.rb
213
+ - lib/wsdirector/utils.rb
212
214
  - lib/wsdirector/version.rb
213
215
  homepage: https://github.com/palkan/wsdirector
214
216
  licenses:
@@ -222,7 +224,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
222
224
  requirements:
223
225
  - - ">="
224
226
  - !ruby/object:Gem::Version
225
- version: '0'
227
+ version: 2.4.0
226
228
  required_rubygems_version: !ruby/object:Gem::Requirement
227
229
  requirements:
228
230
  - - ">="
@@ -230,7 +232,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
230
232
  version: '0'
231
233
  requirements: []
232
234
  rubyforge_project:
233
- rubygems_version: 2.6.13
235
+ rubygems_version: 2.7.4
234
236
  signing_key:
235
237
  specification_version: 4
236
238
  summary: Command line tool for testing websocket servers using scenarios.