mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-06-30 18:46:00 +00:00
80 lines
4.0 KiB
TypeScript
80 lines
4.0 KiB
TypeScript
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
import { HttpException } from '@nestjs/common';
|
|
|
|
// Mock the heavy side-effect imports so the service module loads cleanly; the
|
|
// preview() path under test only touches the extractor + llmParse deps.
|
|
vi.mock('../../../../src/db/database', () => ({ db: { prepare: vi.fn() }, closeDb: () => {}, reinitialize: () => {} }));
|
|
vi.mock('../../../../src/websocket', () => ({ broadcast: vi.fn() }));
|
|
vi.mock('../../../../src/services/permissions', () => ({ checkPermission: vi.fn(() => true) }));
|
|
vi.mock('../../../../src/services/tripAccess', () => ({ verifyTripAccess: vi.fn() }));
|
|
vi.mock('../../../../src/services/reservationService', () => ({ createReservation: vi.fn() }));
|
|
vi.mock('../../../../src/services/placeService', () => ({ createPlace: vi.fn() }));
|
|
vi.mock('../../../../src/services/mapsService', () => ({ searchNominatim: vi.fn() }));
|
|
|
|
import { BookingImportService } from '../../../../src/nest/booking-import/booking-import.service';
|
|
|
|
const HOTEL_KI = { '@type': 'LodgingReservation', reservationNumber: 'ABC', reservationFor: { name: 'Hotel X' }, checkinTime: '2026-06-11T15:00', checkoutTime: '2026-06-12T11:00' };
|
|
const file = (name = 'a.pdf') => ({ buffer: Buffer.from('x'), originalname: name } as any);
|
|
|
|
function make(opts: { kit?: boolean; ai?: boolean; extract?: any; parse?: any }) {
|
|
const extractor = { isAvailable: () => opts.kit ?? false, extract: vi.fn(opts.extract ?? (async () => [])) };
|
|
const llmParse = { isAvailable: () => opts.ai ?? false, parse: vi.fn(opts.parse ?? (async () => ({ kiItems: [], warnings: [] }))) };
|
|
return { svc: new BookingImportService(extractor as any, llmParse as any), extractor, llmParse };
|
|
}
|
|
|
|
beforeEach(() => vi.clearAllMocks());
|
|
|
|
describe('BookingImportService.preview', () => {
|
|
it('no-ai: maps kitinerary items, does not force needs_review, reports aiUsed:false', async () => {
|
|
const { svc, llmParse } = make({ kit: true, ai: false, extract: async () => [HOTEL_KI] });
|
|
const res = await svc.preview([file()], 'no-ai', 1);
|
|
expect(res.items).toHaveLength(1);
|
|
expect(res.items[0].needs_review).toBeFalsy();
|
|
expect(res.files).toEqual([{ fileName: 'a.pdf', aiAvailable: false, aiUsed: false }]);
|
|
expect(llmParse.parse).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('throws 503 when neither parser is available', async () => {
|
|
const { svc } = make({ kit: false, ai: false });
|
|
try {
|
|
await svc.preview([file()], 'no-ai', 1);
|
|
throw new Error('expected throw');
|
|
} catch (err) {
|
|
expect(err).toBeInstanceOf(HttpException);
|
|
expect((err as HttpException).getStatus()).toBe(503);
|
|
}
|
|
});
|
|
|
|
it('fallback-on-empty: runs the LLM when kitinerary finds nothing and flags needs_review', async () => {
|
|
const { svc, extractor, llmParse } = make({
|
|
kit: true, ai: true,
|
|
extract: async () => [],
|
|
parse: async () => ({ kiItems: [HOTEL_KI], warnings: [] }),
|
|
});
|
|
const res = await svc.preview([file()], 'fallback-on-empty', 1);
|
|
expect(extractor.extract).toHaveBeenCalled();
|
|
expect(llmParse.parse).toHaveBeenCalled();
|
|
expect(res.items).toHaveLength(1);
|
|
expect(res.items[0].needs_review).toBe(true);
|
|
expect(res.files![0]).toEqual({ fileName: 'a.pdf', aiAvailable: true, aiUsed: true });
|
|
});
|
|
|
|
it('fallback-on-empty: skips the LLM when kitinerary already found items', async () => {
|
|
const { svc, llmParse } = make({ kit: true, ai: true, extract: async () => [HOTEL_KI] });
|
|
const res = await svc.preview([file()], 'fallback-on-empty', 1);
|
|
expect(llmParse.parse).not.toHaveBeenCalled();
|
|
expect(res.files![0].aiUsed).toBe(false);
|
|
});
|
|
|
|
it('force-ai: skips kitinerary entirely and uses the LLM', async () => {
|
|
const { svc, extractor, llmParse } = make({
|
|
kit: true, ai: true,
|
|
parse: async () => ({ kiItems: [HOTEL_KI], warnings: [] }),
|
|
});
|
|
const res = await svc.preview([file()], 'force-ai', 1);
|
|
expect(extractor.extract).not.toHaveBeenCalled();
|
|
expect(llmParse.parse).toHaveBeenCalled();
|
|
expect(res.items[0].needs_review).toBe(true);
|
|
});
|
|
});
|