/* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ const { BridgedEngine } = ChromeUtils.importESModule( "resource://services-sync/bridged_engine.sys.mjs" ); const { Service } = ChromeUtils.importESModule( "resource://services-sync/service.sys.mjs" ); add_task(async function test_interface() { class TestBridge { constructor() { this.storageVersion = 2; this.syncID = "syncID111111"; this.clear(); } clear() { this.lastSyncMillis = 0; this.wasSyncStarted = false; this.incomingEnvelopes = []; this.uploadedIDs = []; this.wasSyncFinished = false; this.wasReset = false; this.wasWiped = false; } // `mozIBridgedSyncEngine` methods. lastSync() { return this.lastSyncMillis; } setLastSync(millis) { this.lastSyncMillis = millis; } resetSyncId() { return this.syncID; } ensureCurrentSyncId(newSyncId) { equal(newSyncId, this.syncID, "Local and new sync IDs should match"); return this.syncID; } syncStarted() { this.wasSyncStarted = true; } storeIncoming(envelopes) { this.incomingEnvelopes.push(...envelopes.map(r => JSON.parse(r))); } apply() { let outgoingEnvelopes = [ { id: "hanson", data: { plants: ["seed", "flower 💐", "rose"], canYouTell: false, }, }, { id: "sheryl-crow", data: { today: "winding 🛣", tomorrow: "winding 🛣", }, }, ].map(cleartext => JSON.stringify({ id: cleartext.id, payload: JSON.stringify(cleartext), }) ); return outgoingEnvelopes; } setUploaded(millis, ids) { this.uploadedIDs.push(...ids); } syncFinished() { this.wasSyncFinished = true; } reset() { this.clear(); this.wasReset = true; } wipe() { this.clear(); this.wasWiped = true; } } let bridge = new TestBridge(); let engine = new BridgedEngine("Nineties", Service); engine._bridge = bridge; engine.enabled = true; let server = await serverForFoo(engine); try { await SyncTestingInfrastructure(server); info("Add server records"); let foo = server.user("foo"); let collection = foo.collection("nineties"); let now = new_timestamp(); collection.insert( "backstreet", encryptPayload({ id: "backstreet", data: { say: "I want it that way", when: "never", }, }), now ); collection.insert( "tlc", encryptPayload({ id: "tlc", data: { forbidden: ["scrubs 🚫"], numberAvailable: false, }, }), now + 5 ); info("Sync the engine"); // Advance the last sync time to skip the Backstreet Boys... bridge.lastSyncMillis = 1000 * (now + 2); await sync_engine_and_validate_telem(engine, false); let metaGlobal = foo.collection("meta").wbo("global").get(); deepEqual( JSON.parse(metaGlobal.payload).engines.nineties, { version: 2, syncID: "syncID111111", }, "Should write storage version and sync ID to m/g" ); greater(bridge.lastSyncMillis, 0, "Should update last sync time"); ok( bridge.wasSyncStarted, "Should have started sync before storing incoming" ); deepEqual( bridge.incomingEnvelopes .sort((a, b) => a.id.localeCompare(b.id)) .map(({ payload, ...envelope }) => ({ cleartextAsObject: JSON.parse(payload), ...envelope, })), [ { id: "tlc", modified: now + 5, cleartextAsObject: { id: "tlc", data: { forbidden: ["scrubs 🚫"], numberAvailable: false, }, }, }, ], "Should stage incoming records from server" ); deepEqual( bridge.uploadedIDs.sort(), ["hanson", "sheryl-crow"], "Should mark new local records as uploaded" ); ok(bridge.wasSyncFinished, "Should have finished sync after uploading"); deepEqual( collection.keys().sort(), ["backstreet", "hanson", "sheryl-crow", "tlc"], "Should have all records on server" ); let expectedRecords = [ { id: "sheryl-crow", data: { today: "winding 🛣", tomorrow: "winding 🛣", }, }, { id: "hanson", data: { plants: ["seed", "flower 💐", "rose"], canYouTell: false, }, }, ]; for (let expected of expectedRecords) { let actual = collection.cleartext(expected.id); deepEqual( actual, expected, `Should upload record ${expected.id} from bridged engine` ); } await engine.resetClient(); ok(bridge.wasReset, "Should reset local storage for bridge"); await engine.wipeClient(); ok(bridge.wasWiped, "Should wipe local storage for bridge"); await engine.resetSyncID(); ok( !foo.collection("nineties"), "Should delete server collection after resetting sync ID" ); } finally { await promiseStopServer(server); await engine.finalize(); } });