mirror of
https://github.com/pupperpowell/bibdle.git
synced 2026-10-22 05:54:10 -04:00
A verse date is a player's local calendar day, spanning D-1 10:00Z (UTC+14) through D+1 12:00Z (UTC-12). The old UTC end-of-day cutoff both dropped late-night finishes in negative timezones and could persist a partial day. - Scope snapshot loads by the verse date column; use completedBefore only to pin an in-progress point-in-time build. - Gate persistence on isDayClosed (D+1 12:00Z); open days are computed live and never written. - Build the cron snapshot for latestClosedDate instead of EST yesterday, and read the same date for the community page. - Add all-time best single day to snapshot totals. - Bump PAYLOAD_SCHEMA_VERSION to 4 and extend snapshot/window tests.
77 lines
2.1 KiB
TypeScript
77 lines
2.1 KiB
TypeScript
import { describe, test, expect } from 'bun:test';
|
|
import { composeDigest } from '$lib/server/stats/digest';
|
|
import type { SnapshotPayload } from '$lib/types/stats';
|
|
|
|
const payload: SnapshotPayload = {
|
|
asOfDate: '2026-09-14',
|
|
activity: {
|
|
dau: 102,
|
|
wau: 363,
|
|
mau: 852,
|
|
core: 61,
|
|
returningShare: 38,
|
|
newPlayers: 20,
|
|
resurrected: 4,
|
|
churned: 25,
|
|
netGrowth: -1,
|
|
corePrevious: 60,
|
|
previous: null
|
|
},
|
|
dailySeries: [],
|
|
kpis: {
|
|
d1Return: 12.2,
|
|
return7: 18,
|
|
overallReturnRate: 22.8,
|
|
completionRate: null,
|
|
shareRate: null,
|
|
firstTryRate: 33.7,
|
|
avgGuesses: 4.1
|
|
},
|
|
retention: { cohorts: [], curves: { d1: [], d7: [], d30: [] }, timeToSecondPlay: [], segmentMix: [] },
|
|
verseMix: {
|
|
coverage: [],
|
|
testamentMix: [],
|
|
entropy: [],
|
|
repeatIntervals: [],
|
|
firstTryByBook: [],
|
|
retentionByBook: [],
|
|
retentionByGuessBucket: [],
|
|
achievability: []
|
|
},
|
|
habit: { streakSurvival: [], streakLengths: [], calendar: [], weekdayHour: [] },
|
|
growth: { lifecycleFunnel: [], signups: [] },
|
|
totals: { completions: 31775, players: 6589, yesterdayCompletions: 102, yesterdayAvgGuesses: 4.1, bestDay: null },
|
|
signals: [
|
|
{
|
|
id: 'd1_return',
|
|
severity: 'bad',
|
|
title: 'D1 return',
|
|
detail: 'D1 return is 12.2%, below the 20% target.',
|
|
action: 'Test a "come back tomorrow" prompt.'
|
|
}
|
|
],
|
|
meta: { hasStartedData: false, hasShareData: false, firstEventDate: null }
|
|
};
|
|
|
|
describe('composeDigest', () => {
|
|
test('includes the core numbers in plain language', () => {
|
|
const message = composeDigest(payload);
|
|
expect(message).toContain('2026-09-14');
|
|
expect(message).toContain('363');
|
|
expect(message).toContain('12.2');
|
|
expect(message).toContain('Top signal: D1 return');
|
|
expect(message).toContain('/admin/stats?date=2026-09-14');
|
|
});
|
|
|
|
test('is at most a handful of lines', () => {
|
|
expect(composeDigest(payload).split('\n').length).toBeLessThanOrEqual(6);
|
|
});
|
|
|
|
test('shows an em-dash for missing metrics', () => {
|
|
const message = composeDigest({
|
|
...payload,
|
|
kpis: { ...payload.kpis, d1Return: null, return7: null }
|
|
});
|
|
expect(message).toContain('D1 return —%');
|
|
});
|
|
}); |