Revision control

Copy as Markdown

Other Tools

/* 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/. */
var { RemoteFolder } = ChromeUtils.importESModule(
);
// TODO: Figure out inclusion of support files for providing responses to
// requests.
/**
* An EWS client implementation against which we will test.
*
* @type {IExchangeClient}
*/
var ewsClient;
/**
* A Graph client implementation against which we will test.
*
* @type {IExchangeClient}
*/
var graphClient;
/**
* A mock Exchange server instance to provide request/response handling.
*
* @type {EwsServer}
*/
var ewsServer;
/**
* A mock Exchange server instance to provide request/response handling.
*
* @type {GraphServer}
*/
var graphServer;
/**
* Respective incoming server for EWS.
*
* @type {nsIMsgIncomingServer}
*/
var incomingEwsServer;
/**
* Respective incoming server for Graph.
*
* @type {nsIMsgIncomingServer}
*/
var incomingGraphServer;
add_setup(() => {
// Create and configure the EWS and Graph incoming servers.
[ewsServer, incomingEwsServer] = setupBasicEwsTestServer({});
[graphServer, incomingGraphServer] = setupBasicGraphTestServer();
ewsClient = Cc["@mozilla.org/messenger/ews-client;1"].createInstance(
Ci.IExchangeClient
);
ewsClient.initialize(
incomingEwsServer.getStringValue("ews_url"),
incomingEwsServer,
false,
"",
"",
"",
"",
""
);
graphClient = Cc["@mozilla.org/messenger/graph-client;1"].createInstance(
Ci.IExchangeClient
);
graphClient.initialize(
incomingGraphServer.getStringValue("ews_url"),
incomingGraphServer,
false,
"",
"",
"",
"",
""
);
});
/**
* Test sync where the server returns all changes and we don't have to
* worry about batching.
*/
add_task(async function testSimpleSync() {
await runSimpleSyncTest(ewsClient);
await runSimpleSyncTest(graphClient);
});
async function runSimpleSyncTest(syncClient) {
const listener = new EwsFolderCallbackListener();
syncClient.syncFolderHierarchy(listener, null);
await listener._deferred.promise;
Assert.deepEqual(
[...listener._createdFolderIds],
[
"inbox",
"deleteditems",
"drafts",
"outbox",
"sentitems",
"junkemail",
"archive",
],
"all folders should have synced"
);
Assert.ok(listener._syncStateToken, "sync token should exist");
}
/**
* Test sync when a folder does not have a folder class.
*
* These should be created even though they don't have a class.
*/
add_task(async function testSyncClasslessFolder() {
ewsServer.appendRemoteFolder(
new RemoteFolder("classless", "root", "classless", "classless", null)
);
const listener = new EwsFolderCallbackListener();
ewsClient.syncFolderHierarchy(listener, null);
await listener._deferred.promise;
Assert.deepEqual(
[...listener._createdFolderIds],
[
"inbox",
"deleteditems",
"drafts",
"outbox",
"sentitems",
"junkemail",
"archive",
"classless",
]
);
});
/**
* Test sync where the server tells us that not all changes are included
* and we have to loop.
*/
add_task(async function testSecondSyncRequired() {
await runSecondSyncRequiredTest(ewsServer, ewsClient);
await runSecondSyncRequiredTest(graphServer, graphClient);
});
async function runSecondSyncRequiredTest(syncServer, syncClient) {
syncServer.setRemoteFolders(syncServer.getWellKnownFolders());
syncServer.appendRemoteFolder(new RemoteFolder("test1", "inbox", "Test 1"));
syncServer.appendRemoteFolder(new RemoteFolder("test2", "test1", "Test 2"));
syncServer.appendRemoteFolder(new RemoteFolder("test3", "test2", "Test 3"));
syncServer.appendRemoteFolder(new RemoteFolder("test4", "test1", "Test 4"));
syncServer.appendRemoteFolder(new RemoteFolder("test5", "test1", "Test 5"));
syncServer.maxSyncItems = 4; // 12 items requires 3 requests.
const listener = new EwsFolderCallbackListener();
syncClient.syncFolderHierarchy(listener, null);
await listener._deferred.promise;
Assert.deepEqual(
[...listener._createdFolderIds],
[
"inbox",
"deleteditems",
"drafts",
"outbox",
"sentitems",
"junkemail",
"archive",
"test1",
"test2",
"test3",
"test4",
"test5",
],
"all folders should have synced"
);
Assert.ok(listener._syncStateToken, "sync token should exist");
syncServer.maxSyncItems = Infinity;
}
/**
* Test initial Graph sync when there is already a removed (restorable) item.
*/
add_task(async function testInitialGraphSyncWithRemovedItem() {
graphServer.setRemoteFolders(graphServer.getWellKnownFolders());
graphServer.appendRemoteFolder(
new RemoteFolder("restorable-folder", "root", "Restorable Folder")
);
graphServer.deleteRemoteFolderById("restorable-folder");
const listener = new GraphInitialSyncListener();
graphClient.syncFolderHierarchy(listener, null);
await listener._deferred.promise;
Assert.deepEqual(
[...listener._createdFolderIds],
[
"inbox",
"deleteditems",
"drafts",
"outbox",
"sentitems",
"junkemail",
"archive",
],
"initial sync should still create all live folders"
);
Assert.deepEqual(
listener._deletedFolderIds,
["restorable-folder"],
"initial sync should surface removed Graph items as deletes"
);
Assert.ok(listener._syncStateToken, "sync token should exist");
});
class EwsFolderCallbackListener {
QueryInterface = ChromeUtils.generateQI([
"IExchangeFolderListener",
"IExchangeFallibleOperationListener",
]);
constructor() {
this._createdFolderIds = new Set();
this._deferred = Promise.withResolvers();
}
onNewRootFolder(id) {
Assert.equal(id, "root", "root folder id should be 'root'");
}
onFolderCreated(id, _parentId, _name, _flags) {
Assert.ok(
!this._createdFolderIds.has(id),
`should not already have created folder: ${id}`
);
this._createdFolderIds.add(id);
}
onFolderUpdated(id, _name) {
// Graph tries to update, then creates on failure.
if (!this._createdFolderIds.has(id)) {
throw Components.Exception(
`cannot update unknown folder: ${id}`,
Cr.NS_ERROR_FAILURE
);
}
Assert.ok(false, `should not update ${id} on initial sync`);
}
onFolderDeleted(id) {
Assert.ok(false, `should not delete ${id} on initial sync`);
}
onSyncStateTokenChanged(syncStateToken) {
this._syncStateToken = syncStateToken;
}
onSuccess() {
this._deferred.resolve();
}
onOperationFailure(err) {
this._deferred.reject(new Error(`syncFolderHierarchy FAILED: ${err}`));
}
}
class GraphInitialSyncListener extends EwsFolderCallbackListener {
constructor() {
super();
this._deletedFolderIds = [];
}
onFolderDeleted(id) {
this._deletedFolderIds.push(id);
}
}
/**
* Test sync where the server returns busy twice.
*/
add_task(async function testSimpleSyncBusyRetry() {
ewsServer.setRemoteFolders(ewsServer.getWellKnownFolders());
ewsServer.busyResponses = 2;
const listener = new EwsFolderCallbackListener();
ewsClient.syncFolderHierarchy(listener, null);
await listener._deferred.promise;
Assert.deepEqual(
[...listener._createdFolderIds],
[
"inbox",
"deleteditems",
"drafts",
"outbox",
"sentitems",
"junkemail",
"archive",
],
"all folders should have synced"
);
Assert.ok(listener._syncStateToken, "sync token should exist");
});