286 lines · 10.2 KB · Swift
RawDownload
import Foundationimport Testing@testable import CodexBarCore struct CostUsageFetcherCacheSnapshotTests {    @Test    func `cached codex token snapshot loads from existing cache without rescanning`() async throws {        let env = try CostUsageTestEnvironment()        defer { env.cleanup() }         let day = try env.makeLocalNoon(year: 2026, month: 4, day: 8)        try Self.writeCodexSessionFile(            homeRoot: env.codexHomeRoot,            env: env,            day: day,            filename: "cached.jsonl",            tokens: 42)         let options = CostUsageScanner.Options(            codexSessionsRoot: env.codexSessionsRoot,            cacheRoot: env.cacheRoot)        _ = try await CostUsageFetcher.loadTokenSnapshot(            provider: .codex,            now: day,            historyDays: 1,            scannerOptions: options)         let cached = await CostUsageFetcher.loadCachedCodexTokenSnapshot(            now: day,            historyDays: 1,            scannerOptions: options)         #expect(cached?.sessionTokens == 42)        #expect(cached?.last30DaysTokens == 42)        #expect(cached?.daily.map(\.date) == ["2026-04-08"])    }     @Test    func `cached codex token snapshot refuses expanded or managed scopes`() async throws {        let env = try CostUsageTestEnvironment()        defer { env.cleanup() }         let day = try env.makeLocalNoon(year: 2026, month: 4, day: 8)        try Self.writeCodexSessionFile(            homeRoot: env.codexHomeRoot,            env: env,            day: day,            filename: "cached.jsonl",            tokens: 42)         let options = CostUsageScanner.Options(            codexSessionsRoot: env.codexSessionsRoot,            cacheRoot: env.cacheRoot)        _ = try await CostUsageFetcher.loadTokenSnapshot(            provider: .codex,            now: day,            historyDays: 1,            scannerOptions: options)         let expanded = await CostUsageFetcher.loadCachedCodexTokenSnapshot(            now: day,            historyDays: 7,            scannerOptions: options)        let managed = await CostUsageFetcher.loadCachedCodexTokenSnapshot(            now: day,            codexHomePath: env.codexHomeRoot.path,            historyDays: 1,            scannerOptions: options)         #expect(expanded == nil)        #expect(managed == nil)    }     @Test    func `cached codex token snapshot refuses mismatched roots fingerprint`() async throws {        let env = try CostUsageTestEnvironment()        defer { env.cleanup() }         let day = try env.makeLocalNoon(year: 2026, month: 4, day: 8)        try Self.writeCodexSessionFile(            homeRoot: env.codexHomeRoot,            env: env,            day: day,            filename: "cached.jsonl",            tokens: 42)         let options = CostUsageScanner.Options(            codexSessionsRoot: env.codexSessionsRoot,            cacheRoot: env.cacheRoot)        _ = try await CostUsageFetcher.loadTokenSnapshot(            provider: .codex,            now: day,            historyDays: 1,            scannerOptions: options)         var cache = CostUsageCacheIO.load(provider: .codex, cacheRoot: env.cacheRoot)        cache.roots = [env.root.appendingPathComponent("other/sessions", isDirectory: true).path: 0]        CostUsageCacheIO.save(provider: .codex, cache: cache, cacheRoot: env.cacheRoot)         let cached = await CostUsageFetcher.loadCachedCodexTokenSnapshot(            now: day,            historyDays: 1,            scannerOptions: options)         #expect(cached == nil)    }     @Test    func `cached codex token snapshot merges cached pi sessions`() async throws {        let env = try CostUsageTestEnvironment()        defer { env.cleanup() }         let day = try env.makeLocalNoon(year: 2026, month: 4, day: 8)        try Self.writeCodexSessionFile(            homeRoot: env.codexHomeRoot,            env: env,            day: day,            filename: "cached.jsonl",            tokens: 42)        try Self.writePiCodexSessionFile(env: env, day: day, tokens: 165)         let options = CostUsageScanner.Options(            codexSessionsRoot: env.codexSessionsRoot,            cacheRoot: env.cacheRoot)        let piOptions = PiSessionCostScanner.Options(            piSessionsRoot: env.piSessionsRoot,            cacheRoot: env.cacheRoot,            refreshMinIntervalSeconds: 0)        _ = try await CostUsageFetcher.loadTokenSnapshot(            provider: .codex,            now: day,            historyDays: 1,            refreshPricingInBackground: false,            scannerOptions: options,            piScannerOptions: piOptions)         let cached = await CostUsageFetcher.loadCachedCodexTokenSnapshot(            now: day,            historyDays: 1,            scannerOptions: options)         #expect(cached?.sessionTokens == 207)        #expect(cached?.last30DaysTokens == 207)    }     @Test    func `cached codex token snapshot loads cached pi sessions without native codex cache`() async throws {        let env = try CostUsageTestEnvironment()        defer { env.cleanup() }         let day = try env.makeLocalNoon(year: 2026, month: 4, day: 8)        try Self.writePiCodexSessionFile(env: env, day: day, tokens: 165)         let piOptions = PiSessionCostScanner.Options(            piSessionsRoot: env.piSessionsRoot,            cacheRoot: env.cacheRoot,            refreshMinIntervalSeconds: 0)        _ = PiSessionCostScanner.loadDailyReport(            provider: .codex,            since: day,            until: day,            now: day,            options: piOptions)         let cached = await CostUsageFetcher.loadCachedCodexTokenSnapshot(            now: day,            historyDays: 1,            scannerOptions: CostUsageScanner.Options(                codexSessionsRoot: env.codexSessionsRoot,                cacheRoot: env.cacheRoot))         #expect(cached?.sessionTokens == 165)        #expect(cached?.last30DaysTokens == 165)    }     @Test    func `cached codex token snapshot still loads pi sessions when native cache roots mismatch`() async throws {        let env = try CostUsageTestEnvironment()        defer { env.cleanup() }         let day = try env.makeLocalNoon(year: 2026, month: 4, day: 8)        try Self.writeCodexSessionFile(            homeRoot: env.codexHomeRoot,            env: env,            day: day,            filename: "cached.jsonl",            tokens: 42)        try Self.writePiCodexSessionFile(env: env, day: day, tokens: 165)         let options = CostUsageScanner.Options(            codexSessionsRoot: env.codexSessionsRoot,            cacheRoot: env.cacheRoot)        let piOptions = PiSessionCostScanner.Options(            piSessionsRoot: env.piSessionsRoot,            cacheRoot: env.cacheRoot,            refreshMinIntervalSeconds: 0)        _ = try await CostUsageFetcher.loadTokenSnapshot(            provider: .codex,            now: day,            historyDays: 1,            refreshPricingInBackground: false,            scannerOptions: options,            piScannerOptions: piOptions)         var cache = CostUsageCacheIO.load(provider: .codex, cacheRoot: env.cacheRoot)        cache.roots = [env.root.appendingPathComponent("other/sessions", isDirectory: true).path: 0]        CostUsageCacheIO.save(provider: .codex, cache: cache, cacheRoot: env.cacheRoot)         let cached = await CostUsageFetcher.loadCachedCodexTokenSnapshot(            now: day,            historyDays: 1,            scannerOptions: options)         #expect(cached?.sessionTokens == 165)        #expect(cached?.last30DaysTokens == 165)    }     private static func writeCodexSessionFile(        homeRoot: URL,        env: CostUsageTestEnvironment,        day: Date,        filename: String,        tokens: Int) throws    {        let comps = Calendar.current.dateComponents([.year, .month, .day], from: day)        let dir = homeRoot            .appendingPathComponent("sessions", isDirectory: true)            .appendingPathComponent(String(format: "%04d", comps.year ?? 1970), isDirectory: true)            .appendingPathComponent(String(format: "%02d", comps.month ?? 1), isDirectory: true)            .appendingPathComponent(String(format: "%02d", comps.day ?? 1), isDirectory: true)        try FileManager.default.createDirectory(at: dir, withIntermediateDirectories: true)         let model = "openai/gpt-5.4"        let url = dir.appendingPathComponent(filename, isDirectory: false)        try env.jsonl([            [                "type": "turn_context",                "timestamp": env.isoString(for: day),                "payload": ["model": model],            ],            [                "type": "event_msg",                "timestamp": env.isoString(for: day.addingTimeInterval(1)),                "payload": [                    "type": "token_count",                    "info": [                        "last_token_usage": [                            "input_tokens": tokens,                            "cached_input_tokens": 0,                            "output_tokens": 0,                        ],                        "model": model,                    ],                ],            ],        ]).write(to: url, atomically: true, encoding: .utf8)    }     private static func writePiCodexSessionFile(        env: CostUsageTestEnvironment,        day: Date,        tokens: Int) throws    {        _ = try env.writePiSessionFile(            relativePath: "nested/run-0/2026-04-08T10-00-00-000Z_test.jsonl",            contents: env.jsonl([                [                    "type": "message",                    "timestamp": env.isoString(for: day),                    "message": [                        "role": "assistant",                        "provider": "openai-codex",                        "model": "openai/gpt-5.4",                        "timestamp": Int(day.timeIntervalSince1970 * 1000),                        "usage": [                            "input": tokens,                            "output": 0,                            "cacheRead": 0,                            "cacheWrite": 0,                            "totalTokens": tokens,                        ],                    ],                ],            ]))    }}