import { describe, expect, it } from 'vitest'; import { rng } from '../testing/parser'; import { parseOrder } from './parseOrder'; type Line = [ending: string, qty: number]; const pad4 = (n: number): string => String(n).padStart(4, '0'); const SEPARATORS = [' ', ', ', ',', '\n', ' ; ', '\t', ',,, ', '\n\n']; function generate(seed: number): { text: string; expected: Line[] } { const r = rng(seed); const expected: Line[] = []; const parts: string[] = []; const count = 1 + r.int(40); for (let k = 0; k < count; k++) { if (r.int(10) === 0) { // range of 1-5 endings, qty 1 each const from = r.int(9990); const to = from + r.int(5); for (let e = from; e <= to; e++) expected.push([pad4(e), 1]); parts.push(`${pad4(from)}-${pad4(to)}`); continue; } const ending = pad4(r.int(10000)); const qty = r.int(3) === 0 ? 1 : 1 + r.int(150); const form = r.pick(['bare', 'x', ' x ', '*', '=', ' = ', '_', '-', '()', ' ()', 'space'] as const); const q = String(qty); const rendered = form === 'bare' ? (qty === 1 ? ending : `${ending}x${q}`) : form === '()' ? `${ending}(${q})` : form === ' ()' ? `${ending} (${q})` : form === 'space' ? `${ending} ${q}` : `${ending}${form}${q}`; expected.push([ending, qty]); parts.push(rendered); } let text = r.int(3) === 0 ? '[12/10, 14:35] Shop 2: ' : ''; parts.forEach((p, k) => { text += p; if (k < parts.length - 1) text += r.pick(SEPARATORS); if (r.int(12) === 0) text += `\n${r.pick(['Total..', 'Total =', 'Totel ', 'മൊത്തം ', ''])}${r.int(1000)}\n`; }); if (r.int(2) === 0) text += `\nTotal ${r.int(1000)}`; return { text, expected }; } describe('generated orders round-trip', () => { const seeds = process.env.SWEEP ? 20_000 : 400; it(`reads back ${seeds} generated orders exactly`, () => { for (let seed = 1; seed <= seeds; seed++) { const { text, expected } = generate(seed); const r = parseOrder(text); expect(r.unreadable, `seed ${seed}: ${JSON.stringify(text)}`).toEqual([]); expect(r.lines.map((l): Line => [l.ending, l.qty]), `seed ${seed}`).toEqual(expected); } }); });