Files
steipete--codexbar/Tests/CodexBarTests/CostUsageScannerClaudeDesktopTests.swift
2026-07-13 12:22:33 +08:00

162 lines
6.0 KiB
Swift

import Foundation
import Testing
@testable import CodexBarCore
struct CostUsageScannerClaudeDesktopTests {
@Test
func `claude daily report includes nested desktop local agent projects`() throws {
let env = try CostUsageTestEnvironment()
defer { env.cleanup() }
let day = try env.makeLocalNoon(year: 2026, month: 7, day: 5)
let iso0 = env.isoString(for: day)
let assistant: [String: Any] = [
"type": "assistant",
"timestamp": iso0,
"message": [
"model": "claude-test-model",
"usage": [
"input_tokens": 120,
"cache_creation_input_tokens": 30,
"cache_read_input_tokens": 20,
"output_tokens": 40,
],
],
]
let nestedAssistant: [String: Any] = [
"type": "assistant",
"timestamp": iso0,
"message": [
"model": "claude-test-model",
"usage": [
"input_tokens": 10,
"output_tokens": 5,
],
],
]
let currentAssistant: [String: Any] = [
"type": "assistant",
"timestamp": iso0,
"message": [
"model": "claude-test-model",
"usage": [
"input_tokens": 7,
"output_tokens": 3,
],
],
]
let decoyAssistant: [String: Any] = [
"type": "assistant",
"timestamp": iso0,
"message": [
"model": "claude-test-model",
"usage": [
"input_tokens": 999,
"output_tokens": 999,
],
],
]
let projectsRoot = try env.writeClaudeDesktopLocalAgentProjectFile(
relativePath: "project-a/session-a.jsonl",
contents: env.jsonl([assistant]))
.deletingLastPathComponent()
.deletingLastPathComponent()
let nestedProjectsRoot = try env.writeNestedClaudeDesktopLocalAgentProjectFile(
relativePath: "project-b/session-b.jsonl",
contents: env.jsonl([nestedAssistant]))
.deletingLastPathComponent()
.deletingLastPathComponent()
let currentProjectsRoot = try env.writeClaudeDesktopCodeSessionProjectFile(
relativePath: "project-c/session-c.jsonl",
contents: env.jsonl([currentAssistant]))
.deletingLastPathComponent()
.deletingLastPathComponent()
let decoyProjectsRoot = try env.writeClaudeDesktopLocalAgentFile(
relativePath: "outputs/node_modules/package/.claude/projects/project-decoy/session-decoy.jsonl",
contents: env.jsonl([decoyAssistant]))
.deletingLastPathComponent()
.deletingLastPathComponent()
let discovered = CostUsageScanner.defaultClaudeProjectsRoots(
options: CostUsageScanner.Options(cacheRoot: env.cacheRoot),
environment: [:],
homeDirectory: env.root)
#expect(discovered.contains(projectsRoot.standardizedFileURL))
#expect(discovered.contains(nestedProjectsRoot.standardizedFileURL))
#expect(discovered.contains(currentProjectsRoot.standardizedFileURL))
#expect(!discovered.contains(decoyProjectsRoot.standardizedFileURL))
var options = CostUsageScanner.Options(
codexSessionsRoot: nil,
claudeProjectsRoots: discovered,
cacheRoot: env.cacheRoot)
options.refreshMinIntervalSeconds = 0
let report = CostUsageScanner.loadDailyReport(
provider: .claude,
since: day,
until: day,
now: day,
options: options)
#expect(report.data.count == 1)
#expect(report.data[0].inputTokens == 137)
#expect(report.data[0].cacheCreationTokens == 30)
#expect(report.data[0].cacheReadTokens == 20)
#expect(report.data[0].outputTokens == 48)
#expect(report.data[0].totalTokens == 235)
}
@Test
func `current desktop shared claude projects root remains discovered`() throws {
let env = try CostUsageTestEnvironment()
defer { env.cleanup() }
let day = try env.makeLocalNoon(year: 2026, month: 7, day: 5)
let sessionID = "desktop-cli-session"
let assistant: [String: Any] = [
"type": "assistant",
"timestamp": env.isoString(for: day),
"message": [
"model": "claude-test-model",
"usage": [
"input_tokens": 11,
"cache_read_input_tokens": 13,
"output_tokens": 4,
],
],
]
// Current Desktop's cliSessionId points to the matching JSONL in this shared root.
let sharedProjectsRoot = try env.writeClaudeDesktopSharedProjectFile(
relativePath: "desktop-project/\(sessionID).jsonl",
contents: env.jsonl([assistant]))
.deletingLastPathComponent()
.deletingLastPathComponent()
let discovered = CostUsageScanner.defaultClaudeProjectsRoots(
options: CostUsageScanner.Options(cacheRoot: env.cacheRoot),
environment: [:],
homeDirectory: env.root)
#expect(discovered.contains(sharedProjectsRoot.standardizedFileURL))
var options = CostUsageScanner.Options(
codexSessionsRoot: nil,
claudeProjectsRoots: discovered,
cacheRoot: env.cacheRoot)
options.refreshMinIntervalSeconds = 0
let report = CostUsageScanner.loadDailyReport(
provider: .claude,
since: day,
until: day,
now: day,
options: options)
#expect(report.data.count == 1)
#expect(report.data[0].inputTokens == 11)
#expect(report.data[0].cacheReadTokens == 13)
#expect(report.data[0].outputTokens == 4)
#expect(report.data[0].totalTokens == 28)
}
}