import Foundation import Testing @testable import gitbay private func account(_ host: String, _ user: String) throws -> Account { Account(instance: try GitbayInstance(url: "https://\(host)"), username: user) } private func payload(_ instance: String, _ user: String, _ path: String) throws -> PushPayload { try #require(PushPayload(userInfo: [ "aps": ["alert": ["title": "t", "body": "b"]], "instance": instance, "user": user, "path": path, ])) } @Test func targetResolvesToTheMatchingAccount() throws { let accounts = [try account("gitbay.org", "cmc"), try account("dev.local", "cmc")] let target = try #require(PushTarget( payload: try payload("https://dev.local", "cmc", "krz/gitbay/mrs/7"), accounts: accounts)) #expect(target.accountID == accounts[1].id) #expect(target.destination == .mr(repo: "krz/gitbay", number: 7)) } // Same username on two instances is two accounts; the instance decides. @Test func targetDistinguishesTheSameUserOnTwoInstances() throws { let accounts = [try account("gitbay.org", "cmc"), try account("dev.local", "cmc")] let target = try #require(PushTarget( payload: try payload("https://gitbay.org", "cmc", "a/b/issues/1"), accounts: accounts)) #expect(target.accountID == accounts[0].id) } @Test func targetIsNilForAnAccountSignedOut() throws { let accounts = [try account("gitbay.org", "cmc")] #expect(PushTarget(payload: try payload("https://other.example", "cmc", "a/b/issues/1"), accounts: accounts) == nil) } // A repository root, or a section the app has no screen for, resolves // to no target: the tap opens the app without navigating. @Test func targetIsNilForAnUnroutablePath() throws { let accounts = [try account("gitbay.org", "cmc")] #expect(PushTarget(payload: try payload("https://gitbay.org", "cmc", "krz/gitbay"), accounts: accounts) == nil) #expect(PushTarget(payload: try payload("https://gitbay.org", "cmc", "krz/gitbay/wiki/Home"), accounts: accounts) == nil) } // A server's unnormalised site_url must not desync a payload from the // account it belongs to: both sides go through GitbayInstance's own // normalisation before comparing. @Test func targetMatchesAnInstanceWithATrailingSlash() throws { let accounts = [try account("gitbay.org", "cmc")] let target = try #require(PushTarget( payload: try payload("https://gitbay.org/", "cmc", "a/b/issues/1"), accounts: accounts)) #expect(target.accountID == accounts[0].id) } @Test func targetMatchesAnInstanceWithAnExplicitDefaultPort() throws { let accounts = [try account("gitbay.org", "cmc")] let target = try #require(PushTarget( payload: try payload("https://gitbay.org:443", "cmc", "a/b/issues/1"), accounts: accounts)) #expect(target.accountID == accounts[0].id) } // The other side of the same mismatch: an account stored before the // constructor folded the default port keeps ":443" in its baseURL, so // decoding is the only way to build one. Its id keeps the port — // rewriting that would orphan the Keychain token keyed by it. @Test func targetMatchesAnAccountStoredWithAnExplicitDefaultPort() throws { let legacy = try JSONDecoder().decode( GitbayInstance.self, from: Data(#"{"baseURL":"https://gitbay.org:443"}"#.utf8)) let accounts = [Account(instance: legacy, username: "cmc")] #expect(accounts[0].id.contains(":443")) let target = try #require(PushTarget( payload: try payload("https://gitbay.org", "cmc", "a/b/issues/1"), accounts: accounts)) #expect(target.accountID == accounts[0].id) }