(t *testing.T)
| 295 | } |
| 296 | |
| 297 | func TestCursorAccumulateResultUsage(t *testing.T) { |
| 298 | t.Parallel() |
| 299 | |
| 300 | b := &cursorBackend{cfg: Config{Logger: slog.Default()}} |
| 301 | |
| 302 | // Nested usage object (snake_case keys) — compatible with |
| 303 | // cursor-agent versions that wrap usage in a sub-object. |
| 304 | t.Run("nested_usage_object", func(t *testing.T) { |
| 305 | usage := make(map[string]TokenUsage) |
| 306 | evt := &cursorStreamEvent{ |
| 307 | Model: "gpt-5.3", |
| 308 | Usage: &cursorUsage{ |
| 309 | InputTokens: 200, |
| 310 | OutputTokens: 100, |
| 311 | CacheReadInputTokens: 50, |
| 312 | CacheWriteInputTokens: 25, |
| 313 | }, |
| 314 | } |
| 315 | b.accumulateResultUsage(usage, evt, "") |
| 316 | u := usage["gpt-5.3"] |
| 317 | if u.InputTokens != 200 || u.OutputTokens != 100 || u.CacheReadTokens != 50 || u.CacheWriteTokens != 25 { |
| 318 | t.Fatalf("unexpected usage: %+v", u) |
| 319 | } |
| 320 | }) |
| 321 | |
| 322 | // Top-level camelCase fields (cursor-agent v0.46+) — the current |
| 323 | // default shape from the Cursor CLI. When present, they take |
| 324 | // precedence over any nested usage object. |
| 325 | t.Run("top_level_camelcase", func(t *testing.T) { |
| 326 | usage := make(map[string]TokenUsage) |
| 327 | evt := &cursorStreamEvent{ |
| 328 | Model: "gpt-5.3", |
| 329 | InputTokens: 300, |
| 330 | OutputTokens: 150, |
| 331 | CacheReadTokens: 75, |
| 332 | CacheWriteTokens: 25, |
| 333 | } |
| 334 | b.accumulateResultUsage(usage, evt, "") |
| 335 | u := usage["gpt-5.3"] |
| 336 | if u.InputTokens != 300 || u.OutputTokens != 150 || u.CacheReadTokens != 75 || u.CacheWriteTokens != 25 { |
| 337 | t.Fatalf("unexpected usage: %+v (want input=300 output=150 cache_read=75 cache_write=25)", u) |
| 338 | } |
| 339 | }) |
| 340 | |
| 341 | // Top-level fields win when both shapes are present — this |
| 342 | // prevents double-counting from the nested fallback. |
| 343 | t.Run("top_level_wins_over_nested", func(t *testing.T) { |
| 344 | usage := make(map[string]TokenUsage) |
| 345 | evt := &cursorStreamEvent{ |
| 346 | Model: "gpt-5.3", |
| 347 | InputTokens: 300, |
| 348 | OutputTokens: 150, |
| 349 | Usage: &cursorUsage{ |
| 350 | InputTokens: 999, |
| 351 | OutputTokens: 888, |
| 352 | CacheReadInputTokens: 777, |
| 353 | }, |
| 354 | } |
nothing calls this directly
no test coverage detected