summaryrefslogtreecommitdiffstats
path: root/services/sync/tests/unit/test_fxa_migration_sentinel.js
blob: bed2dd756f7e6e49bfc2c78bb8db5bc13bcfd25d (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
/* Any copyright is dedicated to the Public Domain.
   http://creativecommons.org/publicdomain/zero/1.0/ */

// Test the reading and writing of the sync migration sentinel.
Cu.import("resource://gre/modules/Promise.jsm");
Cu.import("resource://gre/modules/FxAccounts.jsm");
Cu.import("resource://gre/modules/FxAccountsCommon.js");

Cu.import("resource://testing-common/services/sync/utils.js");
Cu.import("resource://testing-common/services/common/logging.js");

Cu.import("resource://services-sync/record.js");

// Set our username pref early so sync initializes with the legacy provider.
Services.prefs.setCharPref("services.sync.username", "foo");

// Now import sync
Cu.import("resource://services-sync/service.js");

const USER = "foo";
const PASSPHRASE = "abcdeabcdeabcdeabcdeabcdea";

function promiseStopServer(server) {
  return new Promise((resolve, reject) => {
    server.stop(resolve);
  });
}

let numServerRequests = 0;

// Helpers
function configureLegacySync() {
  let contents = {
    meta: {global: {}},
    crypto: {},
  };

  setBasicCredentials(USER, "password", PASSPHRASE);

  numServerRequests = 0;
  let server = new SyncServer({
    onRequest: () => {
      ++numServerRequests
    }
  });
  server.registerUser(USER, "password");
  server.createContents(USER, contents);
  server.start();

  Service.serverURL = server.baseURI;
  Service.clusterURL = server.baseURI;
  Service.identity.username = USER;
  Service._updateCachedURLs();

  return server;
}

// Test a simple round-trip of the get/set functions.
add_task(function *() {
  // Arrange for a legacy sync user.
  let server = configureLegacySync();

  Assert.equal((yield Service.getFxAMigrationSentinel()), null, "no sentinel to start");

  let sentinel = {foo: "bar"};
  yield Service.setFxAMigrationSentinel(sentinel);

  Assert.deepEqual((yield Service.getFxAMigrationSentinel()), sentinel, "got the sentinel back");

  yield promiseStopServer(server);
});

// Test the records are cached by the record manager.
add_task(function *() {
  // Arrange for a legacy sync user.
  let server = configureLegacySync();
  Service.login();

  // Reset the request count here as the login would have made some.
  numServerRequests = 0;

  Assert.equal((yield Service.getFxAMigrationSentinel()), null, "no sentinel to start");
  Assert.equal(numServerRequests, 1, "first fetch should hit the server");

  let sentinel = {foo: "bar"};
  yield Service.setFxAMigrationSentinel(sentinel);
  Assert.equal(numServerRequests, 2, "setting sentinel should hit the server");

  Assert.deepEqual((yield Service.getFxAMigrationSentinel()), sentinel, "got the sentinel back");
  Assert.equal(numServerRequests, 2, "second fetch should not should hit the server");

  // Clobber the caches and ensure we still get the correct value back when we
  // do hit the server.
  Service.recordManager.clearCache();
  Assert.deepEqual((yield Service.getFxAMigrationSentinel()), sentinel, "got the sentinel back");
  Assert.equal(numServerRequests, 3, "should have re-hit the server with empty caches");

  yield promiseStopServer(server);
});

// Test the records are cached by a sync.
add_task(function* () {
  let server = configureLegacySync();

  // A first sync clobbers meta/global due to it being empty, so we first
  // do a sync which forces a good set of data on the server.
  Service.sync();

  // Now create a sentinel exists on the server.  It's encrypted, so we need to
  // put an encrypted version.
  let cryptoWrapper = new CryptoWrapper("meta", "fxa_credentials");
  let sentinel = {foo: "bar"};
  cryptoWrapper.cleartext = {
    id: "fxa_credentials",
    sentinel: sentinel,
    deleted: false,
  }
  cryptoWrapper.encrypt(Service.identity.syncKeyBundle);
  let payload = {
    ciphertext: cryptoWrapper.ciphertext,
    IV:         cryptoWrapper.IV,
    hmac:       cryptoWrapper.hmac,
  };

  server.createContents(USER, {
    meta: {fxa_credentials: payload},
    crypto: {},
  });

  // Another sync - this will cause the encrypted record to be fetched.
  Service.sync();
  // Reset the request count here as the sync will have made many!
  numServerRequests = 0;

  // Asking for the sentinel should use the copy cached in the record manager.
  Assert.deepEqual((yield Service.getFxAMigrationSentinel()), sentinel, "got it");
  Assert.equal(numServerRequests, 0, "should not have hit the server");

  // And asking for it again should work (we have to work around the fact the
  // ciphertext is clobbered on first decrypt...)
  Assert.deepEqual((yield Service.getFxAMigrationSentinel()), sentinel, "got it again");
  Assert.equal(numServerRequests, 0, "should not have hit the server");

  yield promiseStopServer(server);
});

function run_test() {
  initTestLogging();
  run_next_test();
}