import { describe, test, expect, beforeEach, afterEach } from 'bun:test'; import { eq, and } from 'drizzle-orm'; import { testDb } from '$lib/server/db/test'; import { analyticsEvents } from '$lib/server/db/schema'; import { recordEvent, isEventName, encodeMetadata } from '$lib/server/stats/events'; import { MAX_METADATA_BYTES } from '$lib/types/stats'; async function clear() { await testDb.delete(analyticsEvents); } describe('analytics events', () => { beforeEach(clear); afterEach(clear); test('allowlist accepts known event names and rejects others', () => { expect(isEventName('verse_served')).toBe(true); expect(isEventName('donate_click')).toBe(true); expect(isEventName('not_a_real_event')).toBe(false); expect(isEventName('')).toBe(false); }); test('metadata is capped', () => { const small = encodeMetadata({ utm_source: 'bluesky' }); expect(small).toBe('{"utm_source":"bluesky"}'); const big = encodeMetadata({ blob: 'x'.repeat(MAX_METADATA_BYTES + 100) }); expect(big).toBe('{"truncated":true}'); expect(encodeMetadata(null)).toBeNull(); }); test('records a normal event', async () => { const inserted = await recordEvent( { name: 'donate_click', anonymousId: 'anon-1' }, testDb ); expect(inserted).toBe(true); const rows = await testDb.select().from(analyticsEvents); expect(rows).toHaveLength(1); expect(rows[0].name).toBe('donate_click'); }); test('verse_served is deduped per player + date', async () => { const first = await recordEvent( { name: 'verse_served', anonymousId: 'anon-1', date: '2026-09-01' }, testDb ); const second = await recordEvent( { name: 'verse_served', anonymousId: 'anon-1', date: '2026-09-01' }, testDb ); expect(first).toBe(true); expect(second).toBe(false); const rows = await testDb .select() .from(analyticsEvents) .where( and( eq(analyticsEvents.name, 'verse_served'), eq(analyticsEvents.anonymousId, 'anon-1') ) ); expect(rows).toHaveLength(1); }); test('other event names are not deduped', async () => { await recordEvent({ name: 'share', anonymousId: 'anon-1', date: '2026-09-01' }, testDb); await recordEvent({ name: 'share', anonymousId: 'anon-1', date: '2026-09-01' }, testDb); const rows = await testDb.select().from(analyticsEvents); expect(rows).toHaveLength(2); }); test('user attribution is stored', async () => { await recordEvent( { name: 'share_copy', anonymousId: 'anon-1', userId: 'user-9', metadata: { snippet: true } }, testDb ); const [row] = await testDb.select().from(analyticsEvents); expect(row.userId).toBe('user-9'); expect(JSON.parse(row.metadata!)).toEqual({ snippet: true }); }); });