summaryrefslogtreecommitdiffstats
path: root/services/common/tests/unit/test_blocklist_updater.js
diff options
context:
space:
mode:
Diffstat (limited to 'services/common/tests/unit/test_blocklist_updater.js')
-rw-r--r--services/common/tests/unit/test_blocklist_updater.js173
1 files changed, 173 insertions, 0 deletions
diff --git a/services/common/tests/unit/test_blocklist_updater.js b/services/common/tests/unit/test_blocklist_updater.js
new file mode 100644
index 000000000..1b71c194a
--- /dev/null
+++ b/services/common/tests/unit/test_blocklist_updater.js
@@ -0,0 +1,173 @@
+Cu.import("resource://testing-common/httpd.js");
+
+var server;
+
+const PREF_SETTINGS_SERVER = "services.settings.server";
+const PREF_LAST_UPDATE = "services.blocklist.last_update_seconds";
+const PREF_LAST_ETAG = "services.blocklist.last_etag";
+const PREF_CLOCK_SKEW_SECONDS = "services.blocklist.clock_skew_seconds";
+
+// Check to ensure maybeSync is called with correct values when a changes
+// document contains information on when a collection was last modified
+add_task(function* test_check_maybeSync(){
+ const changesPath = "/v1/buckets/monitor/collections/changes/records";
+
+ // register a handler
+ function handleResponse (serverTimeMillis, request, response) {
+ try {
+ const sampled = getSampleResponse(request, server.identity.primaryPort);
+ if (!sampled) {
+ do_throw(`unexpected ${request.method} request for ${request.path}?${request.queryString}`);
+ }
+
+ response.setStatusLine(null, sampled.status.status,
+ sampled.status.statusText);
+ // send the headers
+ for (let headerLine of sampled.sampleHeaders) {
+ let headerElements = headerLine.split(':');
+ response.setHeader(headerElements[0], headerElements[1].trimLeft());
+ }
+
+ // set the server date
+ response.setHeader("Date", (new Date(serverTimeMillis)).toUTCString());
+
+ response.write(sampled.responseBody);
+ } catch (e) {
+ dump(`${e}\n`);
+ }
+ }
+
+ server.registerPathHandler(changesPath, handleResponse.bind(null, 2000));
+
+ // set up prefs so the kinto updater talks to the test server
+ Services.prefs.setCharPref(PREF_SETTINGS_SERVER,
+ `http://localhost:${server.identity.primaryPort}/v1`);
+
+ // set some initial values so we can check these are updated appropriately
+ Services.prefs.setIntPref(PREF_LAST_UPDATE, 0);
+ Services.prefs.setIntPref(PREF_CLOCK_SKEW_SECONDS, 0);
+ Services.prefs.clearUserPref(PREF_LAST_ETAG);
+
+
+ let startTime = Date.now();
+
+ let updater = Cu.import("resource://services-common/blocklist-updater.js");
+
+ let syncPromise = new Promise(function(resolve, reject) {
+ // add a test kinto client that will respond to lastModified information
+ // for a collection called 'test-collection'
+ updater.addTestBlocklistClient("test-collection", {
+ maybeSync(lastModified, serverTime) {
+ do_check_eq(lastModified, 1000);
+ do_check_eq(serverTime, 2000);
+ resolve();
+ }
+ });
+ updater.checkVersions();
+ });
+
+ // ensure we get the maybeSync call
+ yield syncPromise;
+
+ // check the last_update is updated
+ do_check_eq(Services.prefs.getIntPref(PREF_LAST_UPDATE), 2);
+
+ // How does the clock difference look?
+ let endTime = Date.now();
+ let clockDifference = Services.prefs.getIntPref(PREF_CLOCK_SKEW_SECONDS);
+ // we previously set the serverTime to 2 (seconds past epoch)
+ do_check_true(clockDifference <= endTime / 1000
+ && clockDifference >= Math.floor(startTime / 1000) - 2);
+ // Last timestamp was saved. An ETag header value is a quoted string.
+ let lastEtag = Services.prefs.getCharPref(PREF_LAST_ETAG);
+ do_check_eq(lastEtag, "\"1100\"");
+
+ // Simulate a poll with up-to-date collection.
+ Services.prefs.setIntPref(PREF_LAST_UPDATE, 0);
+ // If server has no change, a 304 is received, maybeSync() is not called.
+ updater.addTestBlocklistClient("test-collection", {
+ maybeSync: () => {throw new Error("Should not be called");}
+ });
+ yield updater.checkVersions();
+ // Last update is overwritten
+ do_check_eq(Services.prefs.getIntPref(PREF_LAST_UPDATE), 2);
+
+
+ // Simulate a server error.
+ function simulateErrorResponse (request, response) {
+ response.setHeader("Date", (new Date(3000)).toUTCString());
+ response.setHeader("Content-Type", "application/json; charset=UTF-8");
+ response.write(JSON.stringify({
+ code: 503,
+ errno: 999,
+ error: "Service Unavailable",
+ }));
+ response.setStatusLine(null, 503, "Service Unavailable");
+ }
+ server.registerPathHandler(changesPath, simulateErrorResponse);
+ // checkVersions() fails with adequate error.
+ let error;
+ try {
+ yield updater.checkVersions();
+ } catch (e) {
+ error = e;
+ }
+ do_check_eq(error.message, "Polling for changes failed.");
+ // When an error occurs, last update was not overwritten (see Date header above).
+ do_check_eq(Services.prefs.getIntPref(PREF_LAST_UPDATE), 2);
+
+ // check negative clock skew times
+
+ // set to a time in the future
+ server.registerPathHandler(changesPath, handleResponse.bind(null, Date.now() + 10000));
+
+ yield updater.checkVersions();
+
+ clockDifference = Services.prefs.getIntPref(PREF_CLOCK_SKEW_SECONDS);
+ // we previously set the serverTime to Date.now() + 10000 ms past epoch
+ do_check_true(clockDifference <= 0 && clockDifference >= -10);
+});
+
+function run_test() {
+ // Set up an HTTP Server
+ server = new HttpServer();
+ server.start(-1);
+
+ run_next_test();
+
+ do_register_cleanup(function() {
+ server.stop(function() { });
+ });
+}
+
+// get a response for a given request from sample data
+function getSampleResponse(req, port) {
+ const responses = {
+ "GET:/v1/buckets/monitor/collections/changes/records?": {
+ "sampleHeaders": [
+ "Content-Type: application/json; charset=UTF-8",
+ "ETag: \"1100\""
+ ],
+ "status": {status: 200, statusText: "OK"},
+ "responseBody": JSON.stringify({"data": [{
+ "host": "localhost",
+ "last_modified": 1100,
+ "bucket": "blocklists:aurora",
+ "id": "330a0c5f-fadf-ff0b-40c8-4eb0d924ff6a",
+ "collection": "test-collection"
+ }, {
+ "host": "localhost",
+ "last_modified": 1000,
+ "bucket": "blocklists",
+ "id": "254cbb9e-6888-4d9f-8e60-58b74faa8778",
+ "collection": "test-collection"
+ }]})
+ }
+ };
+
+ if (req.hasHeader("if-none-match") && req.getHeader("if-none-match", "") == "\"1100\"")
+ return {sampleHeaders: [], status: {status: 304, statusText: "Not Modified"}, responseBody: ""};
+
+ return responses[`${req.method}:${req.path}?${req.queryString}`] ||
+ responses[req.method];
+}