propono 0.6.3 → 0.7.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,15 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 299526d5c3416f58858496b984ec04ff96a74a69
4
- data.tar.gz: 8ab281343f74f9c5bfc233891311f1f21ebbf9e7
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ MTk2ZTQ1YmMxYTg4NzdiOWEyZDQ1ZWQyOTI4N2QyMGE4ZTA5OWRiOQ==
5
+ data.tar.gz: !binary |-
6
+ ZmUwY2I2M2E4ZjQ4ZTAxYzIwNzI4OTVjNWQ1OTkyYmFhYmM4Njk4OA==
5
7
  SHA512:
6
- metadata.gz: 9b3f4bbc2284bf4fad16d8c57dff469d94522b46948a5d1c882d0994b03a4c1d699680615dd653a0325671f512d84f47c165314d0e4d7cc914a8eda7871b318a
7
- data.tar.gz: 16609ea0f80a7cb985b84d4b2015bdbedd2aa83441f6dbcab2263db623bc17d412c2f0a191b0aab7d7b178ca781c98baa181a7aaba18291260a333725333c479
8
+ metadata.gz: !binary |-
9
+ ZTI1OWEzZGUwZTMzNzllNTYyNTE0NGZmOTZiOWUxZjJlYzUyNzY1NmQzMGZk
10
+ NTExYzYzNjE5NmI4NGE1Y2ZkODEzMzNkNzM2NzI1ZDAyMDFjYjQ0ZWIxYzhl
11
+ OGQ2NzBhZDlmNWQxZGYxMzUyMDJkN2I5YTAwMGM1YjIwMzYwMzU=
12
+ data.tar.gz: !binary |-
13
+ ZjFhYzMxYzgxNmZmNWM5MmQ1ODkwMTJlYzY4N2ZmNTE2NTMwYTY3MTA3YWVk
14
+ YjAxYjEyZjgwMjA4MmMxNzZjMDk5ZDA2M2RlZDc5ZmExNzRlNzQ2MTk2Mjc0
15
+ ZDY2OWU0ZjI2MmE2NWFjOWEyZDY5MGQyMTIwNTdhNWViOTI1Zjc=
@@ -1,3 +1,7 @@
1
+ # 0.7.0 / Unreleased
2
+
3
+ * [FEATURE] Add TCP publish and listen methods.
4
+
1
5
  # 0.6.3 / 2013-10-20
2
6
 
3
7
  * [FEATURE] Catch all StandardError exceptions for UDP publishes.
data/README.md CHANGED
@@ -58,34 +58,60 @@ end
58
58
  ```
59
59
  In the background, Propono is automatically setting up a queue using SQS, a notification system using SNS, and glueing them all together for you. But you don't have to worry about any of that.
60
60
 
61
+ ### Using TCP for messages
62
+
63
+ Publishing directly to SNS takes about 15x longer than publishing over a simple TCP connection. It is therefore some times favourable to publish to a seperate machine listening for TCP messages, which will then proxy them on.
64
+
65
+ To send messages this way, you need to set up a little extra config:
66
+
67
+ ```ruby
68
+ Propono.config.tcp_host = "some.host.running.a.propono.listener"
69
+ Propono.config.tcp_port = 12543
70
+ ```
71
+
72
+ You then simply pass the `:tcp` protocol into `publish`
73
+
74
+ ```ruby
75
+ Propono.publish('some-topic', message, protocol: :tcp)
76
+ ```
77
+
78
+ You'll now need another application running Propono to listen to the TCP feed. You can use the same machine or a different one, just make sure the port config is the same in both applications, and you're good to go.
79
+
80
+ ```ruby
81
+ Propono.listen_to_tcp do |topic, message|
82
+ Propono.publish(topic, message) # Proxy the message to SNS
83
+ end
84
+ ```
85
+
86
+ This proxying of TCP to SQS is used so often that there's a simple shortcut. Just run this on the machine receiving the TCP packets.
87
+
88
+ ```ruby
89
+ Propono.proxy_tcp()
90
+ ```
91
+
61
92
  ### Using UDP for messages
62
93
 
63
94
  If you want almost-zero performance impact, and don't mind the occasional message getting lost, you can use UDP. We use this for things like our live dashboard where we don't mind losing a piece of activity here and there, but any perforamnce impact on our Meducation itself is bad news.
64
95
 
65
- To send messages this way, you need to set up a little extra config:
96
+ Sending messages in this way is very similar to using TCP. First add some config:
66
97
 
67
98
  ```ruby
