diff options
Diffstat (limited to 'comm/mailnews/addrbook/test/unit/test_cardDAV_syncV2.js')
-rw-r--r-- | comm/mailnews/addrbook/test/unit/test_cardDAV_syncV2.js | 408 |
1 files changed, 408 insertions, 0 deletions
diff --git a/comm/mailnews/addrbook/test/unit/test_cardDAV_syncV2.js b/comm/mailnews/addrbook/test/unit/test_cardDAV_syncV2.js new file mode 100644 index 0000000000..74f9c5ac88 --- /dev/null +++ b/comm/mailnews/addrbook/test/unit/test_cardDAV_syncV2.js @@ -0,0 +1,408 @@ +/* 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/. */ + +async function subtest() { + // Put some cards on the server. + CardDAVServer.putCardInternal( + "keep-me.vcf", + "BEGIN:VCARD\r\nUID:keep-me\r\nFN:I'm going to stay.\r\nEND:VCARD\r\n" + ); + CardDAVServer.putCardInternal( + "change-me.vcf", + // This one includes a character encoded with UTF-8. + "BEGIN:VCARD\r\nUID:change-me\r\nFN:I'm going to be changed. \xCF\x9E\r\nEND:VCARD\r\n" + ); + CardDAVServer.putCardInternal( + "delete-me.vcf", + "BEGIN:VCARD\r\nUID:delete-me\r\nFN:I'm going to be deleted.\r\nEND:VCARD\r\n" + ); + + let directory = initDirectory(); + + // We'll only use this for the initial sync, so I think it's okay to use + // bulkAddCards and not get a notification for every contact. + info("Initial sync with server."); + await directory.fetchAllFromServer(); + + let lastSyncToken = directory._syncToken; + info(`Token is: ${lastSyncToken}`); + + info("Cards:"); + let cardMap = new Map(); + let oldETags = new Map(); + for (let card of directory.childCards) { + info( + ` ${card.displayName} [${card.getProperty( + "_href", + "" + )}, ${card.getProperty("_etag", "")}]` + ); + + cardMap.set(card.UID, card); + oldETags.set(card.UID, card.getProperty("_etag", "")); + } + + Assert.equal(cardMap.size, 3); + Assert.deepEqual([...cardMap.keys()].sort(), [ + "change-me", + "delete-me", + "keep-me", + ]); + Assert.equal( + cardMap.get("change-me").displayName, + "I'm going to be changed. Ϟ" + ); + + // Make some changes on the server. + + CardDAVServer.putCardInternal( + "change-me.vcf", + "BEGIN:VCARD\r\nUID:change-me\r\nFN:I've been changed.\r\nEND:VCARD\r\n" + ); + CardDAVServer.deleteCardInternal("delete-me.vcf"); + CardDAVServer.putCardInternal( + "new.vcf", + "BEGIN:VCARD\r\nUID:new\r\nFN:I'm new!\r\nEND:VCARD\r\n" + ); + + // Sync with the server. + + info("Second sync with server."); + + observer.init(); + await directory.updateAllFromServerV2(); + Assert.notEqual(directory._syncToken, lastSyncToken); + lastSyncToken = directory._syncToken; + observer.checkAndClearNotifications({ + "addrbook-contact-created": ["new"], + "addrbook-contact-updated": ["change-me"], + "addrbook-contact-deleted": ["delete-me"], + }); + + info("Cards:"); + cardMap.clear(); + for (let card of directory.childCards) { + info( + ` ${card.displayName} [${card.getProperty( + "_href", + "" + )}, ${card.getProperty("_etag", "")}]` + ); + + cardMap.set(card.UID, card); + } + + Assert.equal(cardMap.size, 3); + Assert.deepEqual([...cardMap.keys()].sort(), ["change-me", "keep-me", "new"]); + + Assert.equal( + cardMap.get("keep-me").getProperty("_etag", ""), + oldETags.get("keep-me") + ); + + Assert.equal(cardMap.get("change-me").displayName, "I've been changed."); + Assert.notEqual( + cardMap.get("change-me").getProperty("_etag", ""), + oldETags.get("change-me") + ); + oldETags.set("change-me", cardMap.get("change-me").getProperty("_etag", "")); + + Assert.equal(cardMap.get("new").displayName, "I'm new!"); + oldETags.set("new", cardMap.get("new").getProperty("_etag", "")); + + oldETags.delete("delete-me"); + + // Double-check that what we have matches what's on the server. + + await checkCardsOnServer({ + "change-me": { + etag: cardMap.get("change-me").getProperty("_etag", ""), + href: cardMap.get("change-me").getProperty("_href", ""), + vCard: cardMap.get("change-me").getProperty("_vCard", ""), + }, + "keep-me": { + etag: cardMap.get("keep-me").getProperty("_etag", ""), + href: cardMap.get("keep-me").getProperty("_href", ""), + vCard: cardMap.get("keep-me").getProperty("_vCard", ""), + }, + new: { + etag: cardMap.get("new").getProperty("_etag", ""), + href: cardMap.get("new").getProperty("_href", ""), + vCard: cardMap.get("new").getProperty("_vCard", ""), + }, + }); + + info("Third sync with server. No changes expected."); + + await directory.updateAllFromServerV2(); + // This time the token should NOT change, there's been no contact with the + // server since last time. + Assert.equal(directory._syncToken, lastSyncToken); + lastSyncToken = directory._syncToken; + + observer.checkAndClearNotifications({ + "addrbook-contact-created": [], + "addrbook-contact-updated": [], + "addrbook-contact-deleted": [], + }); + + // Delete a card on the client. + + info("Deleting a card on the client."); + + try { + directory.deleteCards([cardMap.get("new")]); + Assert.ok(!directory.readOnly, "read-only directory should throw."); + observer.checkAndClearNotifications({ + "addrbook-contact-created": [], + "addrbook-contact-updated": [], + "addrbook-contact-deleted": ["new"], + }); + + await checkCardsOnServer({ + "change-me": { + etag: cardMap.get("change-me").getProperty("_etag", ""), + href: cardMap.get("change-me").getProperty("_href", ""), + vCard: cardMap.get("change-me").getProperty("_vCard", ""), + }, + "keep-me": { + etag: cardMap.get("keep-me").getProperty("_etag", ""), + href: cardMap.get("keep-me").getProperty("_href", ""), + vCard: cardMap.get("keep-me").getProperty("_vCard", ""), + }, + }); + } catch (ex) { + Assert.ok(directory.readOnly, "read-write directory should not throw"); + } + + // Change a card on the client. + + info("Changing a card on the client."); + + try { + let changeMeCard = cardMap.get("change-me"); + changeMeCard.displayName = "I've been changed again!"; + directory.modifyCard(changeMeCard); + Assert.ok(!directory.readOnly, "read-only directory should throw."); + + Assert.equal( + await observer.waitFor("addrbook-contact-updated"), + "change-me" + ); + observer.checkAndClearNotifications({ + "addrbook-contact-created": [], + "addrbook-contact-updated": ["change-me"], + "addrbook-contact-deleted": [], + }); + + changeMeCard = directory.childCards.find(c => c.UID == "change-me"); + cardMap.set("change-me", changeMeCard); + + await checkCardsOnServer({ + "change-me": { + etag: changeMeCard.getProperty("_etag", ""), + href: changeMeCard.getProperty("_href", ""), + vCard: changeMeCard.getProperty("_vCard", ""), + }, + "keep-me": { + etag: cardMap.get("keep-me").getProperty("_etag", ""), + href: cardMap.get("keep-me").getProperty("_href", ""), + vCard: cardMap.get("keep-me").getProperty("_vCard", ""), + }, + }); + } catch (ex) { + Assert.ok(directory.readOnly, "read-write directory should not throw"); + } + + // Add a new card on the client. + + info("Adding a new card on the client."); + + try { + let newCard = Cc["@mozilla.org/addressbook/cardproperty;1"].createInstance( + Ci.nsIAbCard + ); + newCard.displayName = "I'm another new contact. ϔ"; + newCard.UID = "another-new"; + newCard = directory.addCard(newCard); + Assert.ok(!directory.readOnly, "read-only directory should throw."); + observer.checkAndClearNotifications({ + "addrbook-contact-created": ["another-new"], + "addrbook-contact-updated": [], + "addrbook-contact-deleted": [], + }); + + Assert.equal( + await observer.waitFor("addrbook-contact-updated"), + "another-new" + ); + + newCard = directory.childCards.find(c => c.UID == "another-new"); + Assert.equal( + newCard.displayName, + "I'm another new contact. ϔ", + "non-ascii character survived the trip to the server" + ); + + await checkCardsOnServer({ + "another-new": { + etag: newCard.getProperty("_etag", ""), + href: newCard.getProperty("_href", ""), + vCard: newCard.getProperty("_vCard", ""), + }, + "change-me": { + etag: cardMap.get("change-me").getProperty("_etag", ""), + href: cardMap.get("change-me").getProperty("_href", ""), + vCard: cardMap.get("change-me").getProperty("_vCard", ""), + }, + "keep-me": { + etag: cardMap.get("keep-me").getProperty("_etag", ""), + href: cardMap.get("keep-me").getProperty("_href", ""), + vCard: cardMap.get("keep-me").getProperty("_vCard", ""), + }, + }); + } catch (ex) { + Assert.ok(directory.readOnly, "read-write directory should not throw"); + } + + info("Fourth sync with server. No changes expected."); + + await directory.updateAllFromServerV2(); + if (directory.readOnly) { + Assert.equal(directory._syncToken, lastSyncToken); + } else { + Assert.notEqual(directory._syncToken, lastSyncToken); + } + + observer.checkAndClearNotifications({ + "addrbook-contact-created": [], + "addrbook-contact-updated": [], + "addrbook-contact-deleted": [], + }); + + await clearDirectory(directory); + CardDAVServer.reset(); +} + +add_task(async function testNormal() { + await subtest(); +}); + +add_task(async function testGoogle() { + CardDAVServer.mimicGoogle = true; + Services.prefs.setBoolPref("ldap_2.servers.carddav.carddav.vcard3", true); + await subtest(); + Services.prefs.clearUserPref("ldap_2.servers.carddav.carddav.vcard3"); + CardDAVServer.mimicGoogle = false; +}); + +add_task(async function testReadOnly() { + Services.prefs.setBoolPref("ldap_2.servers.carddav.readOnly", true); + await subtest(); + Services.prefs.clearUserPref("ldap_2.servers.carddav.readOnly"); +}); + +add_task(async function testExpiredToken() { + // Put some cards on the server. + CardDAVServer.putCardInternal( + "first.vcf", + "BEGIN:VCARD\r\nUID:first\r\nFN:First Person\r\nEND:VCARD\r\n" + ); + CardDAVServer.putCardInternal( + "second.vcf", + "BEGIN:VCARD\r\nUID:second\r\nFN:Second Person\r\nEND:VCARD\r\n" + ); + CardDAVServer.putCardInternal( + "third.vcf", + "BEGIN:VCARD\r\nUID:third\r\nFN:Third Person\r\nEND:VCARD\r\n" + ); + + let directory = initDirectory(); + + info("Initial sync with server."); + await directory.fetchAllFromServer(); + + info(`Token is: ${directory._syncToken}`); + + info("Cards:"); + for (let card of directory.childCards) { + info( + ` ${card.displayName} [${card.getProperty( + "_href", + "" + )}, ${card.getProperty("_etag", "")}]` + ); + } + + Assert.equal(directory.childCardCount, 3); + Assert.deepEqual(Array.from(directory.childCards, c => c.UID).sort(), [ + "first", + "second", + "third", + ]); + + // Corrupt the sync token. This will cause a 400 Bad Request response and a + // complete resync should happen. + + directory._syncToken = "wrong token"; + + // Make some changes on the server. + + CardDAVServer.putCardInternal( + "fourth.vcf", + "BEGIN:VCARD\r\nUID:fourth\r\nFN:Fourth\r\nEND:VCARD\r\n" + ); + CardDAVServer.putCardInternal( + "second.vcf", + "BEGIN:VCARD\r\nUID:second\r\nFN:Second Person, but different\r\nEND:VCARD\r\n" + ); + CardDAVServer.deleteCardInternal("first.vcf"); + + // Sync with the server. + + info("Sync with server."); + + let notificationPromise = TestUtils.topicObserved( + "addrbook-directory-invalidated" + ); + observer.init(); + await directory.updateAllFromServerV2(); + // Check what notifications were fired. There should be an "invalidated" + // notification, making the others redundant, but the "deleted" + // notification is hard to avoid. + observer.checkAndClearNotifications({ + "addrbook-contact-created": [], + "addrbook-contact-updated": [], + "addrbook-contact-deleted": ["first"], + }); + await notificationPromise; + + info(`Token is now: ${directory._syncToken}`); + + info("Cards:"); + for (let card of directory.childCards) { + info( + ` ${card.displayName} [${card.getProperty( + "_href", + "" + )}, ${card.getProperty("_etag", "")}]` + ); + } + + // Check that the changes were synced. + + Assert.equal(directory.childCardCount, 3); + Assert.deepEqual(Array.from(directory.childCards, c => c.UID).sort(), [ + "fourth", + "second", + "third", + ]); + Assert.equal( + directory.childCards.find(c => c.UID == "second").displayName, + "Second Person, but different" + ); + + await clearDirectory(directory); + CardDAVServer.reset(); +}); |