141 lines · 5.7 KB · Swift
RawDownload
import Foundationimport Testing@testable import CodexBarCore struct HookDispatchTests {    private func event(        _ type: HookEventType = .quotaReached,        provider: String = "codex",        usagePercent: Double? = 0.95,        window: String? = "session") -> HookEvent    {        HookEvent(            event: type,            provider: provider,            window: window,            usagePercent: usagePercent,            resetAt: Date(timeIntervalSince1970: 1_700_000_000),            timestamp: Date(timeIntervalSince1970: 1_700_000_100))    }     @Test    func `invalid timeout threshold and provider fail closed`() {        let event = self.event(.quotaLow, provider: "codex", usagePercent: 0.95)        #expect(!HookRule(            event: .quotaLow,            threshold: 1.1,            executable: "/bin/echo").matches(event))        #expect(!HookRule(            event: .quotaLow,            threshold: 0,            executable: "/bin/echo").matches(event))        #expect(!HookRule(            event: .quotaLow,            provider: "unknown",            executable: "/bin/echo").matches(event))        #expect(!HookRule(            event: .quotaLow,            executable: "/bin/echo",            timeoutSeconds: 0).matches(event))        #expect(!HookRule(            event: .quotaReached,            executable: "/bin/echo",            arguments: Array(repeating: "x", count: HookRule.maximumArgumentCount + 1)).matches(event))        let tooManyRules = HooksConfig(            enabled: true,            events: Array(                repeating: HookRule(event: .quotaReached, executable: "/bin/echo"),                count: HooksConfig.maximumRuleCount + 1))        #expect(tooManyRules.matchingRules(for: event).isEmpty)    }     @Test    func `runner writes the complete JSON payload to stdin`() async throws {        let original = self.event(.quotaReached, provider: "claude", usagePercent: 0.42, window: "session")        let result = try await HookRunner.run(            rule: HookRule(event: .quotaReached, executable: "/bin/cat"),            event: original)        let decoder = JSONDecoder()        decoder.dateDecodingStrategy = .iso8601        let decoded = try decoder.decode(HookEvent.self, from: Data(result.stdout.utf8))         #expect(decoded == original)        #expect(result.stdout ==            "{\"event\":\"quota_reached\",\"provider\":\"claude\",\"resetAt\":\"2023-11-14T22:13:20Z\"," +                "\"timestamp\":\"2023-11-14T22:15:00Z\",\"usagePercent\":0.42,\"window\":\"session\"}")    }     @Test    func `runner rejects payloads above the pipe-safe limit`() async {        let oversized = HookEvent(            event: .quotaReached,            provider: "codex",            account: String(repeating: "x", count: HookRunner.maximumPayloadBytes),            timestamp: Date())         await #expect(throws: HookRunnerError.self) {            try await HookRunner.run(                rule: HookRule(event: .quotaReached, executable: "/bin/cat"),                event: oversized)        }    }     @Test    func `runner preserves whitespace and empty argument boundaries`() async throws {        let rule = HookRule(            event: .quotaReached,            executable: "/usr/bin/printf",            arguments: ["<%s>|<%s>|<%s>", "quota reached", "", "tail"])        let result = try await HookRunner.run(rule: rule, event: self.event())         #expect(result.stdout == "<quota reached>|<>|<tail>")    }     @Test    func `dispatch coalesces repeated refresh failures`() async throws {        let output = FileManager.default.temporaryDirectory            .appendingPathComponent("codexbar-hook-rate-limit-\(UUID().uuidString).json")        defer { try? FileManager.default.removeItem(at: output) }        let event = self.event(.refreshFailed, usagePercent: nil, window: nil)        let config = HooksConfig(enabled: true, events: [            HookRule(event: .refreshFailed, executable: "/usr/bin/tee", arguments: ["-a", output.path]),        ])        let limiter = HookRateLimiter(window: 600)         await HookRunner.dispatch(event: event, config: config, rateLimiter: limiter)        await HookRunner.dispatch(event: event, config: config, rateLimiter: limiter)        let contents = try String(contentsOf: output, encoding: .utf8)         #expect(contents.components(separatedBy: "\"event\":\"refresh_failed\"").count - 1 == 1)    }     @Test    func `dispatch contains one rule failure and continues`() async {        let output = FileManager.default.temporaryDirectory            .appendingPathComponent("codexbar-hook-failure-\(UUID().uuidString).json")        defer { try? FileManager.default.removeItem(at: output) }        let event = self.event()        let config = HooksConfig(enabled: true, events: [            HookRule(event: .quotaReached, executable: "/nonexistent/codexbar-hook"),            HookRule(event: .quotaReached, executable: "/usr/bin/tee", arguments: [output.path]),        ])         await HookRunner.dispatch(event: event, config: config, rateLimiter: HookRateLimiter())         #expect(FileManager.default.fileExists(atPath: output.path))    }     @Test    func `disabled dispatch never invokes a rule`() async {        let output = FileManager.default.temporaryDirectory            .appendingPathComponent("codexbar-hook-disabled-\(UUID().uuidString).json")        defer { try? FileManager.default.removeItem(at: output) }        let config = HooksConfig(enabled: false, events: [            HookRule(event: .quotaReached, executable: "/usr/bin/tee", arguments: [output.path]),        ])         await HookRunner.dispatch(event: self.event(), config: config, rateLimiter: HookRateLimiter())         #expect(!FileManager.default.fileExists(atPath: output.path))    }}