File size: 1,628 Bytes
4bbfe8b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
import { describe, expect, test } from "bun:test"
import type { RetentionMetricRow } from "./home"

process.env.SST_RESOURCE_App = JSON.stringify({ name: "opencode", stage: "test" })
process.env.SST_RESOURCE_StatsDatabase = JSON.stringify({ url: "mysql://localhost/stats" })

const { buildRetentionEntries } = await import("./home")

describe("retention aggregates", () => {
  test("pools the latest seven weekly cohorts and ranks models above the sample floor", () => {
    const rows = [
      ...cohorts("model-a", "provider-a", 8, 20, 10),
      ...cohorts("model-b", "provider-b", 8, 20, 12),
      ...cohorts("small-model", "provider-c", 8, 10, 9),
    ]
    const entries = buildRetentionEntries(rows)

    expect(entries.find((item) => item.model === "model-a")).toMatchObject({
      eligibleUserWeeks: 140,
      retainedUserWeeks: 70,
      rate: 50,
      rank: 2,
    })
    expect(entries.find((item) => item.model === "model-b")).toMatchObject({
      eligibleUserWeeks: 140,
      retainedUserWeeks: 84,
      rate: 60,
      rank: 1,
    })
    expect(entries.find((item) => item.model === "small-model")).toMatchObject({
      eligibleUserWeeks: 70,
      retainedUserWeeks: 63,
      rate: 90,
      rank: null,
    })
  })
})

function cohorts(model: string, provider: string, count: number, eligibleUsers: number, retainedUsers: number) {
  return Array.from({ length: count }, (_, index) => ({
    cohortDate: `2026-08-${String(index + 1).padStart(2, "0")}`,
    updatedAt: Date.UTC(2026, 7, index + 9),
    provider,
    model,
    eligibleUsers,
    retainedUsers,
  })) satisfies RetentionMetricRow[]
}