feat: add tests for QuickImportService and ReceiptImportService parse flow
This commit is contained in:
@@ -287,4 +287,121 @@ describe('ReceiptImportService test matrix', () => {
|
||||
expect(result.matchedVia).toBe('none');
|
||||
});
|
||||
});
|
||||
|
||||
describe('AI fallback och prioriteringskedja', () => {
|
||||
function makeContext(
|
||||
aliases: any[],
|
||||
products: any[],
|
||||
aiEnabled: boolean,
|
||||
unitMappings: any[] = [],
|
||||
userId?: number,
|
||||
) {
|
||||
return { userId, aliases, products, unitMappings, categories, aiEnabled };
|
||||
}
|
||||
|
||||
it('använder AI-fallback när ingen alias/wordmatch/regelträff finns och aiEnabled=true', async () => {
|
||||
aiServiceMock.suggestCategory.mockResolvedValue({
|
||||
categoryId: 42,
|
||||
categoryName: 'Kyld juice & nektar',
|
||||
path: 'Dryck > Juice, fruktdryck & smoothie > Kyld juice & nektar',
|
||||
confidence: 'low',
|
||||
});
|
||||
|
||||
const context = makeContext([], [], true, [], 10);
|
||||
const result = await (service as any).matchAndEnrichReceiptItem({ rawName: 'XYZXYZ 123' }, context);
|
||||
|
||||
expect(aiServiceMock.suggestCategory).toHaveBeenCalledWith('XYZXYZ 123', categories);
|
||||
expect(result.matchedVia).toBe('none');
|
||||
expect(result.categorySuggestion?.categoryId).toBe(42);
|
||||
expect(result.categorySuggestion?.path).toBe('Dryck > Juice, fruktdryck & smoothie > Kyld juice & nektar');
|
||||
});
|
||||
|
||||
it('kallar inte AI när aiEnabled=false och ingen annan kategorisering finns', async () => {
|
||||
const context = makeContext([], [], false, [], 10);
|
||||
const result = await (service as any).matchAndEnrichReceiptItem({ rawName: 'XYZXYZ 123' }, context);
|
||||
|
||||
expect(aiServiceMock.suggestCategory).not.toHaveBeenCalled();
|
||||
expect(result.categorySuggestion).toBeUndefined();
|
||||
expect(result.matchedVia).toBe('none');
|
||||
});
|
||||
|
||||
it('hoppar över AI när kategori redan satts via produktmatchning', async () => {
|
||||
const products = [
|
||||
{
|
||||
id: 808,
|
||||
name: 'Mjolk',
|
||||
canonicalName: 'Mjolk',
|
||||
categoryRef: { id: 30, name: 'Mejeri, ost & ägg' },
|
||||
},
|
||||
];
|
||||
|
||||
const context = makeContext([], products, true, [], 10);
|
||||
const result = await (service as any).matchAndEnrichReceiptItem({ rawName: 'MJOLK 1L' }, context);
|
||||
|
||||
expect(result.matchedVia).toBe('wordmatch');
|
||||
expect(result.categorySuggestion?.categoryId).toBe(30);
|
||||
expect(aiServiceMock.suggestCategory).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('följer prioritering: user alias -> global alias -> wordmatch -> AI', async () => {
|
||||
aiServiceMock.suggestCategory.mockResolvedValue({
|
||||
categoryId: 51,
|
||||
categoryName: 'Godis',
|
||||
path: 'Glass, godis & snacks > Godis',
|
||||
confidence: 'low',
|
||||
});
|
||||
|
||||
const globalAlias = {
|
||||
receiptName: 'mixad vara',
|
||||
productId: 900,
|
||||
product: {
|
||||
id: 900,
|
||||
name: 'Global Produkt',
|
||||
canonicalName: 'Global Produkt',
|
||||
categoryRef: { id: 53, name: 'Choklad' },
|
||||
},
|
||||
};
|
||||
|
||||
const userAlias = {
|
||||
receiptName: 'mixad vara',
|
||||
productId: 901,
|
||||
product: {
|
||||
id: 901,
|
||||
name: 'User Produkt',
|
||||
canonicalName: 'User Produkt',
|
||||
categoryRef: { id: 30, name: 'Mejeri, ost & ägg' },
|
||||
},
|
||||
};
|
||||
|
||||
const wordProducts = [
|
||||
{
|
||||
id: 777,
|
||||
name: 'Specialprodukt',
|
||||
canonicalName: 'Specialprodukt',
|
||||
categoryRef: null,
|
||||
},
|
||||
];
|
||||
|
||||
const userAliasContext = makeContext([userAlias, globalAlias], wordProducts, true, [], 10);
|
||||
const userAliasResult = await (service as any).matchAndEnrichReceiptItem({ rawName: 'MIXAD VARA' }, userAliasContext);
|
||||
expect(userAliasResult.matchedVia).toBe('alias');
|
||||
expect(userAliasResult.matchedProductId).toBe(901);
|
||||
|
||||
const globalAliasContext = makeContext([globalAlias], wordProducts, true, [], 10);
|
||||
const globalAliasResult = await (service as any).matchAndEnrichReceiptItem({ rawName: 'MIXAD VARA' }, globalAliasContext);
|
||||
expect(globalAliasResult.matchedVia).toBe('alias');
|
||||
expect(globalAliasResult.matchedProductId).toBe(900);
|
||||
|
||||
const wordMatchContext = makeContext([], wordProducts, true, [], 10);
|
||||
const wordMatchResult = await (service as any).matchAndEnrichReceiptItem({ rawName: 'SPECIALPRODUKT 1st' }, wordMatchContext);
|
||||
expect(wordMatchResult.matchedVia).toBe('wordmatch');
|
||||
expect(wordMatchResult.suggestedProductId).toBe(777);
|
||||
|
||||
const aiFallbackContext = makeContext([], [], true, [], 10);
|
||||
const aiFallbackResult = await (service as any).matchAndEnrichReceiptItem({ rawName: 'helt okänd vara' }, aiFallbackContext);
|
||||
expect(aiFallbackResult.matchedVia).toBe('none');
|
||||
expect(aiServiceMock.suggestCategory).toHaveBeenCalled();
|
||||
expect(aiFallbackResult.categorySuggestion?.categoryId).toBe(51);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user