Init
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
//
|
||||
// ClientAndAuthTests.swift
|
||||
// PortalKitTests
|
||||
//
|
||||
// Verification of CredentialStorage abstractions, PortalAuth, and PortalClient models.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
@testable import PortalKit
|
||||
|
||||
final class ClientAndAuthTests: XCTestCase {
|
||||
func testInMemoryCredentialStorage() {
|
||||
let storage = InMemoryCredentialStorage()
|
||||
XCTAssertNil(storage.getAuthToken())
|
||||
XCTAssertNil(storage.getPinnedCertSha256())
|
||||
|
||||
storage.save(token: "test_token_123", certSha256: "AABBCCDDEEFF")
|
||||
XCTAssertEqual(storage.getAuthToken(), "test_token_123")
|
||||
XCTAssertEqual(storage.getPinnedCertSha256(), "aabbccddeeff") // lowercased
|
||||
|
||||
storage.clear()
|
||||
XCTAssertNil(storage.getAuthToken())
|
||||
XCTAssertNil(storage.getPinnedCertSha256())
|
||||
}
|
||||
|
||||
func testFileCredentialStorage() {
|
||||
let tempDir = FileManager.default.temporaryDirectory
|
||||
let tempFile = tempDir.appendingPathComponent("test_credentials_\(UUID().uuidString).json")
|
||||
defer { try? FileManager.default.removeItem(at: tempFile) }
|
||||
|
||||
let storage = FileCredentialStorage(fileURL: tempFile)
|
||||
XCTAssertNil(storage.getAuthToken())
|
||||
|
||||
storage.save(token: "tok_abc", certSha256: "010203040506")
|
||||
XCTAssertEqual(storage.getAuthToken(), "tok_abc")
|
||||
XCTAssertEqual(storage.getPinnedCertSha256(), "010203040506")
|
||||
|
||||
// Create fresh instance pointing to same file
|
||||
let reloadStorage = FileCredentialStorage(fileURL: tempFile)
|
||||
XCTAssertEqual(reloadStorage.getAuthToken(), "tok_abc")
|
||||
XCTAssertEqual(reloadStorage.getPinnedCertSha256(), "010203040506")
|
||||
|
||||
reloadStorage.clear()
|
||||
XCTAssertNil(reloadStorage.getAuthToken())
|
||||
}
|
||||
|
||||
func testPortalAuthFacade() {
|
||||
let original = PortalAuth.defaultStorage
|
||||
defer { PortalAuth.defaultStorage = original }
|
||||
|
||||
let mockStorage = InMemoryCredentialStorage()
|
||||
PortalAuth.defaultStorage = mockStorage
|
||||
|
||||
PortalAuth.save(token: "facade_token", certSha256: "CAFEBABE")
|
||||
XCTAssertEqual(PortalAuth.token, "facade_token")
|
||||
XCTAssertEqual(PortalAuth.pinnedCertSha256, "cafebabe")
|
||||
|
||||
PortalAuth.clear()
|
||||
XCTAssertNil(PortalAuth.token)
|
||||
XCTAssertNil(PortalAuth.pinnedCertSha256)
|
||||
}
|
||||
|
||||
func testPortalClientHostNormalization() {
|
||||
let c1 = PortalClient(host: "10.0.0.10")
|
||||
XCTAssertEqual(c1.host, "10.0.0.10:\(PortalEndpoints.port)")
|
||||
|
||||
let c2 = PortalClient(host: "https://10.0.0.10:9000/")
|
||||
XCTAssertEqual(c2.host, "10.0.0.10:9000")
|
||||
|
||||
let c3 = PortalClient(host: "http://myportal.local:\(PortalEndpoints.port)")
|
||||
XCTAssertEqual(c3.host, "myportal.local:\(PortalEndpoints.port)")
|
||||
}
|
||||
|
||||
func testPortalClientControlRequiresPairing() async {
|
||||
let storage = InMemoryCredentialStorage()
|
||||
let client = PortalClient(host: "127.0.0.1", credentialStorage: storage)
|
||||
|
||||
do {
|
||||
_ = try await client.control(command: "mode Desk")
|
||||
XCTFail("Should throw notPaired error")
|
||||
} catch {
|
||||
guard case PortalClientError.notPaired = error else {
|
||||
XCTFail("Expected notPaired error, got \(error)")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testTlsPinningChallengeEvaluationNonServerTrust() {
|
||||
let space = URLProtectionSpace(
|
||||
host: "localhost",
|
||||
port: PortalEndpoints.port,
|
||||
protocol: "https",
|
||||
realm: nil,
|
||||
authenticationMethod: NSURLAuthenticationMethodHTTPBasic
|
||||
)
|
||||
let challenge = URLAuthenticationChallenge(protectionSpace: space, proposedCredential: nil, previousFailureCount: 0, failureResponse: nil, error: nil, sender: DummyChallengeSender())
|
||||
|
||||
let exp = expectation(description: "Challenge evaluated")
|
||||
PortalTlsPinning.evaluate(challenge: challenge, pinnedFingerprint: "somehash") { disposition, credential in
|
||||
XCTAssertEqual(disposition, .cancelAuthenticationChallenge)
|
||||
XCTAssertNil(credential)
|
||||
exp.fulfill()
|
||||
}
|
||||
wait(for: [exp], timeout: 1)
|
||||
}
|
||||
}
|
||||
|
||||
private final class DummyChallengeSender: NSObject, URLAuthenticationChallengeSender {
|
||||
func use(_ credential: URLCredential, for challenge: URLAuthenticationChallenge) {}
|
||||
func continueWithoutCredential(for challenge: URLAuthenticationChallenge) {}
|
||||
func cancel(_ challenge: URLAuthenticationChallenge) {}
|
||||
func performDefaultHandling(for challenge: URLAuthenticationChallenge) {}
|
||||
func rejectProtectionSpaceAndContinue(with challenge: URLAuthenticationChallenge) {}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user