This repository has been archived by the owner. It is now read-only.
Permalink
Cannot retrieve contributors at this time
572 lines (479 sloc)
26.6 KB
| /* 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: .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) | |
| } | |
| } |