162 lines
6.0 KiB
Swift
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)
|
|
}
|
|
}
|