68
99
  Propono.config.udp_host = "some.host.running.a.propono.listener"
69
100
  Propono.config.udp_port = 12543
70
101
  ```
71
102
 
72
- You then simply pass the `:udp` protocol into `publish`
103
+ You then simply pass the `:udp` protocol into `publish`:
73
104
 
74
105
  ```ruby
75
106
  Propono.publish('some-topic', message, protocol: :udp)
76
- ```
77
107
 
78
- You'll now need another application running Propono to listen to the UDP feed. You can use the same machine or a different one, just make sure the port config is the same in both applications, and you're good to go.
108
+ As per the `listen_to_tcp` method explained above, you now listen to udp or use the proxy method:
79
109
 
80
110
  ```ruby
81
111
  Propono.listen_to_udp do |topic, message|
82
112
  Propono.publish(topic, message) # Proxy the message to SNS
83
113
  end
84
- ```
85
-
86
- This proxying of UDP to SQS is used so often that there's a simple shortcut. Just run this on the machine receiving the UDP packets.
87
114
 
88
- ```ruby
89
115
  Propono.proxy_udp()
90
116
  ```
91
117
 
@@ -100,7 +126,9 @@ Propono.config do |config|
100
126
  config.queue_region = "An AWS queue region"
101
127
  config.application_name = "A name unique in your network"
102
128
  config.udp_host = "The host of a machine used for UDP proxying"
103
- config.udp_port = "The host of a machine used for UDP proxying"
129
+ config.udp_port = "The port of a machine used for UDP proxying"
130
+ config.tcp_host = "The host of a machine used for TCP proxying"
131
+ config.tcp_port = "The port of a machine used for TCP proxying"
104
132
  config.logger = "A logger such as Log4r or Rails.logger"
105
133
  end
106
134
  ```
@@ -20,6 +20,7 @@ require "propono/services/queue_listener"
20
20
  require "propono/services/subscriber"
21
21
  require "propono/services/topic_creator"
22
22
  require "propono/services/udp_listener"
23
+ require "propono/services/tcp_listener"
23
24
 
24
25
  # Propono is a pub/sub gem built on top of Amazon Web Services (AWS).
25
26
  # It uses Simple Notification Service (SNS) and Simple Queue Service (SQS)
@@ -113,6 +114,17 @@ module Propono
113
114
  UdpListener.listen(&message_processor)
114
115
  end
115
116
 
117
+ # Listens for TCP messages and yields for each.
118
+ #
119
+ # Calling this will enter a queue-listening loop that
120
+ # yields the message_processor for each UDP message received.
121
+ #
122
+ # @param &message_processor The block to yield for each message.
123
+ # Is called with <tt>|topic, message|</tt>.
124
+ def self.listen_to_tcp(&message_processor)
125
+ TcpListener.listen(&message_processor)
126
+ end
127
+
116
128
  # Listens for UDP messages and passes them onto the queue.
117
129
  #
118
130
  # This method uses #listen_to_udp and #publish to proxy
@@ -122,4 +134,14 @@ module Propono
122
134
  Propono.publish(topic, message)
123
135
  end
124
136
  end
137
+
138
+ # Listens for TCP messages and passes them onto the queue.
139
+ #
140
+ # This method uses #listen_to_tcp and #publish to proxy
141
+ # messages from TCP onto the queue.
142
+ def self.proxy_tcp
143
+ Propono.listen_to_tcp do |topic, message|
144
+ Propono.publish(topic, message)
145
+ end
146
+ end
125
147
  end
@@ -9,6 +9,7 @@ module Propono
9
9
  :access_key, :secret_key, :queue_region,
10
10
  :application_name,
11
11
  :udp_host, :udp_port,
12
+ :tcp_host, :tcp_port,
12
13
  :logger
13
14
  ]
14
15
  attr_writer *SETTINGS
@@ -1,3 +1,5 @@
1
+ require 'socket'
2
+
1
3
  module Propono
