anyt-core 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +38 -0
  4. data/bin/anyt +17 -0
  5. data/bin/console +7 -0
  6. data/bin/setup +8 -0
  7. data/lib/anyt/cli.rb +211 -0
  8. data/lib/anyt/client.rb +146 -0
  9. data/lib/anyt/command.rb +83 -0
  10. data/lib/anyt/config.rb +41 -0
  11. data/lib/anyt/dummy/application.rb +86 -0
  12. data/lib/anyt/dummy/config.ru +16 -0
  13. data/lib/anyt/dummy/routes.rb +4 -0
  14. data/lib/anyt/dummy/tmp/development_secret.txt +1 -0
  15. data/lib/anyt/dummy/tmp/local_secret.txt +1 -0
  16. data/lib/anyt/ext/minitest.rb +151 -0
  17. data/lib/anyt/remote_control.rb +33 -0
  18. data/lib/anyt/rpc.rb +44 -0
  19. data/lib/anyt/tests/core/ping_test.rb +23 -0
  20. data/lib/anyt/tests/core/welcome_test.rb +10 -0
  21. data/lib/anyt/tests/features/channel_state_test.rb +81 -0
  22. data/lib/anyt/tests/features/remote_disconnect_test.rb +35 -0
  23. data/lib/anyt/tests/features/server_restart_test.rb +30 -0
  24. data/lib/anyt/tests/request/channel_test.rb +28 -0
  25. data/lib/anyt/tests/request/connection_test.rb +54 -0
  26. data/lib/anyt/tests/request/disconnect_reasons_test.rb +25 -0
  27. data/lib/anyt/tests/request/disconnection_test.rb +148 -0
  28. data/lib/anyt/tests/streams/broadcast_test.rb +65 -0
  29. data/lib/anyt/tests/streams/multiple_clients_test.rb +61 -0
  30. data/lib/anyt/tests/streams/multiple_test.rb +60 -0
  31. data/lib/anyt/tests/streams/single_test.rb +83 -0
  32. data/lib/anyt/tests/streams/stop_test.rb +57 -0
  33. data/lib/anyt/tests/subscriptions/ack_test.rb +39 -0
  34. data/lib/anyt/tests/subscriptions/params_test.rb +29 -0
  35. data/lib/anyt/tests/subscriptions/perform_test.rb +60 -0
  36. data/lib/anyt/tests/subscriptions/transmissions_test.rb +32 -0
  37. data/lib/anyt/tests.rb +62 -0
  38. data/lib/anyt/utils/async_helpers.rb +16 -0
  39. data/lib/anyt/utils.rb +3 -0
  40. data/lib/anyt/version.rb +5 -0
  41. data/lib/anyt.rb +14 -0
  42. metadata +167 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: cd161ab373ec324f2d6ab9f17419858fae589bee036122ce1dac7039999ba360
