// // AuthMiddlewareTests.swift // Zyquo Router // // Author: Simon-Pierre Boucher // Mail: contact@spboucher.ai // // Local API key gate: open with no keys, valid / invalid / revoked tokens, // malformed Authorization headers. // import NIOHTTP1 import XCTest @testable import ZyquoRouter final class AuthMiddlewareTests: XCTestCase { private func request(authorization: String?) -> RouteRequest { var headers = HTTPHeaders() if let authorization { headers.add(name: "Authorization", value: authorization) } return RouteRequest(method: .POST, uri: "/v1/chat/completions", headers: headers, body: Data()) } func testOpenWhenNoKeysConfigured() { let auth = AuthMiddleware(keys: []) guard case .allowed(nil) = auth.authorize(request(authorization: nil)) else { return XCTFail("no keys ⇒ open on localhost") } } func testTokenLifecycle() { var (record, token) = APIKeyRecord.generate(name: "test") let auth = AuthMiddleware(keys: [record]) guard case .allowed(let matched) = auth.authorize(request(authorization: "Bearer \(token)")), matched?.name == "test" else { return XCTFail("valid token must pass and carry its record") } guard case .unauthorized = auth.authorize(request(authorization: "Bearer zyquo-sk-wrong")) else { return XCTFail("unknown token must 401") } guard case .unauthorized = auth.authorize(request(authorization: nil)) else { return XCTFail("missing header must 401 when keys exist") } guard case .unauthorized = auth.authorize(request(authorization: "Basic abc")) else { return XCTFail("non-bearer scheme must 401") } // Revocation (disabled key keeps its hash but must be rejected). record.enabled = false let revoked = AuthMiddleware(keys: [record]) guard case .unauthorized(let message) = revoked.authorize(request(authorization: "Bearer \(token)")), message.contains("revoked") else { return XCTFail("revoked token must 401 with a revocation message") } } func testTokenHashIsStoredNotPlaintext() { let (record, token) = APIKeyRecord.generate(name: "x") XCTAssertFalse(record.tokenHash.contains(token)) XCTAssertEqual(record.tokenHash, APIKeyRecord.hash(token)) XCTAssertEqual(record.tokenPrefix.count, 12) } }