2
4
  class PublisherError < ProponoError
3
5
  end
@@ -38,5 +40,15 @@ module Propono
38
40
  rescue => e
39
41
  Propono.config.logger.error "Propono failed to send : #{e}"
40
42
  end
43
+
44
+ def publish_via_tcp
45
+ payload = {topic: topic_id, message: message}.to_json
46
+
47
+ socket = TCPSocket.new(Propono.config.tcp_host, Propono.config.tcp_port)
48
+ socket.write payload
49
+ socket.close
50
+ rescue => e
51
+ Propono.config.logger.error "Propono failed to send : #{e}"
52
+ end
41
53
  end
42
54
  end
@@ -0,0 +1,40 @@
1
+ require 'json'
2
+
3
+ module Propono
4
+ class TcpListenerError < ProponoError
5
+ end
6
+
7
+ class TcpListener
8
+
9
+ def self.listen(&processor)
10
+ new(&processor).listen
11
+ end
12
+
13
+ def initialize(&processor)
14
+ raise TcpListenerError.new("Please provide a block to call for each message") unless block_given?
15
+ @processor = processor
16
+ end
17
+
18
+ def listen
19
+ loop { receive_and_process }
20
+ end
21
+
22
+ private
23
+
24
+ def receive_and_process
25
+ client = server.accept
26
+ tcp_data = client.recvfrom(1024)[0]
27
+ client.close
28
+ Thread.new { process_tcp_data(tcp_data) }
29
+ end
30
+
31
+ def process_tcp_data(tcp_data)
32
+ json = JSON.parse(tcp_data)
33
+ @processor.call(json['topic'], json['message'])
34
+ end
35
+
36
+ def server
37
+ @server ||= TCPServer.open(Propono.config.tcp_port)
38
+ end
39
+ end
40
+ end
@@ -1,3 +1,3 @@
1
1
  module Propono
2
- VERSION = "0.6.3"
2
+ VERSION = "0.7.0"
3
3
  end
@@ -43,6 +43,30 @@ module Propono
43
43
  assert_equal application_name, Propono.config.application_name
44
44
  end
45
45
 
46
+ def test_udp_host
47
+ val = "test-application-name"
48
+ Propono.config.udp_host = val
49
+ assert_equal val, Propono.config.udp_host
50
+ end
51
+
52
+ def test_udp_port
53
+ val = 10000
54
+ Propono.config.udp_port = val
55
+ assert_equal val, Propono.config.udp_port
56
+ end
57
+
58
+ def test_tcp_host
59
+ val = "test-application-name"
60
+ Propono.config.tcp_host = val
61
+ assert_equal val, Propono.config.tcp_host
62
+ end
63
+
64
+ def test_tcp_port
65
+ val = 9382
66
+ Propono.config.tcp_port = val
67
+ assert_equal val, Propono.config.tcp_port
68
+ end
69
+
46
70
  def test_missing_access_key_throws_exception
47
71
  assert_raises(ProponoConfigurationError) do
48
72
  Propono.config.access_key
@@ -14,6 +14,9 @@ module Propono
14
14
  break
15
15
  end
16
16
  end
17
+
18
+ sleep(2) # Make sure the listener has started
19
+
17
20
  Propono.publish(topic, text)
18
21
  flunk("Test Timeout") unless wait_for_thread(thread)
19
22
  ensure
@@ -0,0 +1,35 @@
1
+ require File.expand_path('../integration_test', __FILE__)
2
+
3
+ module Propono
4
+ class UdpToSqsTest < IntegrationTest
5
+ def test_the_message_gets_there
6
+ topic = "test-topic"
7
+ message = "This is my message"
8
+ Propono.config.tcp_host = "localhost"
9
+ Propono.config.tcp_port = 20009
10
+
11
+ Propono.subscribe_by_queue(topic)
12
+
13
+ sqs_thread = Thread.new do
14
+ Propono.listen_to_queue(topic) do |sqs_message|
15
+ assert_equal message, sqs_message
16
+ sqs_thread.terminate
17
+ end
18
+ end
19
+
20
+ tcp_thread = Thread.new do
21
+ Propono.listen_to_tcp do |tcp_topic, tcp_message|
22
+ Propono.publish(tcp_topic, tcp_message)
23
+ tcp_thread.terminate
24
+ end
25
+ end
26
+ sleep(2) # Make sure the listener has started
27
+
28
+ Propono.publish(topic, message, protocol: :tcp)
29
+ flunk("Test Timeout") unless wait_for_thread(tcp_thread) && wait_for_thread(sqs_thread)
30
+ ensure
31
+ tcp_thread.terminate
32
+ sqs_thread.terminate
33
+ end
34
+ end
35
+ end
@@ -9,10 +9,6 @@ module Propono
9
9
 
