test_migrateRecords.js (13034B)
1 /** 2 * Tests the migration algorithm in profileStorage. 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-profile.json"; 15 16 const { ADDRESS_SCHEMA_VERSION } = ChromeUtils.importESModule( 17 "resource://autofill/FormAutofillStorageBase.sys.mjs" 18 ); 19 const { CREDIT_CARD_SCHEMA_VERSION } = ChromeUtils.importESModule( 20 "resource://autofill/FormAutofillStorageBase.sys.mjs" 21 ); 22 23 const ADDRESS_TESTCASES = [ 24 { 25 description: 26 "The record version is equal to the current version. The migration shouldn't be invoked.", 27 record: { 28 guid: "test-guid", 29 version: ADDRESS_SCHEMA_VERSION, 30 // The cached address-line1 field doesn't align "street-address" but it 31 // won't be recomputed because the migration isn't invoked. 32 "address-line1": "Some Address", 33 "street-address": "32 Vassar Street", 34 }, 35 expectedResult: { 36 guid: "test-guid", 37 version: ADDRESS_SCHEMA_VERSION, 38 "address-line1": "Some Address", 39 "street-address": "32 Vassar Street", 40 }, 41 }, 42 { 43 description: 44 "The record version is greater than the current version. The migration shouldn't be invoked.", 45 record: { 46 guid: "test-guid", 47 version: 99, 48 "address-line1": "Some Address", 49 "street-address": "32 Vassar Street", 50 }, 51 expectedResult: { 52 guid: "test-guid", 53 version: 99, 54 "address-line1": "Some Address", 55 "street-address": "32 Vassar Street", 56 }, 57 }, 58 { 59 description: 60 "The record version is less than the current version. The migration should be invoked.", 61 record: { 62 guid: "test-guid", 63 version: 0, 64 "address-line1": "Some Address", 65 "street-address": "32 Vassar Street", 66 }, 67 expectedResult: { 68 guid: "test-guid", 69 version: ADDRESS_SCHEMA_VERSION, 70 "address-line1": "32 Vassar Street", 71 "street-address": "32 Vassar Street", 72 }, 73 }, 74 { 75 description: 76 "The record version is omitted. The migration should be invoked.", 77 record: { 78 guid: "test-guid", 79 "address-line1": "Some Address", 80 "street-address": "32 Vassar Street", 81 "unknown-1": "an unknown field from another client", 82 }, 83 expectedResult: { 84 guid: "test-guid", 85 version: ADDRESS_SCHEMA_VERSION, 86 "address-line1": "32 Vassar Street", 87 "street-address": "32 Vassar Street", 88 "unknown-1": "an unknown field from another client", 89 }, 90 }, 91 { 92 description: 93 "The record version is an invalid value. The migration should be invoked.", 94 record: { 95 guid: "test-guid", 96 version: "ABCDE", 97 "address-line1": "Some Address", 98 "street-address": "32 Vassar Street", 99 "unknown-1": "an unknown field from another client", 100 }, 101 expectedResult: { 102 guid: "test-guid", 103 version: ADDRESS_SCHEMA_VERSION, 104 "address-line1": "32 Vassar Street", 105 "street-address": "32 Vassar Street", 106 "unknown-1": "an unknown field from another client", 107 }, 108 }, 109 { 110 description: 111 "The omitted computed fields should be always recomputed even the record version is up-to-date.", 112 record: { 113 guid: "test-guid", 114 version: ADDRESS_SCHEMA_VERSION, 115 "street-address": "32 Vassar Street", 116 }, 117 expectedResult: { 118 guid: "test-guid", 119 version: ADDRESS_SCHEMA_VERSION, 120 "address-line1": "32 Vassar Street", 121 "street-address": "32 Vassar Street", 122 }, 123 }, 124 { 125 description: "The migration shouldn't be invoked on tombstones.", 126 record: { 127 guid: "test-guid", 128 timeLastModified: 12345, 129 deleted: true, 130 }, 131 expectedResult: { 132 guid: "test-guid", 133 timeLastModified: 12345, 134 deleted: true, 135 136 // Make sure no new fields are appended. 137 version: undefined, 138 name: undefined, 139 }, 140 }, 141 142 // Bug 1836438 - Migrate "*-name" to "name" 143 { 144 description: 145 "Migrate address - `given-name`, `additional-name`, and `family-name` should be migrated to `name`", 146 record: { 147 guid: "test-guid", 148 version: ADDRESS_SCHEMA_VERSION, 149 "given-name": "Timothy", 150 "additional-name": "John", 151 "family-name": "Berners-Lee", 152 }, 153 expectedResult: { 154 guid: "test-guid", 155 version: ADDRESS_SCHEMA_VERSION, 156 name: "Timothy John Berners-Lee", 157 }, 158 }, 159 { 160 description: "Migrate address - `given-name` should be migrated to `name`", 161 record: { 162 guid: "test-guid", 163 version: ADDRESS_SCHEMA_VERSION, 164 "given-name": "Timothy", 165 }, 166 expectedResult: { 167 guid: "test-guid", 168 version: ADDRESS_SCHEMA_VERSION, 169 name: "Timothy", 170 }, 171 }, 172 { 173 description: 174 "Migrate address - `additional-name` should be migrated to `name`", 175 record: { 176 guid: "test-guid", 177 version: ADDRESS_SCHEMA_VERSION, 178 "additional-name": "John", 179 }, 180 expectedResult: { 181 guid: "test-guid", 182 version: ADDRESS_SCHEMA_VERSION, 183 name: "John", 184 }, 185 }, 186 { 187 description: "Migrate address - `family-name` should be migrated to `name`", 188 record: { 189 guid: "test-guid", 190 version: ADDRESS_SCHEMA_VERSION, 191 "family-name": "Berners-Lee", 192 }, 193 expectedResult: { 194 guid: "test-guid", 195 version: ADDRESS_SCHEMA_VERSION, 196 name: "Berners-Lee", 197 }, 198 }, 199 { 200 description: 201 "Migrate address - `name` should still be empty when there is no *-name fields in the record", 202 record: { 203 guid: "test-guid", 204 version: ADDRESS_SCHEMA_VERSION, 205 }, 206 expectedResult: { 207 guid: "test-guid", 208 version: ADDRESS_SCHEMA_VERSION, 209 }, 210 }, 211 { 212 description: 213 "Migrate address - do not run migration as long as the name field exists", 214 record: { 215 guid: "test-guid", 216 version: ADDRESS_SCHEMA_VERSION, 217 // The cached field doesn't align "name" but it 218 // won't be recomputed because the migration isn't invoked. 219 "given-name": "Timothy", 220 "additional-name": "John", 221 "family-name": "Berners-Lee", 222 name: "Jane", 223 }, 224 expectedResult: { 225 guid: "test-guid", 226 version: ADDRESS_SCHEMA_VERSION, 227 "given-name": "Timothy", 228 "additional-name": "John", 229 "family-name": "Berners-Lee", 230 name: "Jane", 231 }, 232 }, 233 ]; 234 235 const CREDIT_CARD_TESTCASES = [ 236 { 237 description: 238 "The record version is equal to the current version. The migration shouldn't be invoked.", 239 record: { 240 guid: "test-guid", 241 version: CREDIT_CARD_SCHEMA_VERSION, 242 "cc-name": "Timothy", 243 "cc-given-name": "John", // The cached "cc-given-name" field doesn't align 244 // "cc-name" but it won't be recomputed because 245 // the migration isn't invoked. 246 }, 247 expectedResult: { 248 guid: "test-guid", 249 version: CREDIT_CARD_SCHEMA_VERSION, 250 "cc-name": "Timothy", 251 "cc-given-name": "John", 252 }, 253 }, 254 { 255 description: 256 "The record version is greater than the current version. The migration shouldn't be invoked.", 257 record: { 258 guid: "test-guid", 259 version: 99, 260 "cc-name": "Timothy", 261 "cc-given-name": "John", 262 }, 263 expectedResult: { 264 guid: "test-guid", 265 version: 99, 266 "cc-name": "Timothy", 267 "cc-given-name": "John", 268 }, 269 }, 270 { 271 description: 272 "The record version is less than the current version. The migration should be invoked.", 273 record: { 274 guid: "test-guid", 275 version: 0, 276 "cc-name": "Timothy", 277 "cc-given-name": "John", 278 }, 279 expectedResult: { 280 guid: "test-guid", 281 version: CREDIT_CARD_SCHEMA_VERSION, 282 "cc-name": "Timothy", 283 "cc-given-name": "Timothy", 284 }, 285 }, 286 { 287 description: 288 "The record version is omitted. The migration should be invoked.", 289 record: { 290 guid: "test-guid", 291 "cc-name": "Timothy", 292 "cc-given-name": "John", 293 "unknown-1": "an unknown field from another client", 294 }, 295 expectedResult: { 296 guid: "test-guid", 297 version: CREDIT_CARD_SCHEMA_VERSION, 298 "cc-name": "Timothy", 299 "cc-given-name": "Timothy", 300 "unknown-1": "an unknown field from another client", 301 }, 302 }, 303 { 304 description: 305 "The record version is an invalid value. The migration should be invoked.", 306 record: { 307 guid: "test-guid", 308 version: "ABCDE", 309 "cc-name": "Timothy", 310 "cc-given-name": "John", 311 "unknown-1": "an unknown field from another client", 312 }, 313 expectedResult: { 314 guid: "test-guid", 315 version: CREDIT_CARD_SCHEMA_VERSION, 316 "cc-name": "Timothy", 317 "cc-given-name": "Timothy", 318 "unknown-1": "an unknown field from another client", 319 }, 320 }, 321 { 322 description: 323 "The omitted computed fields should be always recomputed even the record version is up-to-date.", 324 record: { 325 guid: "test-guid", 326 version: CREDIT_CARD_SCHEMA_VERSION, 327 "cc-name": "Timothy", 328 }, 329 expectedResult: { 330 guid: "test-guid", 331 version: CREDIT_CARD_SCHEMA_VERSION, 332 "cc-name": "Timothy", 333 "cc-given-name": "Timothy", 334 }, 335 }, 336 { 337 description: "The migration shouldn't be invoked on tombstones.", 338 record: { 339 guid: "test-guid", 340 timeLastModified: 12345, 341 deleted: true, 342 }, 343 expectedResult: { 344 guid: "test-guid", 345 timeLastModified: 12345, 346 deleted: true, 347 348 // Make sure no new fields are appended. 349 version: undefined, 350 "cc-given-name": undefined, 351 }, 352 }, 353 ]; 354 355 let do_check_record_matches = (expectedRecord, record) => { 356 for (let key in expectedRecord) { 357 Assert.equal(expectedRecord[key], record[key]); 358 } 359 }; 360 361 add_task(async function test_migrateAddressRecords() { 362 let path = getTempFile(TEST_STORE_FILE_NAME).path; 363 364 let profileStorage = new FormAutofillStorage(path); 365 await profileStorage.initialize(); 366 367 for (let testcase of ADDRESS_TESTCASES) { 368 info(testcase.description); 369 profileStorage._store.data.addresses = [testcase.record]; 370 await profileStorage.addresses._migrateRecord(testcase.record, 0); 371 do_check_record_matches( 372 testcase.expectedResult, 373 profileStorage.addresses._data[0] 374 ); 375 } 376 }); 377 378 add_task(async function test_migrateCreditCardRecords() { 379 let path = getTempFile(TEST_STORE_FILE_NAME).path; 380 381 let profileStorage = new FormAutofillStorage(path); 382 await profileStorage.initialize(); 383 384 for (let testcase of CREDIT_CARD_TESTCASES) { 385 info(testcase.description); 386 profileStorage._store.data.creditCards = [testcase.record]; 387 await profileStorage.creditCards._migrateRecord(testcase.record, 0); 388 do_check_record_matches( 389 testcase.expectedResult, 390 profileStorage.creditCards._data[0] 391 ); 392 } 393 }); 394 395 add_task(async function test_migrateEncryptedCreditCardNumber() { 396 let path = getTempFile(TEST_STORE_FILE_NAME).path; 397 398 let profileStorage = new FormAutofillStorage(path); 399 await profileStorage.initialize(); 400 401 info("v1 and v2 schema cards should be abandoned."); 402 403 let v1record = { 404 guid: "test-guid1", 405 version: 1, 406 "cc-name": "Timothy", 407 "cc-number-encrypted": "aaaa", 408 }; 409 410 let v2record = { 411 guid: "test-guid2", 412 version: 2, 413 "cc-name": "Bob", 414 "cc-number-encrypted": "bbbb", 415 }; 416 417 profileStorage._store.data.creditCards = [v1record, v2record]; 418 await profileStorage.creditCards._migrateRecord(v1record, 0); 419 await profileStorage.creditCards._migrateRecord(v2record, 1); 420 v1record = profileStorage.creditCards._data[0]; 421 v2record = profileStorage.creditCards._data[1]; 422 423 Assert.ok(v1record.deleted); 424 Assert.ok(v2record.deleted); 425 }); 426 427 add_task(async function test_migrateDeprecatedCreditCardV4() { 428 let path = getTempFile(TEST_STORE_FILE_NAME).path; 429 430 let profileStorage = new FormAutofillStorage(path); 431 await profileStorage.initialize(); 432 433 let records = [ 434 { 435 guid: "test-guid1", 436 version: CREDIT_CARD_SCHEMA_VERSION, 437 "cc-name": "Alice", 438 _sync: { 439 changeCounter: 0, 440 lastSyncedFields: {}, 441 }, 442 }, 443 { 444 guid: "test-guid2", 445 version: 4, 446 "cc-name": "Timothy", 447 _sync: { 448 changeCounter: 0, 449 lastSyncedFields: {}, 450 }, 451 }, 452 { 453 guid: "test-guid3", 454 version: 4, 455 "cc-name": "Bob", 456 }, 457 ]; 458 459 profileStorage._store.data.creditCards = records; 460 for (let idx = 0; idx < records.length; idx++) { 461 await profileStorage.creditCards._migrateRecord(records[idx], idx); 462 } 463 464 profileStorage.creditCards.pullSyncChanges(); 465 466 // Record that has already synced before, do not sync again 467 equal(getSyncChangeCounter(profileStorage.creditCards, records[0].guid), 0); 468 469 // alaways force sync v4 record 470 equal(records[1].version, CREDIT_CARD_SCHEMA_VERSION); 471 equal(getSyncChangeCounter(profileStorage.creditCards, records[1].guid), 1); 472 473 equal(records[2].version, CREDIT_CARD_SCHEMA_VERSION); 474 equal(getSyncChangeCounter(profileStorage.creditCards, records[2].guid), 1); 475 });