4
+ data.tar.gz: b94fb021fab87a9d97f0a1bd3502142e969dd8982662805b0c0f5695ae7e7f45
5
+ SHA512:
6
+ metadata.gz: 3bdbfe03f6aaa1bd8ae6e1cf606e900e56ed06b3e8dda894c73bd89df6484ceeb14e18be2f697f83ea1407a9a24734da32d4988baef423fd84780a201feb55ff
7
+ data.tar.gz: 7f7d8927b2f2d19734c101f91a5beecaeabea5dfed0b7346abdcd0feab50e2daaddafbb5a3b8041c5eed90c4dc443d68a8d1b5f303647680ed3577b92539a708
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2016-2020 palkan
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,38 @@
1
+ [![Cult Of Martians](http://cultofmartians.com/assets/badges/badge.svg)](https://cultofmartians.com/tasks/anycable-conformance-tool.html#task)
2
+ [![Gem Version](https://badge.fury.io/rb/anyt.svg)](https://rubygems.org/gems/anyt)
3
+ ![Test](https://github.com/anycable/anyt/workflows/Test/badge.svg)
4
+
5
+ # Action Cable / AnyCable conformance testing tool
6
+
7
+ AnyT is a command-line tool to test your Action Cable or [AnyCable](http://anycable.io)-compatible WebSocket servers.
8
+
9
+ It contains a set of tests to determine which features are supported by the implementation under consideration.
10
+
11
+ ## Installation
12
+
13
+ ```sh
14
+ gem install anyt
15
+ ```
16
+
17
+ ## Usage
18
+
19
+ You should provide a command to run the server and the target URL for WebSocket clients:
20
+
21
+ ```sh
22
+ anyt -c "anycable-go" --target-url="ws://localhost:8080/cable"
23
+ ```
24
+
25
+ By default it launches gRPC server on `localhost:50051` and use local Redis instance for broadcasts (`localhost:6379`).
26
+
27
+ For more options run:
28
+
29
+ ```sh
30
+ anyt -h
31
+ ```
32
+
33
+ ## Contributing
34
+
35
+ Bug reports and pull requests are welcome on GitHub at https://github.com/anycable/anyt/issues.
36
+
37
+ ## License
38
+ The library is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
data/bin/anyt ADDED
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+
5
+ require 'anyt'
6
+ require 'anyt/cli'
7
+
8
+ begin
9
+ exit_code = Anyt::Cli.run
10
+ rescue => e
11
+ puts 'Bummer! There is an error:'
12
+ puts e
13
+ puts e.backtrace.take(10).join("\n")
14
+ exit 1
15
+ end
16
+
17
+ exit exit_code
data/bin/console ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'anyt'
5
+ require 'irb'
6
+
7
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/lib/anyt/cli.rb ADDED
@@ -0,0 +1,211 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "logger"
4
+ require "optparse"
5
+
6
+ require "anyt/version"
7
+ require "anyt/remote_control"
8
+ require "anyt/rpc"
9
+ require "anyt/command"
10
+ require "anyt/tests"
11
+
12
+ $stdout.sync = true
13
+
14
+ module Anyt
15
+ module Cli # :nodoc:
16
+ DUMMY_ROOT = ::File.expand_path(
17
+ "config.ru",
18
+ ::File.join(::File.dirname(__FILE__), "dummy")
19
+ )
20
+
21
+ RAILS_COMMAND = "bundle exec puma #{DUMMY_ROOT} -t #{ENV.fetch("RAILS_MAX_THREADS", 5)}"
22
+
23
+ class << self
24
+ # CLI entrypoint
25
+ def run(args = ARGV)
26
+ parse_options!(args)
27
+
28
+ ActionCable.server.config.logger = Rails.logger = AnyCable.logger
29
+
30
+ result = 1
31
+
32
+ $stdout.puts "Starting AnyT v#{Anyt::VERSION} (pid: #{Process.pid})\n"
33
+
34
+ begin
35
+ # "Enable" AnyCable as early as possible to activate all the features in tests
36
+ unless Anyt.config.use_action_cable
37
+ ActionCable.server.config.cable = {"adapter" => "any_cable"}
38
+ require "anycable-rails"
39
+ end
40
+
41
+ # Load all test scenarios
42
+ Tests.load_tests unless @skip_tests
43
+
44
+ Rails.application.initialize!
45
+
46
+ # Start RPC server (unless specified otherwise, e.g. when
47
+ # we want to test Action Cable itself)
48
+ unless @skip_rpc
49
+ http_rpc = AnyCable.config.http_rpc_mount_path.present?
50
+
51
+ @rpc_command =
52
+ if http_rpc
53
+ Command.new(RAILS_COMMAND)
54
+ else
55
+ RPC.new
56
+ end
57
+
58
+ @rpc_command.start
59
+
60
+ if @only_rpc
61
+ if http_rpc
62
+ wait_till_terminated
63
+ else
64
+ @rpc_command.server.wait_till_terminated
65
+ end
66
+ return
67
+ end
68
+ end
69
+
70
+ # Start webosocket server under test
71
+ @command = Command.default
72
+ @command.run
73
+
74
+ unless @skip_tests
75
+ # Run tests
76
+ result = Tests.run ? 0 : 1
77
+ end
78
+
79
+ wait_till_terminated if @only_rails
80
+ rescue Interrupt => e
81
+ $stdout.puts "#{e.message}. Good-bye!"
82
+ ensure
83
+ @rpc_command&.stop unless @skip_rpc
84
+ @command&.stop
85
+ end
86
+
87
+ result
88
+ end
89
+
90
+ private
91
+
92
+ def parse_options!(args)
93
+ parser =
94
+ OptionParser.new do |cli|
95
+ cli.banner = <<~BANNER
96
+ Anyt – AnyCable websocket server conformance tool.
97
+
98
+ Usage: anyt [options]
99
+
100
+ Options:
101
+ BANNER
102
+
103
+ cli.on("-cCOMMAND", "--command=COMMAND", "Command to run WS server.") do |command|
104
+ Anyt.config.command = command
105
+ end
106
+
107
+ cli.on("--target-url=TARGET", "URL of target WebSocket server to test.") do |target|
108
+ Anyt.config.target_url = target
109
+ end
110
+
111
+ cli.on("--redis-url=REDIS_URL", "Redis server URL.") do |redis|
112
+ AnyCable.config.redis_url = redis
113
+ end
114
+
115
+ cli.on("--skip-rpc", TrueClass, "Do not run RPC server") do |flag|
116
+ @skip_rpc = flag
117
+ end
118
+
119
+ cli.on("--only-rpc", TrueClass, "Run only RPC server") do |flag|
120
+ @only_rpc = flag
121
+ end
122
+
123
+ cli.on("--only-rails", TrueClass, "Run only Rails server") do
124
+ @skip_rpc = true
125
+ @only_rails = true
126
+ @skip_tests = true
127
+
128
+ configure_rails_command!
129
+ end
130
+
131
+ cli.on("--self-check", "Run tests again Action Cable itself") do
132
+ @skip_rpc = true
133
+
134
+ configure_rails_command!
135
+ end
136
+
137
+ cli.on("--only test1,test2,test3", Array, "Run only specified tests") do |only_tests|
138
+ Anyt.config.only_tests = only_tests
139
+ end
140
+
141
+ cli.on("--except test1,test2,test3", Array, "Exclude specified tests") do |except_tests|
142
+ Anyt.config.except_tests = except_tests
143
+ end
144
+
145
+ cli.on("--wait-command=TIMEOUT", Integer,
146
+ "Number of seconds to wait for WS server initialization") do |timeout|
147
+ Anyt.config.wait_command = timeout
148
+ end
149
+
150
+ cli.on("--timeout-multiplier=VALUE", Float,
151
+ "Default exceptation timeouts multiplier") do |val|
152
+ Anyt.config.timeout_multiplier = val
153
+ end
154
+
155
+ cli.on("-rPATH", "--require=PATH",
156
+ "Path to additional tests (e.g. features/*.rb") do |path|
157
+ Anyt.config.tests_relative_path = path
158
+ ENV["ANYT_TESTS_RELATIVE_PATH"] = path
159
+ end
160
+
161
+ cli.on("--debug", "Enable debug mode.") do
162
+ AnyCable.config.debug = true
163
+ end
164
+
165
+ cli.on("-h", "--help", "Show this message.") do
166
+ puts cli
167
+ exit
168
+ end
169
+
170
+ cli.on("--version", "Print version.") do
171
+ puts Anyt::VERSION
172
+ exit
173
+ end
174
+ end
175
+
176
+ parser.parse!(args)
177
+ rescue OptionParser::InvalidOption => e
178
+ unknown_option = e.args.first
179
+ puts "This option looks unfamiliar: #{unknown_option}. A typo?"
180
+ puts "Use `anyt --help` to list all available options."
181
+ exit 1
182
+ end
183
+
184
+ def configure_rails_command!
185
+ Anyt.config.command = RAILS_COMMAND
186
+ Anyt.config.use_action_cable = true
187
+ end
188
+
189
+ def wait_till_terminated
190
+ self_read = setup_signals
191
+
192
+ while readable_io = IO.select([self_read]) # rubocop:disable Lint/AssignmentInCondition, Lint/IncompatibleIoSelectWithFiberScheduler
193
+ signal = readable_io.first[0].gets.strip
194
+ raise Interrupt, "SIG#{signal} received"
195
+ end
196
+ end
197
+
198
+ def setup_signals
199
+ self_read, self_write = IO.pipe
200
+
201
+ %w[INT TERM].each do |signal|
202
+ trap signal do
203
+ self_write.puts signal
204
+ end
205
+ end
206
+
207
+ self_read
208
+ end
209
+ end
210
+ end
211
+ end
@@ -0,0 +1,146 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "anyt"
4
+
5
+ module Anyt
6
+ # Synchronous websocket client
7
+ # Based on https://github.com/rails/rails/blob/v5.0.1/actioncable/test/client_test.rb
8
+ class Client
9
+ require "websocket-client-simple"
10
+ require "concurrent"
11
+
12
+ class Error < StandardError; end
13
+
14
+ class TimeoutError < Error; end
15
+
16
+ class DisconnectedError < Error
17
+ attr_reader :event
18
+
19
+ def initialize(event)
20
+ @event = event
21
+ if event
22
+ super("WebSocket disconnected (code=#{event.code}, reason=#{event.data})")
23
+ else
24
+ super("WebSocket disconnected abnormally")
25
+ end
26
+ end
27
+ end
28
+
29
+ WAIT_WHEN_EXPECTING_EVENT = 5
30
+ WAIT_WHEN_NOT_EXPECTING_EVENT = 0.5
31
+
32
+ private attr_reader :logger
33
+
34
+ attr_reader :url
35
+
36
+ def initialize(
37
+ ignore: [], url: Anyt.config.target_url, qs: "",
38
+ cookies: "", headers: {},
39
+ protocol: "actioncable-v1-json",
40
+ timeout_multiplier: Anyt.config.timeout_multiplier,
41
+ logger: AnyCable.logger
42
+ )
43
+ @logger = logger
44
+ ignore_message_types = @ignore_message_types = ignore
45
+ messages = @messages = Queue.new
46
+ closed = @closed = Concurrent::Event.new
47
+ has_messages = @has_messages = Concurrent::Semaphore.new(0)
48
+
49
+ @timeout_multiplier = timeout_multiplier
50
+
51
+ headers = headers.merge("cookie" => cookies)
52
+ headers["Sec-WebSocket-Protocol"] = protocol
53
+
54
+ open = Concurrent::Promise.new
55
+
56
+ @url = url
57
+
58
+ if !qs.empty?
59
+ @url += @url.include?("?") ? "&" : "?"
60
+ @url += qs
61
+ end
62
+
63
+ @ws = WebSocket::Client::Simple.connect(
64
+ @url,
65
+ headers: headers
66
+ ) do |ws|
67
+ ws.on(:error) do |event|
68
+ event = RuntimeError.new(event.message) unless event.is_a?(Exception)
69
+
70
+ if open.pending?
71
+ open.fail(event)
72
+ else
73
+ messages << event
74
+ has_messages.release
75
+ end
76
+ end
77
+
78
+ ws.on(:open) do |_event|
79
+ open.set(true)
80
+ end
81
+
82
+ ws.on(:message) do |event|
83
+ next if event.type == :ping
84
+ if event.type == :close
85
+ messages << DisconnectedError.new(event)
86
+ has_messages.release
87
+ closed.set
88
+ else
89
+ message = JSON.parse(event.data)
90
+
91
+ next if ignore_message_types.include?(message["type"])
92
+
93
+ logger.debug "Message received: #{message}"
94
+
95
+ messages << message
96
+ has_messages.release
97
+ end
98
+ end
99
+
100
+ ws.on(:close) do |_event|
101
+ closed.set
102
+ end
103
+ end
104
+
105
+ open.wait!(WAIT_WHEN_EXPECTING_EVENT * @timeout_multiplier)
106
+ end
107
+ # rubocop: enable Metrics/BlockLength
108
+ # rubocop: enable Metrics/AbcSize
109
+ # rubocop: enable Metrics/MethodLength
110
+
111
+ def receive(timeout: WAIT_WHEN_EXPECTING_EVENT)
112
+ timeout *= @timeout_multiplier
113
+
114
+ unless @has_messages.try_acquire(1, timeout)
115
+ raise DisconnectedError if closed?
116
+ raise TimeoutError, "Timed out to receive message"
117
+ end
118
+
119
+ msg = @messages.pop(true)
120
+ raise msg if msg.is_a?(Exception) || msg.is_a?(Error)
121
+
122
+ msg
123
+ end
124
+
125
+ def send(message)
126
+ @ws.send(JSON.generate(message))
127
+ end
128
+
129
+ def close(allow_messages: false)
130
+ sleep WAIT_WHEN_NOT_EXPECTING_EVENT * @timeout_multiplier
131
+
132
+ raise "#{@messages.size} messages unprocessed" unless allow_messages || @messages.empty?
133
+
134
+ @ws.close
135
+ wait_for_close
136
+ end
137
+
138
+ def wait_for_close
139
+ @closed.wait(WAIT_WHEN_EXPECTING_EVENT * @timeout_multiplier)
140
+ end
141
+
142
+ def closed?
143
+ @closed.set?
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "childprocess"
4
+
5
+ module Anyt
6
+ # Runs system command (websocket server)
7
+ class Command
8
+ class << self
9
+ attr_reader :instance
10
+
11
+ def default
12
+ @instance ||= new
13
+ end
14
+
15
+ def restart
16
+ instance&.restart
17
+ end
18
+ end
19
+
20
+ attr_reader :cmd
21
+
22
+ def initialize(cmd = Anyt.config.command)
23
+ @cmd = cmd
24
+ end
25
+
26
+ def run
27
+ return if running?
28
+
29
+ return unless cmd
30
+
31
+ AnyCable.logger.debug "Running command: #{cmd}"
32
+
33
+ @process = ChildProcess.build(*cmd.split(/\s+/))
34
+
35
+ process.io.inherit! if AnyCable.config.debug
36
+
37
+ process.detach = true
38
+
39
+ process.environment["ANYCABLE_DEBUG"] = "1" if AnyCable.config.debug?
40
+ process.environment["ANYT_REMOTE_CONTROL_PORT"] = Anyt.config.remote_control_port
41
+ process.environment["ACTION_CABLE_ADAPTER"] = "any_cable" unless Anyt.config.use_action_cable
42
+
43
+ process.start
44
+
45
+ AnyCable.logger.debug "Command PID: #{process.pid}"
46
+
47
+ sleep Anyt.config.wait_command
48
+ raise "Command failed to start" unless running?
49
+ end
50
+
51
+ alias_method :start, :run
52
+
53
+ # rubocop: enable Metrics/MethodLength
54
+ # rubocop: enable Metrics/AbcSize
55
+
56
+ def restart
57
+ return unless running?
58
+
59
+ AnyCable.logger.debug "Restarting command PID: #{process.pid}"
60
+
61
+ stop
62
+ process.wait
63
+
64
+ run
65
+ end
66
+
67
+ def stop
68
+ return unless running?
69
+
70
+ AnyCable.logger.debug "Terminate PID: #{process.pid}"
71
+
72
+ process.stop
73
+ end
74
+
75
+ def running?
76
+ process&.alive?
77
+ end
78
+
79
+ private
80
+
81
+ attr_reader :process
82
+ end
83
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "anyway"
4
+
5
+ module Anyt
6
+ # Anyt configuration
7
+ class Config < Anyway::Config
8
+ attr_config :command,
9
+ :only_tests,
10
+ :except_tests,
11
+ :tests_relative_path,
12
+ remote_control_port: 8919,
13
+ use_action_cable: false,
14
+ target_url: "ws://localhost:9292/cable",
15
+ wait_command: 2,
16
+ timeout_multiplier: 1
17
+
18
+ coerce_types only_tests: {type: :string, array: true}
19
+ coerce_types except_tests: {type: :string, array: true}
20
+
21
+ def tests_path
22
+ return unless tests_relative_path
23
+
24
+ File.expand_path(tests_relative_path, Dir.pwd)
25
+ end
26
+
27
+ def filter_tests?
28
+ only_tests || except_tests
29
+ end
30
+
31
+ def tests_filter
32
+ only_rxp = /(#{only_tests.join("|")})/ if only_tests
33
+ except_rxp = /(#{except_tests.join("|")})/ if except_tests
34
+
35
+ @tests_filter ||= lambda do |path|
36
+ (only_rxp.nil? || only_rxp.match?(path)) &&
37
+ (except_rxp.nil? || !except_rxp.match?(path))
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails"
4
+ require "action_controller/railtie"
5
+ require "action_cable/engine"
6
+
7
+ require "redis"
8
+ require "anycable-rails"
9
+
10
+ require "action_dispatch/middleware/cookies"
11
+
12
+ class TestApp < Rails::Application
13
+ config.load_defaults 7.0
14
+
15
+ config.logger = Logger.new($stdout)
16
+ config.log_level = AnyCable.config.log_level
17
+ config.eager_load = true
18
+
19
+ config.paths["config/routes.rb"] << File.join(__dir__, "routes.rb")
20
+ end
21
+
22
+ DISCONNECT_DELAY = ENV["ANYCABLE_DISCONNECT_DELAY"].to_f
23
+
24
+ module ApplicationCable
25
+ class Connection < ActionCable::Connection::Base
26
+ delegate :params, to: :request
27
+
28
+ identified_by :uid
29
+
30
+ def connect
31
+ logger.debug "Connected"
32
+ Anyt::ConnectHandlers.call(self)
33
+ end
34
+
35
+ def disconnect
36
+ if DISCONNECT_DELAY > 0
37
+ sleep DISCONNECT_DELAY
38
+ end
39
+ logger.debug "Disconnected"
40
+ end
41
+ end
42
+ end
43
+
44
+ module ApplicationCable
45
+ class Channel < ActionCable::Channel::Base
46
+ end
47
+ end
48
+
49
+ ECHO_DELAY = ENV["ANYCABLE_BENCHMARK_ECHO_DELAY"].to_f
50
+
51
+ # BenchmarkChannel is useful when running Rails app only or RPC only
52
+ class BenchmarkChannel < ApplicationCable::Channel
53
+ def subscribed
54
+ stream_from "all#{stream_id}"
55
+ end
56
+
57
+ def echo(data)
58
+ if ECHO_DELAY > 0
59
+ sleep ECHO_DELAY
60
+ end
61
+ transmit data
62
+ end
63
+
64
+ def broadcast(data)
65
+ ActionCable.server.broadcast "all#{stream_id}", data
66
+ data["action"] = "broadcastResult"
67
+ transmit data
68
+ end
69
+
70
+ def counter(data)
71
+ num = data.fetch("num", 100).to_i
72
+ num.times { ActionCable.server.broadcast "all", {text: "Count: #{_1}"} }
73
+ end
74
+
75
+ private
76
+
77
+ def stream_id
78
+ params[:id] || ""
79
+ end
80
+ end
81
+
82
+ ActionCable.server.config.cable = {"adapter" => ENV.fetch("ACTION_CABLE_ADAPTER", "redis")}
83
+ ActionCable.server.config.connection_class = -> { ApplicationCable::Connection }
84
+ ActionCable.server.config.disable_request_forgery_protection = true
85
+ ActionCable.server.config.logger =
86
+ Rails.logger
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "application"
4
+
5
+ require_relative "../tests"
6
+ require_relative "../remote_control"
7
+
8
+ # Start remote control
9
+ Anyt::RemoteControl::Server.start(Anyt.config.remote_control_port)
10
+
11
+ # Load channels from tests
12
+ Anyt::Tests.load_all_tests
13
+
14
+ Rails.application.initialize!
15
+
16
+ run Rails.application
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ Rails.application.routes.draw do
4
+ end
@@ -0,0 +1 @@
1
+ fe2ecbcf229d5547a64bcdaa9ef4e543404ca3fc9d4ee83aaa7ddab34b6a378fe090c2b1836c9ebd3e5ebbf01c239ddac95e219358baaefc1afaa04cd07bf78c
@@ -0,0 +1 @@
1
+ 8e4e6bba332e819bd6597b2e8961ca1c0ef0c9bcb3401214ea22155b729e262534cefed3012024f57b098e0756baddea2d145675da33f31a0844df44c9230141