git.iko.soy/opencode-android
on 4cdfbda help sign in
opencode-android/app/src/test/java/soy/iko/opencode/ui/usage/UsageAggregationTest.kt
67 lines 2.8 kB history raw
1package soy.iko.opencode.ui.usage
3import org.junit.Assert.assertEquals
4import org.junit.Assert.assertTrue
5import org.junit.Test
6import soy.iko.opencode.data.model.AssistantMessage
7import soy.iko.opencode.data.model.MessageWithParts
8import soy.iko.opencode.data.model.TimeInfo
9import soy.iko.opencode.data.model.Tokens
10import soy.iko.opencode.data.model.UserMessage
12class UsageAggregationTest {
14 private fun assistant(model: String, cost: Double, input: Long, output: Long) =
15 MessageWithParts(
16 info = AssistantMessage(
17 id = "m",
18 sessionID = "s",
19 modelID = model,
20 cost = cost,
21 tokens = Tokens(input = input, output = output),
22 time = TimeInfo(completed = 1L),
23 ),
24 )
26 @Test
27 fun aggregatesTotalsAndByModel() {
28 val sessions = listOf(
29 UsageSessionInput("s1", "One", null, listOf(assistant("gpt", 0.10, 100, 50), assistant("gpt", 0.20, 200, 100))),
30 UsageSessionInput("s2", "Two", null, listOf(assistant("claude", 0.05, 10, 5), MessageWithParts(UserMessage("u", "s2")))),
31 )
32 val report = aggregateUsage(sessions)
33 assertEquals(0.35, report.totalCost, 1e-9)
34 assertEquals(3, report.messageCount)
35 assertEquals(2, report.sessionCount)
36 assertEquals(2, report.byModel.size)
37 // Highest-cost model floats to the top.
38 assertEquals("gpt", report.byModel.first().model)
39 assertEquals(0.30, report.byModel.first().cost, 1e-9)
40 assertEquals(450L, report.byModel.first().tokens.total)
41 }
43 @Test
44 fun ignoresZeroUsageMessages() {
45 val sessions = listOf(UsageSessionInput("s", "t", null, listOf(assistant("m", 0.0, 0, 0))))
46 assertTrue(aggregateUsage(sessions).isEmpty)
47 }
49 @Test
50 fun timeRangeCutoffExcludesOlderSessions() {
51 val now = 10_000_000_000L
52 val dayMs = 24L * 60 * 60 * 1000
53 val recent = UsageSessionInput("recent", "Recent", now - 1000, listOf(assistant("gpt", 0.10, 100, 50)))
54 val old = UsageSessionInput("old", "Old", now - 100_000_000, listOf(assistant("gpt", 0.90, 100, 50)))
55 // A 24h window keeps the recent session only (the old one is ~27h ago).
56 val cutoff = UsageTimeRange.TWENTY_FOUR_HOURS.cutoffMs(now)
57 val report = aggregateUsage(listOf(recent, old), cutoff)
58 assertEquals(0.10, report.totalCost, 1e-9)
59 assertEquals(1, report.sessionCount)
60 // All-time includes both.
61 val all = aggregateUsage(listOf(recent, old), null)
62 assertEquals(1.00, all.totalCost, 1e-9)
63 assertEquals(2, all.sessionCount)
64 // Sanity: the cutoff is exactly one day before now.
65 assertEquals(now - dayMs, cutoff)
66 }