/* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ add_test(function test_creation() { // Explicit callback for this one. let server = new SyncServer(Object.create(SyncServerCallback)); Assert.ok(!!server); // Just so we have a check. server.start(null, function () { _("Started on " + server.port); server.stop(run_next_test); }); }); add_test(function test_url_parsing() { let server = new SyncServer(); // Check that we can parse a WBO URI. let parts = server.pathRE.exec("/1.1/johnsmith/storage/crypto/keys"); let [all, version, username, first, rest] = parts; Assert.equal(all, "/1.1/johnsmith/storage/crypto/keys"); Assert.equal(version, "1.1"); Assert.equal(username, "johnsmith"); Assert.equal(first, "storage"); Assert.equal(rest, "crypto/keys"); Assert.equal(null, server.pathRE.exec("/nothing/else")); // Check that we can parse a collection URI. parts = server.pathRE.exec("/1.1/johnsmith/storage/crypto"); [all, version, username, first, rest] = parts; Assert.equal(all, "/1.1/johnsmith/storage/crypto"); Assert.equal(version, "1.1"); Assert.equal(username, "johnsmith"); Assert.equal(first, "storage"); Assert.equal(rest, "crypto"); // We don't allow trailing slash on storage URI. parts = server.pathRE.exec("/1.1/johnsmith/storage/"); Assert.equal(parts, undefined); // storage alone is a valid request. parts = server.pathRE.exec("/1.1/johnsmith/storage"); [all, version, username, first, rest] = parts; Assert.equal(all, "/1.1/johnsmith/storage"); Assert.equal(version, "1.1"); Assert.equal(username, "johnsmith"); Assert.equal(first, "storage"); Assert.equal(rest, undefined); parts = server.storageRE.exec("storage"); let collection; [all, , collection] = parts; Assert.equal(all, "storage"); Assert.equal(collection, undefined); run_next_test(); }); const { RESTRequest } = ChromeUtils.importESModule( "resource://services-common/rest.sys.mjs" ); function localRequest(server, path) { _("localRequest: " + path); let url = server.baseURI.substr(0, server.baseURI.length - 1) + path; _("url: " + url); return new RESTRequest(url); } add_task(async function test_basic_http() { let server = new SyncServer(); server.registerUser("john", "password"); Assert.ok(server.userExists("john")); server.start(); _("Started on " + server.port); let req = localRequest(server, "/1.1/john/storage/crypto/keys"); _("req is " + req); // Shouldn't reject, beyond that we don't care. await req.get(); await promiseStopServer(server); }); add_task(async function test_info_collections() { let server = new SyncServer(Object.create(SyncServerCallback)); function responseHasCorrectHeaders(r) { Assert.equal(r.status, 200); Assert.equal(r.headers["content-type"], "application/json"); Assert.ok("x-weave-timestamp" in r.headers); } server.registerUser("john", "password"); server.start(); let req = localRequest(server, "/1.1/john/info/collections"); await req.get(); responseHasCorrectHeaders(req.response); Assert.equal(req.response.body, "{}"); let putReq = localRequest(server, "/1.1/john/storage/crypto/keys"); let payload = JSON.stringify({ foo: "bar" }); let putResp = await putReq.put(payload); responseHasCorrectHeaders(putResp); let putResponseBody = putResp.body; _("PUT response body: " + JSON.stringify(putResponseBody)); // When we PUT something to crypto/keys, "crypto" appears in the response. req = localRequest(server, "/1.1/john/info/collections"); await req.get(); responseHasCorrectHeaders(req.response); let expectedColl = server.getCollection("john", "crypto"); Assert.ok(!!expectedColl); let modified = expectedColl.timestamp; Assert.ok(modified > 0); Assert.equal(putResponseBody, modified); Assert.equal(JSON.parse(req.response.body).crypto, modified); await promiseStopServer(server); }); add_task(async function test_storage_request() { let keysURL = "/1.1/john/storage/crypto/keys?foo=bar"; let foosURL = "/1.1/john/storage/crypto/foos"; let storageURL = "/1.1/john/storage"; let server = new SyncServer(); let creation = server.timestamp(); server.registerUser("john", "password"); server.createContents("john", { crypto: { foos: { foo: "bar" } }, }); let coll = server.user("john").collection("crypto"); Assert.ok(!!coll); _("We're tracking timestamps."); Assert.ok(coll.timestamp >= creation); async function retrieveWBONotExists() { let req = localRequest(server, keysURL); let response = await req.get(); _("Body is " + response.body); _("Modified is " + response.newModified); Assert.equal(response.status, 404); Assert.equal(response.body, "Not found"); } async function retrieveWBOExists() { let req = localRequest(server, foosURL); let response = await req.get(); _("Body is " + response.body); _("Modified is " + response.newModified); let parsedBody = JSON.parse(response.body); Assert.equal(parsedBody.id, "foos"); Assert.equal(parsedBody.modified, coll.wbo("foos").modified); Assert.equal(JSON.parse(parsedBody.payload).foo, "bar"); } async function deleteWBONotExists() { let req = localRequest(server, keysURL); server.callback.onItemDeleted = function () { do_throw("onItemDeleted should not have been called."); }; let response = await req.delete(); _("Body is " + response.body); _("Modified is " + response.newModified); Assert.equal(response.status, 200); delete server.callback.onItemDeleted; } async function deleteWBOExists() { let req = localRequest(server, foosURL); server.callback.onItemDeleted = function (username, collection, wboID) { _("onItemDeleted called for " + collection + "/" + wboID); delete server.callback.onItemDeleted; Assert.equal(username, "john"); Assert.equal(collection, "crypto"); Assert.equal(wboID, "foos"); }; await req.delete(); _("Body is " + req.response.body); _("Modified is " + req.response.newModified); Assert.equal(req.response.status, 200); } async function deleteStorage() { _("Testing DELETE on /storage."); let now = server.timestamp(); _("Timestamp: " + now); let req = localRequest(server, storageURL); await req.delete(); _("Body is " + req.response.body); _("Modified is " + req.response.newModified); let parsedBody = JSON.parse(req.response.body); Assert.ok(parsedBody >= now); do_check_empty(server.users.john.collections); } async function getStorageFails() { _("Testing that GET on /storage fails."); let req = localRequest(server, storageURL); await req.get(); Assert.equal(req.response.status, 405); Assert.equal(req.response.headers.allow, "DELETE"); } async function getMissingCollectionWBO() { _("Testing that fetching a WBO from an on-existent collection 404s."); let req = localRequest(server, storageURL + "/foobar/baz"); await req.get(); Assert.equal(req.response.status, 404); } server.start(null); await retrieveWBONotExists(); await retrieveWBOExists(); await deleteWBOExists(); await deleteWBONotExists(); await getStorageFails(); await getMissingCollectionWBO(); await deleteStorage(); await promiseStopServer(server); }); add_task(async function test_x_weave_records() { let server = new SyncServer(); server.registerUser("john", "password"); server.createContents("john", { crypto: { foos: { foo: "bar" }, bars: { foo: "baz" } }, }); server.start(); let wbo = localRequest(server, "/1.1/john/storage/crypto/foos"); await wbo.get(); Assert.equal(false, "x-weave-records" in wbo.response.headers); let col = localRequest(server, "/1.1/john/storage/crypto"); await col.get(); // Collection fetches do. Assert.equal(col.response.headers["x-weave-records"], "2"); await promiseStopServer(server); });