UNPKG

14.8 kBJavaScriptView Raw
1require('./common.js');
2var LastFmSession = require('../lib/lastfm/lastfm-session');
3var LastFmUpdate = require('../lib/lastfm/lastfm-update');
4var fakes = require("./fakes");
5
6(function() {
7 describe("new LastFmUpdate")
8 it("can have success and error handlers specified in option at creation", function() {
9 var gently = new Gently();
10 var lastfm = new LastFmNode();
11 var update = new LastFmUpdate(lastfm, "method", new LastFmSession(lastfm, "user", "key"), { handlers: {
12 error: gently.expect(function error() {}),
13 success: gently.expect(function success() {})
14 }});
15 update.emit("error");
16 update.emit("success");
17 });
18})();
19
20(function() {
21 var request, returndata, options, session, method, gently, lastfm, authorisedSession, errorCode, errorMessage, update;
22
23 function setupFixture() {
24 request = new fakes.LastFmRequest();
25 returndata;
26 options = {};
27 session = null;
28 method = "";
29 gently = new Gently();
30 lastfm = new LastFmNode();
31 authorisedSession = new LastFmSession(lastfm, "user", "key");
32 errorCode = -1;
33 errorMessage = null;
34 update = undefined;
35 }
36
37 function whenRequestReturns(data) {
38 errorCode = -1;
39 errorMessage = null;
40 returndata = JSON.parse(data);
41 request = new fakes.LastFmRequest();
42 gently.expect(lastfm, "request", function() {
43 return request;
44 });
45 }
46
47 function whenRequestThrowsError(code, message) {
48 errorCode = code;
49 errorMessage = message;
50 request = new fakes.LastFmRequest();
51 gently.expect(lastfm, "request", function() {
52 return request;
53 });
54 }
55
56 function andOptionsAre(setOptions) {
57 options = setOptions;
58 }
59
60 function andMethodIs(setMethod) {
61 method = setMethod;
62 }
63
64 function andSessionIs(setSession) {
65 session = setSession;
66 }
67
68 function expectSuccess(assertions) {
69 var checkSuccess = function(track) {
70 if (assertions) {
71 assertions(track);
72 }
73 };
74 if (update) {
75 update.on("success", checkSuccess);
76 }
77 else {
78 options.handlers = options.handlers || {};
79 options.handlers.success = checkSuccess;
80 }
81 doUpdate();
82 }
83
84 function expectError(errorCode, expectedError) {
85 var checkError = function(error) {
86 if (errorCode || expectedError) {
87 assert.equal(expectedError, error.message);
88 assert.equal(errorCode, error.error);
89 }
90 };
91 if (update) {
92 update.on("error", checkError);
93 }
94 else {
95 options.handlers = options.handlers || {};
96 options.handlers.error = gently.expect(checkError);
97 }
98 doUpdate();
99 }
100
101 function doNotExpectError() {
102 options.handlers = options.handlers || {};
103 options.handlers.error = function checkNoErrorThrown(error) {
104 assert.ok(false);
105 };
106 doUpdate();
107 }
108
109 function expectRetry(callback) {
110 callback = callback || function() { };
111 if (update) {
112 gently.expect(update, "emit", function(event, retry) {
113 assert.equal(event, "retrying");
114 callback(retry);
115 });
116 }
117 else {
118 options.handlers = options.handlers || { };
119 options.handlers.retrying = gently.expect(callback);
120 }
121 doUpdate();
122 }
123
124 function doUpdate() {
125 update = update || new LastFmUpdate(lastfm, method, session, options);
126 if (errorMessage) {
127 request.emit("error", { error: errorCode, message: errorMessage });
128 }
129 else {
130 request.emit("success", returndata);
131 }
132 }
133
134 describe("update requests")
135 before(function() {
136 setupFixture();
137 });
138
139 it("fail when the session is not authorised", function() {
140 var session = new LastFmSession()
141 , update = new LastFmUpdate(lastfm, "method", session, {
142 handlers: {
143 error: gently.expect(function(error) {
144 assert.equal(error.error, 4);
145 assert.equal(error.message, "Authentication failed");
146 })
147 }
148 });
149 });
150
151 describe("nowPlaying updates")
152 before(function() {
153 setupFixture();
154 });
155
156 it("uses updateNowPlaying method", function() {
157 gently.expect(lastfm, "request", function(method, params) {
158 assert.equal("track.updateNowPlaying", method);
159 return request;
160 });
161 new LastFmUpdate(lastfm, "nowplaying", authorisedSession, {
162 track: FakeTracks.RunToYourGrave
163 });
164 });
165
166 it("sends required parameters", function() {
167 gently.expect(lastfm, "request", function(method, params) {
168 assert.equal(FakeTracks.RunToYourGrave, params.track);
169 assert.equal("key", params.sk);
170 return request;
171 });
172 new LastFmUpdate(lastfm, "nowplaying", authorisedSession, {
173 track: FakeTracks.RunToYourGrave
174 });
175 });
176
177 it("emits success when updated", function() {
178 whenRequestReturns(FakeData.UpdateNowPlayingSuccess);
179 andMethodIs("nowplaying");
180 andSessionIs(authorisedSession);
181 andOptionsAre({
182 track: FakeTracks.RunToYourGrave
183 });
184 expectSuccess(function(track) {
185 assert.equal("Run To Your Grave", track.name);
186 });
187 });
188
189 it("sends duration when supplied", function() {
190 gently.expect(lastfm, "request", function(method, params) {
191 assert.equal(232000, params.duration);
192 return request;
193 });
194 new LastFmUpdate(lastfm, "nowplaying", authorisedSession, {
195 track: FakeTracks.RunToYourGrave,
196 duration: 232000
197 });
198 });
199
200 it("can have artist and track string parameters supplied", function() {
201 gently.expect(lastfm, "request", function(method, params) {
202 assert.equal("The Mae Shi", params.artist);
203 assert.equal("Run To Your Grave", params.track);
204 assert.equal("key", params.sk);
205 return request;
206 });
207 new LastFmUpdate(lastfm, "nowplaying", authorisedSession, {
208 track: "Run To Your Grave",
209 artist: "The Mae Shi"
210 });
211 });
212
213 it("bubbles up errors", function() {
214 var errorMessage = "Bubbled error";
215 whenRequestThrowsError(100, errorMessage);
216 andMethodIs("nowplaying");
217 andSessionIs(authorisedSession);
218 andOptionsAre({
219 track: FakeTracks.RunToYourGrave,
220 timestamp: 12345678
221 });
222 expectError(100, errorMessage);
223 });
224
225 describe("a scrobble request")
226 before(function() {
227 setupFixture();
228 });
229
230 it("emits error when no timestamp supplied", function() {
231 new LastFmUpdate(lastfm, "scrobble", authorisedSession, {
232 track: FakeTracks.RunToYourGrave,
233 handlers: {
234 error: gently.expect(function error(error) {
235 assert.equal(6, error.error);
236 assert.equal("Invalid parameters - Timestamp is required for scrobbling", error.message);
237 })
238 }
239 });
240 });
241
242 it("uses scrobble method", function() {
243 gently.expect(lastfm, "request", function(method, params) {
244 assert.equal("track.scrobble", method);
245 return request;
246 });
247 new LastFmUpdate(lastfm, "scrobble", authorisedSession, {
248 track: FakeTracks.RunToYourGrave,
249 timestamp: 12345678
250 });
251 });
252
253 it("sends required parameters", function() {
254 gently.expect(lastfm, "request", function(method, params) {
255 assert.equal(FakeTracks.RunToYourGrave, params.track);
256 assert.equal("key", params.sk);
257 assert.equal(12345678, params.timestamp);
258 return request;
259 });
260
261 new LastFmUpdate(lastfm, "scrobble", authorisedSession, {
262 track: FakeTracks.RunToYourGrave,
263 timestamp: 12345678
264 });
265 });
266
267 it("emits success when updated", function() {
268 whenRequestReturns(FakeData.ScrobbleSuccess);
269 andMethodIs("scrobble");
270 andSessionIs(authorisedSession);
271 andOptionsAre({
272 track: FakeTracks.RunToYourGrave,
273 timestamp: 12345678
274 });
275 expectSuccess(function(track) {
276 assert.equal("Run To Your Grave", track.name);
277 });
278 });
279
280 it("bubbles up errors", function() {
281 var errorMessage = "Bubbled error";
282 whenRequestThrowsError(100, errorMessage);
283 andMethodIs("scrobble");
284 andSessionIs(authorisedSession);
285 andOptionsAre({
286 track: FakeTracks.RunToYourGrave,
287 timestamp: 12345678
288 });
289 expectError(100, errorMessage);
290 });
291
292 it("can have artist and track string parameters supplied", function() {
293 gently.expect(lastfm, "request", function(method, params) {
294 assert.equal("The Mae Shi", params.artist);
295 assert.equal("Run To Your Grave", params.track);
296 assert.equal("key", params.sk);
297 return request;
298 });
299 new LastFmUpdate(lastfm, "scrobble", authorisedSession, {
300 track: "Run To Your Grave",
301 artist: "The Mae Shi",
302 timestamp: 12345678
303 });
304 });
305
306 it("can have arbitrary parameters supplied", function() {
307 gently.expect(lastfm, "request", function(method, params) {
308 assert.equal("somevalue", params.arbitrary);
309 return request;
310 });
311 new LastFmUpdate(lastfm, "scrobble", authorisedSession, {
312 track: "Run To Your Grave",
313 artist: "The Mae Shi",
314 timestamp: 12345678,
315 arbitrary: "somevalue"
316 });
317 });
318
319 it("does not include handler parameters", function() {
320 gently.expect(lastfm, "request", function(method, params) {
321 assert.equal(undefined, params.handlers);
322 assert.equal(undefined, params.error);
323 assert.equal(undefined, params.success);
324 return request;
325 });
326 new LastFmUpdate(lastfm, "scrobble", authorisedSession, {
327 track: "Run To Your Grave",
328 artist: "The Mae Shi",
329 timestamp: 12345678,
330 handlers: { success: function() { } },
331 success: function() { },
332 error: function() { }
333 });
334 });
335
336 var tmpFn;
337 describe("update retries")
338 before(function() {
339 tmpFn = LastFmUpdate.prototype.scheduleCallback;
340 LastFmUpdate.prototype.scheduleCallback = function(callback, delay) { };
341 setupFixture();
342 andMethodIs("scrobble");
343 andSessionIs(authorisedSession);
344 andOptionsAre({
345 track: FakeTracks.RunToYourGrave,
346 timestamp: 12345678
347 });
348 });
349
350 after(function() {
351 LastFmUpdate.prototype.scheduleCallback = tmpFn;
352 });
353
354 it("a error which should trigger a retry does not bubble errors", function() {
355 whenRequestThrowsError(11, "Service Offline");
356 doNotExpectError();
357 });
358
359 it("service offline triggers a retry", function() {
360 whenRequestThrowsError(11, "Service Offline");
361 expectRetry();
362 });
363
364 it("rate limit exceeded triggers a retry", function() {
365 whenRequestThrowsError(29, "Rate limit exceeded");
366 expectRetry();
367 });
368
369 it("temporarily unavailable triggers a retry", function() {
370 whenRequestThrowsError(16, "Temporarily unavailable");
371 expectRetry();
372 });
373
374 it("nowplaying update never trigger retries", function() {
375 whenRequestThrowsError(16, "Temporarily unavailable");
376 andMethodIs("nowplaying");
377 expectError();
378 });
379
380 it("first retry schedules a request after a 10 second delay", function() {
381 whenRequestThrowsError(16, "Temporarily unavailable");
382 LastFmUpdate.prototype.scheduleCallback = gently.expect(function testSchedule(callback, delay) {
383 assert.equal(delay, 10000);
384 });
385 doUpdate();
386 });
387
388 function onNextRequests(callback, count) {
389 count = count || 1;
390 var gently = new Gently();
391 LastFmUpdate.prototype.scheduleCallback = gently.expect(count, callback);
392 doUpdate();
393 }
394
395 function lastRequest() {
396 LastFmUpdate.prototype.scheduleCallback = function() { };
397 }
398
399 function whenNextRequestThrowsError(request, code, message) {
400 whenRequestThrowsError(code, message);
401 request();
402 }
403
404 function whenNextRequestReturns(request, data) {
405 whenRequestReturns(data);
406 request();
407 }
408
409 it("retry triggers another request", function() {
410 whenRequestThrowsError(16, "Temporarily unavailable");
411 onNextRequests(function(nextRequest) {
412 lastRequest();
413 whenNextRequestThrowsError(nextRequest, 16, "Temporarily unavailable");
414 expectRetry();
415 });
416 });
417
418 it("emits succes if retry is successful", function() {
419 whenRequestThrowsError(16, "Temporarily unavailable");
420 onNextRequests(function(nextRequest) {
421 whenNextRequestReturns(nextRequest, FakeData.ScrobbleSuccess);
422 expectSuccess(function(track) {
423 assert.equal("Run To Your Grave", track.name);
424 });
425 });
426 });
427
428 it("emits succes if retry is non-retry error", function() {
429 whenRequestThrowsError(16, "Temporarily unavailable");
430 onNextRequests(function(nextRequest) {
431 whenNextRequestThrowsError(nextRequest, 6, "Invalid parameter");
432 expectError(6, "Invalid parameter");
433 });
434 });
435
436 it("retrying events include error received and delay details", function() {
437 whenRequestThrowsError(16, "Temporarily unavailable");
438 expectRetry(function(retry) {
439 assert.equal(retry.delay, 10000);
440 assert.equal(retry.error, 16);
441 assert.equal(retry.message, "Temporarily unavailable");
442 });
443 });
444
445 var retrySchedule = [
446 10 * 1000,
447 30 * 1000,
448 60 * 1000,
449 5 * 60 * 1000,
450 15 * 60 * 1000,
451 30 * 60 * 1000,
452 30 * 60 * 1000,
453 30 * 60 * 1000
454 ];
455
456 it("follows a retry schedule on subsequent failures", function() {
457 var count = 0;
458 whenRequestThrowsError(16, "Temporarily unavailable");
459 onNextRequests(function(nextRequest, delay) {
460 var expectedDelay = retrySchedule[count++];
461 assert.equal(delay, expectedDelay);
462 if (count >= retrySchedule.length) {
463 lastRequest();
464 }
465 whenNextRequestThrowsError(nextRequest, 16, "Temporarily unavailable");
466 expectRetry();
467 }, retrySchedule.length);
468 });
469
470 it("includes delay in subsequent retry events", function() {
471 var count = 0;
472 whenRequestThrowsError(16, "Temporarily unavailable");
473 onNextRequests(function(nextRequest, delay) {
474 count++;
475 if (count >= retrySchedule.length) {
476 lastRequest();
477 }
478 var expectedDelay = retrySchedule[Math.min(count, retrySchedule.length - 1)];
479 whenNextRequestThrowsError(nextRequest, 16, "Temporarily unavailable");
480 expectRetry(function(retry) {
481 assert.equal(retry.delay, expectedDelay);
482 });
483 }, retrySchedule.length);
484 });
485})();