mirror of
https://github.com/pupperpowell/bibdle.git
synced 2026-10-22 05:54:10 -04:00
86 lines
2.6 KiB
TypeScript
86 lines
2.6 KiB
TypeScript
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 });
|
|
});
|
|
});
|