10
10
  Propono.subscribe_by_queue(topic)
11
11
 
12
- udp_thread = Thread.new do
13
- Propono.proxy_udp
14
- end
15
-
16
12
  sqs_thread = Thread.new do
17
13
  Propono.listen_to_queue(topic) do |sqs_message|
18
14
  assert_equal message, sqs_message
@@ -20,6 +16,12 @@ module Propono
20
16
  end
21
17
  end
22
18
 
19
+ udp_thread = Thread.new do
20
+ Propono.proxy_udp
21
+ end
22
+
23
+ sleep(2) # Make sure the proxy has started
24
+
23
25
  Propono.publish(topic, message, protocol: :udp)
24
26
  flunk("Test timeout") unless wait_for_thread(sqs_thread)
25
27
  ensure
@@ -9,6 +9,13 @@ module Propono
9
9
 
10
10
  Propono.subscribe_by_queue(topic)
11
11
 
12
+ sqs_thread = Thread.new do
13
+ Propono.listen_to_queue(topic) do |sqs_message|
14
+ assert_equal message, sqs_message
15
+ sqs_thread.terminate
16
+ end
17
+ end
18
+
12
19
  udp_thread = Thread.new do
13
20
  Propono.listen_to_udp do |udp_topic, udp_message|
14
21
  Propono.publish(udp_topic, udp_message)
@@ -16,12 +23,7 @@ module Propono
16
23
  end
17
24
  end
18
25
 
19
- sqs_thread = Thread.new do
20
- Propono.listen_to_queue(topic) do |sqs_message|
21
- assert_equal message, sqs_message
22
- sqs_thread.terminate
23
- end
24
- end
26
+ sleep(2) # Make sure the listener has started
25
27
 
26
28
  Propono.publish(topic, message, protocol: :udp)
27
29
  flunk("Test Timeout") unless wait_for_thread(udp_thread) && wait_for_thread(sqs_thread)
@@ -32,6 +32,11 @@ module Propono
32
32
  Propono.listen_to_udp()
33
33
  end
34
34
 
35
+ def test_listen_to_tcp_calls_tcp_listener
36
+ TcpListener.expects(:listen).with()
37
+ Propono.listen_to_tcp()
38
+ end
39
+
35
40
  def test_proxy_udp_calls_listen
36
41
  UdpListener.expects(:listen).with()
37
42
  Propono.proxy_udp()
@@ -44,5 +49,18 @@ module Propono
44
49
  Publisher.expects(:publish).with(topic, message, {})
45
50
  Propono.proxy_udp
46
51
  end
52
+
53
+ def test_proxy_tcp_calls_listen
54
+ TcpListener.expects(:listen).with()
55
+ Propono.proxy_tcp()
56
+ end
57
+
58
+ def test_proxy_tcp_calls_publish_in_the_block
59
+ topic = "foobar"
60
+ message = "message"
61
+ Propono.stubs(:listen_to_tcp).yields(topic, message)
62
+ Publisher.expects(:publish).with(topic, message, {})
63
+ Propono.proxy_tcp
64
+ end
47
65
  end
48
66
  end
@@ -122,6 +122,52 @@ module Propono
122
122
  end
123
123
  end
124
124
 
