| 1 | import XCTest |
| 2 | @testable import LChatCore |
| 3 | |
| 4 | // Gate for the invite-link parsing shared by both apps: the two entry points — |
| 5 | // OS-opened URLs and pasted links — accept/reject the right shapes, in particular |
| 6 | // both the new "e1."-prefixed fragments and older unprefixed ones. |
| 7 | final class InviteLinkTests: XCTestCase { |
| 8 | func testParseJoinLink() { |
| 9 | XCTAssertNotNil(parseJoinLink("https://family.example.com/join/join#e1.abc123"), |
| 10 | "e1.-prefixed fragment parses") |
| 11 | XCTAssertNotNil(parseJoinLink("https://family.example.com/join/join#abc123"), |
| 12 | "unprefixed fragment parses (older links)") |
| 13 | XCTAssertNotNil(parseJoinLink(" https://h.example.com/join/x#e1.T \n"), |
| 14 | "whitespace is trimmed") |
| 15 | XCTAssertEqual(parseJoinLink("http://192.168.1.5:8080/join/x#e1.T")?.origin, |
| 16 | "http://192.168.1.5:8080", |
| 17 | "origin keeps scheme/host/port, drops path+fragment") |
| 18 | XCTAssertNil(parseJoinLink("https://h.example.com/join/x#k=legacykey"), |
| 19 | "legacy #k= links are rejected") |
| 20 | XCTAssertNil(parseJoinLink("https://h.example.com/join/x"), |
| 21 | "missing fragment is rejected") |
| 22 | XCTAssertNil(parseJoinLink("https://h.example.com/other/x#e1.T"), |
| 23 | "non-/join/ path is rejected") |
| 24 | XCTAssertNil(parseJoinLink("elevenmessenger://add?url=x"), |
| 25 | "custom scheme is not itself a join link") |
| 26 | } |
| 27 | |
| 28 | func testInviteLinkFromOpenedURL() { |
| 29 | // The welcome page's custom-scheme form. |
| 30 | let encoded = "https%3A%2F%2Ffamily.example.com%2Fjoin%2Fjoin%23e1.abc123" |
| 31 | XCTAssertEqual(opened("elevenmessenger://add?url=\(encoded)"), |
| 32 | "https://family.example.com/join/join#e1.abc123", |
| 33 | "elevenmessenger://add unwraps + percent-decodes the url param") |
| 34 | XCTAssertNil(opened("elevenmessenger://add"), |
| 35 | "custom scheme without a url param is rejected") |
| 36 | XCTAssertNil(opened("elevenmessenger://other?url=\(encoded)"), |
| 37 | "custom scheme with an unknown host is rejected") |
| 38 | |
| 39 | // …and the universal-link form: the https invite passes straight through, |
| 40 | // fragment intact. |
| 41 | XCTAssertEqual(opened("https://family.example.com/join/join#e1.abc123"), |
| 42 | "https://family.example.com/join/join#e1.abc123", |
| 43 | "https universal link passes through with its fragment") |
| 44 | XCTAssertNil(opened("ftp://h/join/x#e1.T"), "other schemes are rejected") |
| 45 | } |
| 46 | |
| 47 | private func opened(_ s: String) -> String? { |
| 48 | URL(string: s).flatMap { inviteLink(fromOpenedURL: $0) } |
| 49 | } |
| 50 | } |
| 51 | |