test_storage_tombstones.js (5841B)
1 /** 2 * Tests tombstones in address/creditcard records. 3 */ 4 5 "use strict"; 6 7 let FormAutofillStorage; 8 add_setup(async () => { 9 ({ FormAutofillStorage } = ChromeUtils.importESModule( 10 "resource://autofill/FormAutofillStorage.sys.mjs" 11 )); 12 }); 13 14 const TEST_STORE_FILE_NAME = "test-tombstones.json"; 15 16 const TEST_ADDRESS_1 = { 17 "given-name": "Timothy", 18 "additional-name": "John", 19 "family-name": "Berners-Lee", 20 organization: "World Wide Web Consortium", 21 "street-address": "32 Vassar Street\nMIT Room 32-G524", 22 "address-level2": "Cambridge", 23 "address-level1": "MA", 24 "postal-code": "02139", 25 country: "US", 26 tel: "+1 617 253 5702", 27 email: "timbl@w3.org", 28 }; 29 30 const TEST_CC_1 = { 31 "cc-name": "John Doe", 32 "cc-number": "4111111111111111", 33 "cc-exp-month": 4, 34 "cc-exp-year": 2017, 35 }; 36 37 let do_check_tombstone_record = profile => { 38 Assert.ok(profile.deleted); 39 Assert.deepEqual( 40 Object.keys(profile).sort(), 41 ["guid", "timeLastModified", "deleted"].sort() 42 ); 43 }; 44 45 // Like add_task, but actually adds 2 - one for addresses and one for cards. 46 function add_storage_task(test_function) { 47 add_task(async function () { 48 let path = getTempFile(TEST_STORE_FILE_NAME).path; 49 let profileStorage = new FormAutofillStorage(path); 50 let testCC1 = Object.assign({}, TEST_CC_1); 51 await profileStorage.initialize(); 52 53 for (let [storage, record] of [ 54 [profileStorage.addresses, TEST_ADDRESS_1], 55 [profileStorage.creditCards, testCC1], 56 ]) { 57 await test_function(storage, record); 58 } 59 }); 60 } 61 62 add_storage_task(async function test_simple_tombstone(storage, record) { 63 info("check simple tombstone semantics"); 64 65 let guid = await storage.add(record); 66 Assert.equal((await storage.getAll()).length, 1); 67 68 storage.remove(guid); 69 70 // should be unable to get it normally. 71 Assert.equal(await storage.get(guid), null); 72 // and getAll should also not return it. 73 Assert.equal((await storage.getAll()).length, 0); 74 75 // but getAll allows us to access deleted items - but we didn't create 76 // a tombstone here, so even that will not get it. 77 let all = await storage.getAll({ includeDeleted: true }); 78 Assert.equal(all.length, 0); 79 }); 80 81 add_storage_task(async function test_simple_synctombstone(storage, record) { 82 info("check simple tombstone semantics for synced records"); 83 84 let guid = await storage.add(record); 85 Assert.equal((await storage.getAll()).length, 1); 86 87 storage.pullSyncChanges(); // force sync metadata, which triggers tombstone behaviour. 88 89 storage.remove(guid); 90 91 // should be unable to get it normally. 92 Assert.equal(await storage.get(guid), null); 93 // and getAll should also not return it. 94 Assert.equal((await storage.getAll()).length, 0); 95 96 // but getAll allows us to access deleted items. 97 let all = await storage.getAll({ includeDeleted: true }); 98 Assert.equal(all.length, 1); 99 100 do_check_tombstone_record(all[0]); 101 102 // a tombstone got from API should look exactly the same as it got from the 103 // disk (besides "_sync"). 104 let tombstoneInDisk = Object.assign( 105 {}, 106 storage._store.data[storage._collectionName][0] 107 ); 108 delete tombstoneInDisk._sync; 109 do_check_tombstone_record(tombstoneInDisk); 110 }); 111 112 add_storage_task(async function test_add_tombstone(storage, _record) { 113 info("Should be able to add a new tombstone"); 114 let guid = await storage.add({ guid: "test-guid-1", deleted: true }); 115 116 // should be unable to get it normally. 117 Assert.equal(await storage.get(guid), null); 118 // and getAll should also not return it. 119 Assert.equal((await storage.getAll()).length, 0); 120 121 // but getAll allows us to access deleted items. 122 let all = await storage.getAll({ rawData: true, includeDeleted: true }); 123 Assert.equal(all.length, 1); 124 125 do_check_tombstone_record(all[0]); 126 127 // a tombstone got from API should look exactly the same as it got from the 128 // disk (besides "_sync"). 129 let tombstoneInDisk = Object.assign( 130 {}, 131 storage._store.data[storage._collectionName][0] 132 ); 133 delete tombstoneInDisk._sync; 134 do_check_tombstone_record(tombstoneInDisk); 135 }); 136 137 add_storage_task( 138 async function test_add_tombstone_without_guid(storage, _record) { 139 info( 140 "Should not be able to add a new tombstone without specifying the guid" 141 ); 142 await Assert.rejects(storage.add({ deleted: true }), /Record missing GUID/); 143 Assert.equal((await storage.getAll({ includeDeleted: true })).length, 0); 144 } 145 ); 146 147 add_storage_task( 148 async function test_add_tombstone_existing_guid(storage, record) { 149 info( 150 "Should not be able to add a new tombstone when a record with that ID exists" 151 ); 152 let guid = await storage.add(record); 153 await Assert.rejects( 154 storage.add({ guid, deleted: true }), 155 /a record with this GUID already exists/ 156 ); 157 158 // same if the existing item is already a tombstone. 159 await storage.add({ guid: "test-guid-1", deleted: true }); 160 await Assert.rejects( 161 storage.add({ guid: "test-guid-1", deleted: true }), 162 /a record with this GUID already exists/ 163 ); 164 } 165 ); 166 167 add_storage_task(async function test_update_tombstone(storage, _record) { 168 info("Updating a tombstone should fail"); 169 let guid = await storage.add({ guid: "test-guid-1", deleted: true }); 170 await Assert.rejects(storage.update(guid, {}), /No matching record./); 171 }); 172 173 add_storage_task( 174 async function test_remove_existing_tombstone(storage, _record) { 175 info("Removing a record that's already a tombstone should be a no-op"); 176 let guid = await storage.add({ 177 guid: "test-guid-1", 178 deleted: true, 179 timeLastModified: 1234, 180 }); 181 182 storage.remove(guid); 183 let all = await storage.getAll({ rawData: true, includeDeleted: true }); 184 Assert.equal(all.length, 1); 185 186 do_check_tombstone_record(all[0]); 187 equal(all[0].timeLastModified, 1234); // should not be updated to now(). 188 } 189 );