summaryrefslogtreecommitdiffstats
path: root/browser/extensions/formautofill/test/unit/test_storage_tombstones.js
blob: 584dac8043b236fdcefb83cdd325cf89729aec21 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
/**
 * Tests tombstones in address/creditcard records.
 */

"use strict";

let FormAutofillStorage;
add_setup(async () => {
  ({ FormAutofillStorage } = ChromeUtils.importESModule(
    "resource://autofill/FormAutofillStorage.sys.mjs"
  ));
});

const TEST_STORE_FILE_NAME = "test-tombstones.json";

const TEST_ADDRESS_1 = {
  "given-name": "Timothy",
  "additional-name": "John",
  "family-name": "Berners-Lee",
  organization: "World Wide Web Consortium",
  "street-address": "32 Vassar Street\nMIT Room 32-G524",
  "address-level2": "Cambridge",
  "address-level1": "MA",
  "postal-code": "02139",
  country: "US",
  tel: "+1 617 253 5702",
  email: "timbl@w3.org",
};

const TEST_CC_1 = {
  "cc-name": "John Doe",
  "cc-number": "4111111111111111",
  "cc-exp-month": 4,
  "cc-exp-year": 2017,
};

let do_check_tombstone_record = profile => {
  Assert.ok(profile.deleted);
  Assert.deepEqual(
    Object.keys(profile).sort(),
    ["guid", "timeLastModified", "deleted"].sort()
  );
};

// Like add_task, but actually adds 2 - one for addresses and one for cards.
function add_storage_task(test_function) {
  add_task(async function () {
    let path = getTempFile(TEST_STORE_FILE_NAME).path;
    let profileStorage = new FormAutofillStorage(path);
    let testCC1 = Object.assign({}, TEST_CC_1);
    await profileStorage.initialize();

    for (let [storage, record] of [
      [profileStorage.addresses, TEST_ADDRESS_1],
      [profileStorage.creditCards, testCC1],
    ]) {
      await test_function(storage, record);
    }
  });
}

add_storage_task(async function test_simple_tombstone(storage, record) {
  info("check simple tombstone semantics");

  let guid = await storage.add(record);
  Assert.equal((await storage.getAll()).length, 1);

  storage.remove(guid);

  // should be unable to get it normally.
  Assert.equal(await storage.get(guid), null);
  // and getAll should also not return it.
  Assert.equal((await storage.getAll()).length, 0);

  // but getAll allows us to access deleted items - but we didn't create
  // a tombstone here, so even that will not get it.
  let all = await storage.getAll({ includeDeleted: true });
  Assert.equal(all.length, 0);
});

add_storage_task(async function test_simple_synctombstone(storage, record) {
  info("check simple tombstone semantics for synced records");

  let guid = await storage.add(record);
  Assert.equal((await storage.getAll()).length, 1);

  storage.pullSyncChanges(); // force sync metadata, which triggers tombstone behaviour.

  storage.remove(guid);

  // should be unable to get it normally.
  Assert.equal(await storage.get(guid), null);
  // and getAll should also not return it.
  Assert.equal((await storage.getAll()).length, 0);

  // but getAll allows us to access deleted items.
  let all = await storage.getAll({ includeDeleted: true });
  Assert.equal(all.length, 1);

  do_check_tombstone_record(all[0]);

  // a tombstone got from API should look exactly the same as it got from the
  // disk (besides "_sync").
  let tombstoneInDisk = Object.assign(
    {},
    storage._store.data[storage._collectionName][0]
  );
  delete tombstoneInDisk._sync;
  do_check_tombstone_record(tombstoneInDisk);
});

add_storage_task(async function test_add_tombstone(storage, record) {
  info("Should be able to add a new tombstone");
  let guid = await storage.add({ guid: "test-guid-1", deleted: true });

  // should be unable to get it normally.
  Assert.equal(await storage.get(guid), null);
  // and getAll should also not return it.
  Assert.equal((await storage.getAll()).length, 0);

  // but getAll allows us to access deleted items.
  let all = await storage.getAll({ rawData: true, includeDeleted: true });
  Assert.equal(all.length, 1);

  do_check_tombstone_record(all[0]);

  // a tombstone got from API should look exactly the same as it got from the
  // disk (besides "_sync").
  let tombstoneInDisk = Object.assign(
    {},
    storage._store.data[storage._collectionName][0]
  );
  delete tombstoneInDisk._sync;
  do_check_tombstone_record(tombstoneInDisk);
});

add_storage_task(async function test_add_tombstone_without_guid(
  storage,
  record
) {
  info("Should not be able to add a new tombstone without specifying the guid");
  await Assert.rejects(storage.add({ deleted: true }), /Record missing GUID/);
  Assert.equal((await storage.getAll({ includeDeleted: true })).length, 0);
});

add_storage_task(async function test_add_tombstone_existing_guid(
  storage,
  record
) {
  info(
    "Should not be able to add a new tombstone when a record with that ID exists"
  );
  let guid = await storage.add(record);
  await Assert.rejects(
    storage.add({ guid, deleted: true }),
    /a record with this GUID already exists/
  );

  // same if the existing item is already a tombstone.
  await storage.add({ guid: "test-guid-1", deleted: true });
  await Assert.rejects(
    storage.add({ guid: "test-guid-1", deleted: true }),
    /a record with this GUID already exists/
  );
});

add_storage_task(async function test_update_tombstone(storage, record) {
  info("Updating a tombstone should fail");
  let guid = await storage.add({ guid: "test-guid-1", deleted: true });
  await Assert.rejects(storage.update(guid, {}), /No matching record./);
});

add_storage_task(async function test_remove_existing_tombstone(
  storage,
  record
) {
  info("Removing a record that's already a tombstone should be a no-op");
  let guid = await storage.add({
    guid: "test-guid-1",
    deleted: true,
    timeLastModified: 1234,
  });

  storage.remove(guid);
  let all = await storage.getAll({ rawData: true, includeDeleted: true });
  Assert.equal(all.length, 1);

  do_check_tombstone_record(all[0]);
  equal(all[0].timeLastModified, 1234); // should not be updated to now().
});