import Foundation import Testing @testable import CodexBarCore struct CostUsageDailyReportMergeTests { @Test func `merged report sums overlapping day totals and model breakdowns`() { let native = CostUsageDailyReport( data: [ CostUsageDailyReport.Entry( date: "2026-04-04", inputTokens: 100, outputTokens: 20, cacheReadTokens: 10, cacheCreationTokens: nil, totalTokens: 130, costUSD: 1.25, modelsUsed: ["gpt-5.4"], modelBreakdowns: [ CostUsageDailyReport.ModelBreakdown( modelName: "gpt-5.4", costUSD: 1.25, totalTokens: 130, standardCostUSD: 0.75, priorityCostUSD: 0.50, standardTokens: 80, priorityTokens: 50), ]), ], summary: CostUsageDailyReport.Summary( totalInputTokens: 100, totalOutputTokens: 20, cacheReadTokens: 10, cacheCreationTokens: nil, totalTokens: 130, totalCostUSD: 1.25)) let pi = CostUsageDailyReport( data: [ CostUsageDailyReport.Entry( date: "2026-04-04", inputTokens: 50, outputTokens: 10, cacheReadTokens: 5, cacheCreationTokens: 2, totalTokens: 67, costUSD: 0.75, modelsUsed: ["gpt-5.4"], modelBreakdowns: [ CostUsageDailyReport.ModelBreakdown( modelName: "gpt-5.4", costUSD: 0.75, totalTokens: 67, standardCostUSD: 0.25, priorityCostUSD: 0.50, standardTokens: 20, priorityTokens: 47), ]), ], summary: CostUsageDailyReport.Summary( totalInputTokens: 50, totalOutputTokens: 10, cacheReadTokens: 5, cacheCreationTokens: 2, totalTokens: 67, totalCostUSD: 0.75)) let merged = native.merged(with: pi) #expect(merged.data.count == 1) #expect(merged.data.first?.inputTokens == 150) #expect(merged.data.first?.outputTokens == 30) #expect(merged.data.first?.cacheReadTokens == 15) #expect(merged.data.first?.cacheCreationTokens == 2) #expect(merged.data.first?.totalTokens == 197) #expect(abs((merged.data.first?.costUSD ?? 0) - 2.0) < 0.000001) #expect(merged.data.first?.modelBreakdowns == [ CostUsageDailyReport.ModelBreakdown( modelName: "gpt-5.4", costUSD: 2.0, totalTokens: 197, standardCostUSD: 1.0, priorityCostUSD: 1.0, standardTokens: 100, priorityTokens: 97), ]) #expect(merged.summary?.totalTokens == 197) #expect(abs((merged.summary?.totalCostUSD ?? 0) - 2.0) < 0.000001) } @Test func `merged report unions days and orders model breakdowns deterministically`() { let first = CostUsageDailyReport( data: [ CostUsageDailyReport.Entry( date: "2026-04-04", inputTokens: nil, outputTokens: nil, cacheReadTokens: nil, cacheCreationTokens: nil, totalTokens: 30, costUSD: 0.30, modelsUsed: ["gpt-5.3-codex"], modelBreakdowns: [ CostUsageDailyReport.ModelBreakdown(modelName: "gpt-5.3-codex", costUSD: 0.30, totalTokens: 30), ]), ], summary: nil) let second = CostUsageDailyReport( data: [ CostUsageDailyReport.Entry( date: "2026-04-05", inputTokens: nil, outputTokens: nil, cacheReadTokens: nil, cacheCreationTokens: nil, totalTokens: 40, costUSD: 0.40, modelsUsed: ["gpt-5.4", "gpt-5.3-codex"], modelBreakdowns: [ CostUsageDailyReport.ModelBreakdown(modelName: "gpt-5.4", costUSD: 0.40, totalTokens: 40), CostUsageDailyReport.ModelBreakdown(modelName: "gpt-5.3-codex", costUSD: 0.00, totalTokens: 0), ]), ], summary: nil) let merged = CostUsageDailyReport.merged([first, second]) #expect(merged.data.map(\.date) == ["2026-04-04", "2026-04-05"]) #expect(merged.data.last?.modelBreakdowns?.map(\.modelName) == ["gpt-5.4", "gpt-5.3-codex"]) #expect(merged.summary?.totalTokens == 70) #expect(abs((merged.summary?.totalCostUSD ?? 0) - 0.70) < 0.000001) } @Test func `merged report includes derived totals when another same day entry has explicit total`() { let explicit = CostUsageDailyReport( data: [ CostUsageDailyReport.Entry( date: "2026-04-04", inputTokens: 70, outputTokens: 30, totalTokens: 100, costUSD: 1.0, modelsUsed: ["gpt-5.4"], modelBreakdowns: nil), ], summary: nil) let derived = CostUsageDailyReport( data: [ CostUsageDailyReport.Entry( date: "2026-04-04", inputTokens: 10, outputTokens: 5, cacheReadTokens: 3, cacheCreationTokens: 2, totalTokens: nil, costUSD: 0.25, modelsUsed: ["gpt-5.3-codex"], modelBreakdowns: nil), ], summary: nil) let merged = CostUsageDailyReport.merged([explicit, derived]) #expect(merged.data.first?.totalTokens == 120) #expect(merged.summary?.totalTokens == 120) #expect(abs((merged.data.first?.costUSD ?? 0) - 1.25) < 0.000001) } }