1 | should = require 'should'
|
2 | _ = require 'lodash'
|
3 | logger = require 'torch'
|
4 | {focus} = require 'qi'
|
5 | {join} = require 'path'
|
6 |
|
7 | bus = require '../lib/bus'
|
8 | core = require '../lib/core'
|
9 |
|
10 | mockRetriever = require './helpers/mockRetriever'
|
11 |
|
12 | describe 'core.request', ->
|
13 | beforeEach (done) ->
|
14 |
|
15 | core.init {timeout: 20}, mockRetriever()
|
16 | @moduleName = 'server'
|
17 | @serviceName = 'start'
|
18 | @channel = "#{@moduleName}.#{@serviceName}"
|
19 | @data =
|
20 | x: 2
|
21 | y: 'hello'
|
22 |
|
23 | done()
|
24 |
|
25 | afterEach ->
|
26 | core.reset()
|
27 |
|
28 | it 'should receive exactly one valid response', (done) ->
|
29 | core.respond @channel, (message, done) ->
|
30 | done null, message
|
31 |
|
32 | core.request @channel, @data, (err, message) =>
|
33 | should.not.exist err
|
34 |
|
35 | should.exist message
|
36 | message.should.eql @data
|
37 |
|
38 | done()
|
39 |
|
40 | it 'should pass an error to the callback the response returns one', (done) ->
|
41 | testError = new Error 'testError'
|
42 | bus.subscribe
|
43 | channel: @channel
|
44 | topic: 'request.#'
|
45 | callback: (message, envelope) ->
|
46 | bus.publish
|
47 | channel: envelope.replyTo.channel
|
48 | topic: envelope.replyTo.topic.err
|
49 | data: testError
|
50 |
|
51 | core.request @channel, @data, (err, message) =>
|
52 | should.exist err
|
53 | should.not.exist message
|
54 |
|
55 | err.should.eql testError
|
56 |
|
57 | done()
|
58 |
|
59 | it 'should invoke the success callback exactly once, then discard all', (done) ->
|
60 | core.respond @channel, (message, finished) =>
|
61 | finished()
|
62 |
|
63 | test = (err, data) ->
|
64 | bus.publish
|
65 | channel: @channel
|
66 | topic: replyTo.topic.err
|
67 | data: new Error 'should not see'
|
68 | done()
|
69 |
|
70 | replyTo = core.request @channel, @data, test
|
71 |
|
72 | it 'should return a timeout error when it times out', (done) ->
|
73 | core.respond @channel, ->
|
74 | core.request @channel, @data, (err, result) =>
|
75 | should.exist err
|
76 | err.message.should.eql "Request timed out on channel '#{@channel}'"
|
77 | should.not.exist result
|
78 | done()
|
79 |
|
80 | it 'should return immediately if there are no listeners', (done) ->
|
81 | core.request @channel, @data, (err, result) =>
|
82 | should.exist err
|
83 | expectedMsg = "No responders for request: '#{@channel}'"
|
84 | err.message.should.eql expectedMsg
|
85 |
|
86 | should.not.exist result
|
87 |
|
88 | done()
|
89 |
|
90 |
|
91 | describe 'core.response', ->
|
92 | afterEach ->
|
93 | core.reset()
|
94 |
|
95 | beforeEach (done) ->
|
96 |
|
97 | core.init {timeout: 20}, mockRetriever()
|
98 | @channel = 'testChannel'
|
99 | @data =
|
100 | x: 2
|
101 | y: 'hello'
|
102 | done()
|
103 |
|
104 | it 'should send exactly one valid response', (done) ->
|
105 | echo = (message, next) -> next null, message
|
106 | core.respond @channel, echo
|
107 |
|
108 | bus.subscribe
|
109 | channel: @channel
|
110 | topic: "success.123"
|
111 | callback: (message) =>
|
112 | should.exist message
|
113 | message.should.eql @data
|
114 | done()
|
115 |
|
116 | bus.publish
|
117 | channel: @channel
|
118 | topic: "request.123"
|
119 | data: @data
|
120 | replyTo:
|
121 | channel: @channel
|
122 | topic:
|
123 | success: "success.123"
|
124 |
|
125 |
|
126 | describe 'core.delegate', ->
|
127 | afterEach ->
|
128 | core.reset()
|
129 |
|
130 | beforeEach (done) ->
|
131 | core.init {timeout: 20}, mockRetriever()
|
132 | done()
|
133 |
|
134 | it 'should should return if there are no responders', (done) ->
|
135 | channel = 'testChannel'
|
136 |
|
137 | core.delegate channel, {}, (err, results) ->
|
138 | should.not.exist err
|
139 | done()
|
140 |
|
141 | it 'responders on another channel should not interfere', (done) ->
|
142 | channel = 'testChannel'
|
143 |
|
144 | core.respond 'fooChannel', (message, next) ->
|
145 | next null, {helloFrom: 'fooChannel'}
|
146 |
|
147 | core.delegate channel, {}, (err, results) ->
|
148 | should.not.exist err
|
149 | done()
|
150 |
|
151 | it 'should should receive multiple responses', (done) ->
|
152 | channel = 'testChannel'
|
153 |
|
154 | core.respond channel, (message, next) ->
|
155 | next null, {helloFrom: 'responderA'}
|
156 |
|
157 | core.respond channel, (message, next) ->
|
158 | next null, {helloFrom: 'responderB'}
|
159 |
|
160 | core.delegate channel, {}, (err, results) ->
|
161 | should.not.exist err
|
162 | should.exist results
|
163 |
|
164 | values = _.values results
|
165 | should.exist values
|
166 |
|
167 | data = _.pluck values, 'data'
|
168 | should.exist data
|
169 |
|
170 | data.should.eql [
|
171 | { helloFrom: 'responderA' }
|
172 | { helloFrom: 'responderB' }
|
173 | ]
|
174 |
|
175 | done()
|
176 |
|
177 | it 'should return an err when a responder returns one', (done) ->
|
178 | channel = 'testChannel'
|
179 |
|
180 | testResponse = {message: 'this works'}
|
181 | core.respond channel, (message, next) ->
|
182 | next null, testResponse
|
183 |
|
184 | testError = new Error 'Expect this error'
|
185 | core.respond channel, (message, next) ->
|
186 | next testError, {}
|
187 |
|
188 | core.delegate channel, {}, (err, results) ->
|
189 | should.exist err
|
190 | expectedMsg = "Errors returned by responders on channel '#{channel}'"
|
191 | err.message.should.eql expectedMsg
|
192 |
|
193 | should.exist err.errors
|
194 | subErrors = (e.err for e in _.values err.errors)
|
195 | should.exist subErrors
|
196 | [subErr] = subErrors
|
197 | should.exist subErr
|
198 | subErr.should.eql testError
|
199 |
|
200 | should.exist results
|
201 | values = _.values results
|
202 | should.exist values
|
203 | [result] = values
|
204 | should.exist result
|
205 | result.data.should.eql testResponse
|
206 |
|
207 | done()
|
208 |
|
209 | it 'should return a timeout err when an implied request times out', (done) ->
|
210 | channel = 'testChannel'
|
211 |
|
212 | wontTimeOutMsg = {message: "I won't time out"}
|
213 | core.respond channel, (message, next) ->
|
214 | next null, wontTimeOutMsg
|
215 |
|
216 |
|
217 | core.respond channel, (message, next) ->
|
218 |
|
219 |
|
220 | core.delegate channel, {}, (err, results) ->
|
221 | should.exist err
|
222 | expectedMsg = "Errors returned by responders on channel '#{channel}'"
|
223 | err.message.should.eql expectedMsg
|
224 |
|
225 | should.exist err.errors
|
226 |
|
227 | [responderId] = _.keys err.errors
|
228 | should.exist responderId
|
229 |
|
230 | subErr = err.errors[responderId]?.err
|
231 | should.exist subErr
|
232 |
|
233 | errMsg = "Responder with id #{responderId} timed out on channel '#{channel}'"
|
234 | subErr.message.should.eql errMsg
|
235 |
|
236 | should.exist results
|
237 | [result] = _.values results
|
238 | should.exist result
|
239 | should.exist result.data
|
240 | result.data.should.eql wontTimeOutMsg
|
241 |
|
242 | done()
|
243 |
|
244 | describe 'core.listen', ->
|
245 | afterEach ->
|
246 | core.reset()
|
247 |
|
248 | beforeEach (done) ->
|
249 |
|
250 | core.init {timeout: 20}, mockRetriever()
|
251 | @channelA = 'testChannelA'
|
252 | @channelB = 'testChannelB'
|
253 | @dataA =
|
254 | x: 2
|
255 | y: 'hello'
|
256 | @dataB =
|
257 | x: 111
|
258 | y: 'goodbye'
|
259 | @topicA = 'info.A'
|
260 | @topicB = 'info.B'
|
261 |
|
262 | done()
|
263 |
|
264 | it 'should listen with a standard-signature callback', (done) ->
|
265 | core.listen @channelA, @topicA, (err, result) =>
|
266 | should.not.exist err
|
267 | should.exist result
|
268 | {data} = result
|
269 | should.exist data
|
270 | data.should.eql @dataA
|
271 | done()
|
272 |
|
273 | bus.publish
|
274 | channel: @channelA
|
275 | data: @dataA
|
276 | topic: @topicA
|
277 |
|
278 | it 'should listen to two topics on one channel', (done) ->
|
279 | cb = focus (err, results) =>
|
280 | should.not.exist err
|
281 |
|
282 | should.exist results
|
283 |
|
284 | [resultA, resultB] = results
|
285 | should.exist resultA
|
286 | should.exist resultB
|
287 |
|
288 | dataA = resultA.data
|
289 | should.exist dataA
|
290 | dataA.should.eql @dataA
|
291 |
|
292 | dataB = resultB.data
|
293 | should.exist dataB
|
294 | dataB.should.eql @dataB
|
295 |
|
296 | done()
|
297 |
|
298 | core.listen @channelA, @topicA, cb()
|
299 |
|
300 | core.listen @channelA, @topicB, cb()
|
301 |
|
302 | bus.publish
|
303 | channel: @channelA
|
304 | data: @dataA
|
305 | topic: @topicA
|
306 |
|
307 | bus.publish
|
308 | channel: @channelA
|
309 | data: @dataB
|
310 | topic: @topicB
|
311 |
|
312 | it 'should listen to two topics on two channels', (done) ->
|
313 | cb = focus (err, results) =>
|
314 | should.not.exist err
|
315 |
|
316 | should.exist results
|
317 |
|
318 | [resultA, resultB] = results
|
319 | should.exist resultA
|
320 | should.exist resultB
|
321 |
|
322 | dataA = resultA.data
|
323 | should.exist dataA
|
324 | dataA.should.eql @dataA
|
325 |
|
326 | dataB = resultB.data
|
327 | should.exist dataB
|
328 | dataB.should.eql @dataB
|
329 |
|
330 | done()
|
331 |
|
332 | core.listen @channelA, @topicA, cb()
|
333 |
|
334 | core.listen @channelB, @topicB, cb()
|
335 |
|
336 | bus.publish
|
337 | channel: @channelA
|
338 | data: @dataA
|
339 | topic: @topicA
|
340 |
|
341 | bus.publish
|
342 | channel: @channelB
|
343 | data: @dataB
|
344 | topic: @topicB
|
345 |
|
346 | it 'should listen to any topic on one channel', (done) ->
|
347 | core.listen @channelA, '#', (err, result) =>
|
348 | should.not.exist err
|
349 | should.exist result
|
350 |
|
351 | {data} = result
|
352 | should.exist data
|
353 | data.should.eql @dataA
|
354 |
|
355 | done()
|
356 |
|
357 | bus.publish
|
358 | channel: @channelA
|
359 | data: @dataA
|
360 | topic: @topicA
|