125
+ def test_tcp_uses_correct_message
126
+ Propono.config.tcp_host = "http://meducation.net"
127
+ Propono.config.tcp_port = 1234
128
+ topic_id = "my-fav-topic"
129
+ message = "foobar"
130
+ payload = {topic: topic_id, message: message}.to_json
131
+
132
+ socket = mock()
133
+ socket.expects(:write).with(payload)
134
+ socket.expects(:close)
135
+ TCPSocket.stubs(new: socket)
136
+
137
+ publisher = Publisher.new(topic_id, message)
138
+ publisher.send(:publish_via_tcp)
139
+ end
140
+
141
+ def test_tcp_uses_correct_message_host_and_port
142
+ host = "http://meducation.net"
143
+ port = 1234
144
+ Propono.config.tcp_host = host
145
+ Propono.config.tcp_port = port
146
+ topic_id = "my-fav-topic"
147
+ message = "foobar"
148
+ TCPSocket.expects(:new).with(host, port)
149
+
150
+ publisher = Publisher.new(topic_id, message)
151
+ publisher.send(:publish_via_tcp)
152
+ end
153
+
154
+ def test_exception_from_tcpsocket_caught_and_logged
155
+ host = "http://meducation.net"
156
+ port = 1234
157
+ Propono.config.tcp_host = host
158
+ Propono.config.tcp_port = port
159
+
160
+ client = Publisher.new("topic_id", "message")
161
+ Propono.config.logger.expects(:error).with() {|x| x =~ /^Propono failed to send : getaddrinfo:.*/}
162
+ client.send(:publish_via_tcp)
163
+ end
164
+
165
+ def test_publish_should_raise_exception_if_topic_is_nil
166
+ assert_raises(PublisherError, "Topic is nil") do
167
+ Publisher.publish(nil, "foobar")
168
+ end
169
+ end
170
+
125
171
  def test_publish_should_raise_exception_if_message_is_nil
126
172
  assert_raises(PublisherError, "Message is nil") do
127
173
  Publisher.publish("foobar", nil)
@@ -0,0 +1,63 @@
1
+ require File.expand_path('../../test_helper', __FILE__)
2
+
3
+ module Propono
4
+ class TcpListenerTest < Minitest::Test
5
+
6
+ def test_intialize_sets_locals
7
+ block = Proc.new {}
8
+ listener = TcpListener.new(&block)
9
+ assert_equal block, listener.instance_variable_get("@processor")
10
+ end
11
+
12
+ def test_server_is_setup_correctly
13
+ port = 1234
14
+ Propono.config.tcp_port = port
15
+
16
+ TCPServer.expects(:open).with(port)
17
+
18
+ listener = TcpListener.new() {}
19
+ server = listener.send(:server)
20
+ end
21
+
22
+ def test_initialize_should_fail_without_a_block
23
+ assert_raises(TcpListenerError) do
24
+ TcpListener.new
25
+ end
26
+ end
27
+
28
+ def test_message_is_processed
29
+ tcp_msg = "Foobar"
30
+ processor = Proc.new {}
31
+ listener = TcpListener.new(&processor)
32
+ client = mock()
33
+ client.expects(:recvfrom => [tcp_msg])
34
+ client.expects(:close)
35
+
36
+ server = mock()
37
+ server.expects(accept: client)
38
+
39
+ listener.stubs(server: server)
40
+ listener.expects(:process_tcp_data).with(tcp_msg)
41
+ thread = listener.send(:receive_and_process)
42
+ thread.join
43
+ end
44
+
45
+ def test_processor_is_called_correctly
46
+ topic = "my-topic"
47
+ message = "my-message"
48
+ processor = Proc.new {}
49
+ tcp_data = {topic: topic, message: message}.to_json
50
+ processor.expects(:call).with(topic, message)
51
+
52
+ listener = TcpListener.new(&processor)
53
+ listener.send(:process_tcp_data, tcp_data)
54
+ end
55
+
56
+ def test_listen_should_loop
57
+ listener = TcpListener.new {}
58
+ listener.expects(:loop)
59
+ listener.listen
60
+ end
61
+ end
62
+ end
63
+
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: propono
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.3
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - MalcyL
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-10-20 00:00:00.000000000 Z
12
+ date: 2013-10-23 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: fog
@@ -43,42 +43,42 @@ dependencies:
43
43
  name: rake
44
44
  requirement: !ruby/object:Gem::Requirement
45
45
  requirements:
46
- - - '>='
46
+ - - ! '>='
47
47
  - !ruby/object:Gem::Version
