summaryrefslogtreecommitdiffstats
path: root/services/settings/test/unit/test_remote_settings_worker.js
blob: 42b85bb92c1415252dbb28dbeec5e3bd5bf469db (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
/* import-globals-from ../../../common/tests/unit/head_helpers.js */

const { AppConstants } = ChromeUtils.importESModule(
  "resource://gre/modules/AppConstants.sys.mjs"
);
const { TestUtils } = ChromeUtils.importESModule(
  "resource://testing-common/TestUtils.sys.mjs"
);

const { RemoteSettingsWorker } = ChromeUtils.importESModule(
  "resource://services-settings/RemoteSettingsWorker.sys.mjs"
);
const { RemoteSettingsClient } = ChromeUtils.importESModule(
  "resource://services-settings/RemoteSettingsClient.sys.mjs"
);
const { Database } = ChromeUtils.importESModule(
  "resource://services-settings/Database.sys.mjs"
);

const IS_ANDROID = AppConstants.platform == "android";

add_task(async function test_canonicaljson() {
  const records = [
    { id: "1", title: "title 1" },
    { id: "2", title: "title 2" },
  ];
  const timestamp = 42;

  const serialized = await RemoteSettingsWorker.canonicalStringify(
    records,
    timestamp
  );

  Assert.equal(
    serialized,
    '{"data":[{"id":"1","title":"title 1"},{"id":"2","title":"title 2"}],"last_modified":"42"}'
  );
});

add_task(async function test_import_json_dump_into_idb() {
  if (IS_ANDROID) {
    // Skip test: we don't ship remote settings dumps on Android (see package-manifest).
    return;
  }
  const client = new RemoteSettingsClient("language-dictionaries");
  const before = await client.get({ syncIfEmpty: false });
  Assert.equal(before.length, 0);

  await RemoteSettingsWorker.importJSONDump("main", "language-dictionaries");

  const after = await client.get({ syncIfEmpty: false });
  Assert.ok(!!after.length);
  let lastModifiedStamp = await client.getLastModified();

  Assert.equal(
    lastModifiedStamp,
    Math.max(...after.map(record => record.last_modified)),
    "Should have correct last modified timestamp"
  );

  // Force a DB close for shutdown so we can delete the DB later.
  Database._shutdownHandler();
});

add_task(async function test_throws_error_if_worker_fails() {
  let error;
  try {
    await RemoteSettingsWorker.canonicalStringify(null, 42);
  } catch (e) {
    error = e;
  }
  Assert.equal(error.message.endsWith("records is null"), true);
});

add_task(async function test_throws_error_if_worker_fails_async() {
  if (IS_ANDROID) {
    // Skip test: we don't ship dump, so importJSONDump() is no-op.
    return;
  }
  // Delete the Remote Settings database, and try to import a dump.
  // This is not supported, and the error thrown asynchronously in the worker
  // should be reported to the caller.
  await new Promise((resolve, reject) => {
    const request = indexedDB.deleteDatabase("remote-settings");
    request.onsuccess = event => resolve();
    request.onblocked = event => reject(new Error("Cannot delete DB"));
    request.onerror = event => reject(event.target.error);
  });
  let error;
  try {
    await RemoteSettingsWorker.importJSONDump("main", "language-dictionaries");
  } catch (e) {
    error = e;
  }
  Assert.ok(/IndexedDB: Error accessing remote-settings/.test(error.message));
});

add_task(async function test_throws_error_if_worker_crashes() {
  // This simulates a crash at the worker level (not within a promise).
  let error;
  try {
    await RemoteSettingsWorker._execute("unknown_method");
  } catch (e) {
    error = e;
  }
  Assert.equal(error.message, "TypeError: Agent[method] is not a function");
});

add_task(async function test_stops_worker_after_timeout() {
  // Change the idle time.
  Services.prefs.setIntPref(
    "services.settings.worker_idle_max_milliseconds",
    1
  );
  // Run a task:
  let serialized = await RemoteSettingsWorker.canonicalStringify([], 42);
  Assert.equal(serialized, '{"data":[],"last_modified":"42"}', "API works.");
  // Check that the worker gets stopped now the task is done:
  await TestUtils.waitForCondition(() => !RemoteSettingsWorker.worker);
  // Ensure the worker stays alive for 10 minutes instead:
  Services.prefs.setIntPref(
    "services.settings.worker_idle_max_milliseconds",
    600000
  );
  // Run another task:
  serialized = await RemoteSettingsWorker.canonicalStringify([], 42);
  Assert.equal(
    serialized,
    '{"data":[],"last_modified":"42"}',
    "API still works."
  );
  Assert.ok(RemoteSettingsWorker.worker, "Worker should stay alive a bit.");

  // Clear the pref.
  Services.prefs.clearUserPref(
    "services.settings.worker_idle_max_milliseconds"
  );
});