1import { describe, expect, it } from 'vitest'; 2import { dataPoint } from '../../worker/src/events.ts'; 3import { dataset, decodeRows, eventQuery } from './events.mjs'; 4 5describe('eventQuery: an event read back by its schema names', () => { 6 it('selects its columns as named, filtered to the event and the window', () => { 7 const sql = eventQuery('jev.relay', { since: '7d', limit: 500 }); 8 expect(sql).toContain('blob1 AS outcome, blob2 AS colo'); 9 expect(sql).toContain('double2 AS status, double3 AS upstreamMs'); 10 expect(sql).toContain(`FROM ${dataset()}`); 11 expect(sql).toContain("WHERE index1 = 'jev.relay' AND timestamp > NOW() - INTERVAL '7' DAY"); 12 expect(sql).toMatch(/LIMIT 500\nFORMAT JSON$/); 13 }); 14 15 it('refuses what is not in the schema or not a window', () => { 16 expect(() => eventQuery("x' OR 1=1 --")).toThrow(/no event/); 17 expect(() => eventQuery('jev.relay', { since: "1d' OR 1" })).toThrow(/--since/); 18 expect(() => eventQuery('jev.relay', { limit: 0 })).toThrow(/--limit/); 19 }); 20}); 21 22describe('decodeRows: the known-mask undone', () => { 23 it('gives back what the Worker wrote, unknown doubles as null', () => { 24 const point = dataPoint('jev.relay', { 25 blobs: { outcome: 'refused', colo: 'DFW' }, 26 doubles: { status: 429, upstreamMs: null, requestBytes: null, sectionInS: 1.5, attempt: 0 }, 27 }); 28 // a row as the SQL API returns it for eventQuery's columns 29 const row = { 30 timestamp: '2026-09-25 12:00:00', 31 weight: '1', 32 _known: point.doubles[0], 33 outcome: point.blobs[0], 34 colo: point.blobs[1], 35 status: point.doubles[1], 36 upstreamMs: point.doubles[2], 37 requestBytes: point.doubles[3], 38 sectionInS: point.doubles[4], 39 attempt: point.doubles[5], 40 }; 41 expect(decodeRows('jev.relay', [row])).toEqual([ 42 { 43 timestamp: '2026-09-25 12:00:00', 44 weight: 1, 45 outcome: 'refused', 46 colo: 'DFW', 47 status: 429, 48 upstreamMs: null, 49 requestBytes: null, 50 sectionInS: 1.5, 51 attempt: 0, 52 }, 53 ]); 54 }); 55});