summaryrefslogtreecommitdiffstats
path: root/comm/mailnews/db/gloda/test/unit/test_migration.js
blob: f7e1bc334d47ec5a43c956eea65f94e6718fe64f (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
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

/**
 * Test migration logic by artificially inducing or simulating the problem, then
 *  trigger the migration logic, then verify things ended up correct, including
 *  the schema version so a second pass of the logic doesn't happen.  (As
 *  opposed to checking in an example of a broken database and running against
 *  that.)
 */

var {
  assertExpectedMessagesIndexed,
  glodaTestHelperInitialize,
  nukeGlodaCachesAndCollections,
  waitForGlodaIndexer,
} = ChromeUtils.import("resource://testing-common/gloda/GlodaTestHelper.jsm");
var { waitForGlodaDBFlush, makeABCardForAddressPair } = ChromeUtils.import(
  "resource://testing-common/gloda/GlodaTestHelperFunctions.jsm"
);
var { sqlRun } = ChromeUtils.import(
  "resource://testing-common/gloda/GlodaQueryHelper.jsm"
);
var { GlodaMsgIndexer } = ChromeUtils.import(
  "resource:///modules/gloda/IndexMsg.jsm"
);
var { GlodaDatastore } = ChromeUtils.import(
  "resource:///modules/gloda/GlodaDatastore.jsm"
);
var { MessageGenerator } = ChromeUtils.import(
  "resource://testing-common/mailnews/MessageGenerator.jsm"
);
var { MessageInjection } = ChromeUtils.import(
  "resource://testing-common/mailnews/MessageInjection.jsm"
);

const GLODA_OLD_BAD_MESSAGE_ID = 1;

var msgGen;
var messageInjection;

add_setup(function () {
  msgGen = new MessageGenerator();
  messageInjection = new MessageInjection({ mode: "local" }, msgGen);
  glodaTestHelperInitialize(messageInjection);
});

/**
 * Fix the fallout from bug 732372 (with this patch for bug 734507) which left
 *  identities whose e-mails were in the address book without contacts and then
 *  broke messages involving them.
 */
add_task(async function test_fix_missing_contacts_and_fallout() {
  // -- Setup

  // - Create 4 e-mail addresses, 2 of which are in the address book.  (We want
  //    to make sure we have to iterate, hence >1).
  let abPeeps = msgGen.makeNamesAndAddresses(2);
  let nonAbPeeps = msgGen.makeNamesAndAddresses(2);
  makeABCardForAddressPair(abPeeps[0]);
  makeABCardForAddressPair(abPeeps[1]);

  // - Create messages of the genres [from, to]: [inAB, inAB], [inAB, !inAB],
  //    [!inAB, inAB], [!inAB, !inAB].  The permutations are black box overkill.
  // Smear the messages over multiple folders for realism.
  let [, yesyesMsgSet, yesnoMsgSet, noyesMsgSet, nonoMsgSet] =
    await messageInjection.makeFoldersWithSets(3, [
      { count: 2, from: abPeeps[0], to: [abPeeps[1]] },
      { count: 2, from: abPeeps[1], to: nonAbPeeps },
      { count: 2, from: nonAbPeeps[0], to: abPeeps },
      { count: 2, from: nonAbPeeps[1], to: [nonAbPeeps[0]] },
    ]);

  // Union the yeses together; we don't care about their composition.
  let yesMsgSet = yesyesMsgSet.union(yesnoMsgSet).union(noyesMsgSet),
    noMsgSet = nonoMsgSet;

  // - Let gloda index the messages so the identities get created.
  await waitForGlodaIndexer();
  Assert.ok(
    ...assertExpectedMessagesIndexed([yesMsgSet, noMsgSet], { augment: true })
  );
  // The messages are now indexed and the contacts created.

  // - Compel an indexing sweep so the folder's dirty statuses get cleared
  GlodaMsgIndexer.initialSweep();
  await waitForGlodaIndexer();
  Assert.ok(...assertExpectedMessagesIndexed([])); // (no new messages to index)

  // - Force a DB commit so the pending commit tracker gets emptied out
  // (otherwise we need to worry about its state overriding our clobbering)
  await waitForGlodaDBFlush();

  // - Delete the contact records for the people in the address book.
  await sqlRun(
    "DELETE FROM contacts WHERE id IN (" +
      yesMsgSet.glodaMessages[0].from.contact.id +
      ", " +
      yesMsgSet.glodaMessages[0].to[0].contact.id +
      ")"
  );

  // - Nuke the gloda caches so we totally forget those contact records.
  nukeGlodaCachesAndCollections();

  // - Manually mark the messages involving the inAB people with the _old_ bad
  //    id marker so that our scan will see them.
  for (let msgHdr of yesMsgSet.msgHdrs()) {
    msgHdr.setUint32Property("gloda-id", GLODA_OLD_BAD_MESSAGE_ID);
  }

  // - Mark the db schema version to the version with the bug (26).
  // Sanity check that gloda actually populates the value with the current
  //  version correctly.
  Assert.equal(
    GlodaDatastore._actualSchemaVersion,
    GlodaDatastore._schemaVersion
  );
  GlodaDatastore._actualSchemaVersion = 26;
  await sqlRun("PRAGMA user_version = 26");
  // Make sure that took, since we check it below as a success indicator.
  let verRows = await sqlRun("PRAGMA user_version");
  Assert.equal(verRows[0].getInt64(0), 26);

  // -- Test
  // - Trigger the migration logic and request an indexing sweep.
  GlodaMsgIndexer.disable();
  GlodaMsgIndexer.enable();
  GlodaMsgIndexer.initialSweep();

  // - Wait for the indexer to complete, expecting that the messages that we
  //    marked bad will get indexed but not the good messages.
  await waitForGlodaIndexer();
  Assert.ok(...assertExpectedMessagesIndexed([yesMsgSet], { augment: true }));

  // - Verify that the identities have contacts again.
  // Must have the contact object.
  Assert.notEqual(yesMsgSet.glodaMessages[0].from.contact, undefined);
  // The contact's name should come from the address book card
  Assert.equal(yesMsgSet.glodaMessages[0].from.contact.name, abPeeps[0][0]);

  // - Verify that the schema version changed from gloda's perspective and from
  //    the db's perspective.
  verRows = await sqlRun("PRAGMA user_version");
  Assert.equal(verRows[0].getInt64(0), GlodaDatastore._schemaVersion);
  Assert.equal(
    GlodaDatastore._actualSchemaVersion,
    GlodaDatastore._schemaVersion
  );
});