Dactyloidae/mobile/ios/SyncTests/RecordTests.swift
2026-06-26 21:04:09 -07:00

572 lines
27 KiB
Swift
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* 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/. */
import Shared
import Storage
@testable import Sync
import UIKit
import XCTest
import SwiftyJSON
class RecordTests: XCTestCase {
func testGUIDs() {
let s = Bytes.generateGUID()
print("Got GUID: \(s)", terminator: "\n")
XCTAssertEqual(12, s.lengthOfBytes(using: String.Encoding.utf8))
}
func testSwiftyJSONSerializingControlChars() {
let input = "{\"foo\":\"help \\u000b this\"}"
let json = JSON(parseJSON: input)
XCTAssertNil(json.error)
XCTAssertNil(json.null)
XCTAssertEqual(input, json.stringValue())
let pairs: [String: Any] = ["foo": "help \(Character(UnicodeScalar(11))) this"]
let built = JSON(object: pairs)
XCTAssertEqual(input, built.stringValue())
}
func testEnvelopeNullTTL() {
let p = CleartextPayloadJSON(JSON(object: ["id": "guid"]))
let r = Record<CleartextPayloadJSON>(id: "guid", payload: p, modified: Date.now(), sortindex: 15, ttl: nil)
let k = KeyBundle.random()
let s = k.serializer({ $0.json })
let json = s(r)!
XCTAssertEqual(json["id"].stringValue, "guid")
XCTAssertTrue(json["ttl"].isNull())
}
func testParsedNulls() {
// Make this a thorough test: use a real-ish blob of JSON.
// Look to see whether fields with explicit null values match isNull().
let fullRecord = "{\"id\":\"global\"," +
"\"payload\":" +
"\"{\\\"syncID\\\":\\\"zPSQTm7WBVWB\\\"," +
"\\\"declined\\\":[\\\"bookmarks\\\"]," +
"\\\"storageVersion\\\":5," +
"\\\"engines\\\":{" +
"\\\"clients\\\":{\\\"version\\\":1,\\\"syncID\\\": null}," +
"\\\"tabs\\\":null}}\"," +
"\"username\":\"5817483\"," +
"\"modified\":1.32046073744E9}"
let record = EnvelopeJSON(fullRecord)
let bodyJSON = JSON(parseJSON: record.payload)
XCTAssertTrue(bodyJSON["engines"]["tabs"].isNull())
let clients = bodyJSON["engines"]["clients"]
// Make sure we're really getting a value out.
XCTAssertEqual(clients["version"].int, 1)
// An explicit null in the input has .type == null, so our .isNull works.
XCTAssertTrue(clients["syncID"].isNull())
// Oh, and it's a valid meta/global.
let global = MetaGlobal.fromJSON(bodyJSON)
XCTAssertTrue(global != nil)
}
func testEnvelopeJSON() {
let e = EnvelopeJSON(JSON(parseJSON: "{}"))
XCTAssertFalse(e.isValid())
let ee = EnvelopeJSON("{\"id\": \"foo\"}")
XCTAssertFalse(ee.isValid())
XCTAssertEqual(ee.id, "foo")
let eee = EnvelopeJSON(JSON(parseJSON: "{\"id\": \"foo\", \"collection\": \"bar\", \"payload\": \"baz\"}"))
XCTAssertTrue(eee.isValid())
XCTAssertEqual(eee.id, "foo")
XCTAssertEqual(eee.collection, "bar")
XCTAssertEqual(eee.payload, "baz")
}
func testRecord() {
// This is malformed JSON (no closing brace).
let malformedPayload = "{\"id\": \"abcdefghijkl\", \"collection\": \"clients\", \"payload\": \"in"
// Invalid: the payload isn't stringified JSON.
let invalidPayload = "{\"id\": \"abcdefghijkl\", \"collection\": \"clients\", \"payload\": \"invalid\"}"
// Invalid: the payload is missing a GUID.
let emptyPayload = "{\"id\": \"abcdefghijkl\", \"collection\": \"clients\", \"payload\": \"{}\"}"
// This one is invalid because the payload "id" isn't a string.
// (It'll also fail implicitly because the guid doesn't match the envelope.)
let badPayloadGUIDPayload: [String: Any] = ["id": 0]
let badPayloadGUIDPayloadString = JSON(object: badPayloadGUIDPayload).stringValue()!
let badPayloadGUIDRecord: [String: Any] = ["id": "abcdefghijkl",
"collection": "clients",
"payload": badPayloadGUIDPayloadString]
let badPayloadGUIDRecordString = JSON(object: badPayloadGUIDRecord).stringValue()!
// This one is invalid because the payload doesn't contain an "id" at all, but it's non-empty.
// See also `emptyPayload` above.
// (It'll also fail implicitly because the guid doesn't match the envelope.)
let noPayloadGUIDPayload: [String: Any] = ["some": "thing"]
let noPayloadGUIDPayloadString = JSON(object: noPayloadGUIDPayload).stringValue()!
let noPayloadGUIDRecord: [String: Any] = ["id": "abcdefghijkl",
"collection": "clients",
"payload": noPayloadGUIDPayloadString]
let noPayloadGUIDRecordString = JSON(object: noPayloadGUIDRecord).stringValue()!
// And this is a valid record.
let clientBody: [String: Any] = ["id": "abcdefghijkl", "name": "Foobar", "commands": [], "type": "mobile"]
let clientBodyString = JSON(object: clientBody).stringValue()!
let clientRecord: [String: Any] = ["id": "abcdefghijkl", "collection": "clients", "payload": clientBodyString]
let clientPayload = JSON(object: clientRecord).stringValue()!
let cleartextClientsFactory: (String) -> ClientPayload? = {
(s: String) -> ClientPayload? in
return ClientPayload(s)
}
let clearFactory: (String) -> CleartextPayloadJSON? = {
(s: String) -> CleartextPayloadJSON? in
return CleartextPayloadJSON(s)
}
print(clientPayload, terminator: "\n")
// Non-JSON malformed payloads don't even yield a value.
XCTAssertNil(Record<CleartextPayloadJSON>.fromEnvelope(EnvelopeJSON(malformedPayload), payloadFactory: clearFactory))
// Only payloads that parse as JSON objects are valid.
XCTAssertNil(Record<CleartextPayloadJSON>.fromEnvelope(EnvelopeJSON(invalidPayload), payloadFactory: clearFactory))
// Missing ID.
XCTAssertNil(Record<CleartextPayloadJSON>.fromEnvelope(EnvelopeJSON(emptyPayload), payloadFactory: clearFactory))
// No ID in non-empty payload.
let noPayloadGUIDEnvelope = EnvelopeJSON(noPayloadGUIDRecordString)
// The envelope is valid...
XCTAssertTrue(noPayloadGUIDEnvelope.isValid())
// ... but the payload is not.
let noID = Record<CleartextPayloadJSON>.fromEnvelope(noPayloadGUIDEnvelope, payloadFactory: cleartextClientsFactory)
XCTAssertNil(noID)
// Non-string ID in payload.
let badPayloadGUIDEnvelope = EnvelopeJSON(badPayloadGUIDRecordString)
// The envelope is valid...
XCTAssertTrue(badPayloadGUIDEnvelope.isValid())
// ... but the payload is not.
let badID = Record<CleartextPayloadJSON>.fromEnvelope(badPayloadGUIDEnvelope, payloadFactory: cleartextClientsFactory)
XCTAssertNil(badID)
// Only valid ClientPayloads are valid.
XCTAssertNil(Record<ClientPayload>.fromEnvelope(EnvelopeJSON(invalidPayload), payloadFactory: cleartextClientsFactory))
XCTAssertTrue(Record<ClientPayload>.fromEnvelope(EnvelopeJSON(clientPayload), payloadFactory: cleartextClientsFactory)!.payload.isValid())
}
func testEncryptedClientRecord() {
let b64E = "0A7mU5SZ/tu7ZqwXW1og4qHVHN+zgEi4Xwfwjw+vEJw="
let b64H = "11GN34O9QWXkjR06g8t0gWE1sGgQeWL0qxxWwl8Dmxs="
let expectedGUID = "0-P9fabp9vJD"
let expectedSortIndex = 131
let expectedLastModified: Timestamp = 1326254123650
let inputString = "{\"sortindex\": 131, \"payload\": \"{\\\"ciphertext\\\":\\\"YJB4dr0vZEIWPirfU2FCJvfzeSLiOP5QWasol2R6ILUxdHsJWuUuvTZVhxYQfTVNou6hVV67jfAvi5Cs+bqhhQsv7icZTiZhPTiTdVGt+uuMotxauVA5OryNGVEZgCCTvT3upzhDFdDbJzVd9O3/gU/b7r/CmAHykX8bTlthlbWeZ8oz6gwHJB5tPRU15nM/m/qW1vyKIw5pw/ZwtAy630AieRehGIGDk+33PWqsfyuT4EUFY9/Ly+8JlnqzxfiBCunIfuXGdLuqTjJOxgrK8mI4wccRFEdFEnmHvh5x7fjl1ID52qumFNQl8zkB75C8XK25alXqwvRR6/AQSP+BgQ==\\\",\\\"IV\\\":\\\"v/0BFgicqYQsd70T39rraA==\\\",\\\"hmac\\\":\\\"59605ed696f6e0e6e062a03510cff742bf6b50d695c042e8372a93f4c2d37dac\\\"}\", \"id\": \"0-P9fabp9vJD\", \"modified\": 1326254123.65}"
let keyBundle = KeyBundle(encKeyB64: b64E, hmacKeyB64: b64H)!
let decryptClient = keyBundle.factory({ CleartextPayloadJSON($0) })
let encryptClient = keyBundle.serializer({ $0.json }) // It's already a JSON.
let toRecord = {
return Record<CleartextPayloadJSON>.fromEnvelope($0, payloadFactory: decryptClient)
}
let envelope = EnvelopeJSON(inputString)
if let r = toRecord(envelope) {
XCTAssertEqual(r.id, expectedGUID)
XCTAssertTrue(r.modified == expectedLastModified) //1326254123650
XCTAssertEqual(r.sortindex, expectedSortIndex)
if let ee = encryptClient(r) {
let envelopePrime = EnvelopeJSON(ee)
XCTAssertEqual(envelopePrime.id, expectedGUID)
XCTAssertEqual(envelopePrime.id, envelope.id)
XCTAssertEqual(envelopePrime.sortindex, envelope.sortindex)
XCTAssertTrue(envelopePrime.modified == 0)
if let rPrime = toRecord(envelopePrime) {
// The payloads should be identical.
XCTAssertTrue(rPrime.equalPayloads(r))
} else {
XCTFail("No record.")
}
} else {
XCTFail("No record.")
}
} else {
XCTFail("No record.")
}
// Test invalid Base64.
let badInputString = "{\"sortindex\": 131, \"payload\": \"{\\\"ciphertext\\\":\\\"~~~YJB4dr0vZEIWPirfU2FCJvfzeSLiOP5QWasol2R6ILUxdHsJWuUuvTZVhxYQfTVNou6hVV67jfAvi5Cs+bqhhQsv7icZTiZhPTiTdVGt+uuMotxauVA5OryNGVEZgCCTvT3upzhDFdDbJzVd9O3/gU/b7r/CmAHykX8bTlthlbWeZ8oz6gwHJB5tPRU15nM/m/qW1vyKIw5pw/ZwtAy630AieRehGIGDk+33PWqsfyuT4EUFY9/Ly+8JlnqzxfiBCunIfuXGdLuqTjJOxgrK8mI4wccRFEdFEnmHvh5x7fjl1ID52qumFNQl8zkB75C8XK25alXqwvRR6/AQSP+BgQ==\\\",\\\"IV\\\":\\\"v/0BFgicqYQsd70T39rraA==\\\",\\\"hmac\\\":\\\"59605ed696f6e0e6e062a03510cff742bf6b50d695c042e8372a93f4c2d37dac\\\"}\", \"id\": \"0-P9fabp9vJD\", \"modified\": 1326254123.65}"
let badEnvelope = EnvelopeJSON(badInputString)
XCTAssertTrue(badEnvelope.isValid()) // It's a valid envelope containing nonsense ciphertext.
XCTAssertNil(toRecord(badEnvelope)) // Even though the envelope is valid, the payload is invalid, so we can't construct a record.
}
func testMeta() {
let fullRecord = "{\"id\":\"global\"," +
"\"payload\":" +
"\"{\\\"syncID\\\":\\\"zPSQTm7WBVWB\\\"," +
"\\\"declined\\\":[\\\"bookmarks\\\"]," +
"\\\"storageVersion\\\":5," +
"\\\"engines\\\":{" +
"\\\"clients\\\":{\\\"version\\\":1,\\\"syncID\\\":\\\"fDg0MS5bDtV7\\\"}," +
"\\\"forms\\\":{\\\"version\\\":1,\\\"syncID\\\":\\\"GXF29AFprnvc\\\"}," +
"\\\"history\\\":{\\\"version\\\":1,\\\"syncID\\\":\\\"av75g4vm-_rp\\\"}," +
"\\\"passwords\\\":{\\\"version\\\":1,\\\"syncID\\\":\\\"LT_ACGpuKZ6a\\\"}," +
"\\\"prefs\\\":{\\\"version\\\":2,\\\"syncID\\\":\\\"-3nsksP9wSAs\\\"}," +
"\\\"tabs\\\":{\\\"version\\\":1,\\\"syncID\\\":\\\"W4H5lOMChkYA\\\"}}}\"," +
"\"username\":\"5817483\"," +
"\"modified\":1.32046073744E9}"
let record = EnvelopeJSON(fullRecord)
XCTAssertTrue(record.isValid())
let global = MetaGlobal.fromJSON(JSON(parseJSON: record.payload))
XCTAssertTrue(global != nil)
if let global = global {
XCTAssertEqual(["bookmarks"], global.declined)
XCTAssertEqual(5, global.storageVersion)
let modified = record.modified
XCTAssertTrue(1320460737440 == modified)
let forms = global.engines["forms"]
let syncID = forms!.syncID
XCTAssertEqual("GXF29AFprnvc", syncID)
let payload: JSON = global.asPayload().json
XCTAssertEqual("GXF29AFprnvc", payload["engines"]["forms"]["syncID"].stringValue)
XCTAssertEqual(1, payload["engines"]["forms"]["version"].intValue)
XCTAssertEqual("bookmarks", payload["declined"].arrayValue[0].stringValue)
}
}
func testHistoryPayload() {
let payloadJSON = "{\"id\":\"--DzSJTCw-zb\",\"histUri\":\"https://bugzilla.mozilla.org/show_bug.cgi?id=1154549\",\"title\":\"1154549 Encapsulate synced profile data within an account-centric object\",\"visits\":[{\"date\":1429061233163240,\"type\":1}]}"
let json = JSON(parseJSON: payloadJSON)
if let payload = HistoryPayload.fromJSON(json) {
XCTAssertEqual("--DzSJTCw-zb", payload["id"].stringValue)
XCTAssertEqual("1154549 Encapsulate synced profile data within an account-centric object", payload["title"].stringValue)
XCTAssertEqual(1, payload.visits[0].type.rawValue)
XCTAssertEqual(1429061233163240, payload.visits[0].date)
let v = payload.visits[0]
let j = v.toJSON()
XCTAssertEqual(1, j["type"] as! Int)
XCTAssertEqual(1429061233163240, j["date"] as! Int64)
} else {
XCTFail("Should have parsed.")
}
}
func testHistoryPayloadWithNoURL() {
let payloadJSON = "{\"id\":\"--DzSJTCw-zb\",\"histUri\":null,\"visits\":[{\"date\":1429061233163240,\"type\":1}]}"
let json = JSON(parseJSON: payloadJSON)
XCTAssertNil(HistoryPayload.fromJSON(json))
}
func testHistoryPayloadWithNoTitle() {
let payloadJSON = "{\"id\":\"--DzSJTCw-zb\",\"histUri\":\"https://foo.com/\",\"visits\":[{\"date\":1429061233163240,\"type\":1}]}"
let json = JSON(parseJSON: payloadJSON)
if let payload = HistoryPayload.fromJSON(json) {
// Missing fields are null-valued in SwiftyJSON.
XCTAssertTrue(payload["title"].isNull())
XCTAssertEqual("", payload.title)
} else {
XCTFail("Should have parsed.")
}
}
func testHistoryPayloadWithNullTitle() {
let payloadJSON = "{\"id\":\"--DzSJTCw-zb\",\"histUri\":\"https://foo.com/\",\"title\":null,\"visits\":[{\"date\":1429061233163240,\"type\":1}]}"
let json = JSON(parseJSON: payloadJSON)
if let payload = HistoryPayload.fromJSON(json) {
XCTAssertEqual("", payload.title)
} else {
XCTFail("Should have parsed.")
}
}
func testLoginPayload() {
let input = JSON([
"id": "abcdefabcdef",
"hostname": "http://foo.com/",
"username": "foo",
"password": "bar",
"usernameField": "field",
"passwordField": "bar",
// No formSubmitURL.
"httpRealm": "",
])
// fromJSON returns nil if not valid.
XCTAssertNotNil(LoginPayload.fromJSON(input))
}
func testSeparators() {
// Mistyped parentid.
let invalidSeparator = JSON(["type": "separator", "arentid": "toolbar", "parentName": "Bookmarks Toolbar", "pos": 3])
let sep = BookmarkType.payloadFromJSON(invalidSeparator)
XCTAssertTrue(sep is SeparatorPayload)
XCTAssertFalse(sep!.isValid())
// This one's right.
let validSeparator = JSON(["id": "abcabcabcabc", "type": "separator", "parentid": "toolbar", "parentName": "Bookmarks Toolbar", "pos": 3])
let separator = BookmarkType.payloadFromJSON(validSeparator)!
XCTAssertTrue(separator is SeparatorPayload)
XCTAssertTrue(separator.isValid())
XCTAssertEqual(3, separator["pos"].intValue)
}
func testFolders() {
let validFolder = JSON([
"id": "abcabcabcabc",
"type": "folder",
"parentid": "toolbar",
"parentName": "Bookmarks Toolbar",
"title": "Sóme stüff",
"description": "",
"children": ["foo", "bar"],
])
let folder = BookmarkType.payloadFromJSON(validFolder)!
XCTAssertTrue(folder is FolderPayload)
XCTAssertTrue(folder.isValid())
XCTAssertEqual((folder as! FolderPayload).children, ["foo", "bar"])
}
// swiftlint:disable line_length
func testMobileBookmarksFolder() {
let children = ["M87np9Vfh_2s", "-JxRyqNte-ue", "6lIQzUtbjE8O", "eOg3jPSslzXl", "1WJIi9EjQErp", "z5uRo45Rvfbd", "EK3lcNd0sUFN", "gFD3GTljgu12", "eRZGsbN1ew9-", "widfEdgGn9de", "l7eTOR4Uf6xq", "vPbxG-gpN4Rb", "4dwJ8CototFe", "zK-kw9Ii6ScW", "eDmDU-gtEFW6", "lKjqWQaL_syt", "ETVDvWgGT31Q", "3Z_bMIHPSZQ8", "Fqu4_bJOk7fT", "Uo_5K1QrA67j", "gDTXNg4m1AJZ", "zpds8P-9xews", "87zjNtVGPtEp", "ZJru8Sn3qhW7", "txVnzBBBOgLP", "JTnRqFaj_oNa", "soaMlfmM4kjR", "g8AcVBjo6IRf", "uPUDaiG4q637", "rfq2bUud_w4d", "XBGxsiuUG2UD", "-VQRnJlyAvMs", "6wu7TScKdTU7", "ZeFji2hLVpLj", "HpCn_TVizMWX", "IPR5HZwRdlwi", "00JFOGuWnhWB", "P1jb3qKt32Vg", "D6MQJ43V1Ir5", "qWSoXFteRfsq", "o2avfYqEdomL", "xRS0U0YnjK9G", "VgOgzE_xfP4w", "SwP3rMJGvoO3", "Hf2jEgI_-PWa", "AyhmBi7Cv598", "-PaMuzTJXxVk", "JMhYrg8SlY5K", "SQeySEjzyplL", "GTAwd2UkEQEe", "x3RsZj5Ilebr", "sRZWZqPi74FP", "amHR50TpygA6", "XSk782ceVNN6", "ipiMyYQzeypI", "ph2k3Nqfhau4", "m5JKC3hAEQ0H", "yTVerkmQbNxk", "7taA6FbbbUbH", "PZvpbSRuJLPs", "C8atoa25U94F", "KOfNJk_ISLc6", "Bt74lBG9tJq6", "BuHoY2rUhuKA", "XTmoWKnwfIPl", "ZATwa3oTD1m0", "e8TczN5It6Am", "6kCUYs8hQtKg", "jDD8s5aiKoex", "QmpmcrYwLU29", "nCRcekynuJ08", "resttaI4J9tu", "EKSX3HV55VU3", "2-yCz0EIsVls", "sSeeGw3VbBY-", "qfpCrU34w9y0", "RKDgzPWecD6m", "5SgXEKu_dICW", "R143WAeB5E5r", "8Ns4-NiKG62r", "4AHuZDvop5XX", "YCP1OsO1goFF", "CYYaU1mQ_N6t", "UGkzEOMK8cuU", "1RzZOarkzQBa", "qSW2Z3cZSI9c", "ooPlKEAfQsnn", "jIUScoKLiXQt", "bjNTKugzRRL1", "hR24ZVnHUZcs", "3j2IDAZgUyYi", "xnWcy-sQDJRu", "UCcgJqGk3bTV", "WSSRWeptH9tq", "4ugv47OGD2E2", "XboCZgUx-x3x", "HrmWqiqsuLrm", "OjdxvRJ3Jb6j"]
let json = JSON([
"id": "UjAHxFOGEqU8",
"type": "folder",
"parentName": "",
"title": "mobile",
"description": JSON.null,
"children": children,
"parentid": "places",
])
let bookmark = BookmarkType.payloadFromJSON(json)
XCTAssertTrue(bookmark is FolderPayload)
XCTAssertTrue(bookmark?.isValid() ?? false)
}
// swiftlint:enable line_length
func testLivemarkMissingFields() {
let json = JSON([
"id": "M5bwUKK8hPyF",
"type": "livemark",
"siteUri": "http://www.bbc.co.uk/go/rss/int/news/-/news/",
"feedUri": "http://fxfeeds.mozilla.com/en-US/firefox/headlines.xml",
"parentName": "Bookmarks Toolbar",
"parentid": "toolbar",
"children": ["3Qr13GucOtEh"]])
let bookmark = BookmarkType.payloadFromJSON(json)
XCTAssertTrue(bookmark is LivemarkPayload)
let livemark = bookmark as! LivemarkPayload
XCTAssertTrue(livemark.isValid())
let siteURI = "http://www.bbc.co.uk/go/rss/int/news/-/news/"
let feedURI = "http://fxfeeds.mozilla.com/en-US/firefox/headlines.xml"
XCTAssertEqual(feedURI, livemark.feedURI)
XCTAssertEqual(siteURI, livemark.siteURI)
let m = (livemark as MirrorItemable).toMirrorItem(Date.now())
XCTAssertEqual("http://fxfeeds.mozilla.com/en-US/firefox/headlines.xml", m.feedURI)
XCTAssertEqual("http://www.bbc.co.uk/go/rss/int/news/-/news/", m.siteURI)
}
func testDeletedRecord() {
let json = JSON([
"id": "abcdefghijkl",
"deleted": true,
"type": "bookmark",
])
guard let payload = BookmarkType.payloadFromJSON(json) else {
XCTFail()
return
}
XCTAssertFalse(payload is BookmarkPayload) // Only BookmarkBasePayload.
XCTAssertTrue(payload.isValid())
}
func testUnknownRecordType() {
// It'll return a base payload that's invalid because its type is unknown.
let json = JSON([
"parentid": "mobile",
"tags": [],
"title": "Dispozitivul meu",
"id": "pQSMHiA7fD0Z",
"type": "something",
"parentName": "mobile",
])
XCTAssertNil(BookmarkType.payloadFromJSON(json))
let payload = BookmarkType.somePayloadFromJSON(json)
XCTAssertFalse(payload.isValid()) // Not valid because type is unknown.
}
func testInvalidRecordWithType() {
// It should still return the right payload type, even if it's not valid.
let json = JSON([
"parentid": "mobile",
"bmkUri": JSON.null,
"tags": [],
"title": "Dispozitivul meu",
"id": "pQSMHiA7fD0Z",
"type": "bookmark",
"parentName": "mobile",
])
guard let payload = BookmarkType.payloadFromJSON(json) else {
XCTFail()
return
}
XCTAssertFalse(payload.isValid())
XCTAssertTrue(payload is BookmarkPayload)
}
func testLivemark() {
let json = JSON([
"id": "M5bwUKK8hPyF",
"type": "livemark",
"siteUri": "http://www.bbc.co.uk/go/rss/int/news/-/news/",
"feedUri": "http://fxfeeds.mozilla.com/en-US/firefox/headlines.xml",
"parentName": "Bookmarks Toolbar",
"parentid": "toolbar",
"title": "Latest Headlines",
"description": "",
"children":
["7oBdEZB-8BMO", "SUd1wktMNCTB", "eZe4QWzo1BcY", "YNBhGwhVnQsN",
"92Aw2SMEkFg0", "uw0uKqrVFwd-", "x7mx2P3--8FJ", "d-jVF8UuC9Ye",
"DV1XVtKLEiZ5", "g4mTaTjr837Z", "1Zi5W3lwBw8T", "FEYqlUHtbBWS",
"qQd2u7LjosCB", "VUs2djqYfbvn", "KuhYnHocu7eg", "u2gcg9ILRg-3",
"hfK_RP-EC7Ol", "Aq5qsa4E5msH", "6pZIbxuJTn-K", "k_fp0iN3yYMR",
"59YD3iNOYO8O", "01afpSdAk2iz", "Cq-kjXDEPIoP", "HtNTjt9UwWWg",
"IOU8QRSrTR--", "HJ5lSlBx6d1D", "j2dz5R5U6Khc", "5GvEjrNR0yJl",
"67ozIBF5pNVP", "r5YB0cUx6C_w", "FtmFDBNxDQ6J", "BTACeZq9eEtw",
"ll4ozQ-_VNJe", "HpImsA4_XuW7", "nJvCUQPLSXwA", "94LG-lh6TUYe",
"WHn_QoOL94Os", "l-RvjgsZYlej", "LipQ8abcRstN", "74TiLvarE3n_",
"8fCiLQpQGK1P", "Z6h4WkbwfQFa", "GgAzhqakoS6g", "qyt92T8vpMsK",
"RyOgVCe2EAOE", "bgSEhW3w6kk5", "hWODjHKGD7Ph", "Cky673aqOHbT",
"gZCYT7nx3Nwu", "iJzaJxxrM58L", "rUHCRv68aY5L", "6Jc1hNJiVrV9",
"lmNgoayZ-ym8", "R1lyXsDzlfOd", "pinrXwDnRk6g", "Sn7TmZV01vMM",
"qoXyU6tcS1dd", "TRLanED-QfBK", "xHbhMeX_FYEA", "aPqacdRlAtaW",
"E3H04Wn2RfSi", "eaSIMI6kSrcz", "rtkRxFoG5Vqi", "dectkUglV0Dz",
"B4vUE0BE15No", "qgQFW5AQrgB0", "SxAXvwOhu8Zi", "0S6cRPOg-5Z2",
"zcZZBGeLnaWW", "B0at8hkQqVZQ", "sgPtgGulbP66", "lwtwGHSCPYaQ",
"mNTdpgoRZMbW", "-L8Vci6CbkJY", "bVzudKSQERc1", "Gxl9lb4DXsmL",
"3Qr13GucOtEh"]])
let bookmark = BookmarkType.payloadFromJSON(json)
XCTAssertTrue(bookmark is LivemarkPayload)
let livemark = bookmark as! LivemarkPayload
XCTAssertTrue(livemark.isValid())
let siteURI = "http://www.bbc.co.uk/go/rss/int/news/-/news/"
let feedURI = "http://fxfeeds.mozilla.com/en-US/firefox/headlines.xml"
XCTAssertEqual(feedURI, livemark.feedURI)
XCTAssertEqual(siteURI, livemark.siteURI)
let m = (livemark as MirrorItemable).toMirrorItem(Date.now())
XCTAssertEqual("http://fxfeeds.mozilla.com/en-US/firefox/headlines.xml", m.feedURI)
XCTAssertEqual("http://www.bbc.co.uk/go/rss/int/news/-/news/", m.siteURI)
}
func testMobileBookmark() {
let json = JSON([
"id": "jIUScoKLiXQt",
"type": "bookmark",
"title": "Join the Engineering Leisure Class — Medium",
"parentName": "mobile",
"bmkUri": "https://medium.com/@chrisloer/join-the-engineering-leisure-class-b3083c09a78e",
"tags": [],
"keyword": JSON.null,
"description": JSON.null,
"loadInSidebar": false,
"parentid": "mobile",
])
let bookmark = BookmarkType.payloadFromJSON(json)
XCTAssertTrue(bookmark is BookmarkPayload)
XCTAssertTrue(bookmark?.isValid() ?? false)
}
func testQuery() {
let str = "{\"title\":\"Downloads\",\"parentName\":\"\",\"bmkUri\":\"place:transition=7&sort=4\",\"id\":\"7gdp9S1okhKf\",\"parentid\":\"rq6WHyfHkoUV\",\"type\":\"query\"}"
let query = BookmarkType.payloadFromJSON(JSON(parseJSON: str))
XCTAssertTrue(query is BookmarkQueryPayload)
let mirror = query?.toMirrorItem(Date.now())
let roundtrip = mirror?.asPayload()
XCTAssertTrue(roundtrip! is BookmarkQueryPayload)
}
func testBookmarks() {
let validBookmark = JSON([
"id": "abcabcabcabc",
"type": "bookmark",
"parentid": "menu",
"parentName": "Bookmarks Menu",
"title": "Anøther",
"bmkUri": "http://terrible.sync/naming",
"description": "",
"tags": [],
"keyword": "",
])
let bookmark = BookmarkType.payloadFromJSON(validBookmark)
XCTAssertTrue(bookmark is BookmarkPayload)
let query = JSON(parseJSON: "{\"id\":\"ShCZLGEFQMam\",\"type\":\"query\",\"title\":\"Downloads\",\"parentName\":\"\",\"bmkUri\":\"place:transition=7&sort=4\",\"tags\":[],\"keyword\":null,\"description\":null,\"loadInSidebar\":false,\"parentid\":\"T6XK5oJMU8ih\"}")
guard let q = BookmarkType.payloadFromJSON(query) else {
XCTFail("Failed to generate payload from json: \(query)")
return
}
XCTAssertTrue(q is BookmarkQueryPayload)
let item = q.toMirrorItem(Date.now())
XCTAssertEqual(6, item.type.rawValue)
XCTAssertEqual("ShCZLGEFQMam", item.guid)
let places = JSON(parseJSON: "{\"id\":\"places\",\"type\":\"folder\",\"title\":\"\",\"description\":null,\"children\":[\"menu________\",\"toolbar_____\",\"tags________\",\"unfiled_____\",\"jKnyPDrBQSDg\",\"T6XK5oJMU8ih\"],\"parentid\":\"2hYxKgBwvkEH\"}")
guard let p = BookmarkType.payloadFromJSON(places) else {
XCTFail("Failed to generate payload from json: \(places)")
return
}
XCTAssertTrue(p is FolderPayload)
// Items keep their GUID until they're written into the mirror table.
XCTAssertEqual("places", p.id)
let pMirror = p.toMirrorItem(Date.now())
XCTAssertEqual(2, pMirror.type.rawValue)
// The mirror item has a translated GUID.
XCTAssertEqual(BookmarkRoots.RootGUID, pMirror.guid)
}
}