Files
2026-07-13 12:22:33 +08:00

196 lines
7.2 KiB
Swift

import Foundation
import Testing
struct MiMoUsageScriptTests {
@Test
func `script keeps final cumulative streaming usage`() throws {
let rows = [
self.assistantRow(outputTokens: 10),
self.assistantRow(outputTokens: 40),
self.assistantRow(outputTokens: 90),
]
let allTime = try self.runScript(files: ["session.jsonl": rows])
self.assertUsage(
allTime,
expected: .init(input: 120, cacheCreate: 10, cacheRead: 5, output: 90, messages: 1))
}
@Test
func `script keeps final cumulative streaming usage without session id`() throws {
let rows = [
self.assistantRow(outputTokens: 10, sessionID: nil),
self.assistantRow(outputTokens: 40, sessionID: nil),
self.assistantRow(outputTokens: 90, sessionID: nil),
]
let allTime = try self.runScript(files: ["session.jsonl": rows])
self.assertUsage(
allTime,
expected: .init(input: 120, cacheCreate: 10, cacheRead: 5, output: 90, messages: 1))
}
@Test
func `script keeps final cumulative usage without request id`() throws {
let rows = [
self.assistantRow(outputTokens: 10, requestID: nil),
self.assistantRow(outputTokens: 40, requestID: nil),
self.assistantRow(outputTokens: 90, requestID: nil),
]
let allTime = try self.runScript(files: ["session.jsonl": rows])
self.assertUsage(
allTime,
expected: .init(input: 120, cacheCreate: 10, cacheRead: 5, output: 90, messages: 1))
}
@Test
func `script counts rows without session identity conservatively`() throws {
let rows = [
self.assistantRow(outputTokens: 10, sessionID: nil, requestID: nil),
self.assistantRow(outputTokens: 40, sessionID: nil, requestID: nil),
self.assistantRow(outputTokens: 90, sessionID: nil, requestID: nil),
]
let allTime = try self.runScript(files: ["session.jsonl": rows])
self.assertUsage(
allTime,
expected: .init(input: 360, cacheCreate: 30, cacheRead: 15, output: 140, messages: 3))
}
@Test
func `script keeps distinct requests sharing a message id`() throws {
let rows = [
self.assistantRow(outputTokens: 40, requestID: "req_one"),
self.assistantRow(outputTokens: 90, requestID: "req_two"),
]
let allTime = try self.runScript(files: ["session.jsonl": rows])
self.assertUsage(
allTime,
expected: .init(input: 240, cacheCreate: 20, cacheRead: 10, output: 130, messages: 2))
}
@Test
func `script deduplicates copied rows from the same session`() throws {
let rows = [self.assistantRow(outputTokens: 90)]
let allTime = try self.runScript(files: [
"session.jsonl": rows,
"session-copy.jsonl": rows,
])
self.assertUsage(
allTime,
expected: .init(input: 120, cacheCreate: 10, cacheRead: 5, output: 90, messages: 1))
}
@Test
func `script deduplicates copied requests from different sessions`() throws {
let allTime = try self.runScript(files: [
"session-a.jsonl": [self.assistantRow(outputTokens: 90, sessionID: "session_a")],
"session-b.jsonl": [self.assistantRow(outputTokens: 90, sessionID: "session_b")],
])
self.assertUsage(
allTime,
expected: .init(input: 120, cacheCreate: 10, cacheRead: 5, output: 90, messages: 1))
}
private func runScript(files: [String: [[String: Any]]]) throws -> [String: Any] {
let root = FileManager.default.temporaryDirectory
.appendingPathComponent("mimo-usage-script-\(UUID().uuidString)")
defer { try? FileManager.default.removeItem(at: root) }
let mimoHome = root.appendingPathComponent("mimo")
let projects = mimoHome
.appendingPathComponent(".claude")
.appendingPathComponent("projects")
.appendingPathComponent("project-a")
try FileManager.default.createDirectory(at: projects, withIntermediateDirectories: true)
for (name, rows) in files {
let session = projects.appendingPathComponent(name)
let jsonl = try rows
.map { try JSONSerialization.data(withJSONObject: $0) }
.map { try #require(String(bytes: $0, encoding: .utf8)) }
.joined(separator: "\n")
try jsonl.write(to: session, atomically: true, encoding: .utf8)
}
let cache = root.appendingPathComponent("usage.json")
let process = Process()
process.executableURL = URL(fileURLWithPath: "/usr/bin/env")
process.arguments = ["python3", self.scriptURL.path, "--update"]
process.environment = ProcessInfo.processInfo.environment.merging([
"MIMO_CLAUDE_HOME": mimoHome.path,
"MIMO_LOCAL_USAGE_PATH": cache.path,
]) { _, new in new }
let stderr = Pipe()
process.standardError = stderr
try process.run()
process.waitUntilExit()
let errorText = try #require(String(
bytes: stderr.fileHandleForReading.readDataToEndOfFile(),
encoding: .utf8))
#expect(process.terminationStatus == 0, Comment(rawValue: errorText))
let payload = try #require(
JSONSerialization.jsonObject(with: Data(contentsOf: cache)) as? [String: Any])
let windows = try #require(payload["windows"] as? [String: Any])
return try #require(windows["all_time"] as? [String: Any])
}
private func assertUsage(_ allTime: [String: Any], expected: UsageExpectation) {
#expect(allTime["input"] as? Int == expected.input)
#expect(allTime["cache_create"] as? Int == expected.cacheCreate)
#expect(allTime["cache_read"] as? Int == expected.cacheRead)
#expect(allTime["output"] as? Int == expected.output)
#expect(allTime["messages"] as? Int == expected.messages)
}
private var scriptURL: URL {
URL(fileURLWithPath: #filePath)
.deletingLastPathComponent()
.deletingLastPathComponent()
.deletingLastPathComponent()
.appendingPathComponent("Scripts/mimo-usage.py")
}
private func assistantRow(
outputTokens: Int,
sessionID: String? = "session_stream",
requestID: String? = "req_stream") -> [String: Any]
{
var row: [String: Any] = [
"type": "assistant",
"timestamp": ISO8601DateFormatter().string(from: Date()),
"message": [
"id": "msg_stream",
"usage": [
"input_tokens": 120,
"cache_creation_input_tokens": 10,
"cache_read_input_tokens": 5,
"output_tokens": outputTokens,
],
],
]
if let sessionID {
row["sessionId"] = sessionID
}
if let requestID {
row["requestId"] = requestID
}
return row
}
private struct UsageExpectation {
let input: Int
let cacheCreate: Int
let cacheRead: Int
let output: Int
let messages: Int
}
}