117 lines
4.8 KiB
JavaScript
117 lines
4.8 KiB
JavaScript
import { strict as assert } from 'node:assert'
|
|
import { test } from 'node:test'
|
|
|
|
import { usageProjection } from '../index.js'
|
|
import { deriveUsage, emptyUsage, foldUsage, usageSummary } from '../lib/usage.js'
|
|
|
|
/**
|
|
* The projection contract, tested against the framework's stated requirements
|
|
* rather than against the shape of my own code:
|
|
*
|
|
* - the state schema validates the persisted checkpoint before it seeds a fold,
|
|
* so a stale or corrupt row must be *rejected*, not coerced;
|
|
* - `apply` must return the same reference for an event it does not own, or the
|
|
* unit republishes on every step of every session;
|
|
* - the view is compared by `Object.is`, so one state must yield one object.
|
|
*/
|
|
|
|
/** A folded state, as a conversation with a couple of searches would produce. */
|
|
function foldedState() {
|
|
const value = {
|
|
action: 'search',
|
|
ok: true,
|
|
weak: false,
|
|
focus: { path: 'src/auth' },
|
|
results: [{ excerpt: 'x'.repeat(400) }, { excerpt: 'y'.repeat(200) }],
|
|
measure: { excerpts: 2, returnedBytes: 600, sourceBytes: 24_000, files: 2 },
|
|
}
|
|
const event = { type: 'tool/result', meta: deriveUsage({ action: 'search' }, value) }
|
|
return foldUsage(foldUsage(emptyUsage(), event), event)
|
|
}
|
|
|
|
test('the unit owns the key the client will read', () => {
|
|
assert.equal(usageProjection.key, 'recallUsage')
|
|
assert.equal(usageProjection.stateVersion, 1)
|
|
assert.ok(Number.isInteger(usageProjection.stateVersion) && usageProjection.stateVersion >= 0)
|
|
})
|
|
|
|
test('init produces the empty state, and it survives a round trip', () => {
|
|
const state = usageProjection.init()
|
|
assert.deepEqual(state, emptyUsage())
|
|
assert.deepEqual(usageProjection.stateSchema.parse(state), state)
|
|
})
|
|
|
|
test('the state schema accepts a folded state and preserves it', () => {
|
|
const state = foldedState()
|
|
assert.equal(state.searches, 2)
|
|
// Persisted as JSON and re-parsed on restore, so that round trip must hold.
|
|
const restored = usageProjection.stateSchema.parse(JSON.parse(JSON.stringify(state)))
|
|
assert.deepEqual(restored, state)
|
|
})
|
|
|
|
test('the state schema REJECTS a corrupt checkpoint rather than coercing it', () => {
|
|
// The service catches a parse failure and skips the row; coercing garbage
|
|
// would forward-apply nonsense into every later total instead.
|
|
for (const bad of [null, undefined, 42, 'state', {}, { searches: 'two' }, { ...emptyUsage(), searches: null }]) {
|
|
assert.equal(usageProjection.stateSchema.safeParse(bad).success, false, `accepted ${JSON.stringify(bad)}`)
|
|
}
|
|
})
|
|
|
|
test('apply returns the SAME reference for events the unit does not own', () => {
|
|
const state = foldedState()
|
|
assert.equal(usageProjection.apply(state, { type: 'user/message' }), state)
|
|
assert.equal(usageProjection.apply(state, { type: 'tool/result', meta: { other: 1 } }), state)
|
|
assert.equal(usageProjection.apply(state, { type: 'assistant/message' }), state)
|
|
})
|
|
|
|
test('apply folds an event the unit does own', () => {
|
|
const state = usageProjection.init()
|
|
const value = {
|
|
action: 'search',
|
|
ok: true,
|
|
results: [{ excerpt: 'abc' }],
|
|
measure: { excerpts: 1, returnedBytes: 3, sourceBytes: 900, files: 1 },
|
|
}
|
|
const event = { type: 'tool/result', meta: deriveUsage({ action: 'search' }, value) }
|
|
const next = usageProjection.apply(state, event)
|
|
assert.notEqual(next, state, 'a real event must produce a new state')
|
|
assert.equal(next.searches, 1)
|
|
assert.equal(next.sourceBytes, 900)
|
|
})
|
|
|
|
test('the view validates against its own schema', () => {
|
|
const state = foldedState()
|
|
const view = usageProjection.wire.view(state)
|
|
assert.deepEqual(usageProjection.wire.viewSchema.parse(view), view)
|
|
})
|
|
|
|
test('one state yields ONE view object, by reference', () => {
|
|
// The live drive compares the two latest views with Object.is; returning a
|
|
// fresh object per call would republish on every event that touched the state
|
|
// even when the numbers a surface shows had not moved.
|
|
const state = foldedState()
|
|
const first = usageProjection.wire.view(state)
|
|
const second = usageProjection.wire.view(state)
|
|
assert.equal(first, second)
|
|
})
|
|
|
|
test('a different state yields a different view', () => {
|
|
const one = usageProjection.wire.view(foldedState())
|
|
const two = usageProjection.wire.view(emptyUsage())
|
|
assert.notEqual(one, two)
|
|
assert.equal(one.searches, 2)
|
|
assert.equal(two.searches, 0)
|
|
})
|
|
|
|
test('the view carries the comparison and the compounding figure', () => {
|
|
const view = usageProjection.wire.view(foldedState())
|
|
assert.equal(view.searches, 2)
|
|
assert.equal(view.sourceBytes, 48_000)
|
|
assert.equal(view.returnedBytes, 1200)
|
|
assert.equal(view.avoidedBytes, 46_800)
|
|
// bytes/4 is an approximation and is labelled as one where it is displayed.
|
|
assert.equal(view.avoidedTokens, Math.round(46_800 / 4))
|
|
assert.ok(view.compression > 1)
|
|
assert.equal(view.avoidedUsd, usageSummary(foldedState()).avoidedUsd)
|
|
})
|