Files
TREK/server/tests/unit/nest/llm-parse/extraction-router.test.ts
T
Maurice 55ff5c03dd refactor(extract): drop vendor templates, let the model drive with deterministic backfill
Now that a capable instruct model (Qwen3-8B, thinking off) reads name/address/dates/legs reliably across formats, the per-vendor template short-circuit distorted more than it fixed: brittle on layout variations and overriding the better model output. Remove the template layer; the model extracts the structure and Schicht 2 backfills the confirmation/total and takes the currency from the document's own symbol (correcting model misreads like ¥→$). Per-type prompts now also ask for address and price/currency.
2026-06-28 11:53:19 +02:00

51 lines
2.4 KiB
TypeScript

import { describe, it, expect } from 'vitest';
import { extractBookingRef, extractTotalPrice, normCurrency } from '../../../../src/nest/llm-parse/router/extraction-router';
describe('extractBookingRef', () => {
it('reads an Airbnb "Bestätigungs-Code"', () => {
expect(extractBookingRef('Bestätigungs-Code\nHMHJ9RTEEK')).toBe('HMHJ9RTEEK');
});
it('prefers the customer "Reservation No." over a later "Supplier Reference"', () => {
expect(extractBookingRef('Reservation No.: G72820729\nSUPPLIER DETAILS\nSupplier Reference: IT587200464')).toBe('G72820729');
});
it('reads an Expedia "Reiseplan" number', () => {
expect(extractBookingRef('Expedia-Reiseplan: 73222406755286')).toBe('73222406755286');
});
it('reads a classic "Buchungsnummer" / "PNR"', () => {
expect(extractBookingRef('Buchungsnummer: ABC123')).toBe('ABC123');
expect(extractBookingRef('PNR XY7Q9Z')).toBe('XY7Q9Z');
});
it('does not capture a prose word after a bare "Confirmation"/"reference"', () => {
expect(extractBookingRef('Booking Confirmation\n\nThank you for choosing us')).toBeUndefined();
expect(extractBookingRef('For future reference please retain this email')).toBeUndefined();
});
});
describe('extractTotalPrice', () => {
it('reads a labeled German total', () => {
expect(extractTotalPrice('Gesamtpreis 61,23 €')).toEqual({ price: '61,23', currency: 'EUR' });
});
it('reads an Airbnb "Bezahlter Betrag"', () => {
expect(extractTotalPrice('Bezahlter Betrag\n651,86 €')).toEqual({ price: '651,86', currency: 'EUR' });
});
it('falls back to a standalone ¥ voucher price (JPY) with no nearby label', () => {
expect(extractTotalPrice('Price (consumption tax included)\n金額(消費税込)\n¥9,400\nAdult')).toEqual({ price: '9,400', currency: 'JPY' });
});
it('returns null when there is neither a labeled nor a symbol amount', () => {
expect(extractTotalPrice('Just some terms and conditions, no price here.')).toBeNull();
});
});
describe('normCurrency', () => {
it('maps symbols and codes to ISO 4217', () => {
expect(normCurrency('€')).toBe('EUR');
expect(normCurrency('¥')).toBe('JPY');
expect(normCurrency('$')).toBe('USD');
expect(normCurrency('CHF')).toBe('CHF');
});
it('returns undefined for an unrecognised token', () => {
expect(normCurrency('')).toBeUndefined();
expect(normCurrency('hello world')).toBeUndefined();
});
});