wsdirector-cli 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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.