Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge branch 'release/v0.8.0'

  • Loading branch information...
commit b94f77c05ab99afc4886fb9fd812c53d9667f1af 2 parents f78f58a + ad67488
@jammus authored
View
8 History.md
@@ -1,5 +1,13 @@
# Changelog
+## 0.8.0
+
+### Breaking changes
+* Removed old handler options which were deprecated in 0.6.0.
+
+### New features
+* Scrobble request which return error codes 11, 16 or 29 are automatically retried.
+
## 0.7.0
### Breaking changes
View
29 README.md
@@ -94,10 +94,6 @@ Options:
Default event handlers to attach to the request object on creation.
-- *lastPlayed*, *nowPlaying*, *scrobbled*, *stoppedPlaying*, *error*
-
- **Deprecated:** Event listeners.
-
Events:
- *lastPlayed(track)*
@@ -177,6 +173,8 @@ Valid methods are 'nowplaying' and 'scrobble'.
An authorised `LastFmSession` instance is required to make a successful update.
+If a scrobble request receives an 11 (service offline), 16 (temporarily unavailable) or 29 (rate limit exceeded) error code from Last.fm then the request is automatically retried until it is permanently rejected or accepted. The first retry attempt is made after 10 seconds with subsequent requests delayed by 30 seconds, 1 minute, 5 minutes, 15 minutes and then every 30 minutes.
+
Options:
Accepts all parameters used by track.updateNowPlaying and user.scrobble (see Last.Fm API) as well as:
@@ -193,20 +191,19 @@ Accepts all parameters used by track.updateNowPlaying and user.scrobble (see Las
Default event handlers to attach to the request object on creation.
-- *success*
-
- **Deprecated:** Listener for `success` event.
-
-- *error*
-
- **Deprecated:** Listener for `error` event.
-
Events:
- *success(track)*
Update request was successful.
+- *retrying(retry)*
+
+ Scrobble request was not successful but will be retried after a delay. Retry object contains the following properties:
+ `delay` - The time in milliseconds before the request will be retried.
+ `error` - The error code returned by the Last.fm API.
+ `message` - The error message returned by the Last.fm API.
+
- *error(track, error)*
Ruh-roh.
@@ -235,14 +232,6 @@ Options:
Params as specified in Last.fm API, eg user: "username"
-- *success*
-
- **Deprecated:** Listener for `success` event.
-
-- *error*
-
- **Deprecated:** Listener for `error` event.
-
Special cases:
When requesting track info the `track` param can be either the track name or a track object as returned by `RecentTracksStream`.
View
11 lib/lastfm/lastfm-base.js
@@ -18,6 +18,7 @@ LastFmBase.prototype.registerHandlers = function(handlers) {
});
};
+var defaultBlacklist = ["error", "success", "handlers"];
LastFmBase.prototype.filterParameters = function(parameters, blacklist) {
var filteredParams = {};
_(parameters).each(function(value, key) {
@@ -29,8 +30,16 @@ LastFmBase.prototype.filterParameters = function(parameters, blacklist) {
return filteredParams;
function isBlackListed(name) {
- return _(blacklist).include(name);
+ return _(defaultBlacklist).include(name) || _(blacklist).include(name);
}
};
+LastFmBase.prototype.scheduleCallback = function(callback, delay) {
+ return setTimeout(callback, delay);
+};
+
+LastFmBase.prototype.cancelCallback = function(identifier) {
+ clearTimeout(identifier);
+};
+
module.exports = LastFmBase;
View
8 lib/lastfm/lastfm-info.js
@@ -9,12 +9,6 @@ var LastFmInfo = function(lastfm, type, options) {
requestInfo(type, options);
function registerEventHandlers(options) {
- if (options.error) {
- that.on("error", options.error);
- }
- if (options.success) {
- that.on("success", options.success);
- }
that.registerHandlers(options.handlers);
}
@@ -24,7 +18,7 @@ var LastFmInfo = function(lastfm, type, options) {
return;
}
- var params = that.filterParameters(options, ["error", "success", "handlers"])
+ var params = that.filterParameters(options)
, method = type + ".getinfo"
, request = lastfm.request(method, params);
request.on("success", success);
View
6 lib/lastfm/lastfm-request.js
@@ -28,10 +28,6 @@ var LastFmRequest = module.exports = function(lastfm, method, params) {
sendRequest(lastfm.host, lastfm.url, params);
- function registerEventHandlers(handlers) {
- utils.registerHandlers(that, handlers);
- }
-
function sendRequest(host, url, params) {
var httpVerb = isWriteRequest() ? "POST" : "GET"
, port = 80
@@ -55,7 +51,7 @@ var LastFmRequest = module.exports = function(lastfm, method, params) {
}
function buildRequestParams(params) {
- var requestParams = that.filterParameters(params, ["handlers", "signed", "write", "error", "success"]);
+ var requestParams = that.filterParameters(params, ["signed", "write"]);
requestParams.method = method;
requestParams.api_key = requestParams.api_key || lastfm.api_key;
requestParams.format = requestParams.format || lastfm.format;
View
7 lib/lastfm/lastfm-session.js
@@ -27,13 +27,6 @@ var LastFmSession = function(lastfm, user, key) {
}
function registerEventHandlers(options) {
- if (options.error) {
- that.on("error", options.error);
- }
- if (options.authorised) {
- that.on("authorised", options.authorised);
- }
-
that.registerHandlers(options.handlers);
}
View
85 lib/lastfm/lastfm-update.js
@@ -1,49 +1,86 @@
var _ = require("underscore")
- , LastFmBase = require("./lastfm-base");
+ , LastFmBase = require("./lastfm-base")
+ , retryOnErrors = [
+ 11, // Service offline
+ 16, // Temporarily unavailable
+ 29 // Rate limit exceeded
+ ]
+ , retrySchedule = [
+ 10 * 1000, // 10 seconds
+ 30 * 1000, // 30 seconds
+ 60 * 1000, // 1 minute
+ 5 * 60 * 1000, // 5 minutes
+ 15 * 60 * 1000, // 15 minutes
+ 30 * 60 * 1000 // 30 minutes
+ ];
var LastFmUpdate = function(lastfm, method, session, options) {
var that = this;
- options = options || {};
+ options = options || { };
LastFmBase.call(this);
registerEventHandlers(options);
if (!session.isAuthorised()) {
- this.emit("error", new Error("Session is not authorised"));
+ this.emit("error", {
+ error: 4,
+ message: "Authentication failed"
+ });
return;
}
if (method !== "scrobble" && method !== "nowplaying") {
return;
}
-
update(method, options);
function registerEventHandlers(options) {
- if (options.error) {
- that.on("error", options.error);
- }
- if (options.success) {
- that.on("success", options.success);
- }
that.registerHandlers(options.handlers);
}
function update(method, options) {
if (method == "scrobble" && !options.timestamp) {
- that.emit("error", new Error("Timestamp is required for scrobbling"));
+ that.emit("error", {
+ error: 6,
+ message: "Invalid parameters - Timestamp is required for scrobbling"
+ });
return;
}
- var params = buildRequestParams(options),
- requestMethod = method == "scrobble" ? "track.scrobble" : "track.updateNowPlaying",
- request = lastfm.request(requestMethod, params);
- request.on("success", handleResponse);
- request.on("error", bubbleError);
- }
+ var retryCount = 0
+ , params = buildRequestParams(options)
+ , requestMethod = method == "scrobble" ? "track.scrobble" : "track.updateNowPlaying";
+ makeRequest();
+
+ function makeRequest() {
+ var request = lastfm.request(requestMethod, params);
+ request.on("error", errorCallback);
+ request.on("success", successCallback);
+ }
+
+ function successCallback(response) {
+ if (response) {
+ that.emit("success", options.track);
+ }
+ }
+
+ function errorCallback(error) {
+ if (shouldBeRetried(error)) {
+ var delay = delayFor(retryCount++)
+ , retry = {
+ error: error.error,
+ message: error.message,
+ delay: delay
+ };
+ that.emit("retrying", retry);
+ that.scheduleCallback(makeRequest, delay);
+ return;
+ }
+ bubbleError(error);
+ }
- function handleResponse(response) {
- if (!response) return;
- that.emit("success", options.track);
+ function shouldBeRetried(error) {
+ return method == "scrobble" && _(retryOnErrors).include(error.error)
+ }
}
function bubbleError(error) {
@@ -51,13 +88,17 @@ var LastFmUpdate = function(lastfm, method, session, options) {
}
function buildRequestParams(params) {
- var requestParams = that.filterParameters(params, ["error", "success", "handlers"]);
+ var requestParams = that.filterParameters(params);
requestParams.sk = session.key;
return requestParams;
}
+
+ function delayFor(retryCount) {
+ var index = Math.min(retryCount, retrySchedule.length - 1);
+ return retrySchedule[index];
+ }
}
LastFmUpdate.prototype = Object.create(LastFmBase.prototype);
module.exports = LastFmUpdate;
-
View
11 lib/lastfm/recenttracks-stream.js
@@ -31,11 +31,6 @@ var RecentTracksStream = module.exports = function(lastfm, user, options) {
function registerEventHandlers(options) {
that.registerHandlers(options.handlers);
-
- var events = ["error", "lastPlayed", "nowPlaying", "stoppedPlaying", "scrobbled"];
- events.forEach(function (event) {
- if (options[event]) that.on(event, options[event]);
- });
}
function start() {
@@ -49,7 +44,9 @@ var RecentTracksStream = module.exports = function(lastfm, user, options) {
});
request.on("success", handleSuccess);
request.on("error", bubbleError);
- if (isStreaming) timeout = setTimeout(check, rate * 1000);
+ if (isStreaming) {
+ timeout = that.scheduleCallback(check, rate * 1000);
+ }
}
function handleSuccess(data) {
@@ -106,7 +103,7 @@ var RecentTracksStream = module.exports = function(lastfm, user, options) {
}
function stop() {
- clearTimeout(timeout);
+ that.cancelCallback(timeout);
isStreaming = false;
}
};
View
2  package.json
@@ -1,7 +1,7 @@
{
"name": "lastfm",
"description": "Read and write to Last.fm",
- "version": "0.7.0",
+ "version": "0.8.0",
"author": "James Scott <jammus@gmail.com>",
"contributors": [
"Garrett Wilkin <garrett.wilkin@gmail.com> (http://geethink.com)"
View
10 tests/all.js
@@ -1,10 +0,0 @@
-require("./lastfm-node-test.js");
-require("./lastfm-recenttracks-stream-test.js");
-require("./lastfm-session-test.js");
-require("./lastfm-info-test.js");
-require("./lastfm-read-test.js");
-require("./lastfm-info-track-test.js");
-require("./lastfm-update-test.js");
-require("./lastfm-request-test.js");
-require("./lastfm-base-test.js");
-require("./lastfm-recenttracks-stream-slowtests.js");
View
1  tests/common.js
@@ -14,3 +14,4 @@ global.FakeTracks = require("./TestData.js").FakeTracks;
if (process.setMaxListeners) {
process.setMaxListeners(900);
}
+global.emptyFn = function() { };
View
13 tests/lastfm-base-test.js
@@ -58,10 +58,21 @@ var LastFmBase = require("lastfm/lastfm-base");
assert.notDeepEqual(copy, original);
});
- it("ignores blacklist of parameters", function() {
+ it("filteres out blacklisted parameters", function() {
var copy = lastfmBase.filterParameters(original, ["one", "three"]);
assert.equal(typeof copy.one, "undefined");
assert.equal(typeof copy.three, "undefined");
assert.equal(copy.two, 2);
});
+
+ it("automatically removed error, success, handler parameters", function() {
+ var copy = lastfmBase.filterParameters({
+ error: emptyFn,
+ success: emptyFn,
+ handlers: { }
+ });
+ assert.equal(typeof copy.error, "undefined");
+ assert.equal(typeof copy.success, "undefined");
+ assert.equal(typeof copy.handlers, "undefined");
+ });
})();
View
8 tests/lastfm-info-test.js
@@ -11,14 +11,6 @@ describe("a new info instance")
gently = new Gently();
});
- it("accepts listeners in options (deprecated)", function() {
- var handlers = { error: function() {}, success: function() {} };
- gently.expect(handlers, "error");
- gently.expect(handlers, "success");
- var info = new LastFmInfo(lastfm, "", handlers);
- info.emit("success");
- });
-
it("accepts listeners in handler options", function() {
var handlers = { error: function() {}, success: function() {} };
var options = { handlers: handlers };
View
32 tests/lastfm-recenttracks-stream-slowtests.js
@@ -1,32 +0,0 @@
-require("./common.js");
-
-var _ = require("underscore")
- , RecentTracksStream = require("lastfm/recenttracks-stream")
- , fakes = require("./fakes");
-
-(function() {
- var lastfm, gently, request;
-
- describe("Streaming")
-
- before(function() {
- lastfm = new LastFmNode();
- gently = new Gently();
- request = new fakes.LastFmRequest();
- });
-
- it("queries API every 10 seconds", function() {
- var trackStream = new RecentTracksStream(lastfm, "username");
- var timestamps = [];
- gently.expect(lastfm, "request", 2, function(method, params) {
- timestamps.push((new Date).getTime());
- if (timestamps.length === 2) {
- trackStream.stop();
- var delay = timestamps[1] - timestamps[0];
- assert.ok(delay >= (10000 - 50) && delay <= (10000 + 50));
- }
- return request;
- });
- trackStream.start();
- });
-})();
View
61 tests/lastfm-recenttracks-stream-test.js
@@ -24,30 +24,6 @@ var _ = require("underscore")
assert.ok(!trackStream.isStreaming());
});
- it("event handlers can be specified in options (deprecated)", function() {
- var handlers = {};
-
- gently.expect(handlers, "error");
- gently.expect(handlers, "lastPlayed");
- gently.expect(handlers, "nowPlaying");
- gently.expect(handlers, "stoppedPlaying");
- gently.expect(handlers, "scrobbled");
-
- var trackStream = new RecentTracksStream(lastfm, "username", {
- error: handlers.error,
- lastPlayed: handlers.lastPlayed,
- nowPlaying: handlers.nowPlaying,
- stoppedPlaying: handlers.stoppedPlaying,
- scrobbled: handlers.scrobbled
- });
-
- trackStream.emit("error");
- trackStream.emit("lastPlayed");
- trackStream.emit("nowPlaying");
- trackStream.emit("stoppedPlaying");
- trackStream.emit("scrobbled");
- });
-
it("event handlers can be specified in options", function() {
var handlers = {};
@@ -309,3 +285,40 @@ var _ = require("underscore")
trackStream.stop();
});
})();
+
+(function() {
+ var lastfm, gently;
+
+ describe("Streaming")
+
+ var tmpScheduleFn;
+ before(function() {
+ tmpScheduleFn = RecentTracksStream.prototype.scheduleCallback;
+ lastfm = new LastFmNode();
+ gently = new Gently();
+ });
+
+ after(function() {
+ RecentTracksStream.prototype.scheduleCallback = tmpScheduleFn;
+ });
+
+ it("queries API every 10 seconds", function() {
+ var trackStream = new RecentTracksStream(lastfm, "username");
+ var count = 0;
+ RecentTracksStream.prototype.scheduleCallback = function(callback, delay) {
+ count++;
+ if (count === 10) {
+ trackStream.stop();
+ }
+ assert.ok(delay, 10000);
+ gently.expect(lastfm, "request", function(method, params) {
+ return new fakes.LastFmRequest();
+ });
+ callback();
+ };
+ gently.expect(lastfm, "request", function(method, params) {
+ return new fakes.LastFmRequest();
+ });
+ trackStream.start();
+ });
+})();
View
20 tests/lastfm-session-test.js
@@ -147,16 +147,6 @@ var fakes = require("./fakes");
}});
});
- it("can have error handler specified with authorise call (deprecated)", function() {
- var handler = { error: function(error) { } };
- gently.expect(handler, "error", function(error) {
- assert.equal("No token supplied", error.message);
- });
- session.authorise("", {
- error: handler.error
- });
- });
-
it("updates session key and user when successful", function() {
whenReadRequestReturns(FakeData.SuccessfulAuthorisation);
andTokenIs("token");
@@ -177,16 +167,6 @@ var fakes = require("./fakes");
request.emit("success", returndata);
});
- it("can have authorised handler specified with authorise call (deprecated)", function() {
- var handler = { authorised: function(session) { } };
- whenReadRequestReturns(FakeData.SuccessfulAuthorisation);
- gently.expect(handler, "authorised");
- session.authorise("token", {
- authorised: handler.authorised
- });
- request.emit("success", returndata);
- });
-
it("bubbles up errors", function() {
var errorMessage = "Bubbled error";
whenReadRequestThrowsError(errorMessage);
View
273 tests/lastfm-update-test.js
@@ -5,17 +5,6 @@ var fakes = require("./fakes");
(function() {
describe("new LastFmUpdate")
- it("can have success and error handlers specified at creation (deprecated)", function() {
- var gently = new Gently();
- var lastfm = new LastFmNode();
- var update = new LastFmUpdate(lastfm, "method", new LastFmSession(lastfm, "user", "key"), {
- error: gently.expect(function error() {}),
- success: gently.expect(function success() {})
- });
- update.emit("error");
- update.emit("success");
- });
-
it("can have success and error handlers specified in option at creation", function() {
var gently = new Gently();
var lastfm = new LastFmNode();
@@ -29,7 +18,7 @@ var fakes = require("./fakes");
})();
(function() {
- var request, returndata, options, session, method, gently, lastfm, authorisedSession, requestError;
+ var request, returndata, options, session, method, gently, lastfm, authorisedSession, errorCode, errorMessage, update;
function setupFixture() {
request = new fakes.LastFmRequest();
@@ -40,19 +29,26 @@ var fakes = require("./fakes");
gently = new Gently();
lastfm = new LastFmNode();
authorisedSession = new LastFmSession(lastfm, "user", "key");
- requestError = null;
+ errorCode = -1;
+ errorMessage = null;
+ update = undefined;
}
- function whenWriteRequestReturns(data) {
+ function whenRequestReturns(data) {
+ errorCode = -1;
+ errorMessage = null;
returndata = JSON.parse(data);
- gently.expect(lastfm, "request", function(method, params) {
+ request = new fakes.LastFmRequest();
+ gently.expect(lastfm, "request", function() {
return request;
});
}
- function whenWriteRequestThrowsError(errorMessage) {
- requestError = errorMessage;
- gently.expect(lastfm, "request", function(method, params) {
+ function whenRequestThrowsError(code, message) {
+ errorCode = code;
+ errorMessage = message;
+ request = new fakes.LastFmRequest();
+ gently.expect(lastfm, "request", function() {
return request;
});
}
@@ -70,24 +66,65 @@ var fakes = require("./fakes");
}
function expectSuccess(assertions) {
- options.handlers = options.handlers || {};
- options.handlers.success = function(track) {
+ var checkSuccess = function(track) {
if (assertions) {
assertions(track);
}
};
- new LastFmUpdate(lastfm, method, session, options);
- request.emit("success", returndata);
+ if (update) {
+ update.on("success", checkSuccess);
+ }
+ else {
+ options.handlers = options.handlers || {};
+ options.handlers.success = checkSuccess;
+ }
+ doUpdate();
+ }
+
+ function expectError(errorCode, expectedError) {
+ var checkError = function(error) {
+ if (errorCode || expectedError) {
+ assert.equal(expectedError, error.message);
+ assert.equal(errorCode, error.error);
+ }
+ };
+ if (update) {
+ update.on("error", checkError);
+ }
+ else {
+ options.handlers = options.handlers || {};
+ options.handlers.error = gently.expect(checkError);
+ }
+ doUpdate();
}
- function expectError(expectedError) {
+ function doNotExpectError() {
options.handlers = options.handlers || {};
- options.handlers.error = gently.expect(function(error) {
- assert.equal(expectedError, error.message);
- });
- new LastFmUpdate(lastfm, method, session, options);
- if (requestError) {
- request.emit("error", new Error(requestError));
+ options.handlers.error = function checkNoErrorThrown(error) {
+ assert.ok(false);
+ };
+ doUpdate();
+ }
+
+ function expectRetry(callback) {
+ callback = callback || function() { };
+ if (update) {
+ gently.expect(update, "emit", function(event, retry) {
+ assert.equal(event, "retrying");
+ callback(retry);
+ });
+ }
+ else {
+ options.handlers = options.handlers || { };
+ options.handlers.retrying = gently.expect(callback);
+ }
+ doUpdate();
+ }
+
+ function doUpdate() {
+ update = update || new LastFmUpdate(lastfm, method, session, options);
+ if (errorMessage) {
+ request.emit("error", { error: errorCode, message: errorMessage });
}
else {
request.emit("success", returndata);
@@ -100,10 +137,15 @@ var fakes = require("./fakes");
});
it("fail when the session is not authorised", function() {
- var session = new LastFmSession();
- assert.throws(function() {
- new LastFmUpdate(lastfm, "method", session);
- });
+ var session = new LastFmSession()
+ , update = new LastFmUpdate(lastfm, "method", session, {
+ handlers: {
+ error: gently.expect(function(error) {
+ assert.equal(error.error, 4);
+ assert.equal(error.message, "Authentication failed");
+ })
+ }
+ });
});
describe("nowPlaying updates")
@@ -133,7 +175,7 @@ var fakes = require("./fakes");
});
it("emits success when updated", function() {
- whenWriteRequestReturns(FakeData.UpdateNowPlayingSuccess);
+ whenRequestReturns(FakeData.UpdateNowPlayingSuccess);
andMethodIs("nowplaying");
andSessionIs(authorisedSession);
andOptionsAre({
@@ -170,14 +212,14 @@ var fakes = require("./fakes");
it("bubbles up errors", function() {
var errorMessage = "Bubbled error";
- whenWriteRequestThrowsError(errorMessage);
+ whenRequestThrowsError(100, errorMessage);
andMethodIs("nowplaying");
andSessionIs(authorisedSession);
andOptionsAre({
track: FakeTracks.RunToYourGrave,
timestamp: 12345678
});
- expectError(errorMessage);
+ expectError(100, errorMessage);
});
describe("a scrobble request")
@@ -190,7 +232,8 @@ var fakes = require("./fakes");
track: FakeTracks.RunToYourGrave,
handlers: {
error: gently.expect(function error(error) {
- assert.equal("Timestamp is required for scrobbling", error.message);
+ assert.equal(6, error.error);
+ assert.equal("Invalid parameters - Timestamp is required for scrobbling", error.message);
})
}
});
@@ -222,7 +265,7 @@ var fakes = require("./fakes");
});
it("emits success when updated", function() {
- whenWriteRequestReturns(FakeData.ScrobbleSuccess);
+ whenRequestReturns(FakeData.ScrobbleSuccess);
andMethodIs("scrobble");
andSessionIs(authorisedSession);
andOptionsAre({
@@ -236,14 +279,14 @@ var fakes = require("./fakes");
it("bubbles up errors", function() {
var errorMessage = "Bubbled error";
- whenWriteRequestThrowsError(errorMessage);
+ whenRequestThrowsError(100, errorMessage);
andMethodIs("scrobble");
andSessionIs(authorisedSession);
andOptionsAre({
track: FakeTracks.RunToYourGrave,
timestamp: 12345678
});
- expectError(errorMessage);
+ expectError(100, errorMessage);
});
it("can have artist and track string parameters supplied", function() {
@@ -289,4 +332,154 @@ var fakes = require("./fakes");
error: function() { }
});
});
+
+ var tmpFn;
+ describe("update retries")
+ before(function() {
+ tmpFn = LastFmUpdate.prototype.scheduleCallback;
+ LastFmUpdate.prototype.scheduleCallback = function(callback, delay) { };
+ setupFixture();
+ andMethodIs("scrobble");
+ andSessionIs(authorisedSession);
+ andOptionsAre({
+ track: FakeTracks.RunToYourGrave,
+ timestamp: 12345678
+ });
+ });
+
+ after(function() {
+ LastFmUpdate.prototype.scheduleCallback = tmpFn;
+ });
+
+ it("a error which should trigger a retry does not bubble errors", function() {
+ whenRequestThrowsError(11, "Service Offline");
+ doNotExpectError();
+ });
+
+ it("service offline triggers a retry", function() {
+ whenRequestThrowsError(11, "Service Offline");
+ expectRetry();
+ });
+
+ it("rate limit exceeded triggers a retry", function() {
+ whenRequestThrowsError(29, "Rate limit exceeded");
+ expectRetry();
+ });
+
+ it("temporarily unavailable triggers a retry", function() {
+ whenRequestThrowsError(16, "Temporarily unavailable");
+ expectRetry();
+ });
+
+ it("nowplaying update never trigger retries", function() {
+ whenRequestThrowsError(16, "Temporarily unavailable");
+ andMethodIs("nowplaying");
+ expectError();
+ });
+
+ it("first retry schedules a request after a 10 second delay", function() {
+ whenRequestThrowsError(16, "Temporarily unavailable");
+ LastFmUpdate.prototype.scheduleCallback = gently.expect(function testSchedule(callback, delay) {
+ assert.equal(delay, 10000);
+ });
+ doUpdate();
+ });
+
+ function onNextRequests(callback, count) {
+ count = count || 1;
+ var gently = new Gently();
+ LastFmUpdate.prototype.scheduleCallback = gently.expect(count, callback);
+ doUpdate();
+ }
+
+ function lastRequest() {
+ LastFmUpdate.prototype.scheduleCallback = function() { };
+ }
+
+ function whenNextRequestThrowsError(request, code, message) {
+ whenRequestThrowsError(code, message);
+ request();
+ }
+
+ function whenNextRequestReturns(request, data) {
+ whenRequestReturns(data);
+ request();
+ }
+
+ it("retry triggers another request", function() {
+ whenRequestThrowsError(16, "Temporarily unavailable");
+ onNextRequests(function(nextRequest) {
+ lastRequest();
+ whenNextRequestThrowsError(nextRequest, 16, "Temporarily unavailable");
+ expectRetry();
+ });
+ });
+
+ it("emits succes if retry is successful", function() {
+ whenRequestThrowsError(16, "Temporarily unavailable");
+ onNextRequests(function(nextRequest) {
+ whenNextRequestReturns(nextRequest, FakeData.ScrobbleSuccess);
+ expectSuccess(function(track) {
+ assert.equal("Run To Your Grave", track.name);
+ });
+ });
+ });
+
+ it("emits succes if retry is non-retry error", function() {
+ whenRequestThrowsError(16, "Temporarily unavailable");
+ onNextRequests(function(nextRequest) {
+ whenNextRequestThrowsError(nextRequest, 6, "Invalid parameter");
+ expectError(6, "Invalid parameter");
+ });
+ });
+
+ it("retrying events include error received and delay details", function() {
+ whenRequestThrowsError(16, "Temporarily unavailable");
+ expectRetry(function(retry) {
+ assert.equal(retry.delay, 10000);
+ assert.equal(retry.error, 16);
+ assert.equal(retry.message, "Temporarily unavailable");
+ });
+ });
+
+ var retrySchedule = [
+ 10 * 1000,
+ 30 * 1000,
+ 60 * 1000,
+ 5 * 60 * 1000,
+ 15 * 60 * 1000,
+ 30 * 60 * 1000,
+ 30 * 60 * 1000,
+ 30 * 60 * 1000
+ ];
+
+ it("follows a retry schedule on subsequent failures", function() {
+ var count = 0;
+ whenRequestThrowsError(16, "Temporarily unavailable");
+ onNextRequests(function(nextRequest, delay) {
+ var expectedDelay = retrySchedule[count++];
+ assert.equal(delay, expectedDelay);
+ if (count >= retrySchedule.length) {
+ lastRequest();
+ }
+ whenNextRequestThrowsError(nextRequest, 16, "Temporarily unavailable");
+ expectRetry();
+ }, retrySchedule.length);
+ });
+
+ it("includes delay in subsequent retry events", function() {
+ var count = 0;
+ whenRequestThrowsError(16, "Temporarily unavailable");
+ onNextRequests(function(nextRequest, delay) {
+ count++;
+ if (count >= retrySchedule.length) {
+ lastRequest();
+ }
+ var expectedDelay = retrySchedule[Math.min(count, retrySchedule.length - 1)];
+ whenNextRequestThrowsError(nextRequest, 16, "Temporarily unavailable");
+ expectRetry(function(retry) {
+ assert.equal(retry.delay, expectedDelay);
+ });
+ }, retrySchedule.length);
+ });
})();

0 comments on commit b94f77c

Please sign in to comment.
Something went wrong with that request. Please try again.