2022-09-30 05:39:11 +00:00

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);
});
})();