zotero/test/tests/syncAPIClientTest.js

204 lines
4.9 KiB
JavaScript
Raw Normal View History

"use strict";
describe("Zotero.Sync.APIClient", function () {
Components.utils.import("resource://zotero/config.js");
var apiKey = Zotero.Utilities.randomString(24);
var baseURL = "http://local.zotero/";
var server, client;
function setResponse(response) {
setHTTPResponse(server, baseURL, response, {});
}
before(function () {
Zotero.HTTP.mock = sinon.FakeXMLHttpRequest;
});
beforeEach(function () {
Components.utils.import("resource://zotero/concurrentCaller.js");
var caller = new ConcurrentCaller(1);
caller.setLogger(msg => Zotero.debug(msg));
caller.stopOnError = true;
caller.onError = function (e) {
Zotero.logError(e);
if (e.fatal) {
caller.stop();
throw e;
}
};
client = new Zotero.Sync.APIClient({
baseURL,
apiVersion: ZOTERO_CONFIG.API_VERSION,
apiKey,
caller
})
server = sinon.fakeServer.create();
server.autoRespond = true;
})
after(function () {
Zotero.HTTP.mock = null;
})
describe("#makeRequest()", function () {
after(function () {
sinon.restore();
});
it("should send Zotero-Schema-Version", async function () {
server.respond(function (req) {
if (req.method == "GET" && req.url == baseURL + "test-schema-version") {
assert.propertyVal(
req.requestHeaders,
'Zotero-Schema-Version',
Zotero.Schema.globalSchemaVersion.toString()
);
req.respond(200, {}, "");
}
});
var spy = sinon.spy(Zotero.HTTP, "request");
await client.makeRequest("GET", baseURL + "test-schema-version");
assert.isTrue(spy.calledOnce);
});
});
describe("#getGroups()", function () {
it("should automatically fetch multiple pages of results", function* () {
function groupJSON(groupID) {
return {
id: groupID,
version: 1,
data: {
id: groupID,
version: 1,
name: "Group " + groupID
}
};
}
server.respond(function (req) {
if (req.method == "GET" && req.url.startsWith(baseURL + "users/1/groups")) {
// TODO: Use a real parser
let matches = req.url.match(/start=(\d+)/);
let start = matches ? parseInt(matches[1]) : null;
matches = req.url.match(/limit=(\d+)/);
let limit = matches ? parseInt(matches[1]) : null;
if (start === null && limit === null) {
req.respond(
200,
{
Link: `<${baseURL}users/1/groups?limit=2&start=2>; rel="next", <${baseURL}users/1/groups?limit=2&start=4>; rel="last", <${baseURL}users/1/groups>; rel="alternate"`,
"Total-Results": 2
},
JSON.stringify([
groupJSON(1),
groupJSON(2)
])
);
}
else if (start == 2 && limit == 2) {
req.respond(
200,
{
Link: `<${baseURL}users/1/groups?limit=2&start=4>; rel="next", <${baseURL}users/1/groups?limit=2&start=4>; rel="last", <${baseURL}users/1/groups>; rel="alternate"`,
"Total-Results": 5
},
JSON.stringify([
groupJSON(3),
groupJSON(4)
])
);
}
else if (start == 4 && limit == 2) {
req.respond(
200,
{
Link: `<${baseURL}users/1/groups?limit=2&start=4>; rel="last", <${baseURL}users/1/groups>; rel="alternate"`,
"Total-Results": 5
},
JSON.stringify([
groupJSON(5),
])
);
}
}
});
var results = yield client.getGroups(1);
assert.lengthOf(results, 5);
assert.sameMembers(results.map(o => o.id), [1, 2, 3, 4, 5]);
});
});
describe("Retries", function () {
var spy;
var delayStub;
var delayDelay = 100;
before(function () {
delayStub = sinon.stub(Zotero.Promise, "delay").callsFake(() => {
return new Zotero.Promise((resolve) => {
setTimeout(resolve, delayDelay);
});
});
});
beforeEach(function () {
client.rateDelayIntervals = [15, 25];
});
afterEach(function () {
if (spy) {
spy.restore();
}
delayStub.resetHistory();
});
after(function () {
sinon.restore();
});
it("should retry on 429 error", async function () {
var called = 0;
server.respond(function (req) {
if (req.method == "GET" && req.url == baseURL + "error") {
if (called < 2) {
req.respond(
429,
{},
""
);
}
else {
req.respond(
200,
{},
""
);
}
}
called++;
});
spy = sinon.spy(Zotero.HTTP, "request");
var d = new Date();
await client.makeRequest("GET", baseURL + "error");
// Make sure we've paused for the expected delay twice
assert.isAbove(new Date() - d, delayDelay * 2);
assert.isTrue(spy.calledThrice);
assert.equal(called, 3);
// Slightly off because concurrentCaller sets the delay to the time remaining until the
// previously set `pauseUntil` time, and a few milliseconds might have gone by
assert.approximately(delayStub.args[0][0], 15 * 1000, 10);
assert.approximately(delayStub.args[1][0], 25 * 1000, 10);
});
});
})