48
48
  version: '0'
49
49
  type: :development
50
50
  prerelease: false
51
51
  version_requirements: !ruby/object:Gem::Requirement
52
52
  requirements:
53
- - - '>='
53
+ - - ! '>='
54
54
  - !ruby/object:Gem::Version
55
55
  version: '0'
56
56
  - !ruby/object:Gem::Dependency
57
57
  name: mocha
58
58
  requirement: !ruby/object:Gem::Requirement
59
59
  requirements:
60
- - - '>='
60
+ - - ! '>='
61
61
  - !ruby/object:Gem::Version
62
62
  version: '0'
63
63
  type: :development
64
64
  prerelease: false
65
65
  version_requirements: !ruby/object:Gem::Requirement
66
66
  requirements:
67
- - - '>='
67
+ - - ! '>='
68
68
  - !ruby/object:Gem::Version
69
69
  version: '0'
70
70
  - !ruby/object:Gem::Dependency
71
71
  name: yard
72
72
  requirement: !ruby/object:Gem::Requirement
73
73
  requirements:
74
- - - '>='
74
+ - - ! '>='
75
75
  - !ruby/object:Gem::Version
76
76
  version: '0'
77
77
  type: :development
78
78
  prerelease: false
79
79
  version_requirements: !ruby/object:Gem::Requirement
80
80
  requirements:
81
- - - '>='
81
+ - - ! '>='
82
82
  - !ruby/object:Gem::Version
83
83
  version: '0'
84
84
  - !ruby/object:Gem::Dependency
@@ -125,6 +125,7 @@ files:
125
125
  - lib/propono/services/queue_creator.rb
126
126
  - lib/propono/services/queue_listener.rb
127
127
  - lib/propono/services/subscriber.rb
128
+ - lib/propono/services/tcp_listener.rb
128
129
  - lib/propono/services/topic_creator.rb
129
130
  - lib/propono/services/udp_listener.rb
130
131
  - lib/propono/version.rb
@@ -139,6 +140,7 @@ files:
139
140
  - test/configuration_test.rb
140
141
  - test/integration/integration_test.rb
141
142
  - test/integration/sns_to_sqs_test.rb
143
+ - test/integration/tcp_to_sqs_test.rb
142
144
  - test/integration/udp_proxy_test.rb
143
145
  - test/integration/udp_to_sqs_test.rb
144
146
  - test/logger_test.rb
@@ -147,6 +149,7 @@ files:
147
149
  - test/services/queue_creator_test.rb
148
150
  - test/services/queue_listener_test.rb
149
151
  - test/services/subscriber_test.rb
152
+ - test/services/tcp_listener_test.rb
150
153
  - test/services/topic_creator_test.rb
151
154
  - test/services/udp_listener_test.rb
152
155
  - test/test_helper.rb
@@ -160,12 +163,12 @@ require_paths:
160
163
  - lib
161
164
  required_ruby_version: !ruby/object:Gem::Requirement
162
165
  requirements:
163
- - - '>='
166
+ - - ! '>='
164
167
  - !ruby/object:Gem::Version
165
168
  version: '0'
166
169
  required_rubygems_version: !ruby/object:Gem::Requirement
167
170
  requirements:
168
- - - '>='
171
+ - - ! '>='
169
172
  - !ruby/object:Gem::Version
170
173
  version: '0'
171
174
  requirements: []
@@ -185,6 +188,7 @@ test_files:
185
188
  - test/configuration_test.rb
186
189
  - test/integration/integration_test.rb
187
190
  - test/integration/sns_to_sqs_test.rb
191
+ - test/integration/tcp_to_sqs_test.rb
188
192
  - test/integration/udp_proxy_test.rb
189
193
  - test/integration/udp_to_sqs_test.rb
190
194
  - test/logger_test.rb
@@ -193,6 +197,7 @@ test_files:
193
197
  - test/services/queue_creator_test.rb
194
198
  - test/services/queue_listener_test.rb
195
199
  - test/services/subscriber_test.rb
200
+ - test/services/tcp_listener_test.rb
196
201
  - test/services/topic_creator_test.rb
197
202
  - test/services/udp_listener_test.rb
198
203
  - test/test_helper.rb