test_remote_settings_startup_bundle.js (4198B)
1 const { Database } = ChromeUtils.importESModule( 2 "resource://services-settings/Database.sys.mjs" 3 ); 4 const { RemoteSettings } = ChromeUtils.importESModule( 5 "resource://services-settings/remote-settings.sys.mjs" 6 ); 7 8 let server; 9 let startupClients; 10 11 add_setup(async () => { 12 // Disable signature verification of collections listed in test startup.json.mzlz4 13 // since their metadata will eventually expire. 14 startupClients = [ 15 "message-groups", 16 "nimbus-desktop-experiments", 17 "search-categorization", 18 "tracking-protection-lists", 19 "query-stripping", 20 "cfr", 21 ].map(id => { 22 const c = RemoteSettings(id); 23 c.verifySignature = false; 24 return c; 25 }); 26 27 // Setup an HTTP server to serve the 'startup.json.mzlz4' bundle. 28 server = new HttpServer(); 29 server.start(-1); 30 server.registerDirectory( 31 "/cdn/bundles/", 32 do_get_file("test_remote_settings_startup_bundle") 33 ); 34 server.registerPathHandler("/v1/", (request, response) => { 35 response.write( 36 JSON.stringify({ 37 capabilities: { 38 attachments: { 39 base_url: `http://localhost:${server.identity.primaryPort}/cdn/`, 40 }, 41 }, 42 }) 43 ); 44 response.setHeader("Content-Type", "application/json; charset=UTF-8"); 45 response.setStatusLine(null, 200, "OK"); 46 }); 47 Services.prefs.setStringPref( 48 "services.settings.server", 49 `http://localhost:${server.identity.primaryPort}/v1` 50 ); 51 registerCleanupFunction(() => { 52 server.stop(() => {}); 53 Services.prefs.clearUserPref("services.settings.loglevel"); 54 }); 55 }); 56 57 async function clear_state() { 58 await Database.destroy(); 59 RemoteSettings._ongoingExtractBundlePromise = null; 60 } 61 62 add_task(async function test_bundle_is_pulled_when_get_needs_sync() { 63 const client = startupClients[0]; 64 65 Assert.ok( 66 !(await Utils.hasLocalDump(client.bucketName, client.collectionName)), 67 "Client has no packaged dump" 68 ); 69 70 const records = await client.get(); 71 72 Assert.equal(records.length, 6, "Records were read from startup bundle"); 73 }); 74 add_task(clear_state); 75 76 add_task( 77 async function test_signature_of_extracted_data_from_bundle_is_verified() { 78 const c = RemoteSettings("tracking-protection-lists"); // part of startup.json.mzlz4 79 c.verifySignature = true; 80 81 let called = null; 82 c.validateCollectionSignature = (records, timestamp, metadata) => { 83 called = { records, timestamp, metadata }; 84 }; 85 86 await c.get(); 87 88 Assert.ok(!!called.records.length); 89 Assert.greaterOrEqual(called.timestamp, 1694684362860); 90 Assert.ok(called.metadata.flags.includes("startup")); 91 } 92 ); 93 add_task(clear_state); 94 95 add_task(async function test_bundle_is_not_importent_when_signature_fails() { 96 const c = RemoteSettings("tracking-protection-lists"); // part of startup.json.mzlz4 97 c.verifySignature = true; 98 99 let called = false; 100 c.validateCollectionSignature = () => { 101 called = true; 102 throw new Error("boom!"); 103 }; 104 105 let error; 106 try { 107 await c.get({ emptyListFallback: false }); 108 Assert.ok(false, ".get() should fail without network"); 109 } catch (e) { 110 error = e; 111 } 112 Assert.ok(called, "Signature was verified"); 113 Assert.equal( 114 error.name, 115 "UnknownCollectionError", 116 ".get() fails without network and with bad startup bundle" 117 ); 118 }); 119 add_task(clear_state); 120 121 add_task(async function test_sync_occurs_if_collection_not_part_of_bundle() { 122 const c = RemoteSettings("foo"); 123 124 let error; 125 try { 126 await c.get({ emptyListFallback: false }); 127 Assert.ok(false, ".get() should fail when bundle disabled"); 128 } catch (e) { 129 error = e; 130 } 131 Assert.equal( 132 error.name, 133 "UnknownCollectionError", 134 ".get() fails to sync data" 135 ); 136 }); 137 add_task(clear_state); 138 139 add_task(async function test_several_clients_wait_for_bundle() { 140 // several clients calling .get() in parallel will all take content from bundle. 141 const results = await Promise.allSettled( 142 startupClients.map(c => c.get({ emptyListFallback: false })) 143 ); 144 145 Assert.deepEqual( 146 [6, 70, 5, "UnknownCollectionError", 3, 11], 147 results.map(({ status, value, reason }) => 148 status == "fulfilled" ? value.length : reason.name 149 ) 150 ); 151 }); 152 add_task(clear_state);