486 lines
14 KiB
JavaScript
486 lines
14 KiB
JavaScript
require('./common.js');
|
|
var LastFmSession = require('../lib/lastfm/lastfm-session');
|
|
var LastFmUpdate = require('../lib/lastfm/lastfm-update');
|
|
var fakes = require("./fakes");
|
|
|
|
(function() {
|
|
describe("new LastFmUpdate")
|
|
it("can have success and error handlers specified in option at creation", function() {
|
|
var gently = new Gently();
|
|
var lastfm = new LastFmNode();
|
|
var update = new LastFmUpdate(lastfm, "method", new LastFmSession(lastfm, "user", "key"), { handlers: {
|
|
error: gently.expect(function error() {}),
|
|
success: gently.expect(function success() {})
|
|
}});
|
|
update.emit("error");
|
|
update.emit("success");
|
|
});
|
|
})();
|
|
|
|
(function() {
|
|
var request, returndata, options, session, method, gently, lastfm, authorisedSession, errorCode, errorMessage, update;
|
|
|
|
function setupFixture() {
|
|
request = new fakes.LastFmRequest();
|
|
returndata;
|
|
options = {};
|
|
session = null;
|
|
method = "";
|
|
gently = new Gently();
|
|
lastfm = new LastFmNode();
|
|
authorisedSession = new LastFmSession(lastfm, "user", "key");
|
|
errorCode = -1;
|
|
errorMessage = null;
|
|
update = undefined;
|
|
}
|
|
|
|
function whenRequestReturns(data) {
|
|
errorCode = -1;
|
|
errorMessage = null;
|
|
returndata = JSON.parse(data);
|
|
request = new fakes.LastFmRequest();
|
|
gently.expect(lastfm, "request", function() {
|
|
return request;
|
|
});
|
|
}
|
|
|
|
function whenRequestThrowsError(code, message) {
|
|
errorCode = code;
|
|
errorMessage = message;
|
|
request = new fakes.LastFmRequest();
|
|
gently.expect(lastfm, "request", function() {
|
|
return request;
|
|
});
|
|
}
|
|
|
|
function andOptionsAre(setOptions) {
|
|
options = setOptions;
|
|
}
|
|
|
|
function andMethodIs(setMethod) {
|
|
method = setMethod;
|
|
}
|
|
|
|
function andSessionIs(setSession) {
|
|
session = setSession;
|
|
}
|
|
|
|
function expectSuccess(assertions) {
|
|
var checkSuccess = function(track) {
|
|
if (assertions) {
|
|
assertions(track);
|
|
}
|
|
};
|
|
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 doNotExpectError() {
|
|
options.handlers = options.handlers || {};
|
|
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);
|
|
}
|
|
}
|
|
|
|
describe("update requests")
|
|
before(function() {
|
|
setupFixture();
|
|
});
|
|
|
|
it("fail when the session is not authorised", function() {
|
|
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")
|
|
before(function() {
|
|
setupFixture();
|
|
});
|
|
|
|
it("uses updateNowPlaying method", function() {
|
|
gently.expect(lastfm, "request", function(method, params) {
|
|
assert.equal("track.updateNowPlaying", method);
|
|
return request;
|
|
});
|
|
new LastFmUpdate(lastfm, "nowplaying", authorisedSession, {
|
|
track: FakeTracks.RunToYourGrave
|
|
});
|
|
});
|
|
|
|
it("sends required parameters", function() {
|
|
gently.expect(lastfm, "request", function(method, params) {
|
|
assert.equal(FakeTracks.RunToYourGrave, params.track);
|
|
assert.equal("key", params.sk);
|
|
return request;
|
|
});
|
|
new LastFmUpdate(lastfm, "nowplaying", authorisedSession, {
|
|
track: FakeTracks.RunToYourGrave
|
|
});
|
|
});
|
|
|
|
it("emits success when updated", function() {
|
|
whenRequestReturns(FakeData.UpdateNowPlayingSuccess);
|
|
andMethodIs("nowplaying");
|
|
andSessionIs(authorisedSession);
|
|
andOptionsAre({
|
|
track: FakeTracks.RunToYourGrave
|
|
});
|
|
expectSuccess(function(track) {
|
|
assert.equal("Run To Your Grave", track.name);
|
|
});
|
|
});
|
|
|
|
it("sends duration when supplied", function() {
|
|
gently.expect(lastfm, "request", function(method, params) {
|
|
assert.equal(232000, params.duration);
|
|
return request;
|
|
});
|
|
new LastFmUpdate(lastfm, "nowplaying", authorisedSession, {
|
|
track: FakeTracks.RunToYourGrave,
|
|
duration: 232000
|
|
});
|
|
});
|
|
|
|
it("can have artist and track string parameters supplied", function() {
|
|
gently.expect(lastfm, "request", function(method, params) {
|
|
assert.equal("The Mae Shi", params.artist);
|
|
assert.equal("Run To Your Grave", params.track);
|
|
assert.equal("key", params.sk);
|
|
return request;
|
|
});
|
|
new LastFmUpdate(lastfm, "nowplaying", authorisedSession, {
|
|
track: "Run To Your Grave",
|
|
artist: "The Mae Shi"
|
|
});
|
|
});
|
|
|
|
it("bubbles up errors", function() {
|
|
var errorMessage = "Bubbled error";
|
|
whenRequestThrowsError(100, errorMessage);
|
|
andMethodIs("nowplaying");
|
|
andSessionIs(authorisedSession);
|
|
andOptionsAre({
|
|
track: FakeTracks.RunToYourGrave,
|
|
timestamp: 12345678
|
|
});
|
|
expectError(100, errorMessage);
|
|
});
|
|
|
|
describe("a scrobble request")
|
|
before(function() {
|
|
setupFixture();
|
|
});
|
|
|
|
it("emits error when no timestamp supplied", function() {
|
|
new LastFmUpdate(lastfm, "scrobble", authorisedSession, {
|
|
track: FakeTracks.RunToYourGrave,
|
|
handlers: {
|
|
error: gently.expect(function error(error) {
|
|
assert.equal(6, error.error);
|
|
assert.equal("Invalid parameters - Timestamp is required for scrobbling", error.message);
|
|
})
|
|
}
|
|
});
|
|
});
|
|
|
|
it("uses scrobble method", function() {
|
|
gently.expect(lastfm, "request", function(method, params) {
|
|
assert.equal("track.scrobble", method);
|
|
return request;
|
|
});
|
|
new LastFmUpdate(lastfm, "scrobble", authorisedSession, {
|
|
track: FakeTracks.RunToYourGrave,
|
|
timestamp: 12345678
|
|
});
|
|
});
|
|
|
|
it("sends required parameters", function() {
|
|
gently.expect(lastfm, "request", function(method, params) {
|
|
assert.equal(FakeTracks.RunToYourGrave, params.track);
|
|
assert.equal("key", params.sk);
|
|
assert.equal(12345678, params.timestamp);
|
|
return request;
|
|
});
|
|
|
|
new LastFmUpdate(lastfm, "scrobble", authorisedSession, {
|
|
track: FakeTracks.RunToYourGrave,
|
|
timestamp: 12345678
|
|
});
|
|
});
|
|
|
|
it("emits success when updated", function() {
|
|
whenRequestReturns(FakeData.ScrobbleSuccess);
|
|
andMethodIs("scrobble");
|
|
andSessionIs(authorisedSession);
|
|
andOptionsAre({
|
|
track: FakeTracks.RunToYourGrave,
|
|
timestamp: 12345678
|
|
});
|
|
expectSuccess(function(track) {
|
|
assert.equal("Run To Your Grave", track.name);
|
|
});
|
|
});
|
|
|
|
it("bubbles up errors", function() {
|
|
var errorMessage = "Bubbled error";
|
|
whenRequestThrowsError(100, errorMessage);
|
|
andMethodIs("scrobble");
|
|
andSessionIs(authorisedSession);
|
|
andOptionsAre({
|
|
track: FakeTracks.RunToYourGrave,
|
|
timestamp: 12345678
|
|
});
|
|
expectError(100, errorMessage);
|
|
});
|
|
|
|
it("can have artist and track string parameters supplied", function() {
|
|
gently.expect(lastfm, "request", function(method, params) {
|
|
assert.equal("The Mae Shi", params.artist);
|
|
assert.equal("Run To Your Grave", params.track);
|
|
assert.equal("key", params.sk);
|
|
return request;
|
|
});
|
|
new LastFmUpdate(lastfm, "scrobble", authorisedSession, {
|
|
track: "Run To Your Grave",
|
|
artist: "The Mae Shi",
|
|
timestamp: 12345678
|
|
});
|
|
});
|
|
|
|
it("can have arbitrary parameters supplied", function() {
|
|
gently.expect(lastfm, "request", function(method, params) {
|
|
assert.equal("somevalue", params.arbitrary);
|
|
return request;
|
|
});
|
|
new LastFmUpdate(lastfm, "scrobble", authorisedSession, {
|
|
track: "Run To Your Grave",
|
|
artist: "The Mae Shi",
|
|
timestamp: 12345678,
|
|
arbitrary: "somevalue"
|
|
});
|
|
});
|
|
|
|
it("does not include handler parameters", function() {
|
|
gently.expect(lastfm, "request", function(method, params) {
|
|
assert.equal(undefined, params.handlers);
|
|
assert.equal(undefined, params.error);
|
|
assert.equal(undefined, params.success);
|
|
return request;
|
|
});
|
|
new LastFmUpdate(lastfm, "scrobble", authorisedSession, {
|
|
track: "Run To Your Grave",
|
|
artist: "The Mae Shi",
|
|
timestamp: 12345678,
|
|
handlers: { success: function() { } },
|
|
success: function() { },
|
|
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);
|
|
});
|
|
})();
|