import Foundation #if canImport(SQLite3) import SQLite3 import Testing @testable import CodexBarCore /// Regression gates for the two cost-usage scan-storm classes that have shipped before: /// re-parsing unchanged session files on every refresh (#1387, #1392) and re-running the /// full trace-database scan on every refresh (#1392, the pre-memo priority-turns path). @Suite(.serialized) struct CostUsagePerformanceGateTests { @Test func `warm codex refresh over an unchanged session corpus must not re-parse it`() throws { let env = try CostUsageTestEnvironment() defer { env.cleanup() } let day = try env.makeLocalNoon(year: 2026, month: 5, day: 10) let fileURLs = try Self.writeSyntheticCodexCorpus(env: env, day: day, files: 2, turnsPerFile: 4) var options = CostUsageScanner.Options( codexSessionsRoot: env.codexSessionsRoot, claudeProjectsRoots: nil, cacheRoot: env.cacheRoot, codexTraceDatabaseURL: env.root.appendingPathComponent("missing.sqlite")) options.refreshMinIntervalSeconds = 0 let cold = CostUsageScanner.loadDailyReport( provider: .codex, since: day, until: day, now: day, options: options) let changedFile = try #require(fileURLs.first) let originalAttributes = try FileManager.default.attributesOfItem(atPath: changedFile.path) let originalModificationDate = try #require(originalAttributes[.modificationDate] as? Date) let original = try String(contentsOf: changedFile, encoding: .utf8) let modified = original.replacingOccurrences( of: #""input_tokens":100,"#, with: #""input_tokens":900,"#) #expect(modified != original) #expect(modified.utf8.count == original.utf8.count) try modified.write(to: changedFile, atomically: false, encoding: .utf8) try FileManager.default.setAttributes( [.modificationDate: originalModificationDate], ofItemAtPath: changedFile.path) let warm = CostUsageScanner.loadDailyReport( provider: .codex, since: day, until: day, now: day, options: options) #expect(cold.data.count == 1) #expect(warm.data.first?.totalTokens == cold.data.first?.totalTokens) } @Test func `priority turns refresh must scan only appended trace rows`() throws { let env = try CostUsageTestEnvironment() defer { env.cleanup() } let dbURL = env.root.appendingPathComponent("logs_2.sqlite") try CostUsageScannerCodexPriorityTests.createTestLogsDatabase(at: dbURL) let epoch: Int64 = 1_760_000_000 var rows: [(epochSeconds: Int64, body: String)] = (0..<50).map { index in (epochSeconds: epoch, body: "thread_id=t-\(index) turn.id=u-\(index) routine trace row") } rows.append(( epochSeconds: epoch, body: "thread_id=thread-a turn.id=turn-a websocket request: " + #"{"type":"response.create","model":"gpt-5.5","service_tier":"priority"}"#)) try CostUsageScannerCodexPriorityTests.insertTestLogs(dbURL: dbURL, rows: rows) let full = CostUsageScanner.codexPriorityTurns(databaseURL: dbURL) #expect(full.keys.sorted() == ["turn-a"]) let scanned = try #require(CostUsageScanner._test_codexPriorityTurnsMemoState(forPath: dbURL.path)) try Self.replaceTraceBody( dbURL: dbURL, rowID: 1, body: "thread_id=mutated turn.id=mutated-old websocket request: " + #"{"type":"response.create","model":"gpt-5.5","service_tier":"priority"}"#) try CostUsageScannerCodexPriorityTests.insertTestLogs(dbURL: dbURL, rows: [( epochSeconds: epoch, body: "thread_id=thread-b turn.id=turn-b websocket request: " + #"{"type":"response.create","model":"gpt-5.5","service_tier":"priority"}"#)]) let refreshed = CostUsageScanner.codexPriorityTurns(databaseURL: dbURL) #expect(refreshed.keys.sorted() == ["turn-a", "turn-b"]) let advanced = try #require(CostUsageScanner._test_codexPriorityTurnsMemoState(forPath: dbURL.path)) #expect(advanced.lastRowID == scanned.lastRowID + 1) } private static func writeSyntheticCodexCorpus( env: CostUsageTestEnvironment, day: Date, files: Int, turnsPerFile: Int) throws -> [URL] { let model = "openai/gpt-5.2-codex" let baseISO = env.isoString(for: day) var fileURLs: [URL] = [] for fileIndex in 0..