summaryrefslogtreecommitdiffstats
path: root/toolkit/components/places/tests/migration/test_current_from_v43.js
blob: 70a383bb2e7eccc7be0ce7a8122fa9c824cb4cdc (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
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
/* Any copyright is dedicated to the Public Domain.
 * http://creativecommons.org/publicdomain/zero/1.0/ */

"use strict";

const EXPECTED_REMAINING_ROOTS = [
  ...PlacesUtils.bookmarks.userContentRoots,
  PlacesUtils.bookmarks.tagsGuid,
];

const EXPECTED_REMOVED_BOOKMARK_GUIDS = [
  // These first ones are the old left-pane folder queries
  "SNLmwJH6GtW9", // Root Query
  "r0dY_2_y4mlx", // History
  "xGGhZK3b6GnW", // Downloads
  "EJG6I1nKkQFQ", // Tags
  "gSyHo5oNSUJV", // All Bookmarks
  // These are simulated add-on injections that we expect to be removed.
  "exaddon_____",
  "exaddon1____",
  "exaddon2____",
  "exaddon3____",
  "test________",
];

const EXPECTED_REMOVED_ANNOTATIONS = [
  "PlacesOrganizer/OrganizerFolder",
  "PlacesOrganizer/OrganizerQuery",
];

const EXPECTED_REMOVED_PLACES_ENTRIES = ["exaddonh____", "exaddonh3___"];
const EXPECTED_KEPT_PLACES_ENTRY = "exaddonh2___";
const EXPECTED_REMOVED_KEYWORDS = ["exaddon", "exaddon2"];

async function assertItemIn(db, table, field, expectedItems) {
  let rows = await db.execute(`SELECT ${field} from ${table}`);

  Assert.ok(
    rows.length >= expectedItems.length,
    "Should be at least the number of annotations we expect to be removed."
  );

  let fieldValues = rows.map(row => row.getResultByName(field));

  for (let item of expectedItems) {
    Assert.ok(
      fieldValues.includes(item),
      `${table} should have ${expectedItems}`
    );
  }
}

add_task(async function setup() {
  await setupPlacesDatabase("places_v43.sqlite");

  // Setup database contents to be migrated.
  let path = PathUtils.join(PathUtils.profileDir, DB_FILENAME);
  let db = await Sqlite.openConnection({ path });

  let rows = await db.execute(`SELECT * FROM moz_bookmarks_deleted`);
  Assert.equal(rows.length, 0, "Should be nothing in moz_bookmarks_deleted");

  // Break roots parenting, to test for Bug 1472127.
  await db.execute(`INSERT INTO moz_bookmarks (title, parent, position, guid)
                    VALUES ("test", 1, 0, "test________")`);
  await db.execute(`UPDATE moz_bookmarks
                    SET parent = (SELECT id FROM moz_bookmarks WHERE guid = "test________")
                    WHERE guid = "menu________"`);

  await assertItemIn(
    db,
    "moz_anno_attributes",
    "name",
    EXPECTED_REMOVED_ANNOTATIONS
  );
  await assertItemIn(
    db,
    "moz_bookmarks",
    "guid",
    EXPECTED_REMOVED_BOOKMARK_GUIDS
  );
  await assertItemIn(db, "moz_keywords", "keyword", EXPECTED_REMOVED_KEYWORDS);
  await assertItemIn(db, "moz_places", "guid", EXPECTED_REMOVED_PLACES_ENTRIES);

  await db.close();
});

add_task(async function database_is_valid() {
  // Accessing the database for the first time triggers migration.
  Assert.equal(
    PlacesUtils.history.databaseStatus,
    PlacesUtils.history.DATABASE_STATUS_UPGRADED
  );

  let db = await PlacesUtils.promiseDBConnection();
  Assert.equal(await db.getSchemaVersion(), CURRENT_SCHEMA_VERSION);
});

add_task(async function test_roots_removed() {
  let db = await PlacesUtils.promiseDBConnection();
  let rows = await db.execute(
    `
    SELECT id FROM moz_bookmarks
    WHERE guid = :guid
  `,
    { guid: PlacesUtils.bookmarks.rootGuid }
  );
  Assert.equal(rows.length, 1, "Should have exactly one root row.");
  let rootId = rows[0].getResultByName("id");

  rows = await db.execute(
    `
    SELECT guid FROM moz_bookmarks
    WHERE parent = :rootId`,
    { rootId }
  );

  Assert.equal(
    rows.length,
    EXPECTED_REMAINING_ROOTS.length,
    "Should only have the built-in folder roots."
  );

  for (let row of rows) {
    let guid = row.getResultByName("guid");
    Assert.ok(
      EXPECTED_REMAINING_ROOTS.includes(guid),
      `Should have only the expected guids remaining, unexpected guid: ${guid}`
    );
  }

  // Check the reparented menu now.
  rows = await db.execute(
    `
    SELECT id, parent FROM moz_bookmarks
    WHERE guid = :guid
  `,
    { guid: PlacesUtils.bookmarks.menuGuid }
  );
  Assert.equal(rows.length, 1, "Should have found the menu root.");
  Assert.equal(
    rows[0].getResultByName("parent"),
    await PlacesUtils.promiseItemId(PlacesUtils.bookmarks.rootGuid),
    "Should have moved the menu back to the Places root."
  );
});

add_task(async function test_tombstones_added() {
  let db = await PlacesUtils.promiseDBConnection();

  let rows = await db.execute(`
    SELECT guid FROM moz_bookmarks_deleted
  `);

  for (let row of rows) {
    let guid = row.getResultByName("guid");
    Assert.ok(
      EXPECTED_REMOVED_BOOKMARK_GUIDS.includes(guid),
      `Should have tombstoned the expected guids, unexpected guid: ${guid}`
    );
  }

  Assert.equal(
    rows.length,
    EXPECTED_REMOVED_BOOKMARK_GUIDS.length,
    "Should have removed all the expected bookmarks."
  );
});

add_task(async function test_annotations_removed() {
  let db = await PlacesUtils.promiseDBConnection();

  await assertAnnotationsRemoved(db, EXPECTED_REMOVED_ANNOTATIONS);
});

add_task(async function test_check_history_entries() {
  let db = await PlacesUtils.promiseDBConnection();

  for (let entry of EXPECTED_REMOVED_PLACES_ENTRIES) {
    let rows = await db.execute(`
      SELECT id FROM moz_places
      WHERE guid = '${entry}'`);

    Assert.equal(
      rows.length,
      0,
      `Should have removed an orphaned history entry ${EXPECTED_REMOVED_PLACES_ENTRIES}.`
    );
  }

  let rows = await db.execute(
    `
    SELECT foreign_count FROM moz_places
    WHERE guid = :guid
  `,
    { guid: EXPECTED_KEPT_PLACES_ENTRY }
  );

  Assert.equal(
    rows.length,
    1,
    `Should have kept visited history entry ${EXPECTED_KEPT_PLACES_ENTRY}`
  );

  let foreignCount = rows[0].getResultByName("foreign_count");
  Assert.equal(
    foreignCount,
    0,
    `Should have updated the foreign_count for ${EXPECTED_KEPT_PLACES_ENTRY}`
  );
});

add_task(async function test_check_keyword_removed() {
  let db = await PlacesUtils.promiseDBConnection();

  for (let keyword of EXPECTED_REMOVED_KEYWORDS) {
    let rows = await db.execute(
      `
      SELECT keyword FROM moz_keywords
      WHERE keyword = :keyword
    `,
      { keyword }
    );

    Assert.equal(
      rows.length,
      0,
      `Should have removed the expected keyword: ${keyword}.`
    );
  }
});

add_task(async function test_no_orphan_annotations() {
  let db = await PlacesUtils.promiseDBConnection();

  await assertNoOrphanAnnotations(db);
});

add_task(async function test_no_orphan_keywords() {
  let db = await PlacesUtils.promiseDBConnection();

  let rows = await db.execute(`
    SELECT place_id FROM moz_keywords
    WHERE place_id NOT IN (SELECT id from moz_places)
  `);

  Assert.equal(rows.length, 0, `Should have no orphan keywords.`);
});

add_task(async function test_meta_exists() {
  let db = await PlacesUtils.promiseDBConnection();
  await db.execute(`SELECT 1 FROM moz_meta`);
});