Refactor code structure for improved readability and maintainability
Test Suite / test (24.15.0) (push) Has been cancelled
Test Suite / test (24.15.0) (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,176 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const receipt_import_service_1 = require("./receipt-import.service");
|
||||
function cat(id, name, path) {
|
||||
return { id, name, path };
|
||||
}
|
||||
describe('ReceiptImportService test matrix', () => {
|
||||
const categories = [
|
||||
cat(1, 'Bröd & Kakor', 'Bröd & Kakor'),
|
||||
cat(2, 'Kondis & fika', 'Bröd & Kakor > Kondis & fika'),
|
||||
cat(3, 'Kaffebröd', 'Bröd & Kakor > Kondis & fika > Kaffebröd'),
|
||||
cat(10, 'Skafferi', 'Skafferi'),
|
||||
cat(11, 'Pasta, ris & matgryn', 'Skafferi > Pasta, ris & matgryn'),
|
||||
cat(12, 'Pasta', 'Skafferi > Pasta, ris & matgryn > Pasta'),
|
||||
cat(20, 'Frukt & Grönt', 'Frukt & Grönt'),
|
||||
cat(21, 'Potatis & rotsaker', 'Frukt & Grönt > Potatis & rotsaker'),
|
||||
cat(22, 'Potatis', 'Frukt & Grönt > Potatis & rotsaker > Potatis'),
|
||||
cat(30, 'Mejeri, ost & ägg', 'Mejeri, ost & ägg'),
|
||||
cat(31, 'Matlagning', 'Mejeri, ost & ägg > Matlagning'),
|
||||
cat(32, 'Grädde', 'Mejeri, ost & ägg > Matlagning > Grädde'),
|
||||
cat(33, 'Ägg', 'Mejeri, ost & ägg > Ägg'),
|
||||
cat(40, 'Dryck', 'Dryck'),
|
||||
cat(41, 'Juice, fruktdryck & smoothie', 'Dryck > Juice, fruktdryck & smoothie'),
|
||||
cat(42, 'Kyld juice & nektar', 'Dryck > Juice, fruktdryck & smoothie > Kyld juice & nektar'),
|
||||
cat(50, 'Glass, godis & snacks', 'Glass, godis & snacks'),
|
||||
cat(51, 'Godis', 'Glass, godis & snacks > Godis'),
|
||||
cat(52, 'Godispåsar', 'Glass, godis & snacks > Godis > Godispåsar'),
|
||||
cat(53, 'Choklad', 'Glass, godis & snacks > Choklad'),
|
||||
cat(54, 'Chokladkakor & rullar', 'Glass, godis & snacks > Choklad > Chokladkakor & rullar'),
|
||||
];
|
||||
const prismaMock = {
|
||||
category: { findMany: jest.fn().mockResolvedValue([]) },
|
||||
receiptAlias: { findMany: jest.fn().mockResolvedValue([]) },
|
||||
product: { findMany: jest.fn().mockResolvedValue([]) },
|
||||
};
|
||||
const aiServiceMock = {
|
||||
suggestCategory: jest.fn(),
|
||||
};
|
||||
const categoriesServiceMock = {
|
||||
findFlattened: jest.fn(),
|
||||
};
|
||||
const service = new receipt_import_service_1.ReceiptImportService(prismaMock, aiServiceMock, categoriesServiceMock);
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
prismaMock.receiptAlias.findMany.mockResolvedValue([]);
|
||||
prismaMock.product.findMany.mockResolvedValue([]);
|
||||
});
|
||||
describe('ignore patterns', () => {
|
||||
it.each([
|
||||
'Willys Plus:Bröd',
|
||||
'willys plus: mjölk',
|
||||
'WILLYS PLUS - ÄGG',
|
||||
'Willys Plus : Ost',
|
||||
'Rabatt kupong',
|
||||
'Summa',
|
||||
])('ignorerar "%s"', (raw) => {
|
||||
expect((0, receipt_import_service_1.isIgnoredReceiptName)(raw)).toBe(true);
|
||||
});
|
||||
it.each([
|
||||
'Mezze Maniche',
|
||||
'Snickers',
|
||||
'Nappar Cola 80g',
|
||||
'Vispgrädde 5DL',
|
||||
])('ignorerar inte "%s"', (raw) => {
|
||||
expect((0, receipt_import_service_1.isIgnoredReceiptName)(raw)).toBe(false);
|
||||
});
|
||||
});
|
||||
describe('rule matrix', () => {
|
||||
const matrix = [
|
||||
{ raw: 'Mezze Maniche', expectedPath: 'Skafferi > Pasta, ris & matgryn > Pasta' },
|
||||
{ raw: 'Nappar Cola 80g', expectedPath: 'Glass, godis & snacks > Godis > Godispåsar' },
|
||||
{ raw: 'Snickers', expectedPath: 'Glass, godis & snacks > Choklad > Chokladkakor & rullar' },
|
||||
{ raw: 'Potatis Fast', expectedPath: 'Frukt & Grönt > Potatis & rotsaker > Potatis' },
|
||||
{ raw: 'Ägg 24p Inne M', expectedPath: 'Mejeri, ost & ägg > Ägg' },
|
||||
{ raw: 'Dryck Multivitamin', expectedPath: 'Dryck > Juice, fruktdryck & smoothie > Kyld juice & nektar' },
|
||||
{ raw: 'Vispgrädde 5DL', expectedPath: 'Mejeri, ost & ägg > Matlagning > Grädde' },
|
||||
{ raw: 'Wienerbröd', expectedPath: 'Bröd & Kakor > Kondis & fika > Kaffebröd' },
|
||||
];
|
||||
it.each(matrix)('klassar "$raw" -> "$expectedPath"', ({ raw, expectedPath }) => {
|
||||
const suggestion = service.ruleBasedCategorySuggestion(raw, categories);
|
||||
expect(suggestion).not.toBeNull();
|
||||
expect(suggestion?.path).toBe(expectedPath);
|
||||
});
|
||||
});
|
||||
describe('alias fallback och prioritet', () => {
|
||||
it('prioriterar user-alias före global alias för samma receiptName', async () => {
|
||||
prismaMock.receiptAlias.findMany.mockResolvedValue([
|
||||
{
|
||||
receiptName: 'mjolk 1l',
|
||||
productId: 501,
|
||||
product: {
|
||||
id: 501,
|
||||
name: 'Mjolk user',
|
||||
canonicalName: 'Mjolk user',
|
||||
categoryId: 30,
|
||||
categoryRef: { id: 30, name: 'Mejeri' },
|
||||
},
|
||||
},
|
||||
{
|
||||
receiptName: 'mjolk 1l',
|
||||
productId: 999,
|
||||
product: {
|
||||
id: 999,
|
||||
name: 'Mjolk global',
|
||||
canonicalName: 'Mjolk global',
|
||||
categoryId: 30,
|
||||
categoryRef: { id: 30, name: 'Mejeri' },
|
||||
},
|
||||
},
|
||||
]);
|
||||
prismaMock.product.findMany.mockResolvedValue([]);
|
||||
const result = await service.matchProducts([{ rawName: 'MJOLK 1L' }], 77);
|
||||
expect(prismaMock.receiptAlias.findMany).toHaveBeenCalledWith(expect.objectContaining({
|
||||
where: {
|
||||
OR: [
|
||||
{ ownerId: 77, isGlobal: false },
|
||||
{ isGlobal: true },
|
||||
],
|
||||
},
|
||||
}));
|
||||
expect(result[0].matchedProductId).toBe(501);
|
||||
expect(result[0].matchedProductName).toBe('Mjolk user');
|
||||
});
|
||||
it('använder global alias när user-alias saknas', async () => {
|
||||
prismaMock.receiptAlias.findMany.mockResolvedValue([
|
||||
{
|
||||
receiptName: 'snickers',
|
||||
productId: 222,
|
||||
product: {
|
||||
id: 222,
|
||||
name: 'Snickers',
|
||||
canonicalName: 'Snickers',
|
||||
categoryId: 53,
|
||||
categoryRef: { id: 53, name: 'Choklad' },
|
||||
},
|
||||
},
|
||||
]);
|
||||
prismaMock.product.findMany.mockResolvedValue([]);
|
||||
const result = await service.matchProducts([{ rawName: 'SNICKERS' }], 88);
|
||||
expect(result[0].matchedProductId).toBe(222);
|
||||
expect(result[0].matchedProductName).toBe('Snickers');
|
||||
});
|
||||
it('flöde: manuell korrigering lär alias och nästa import matchar direkt', async () => {
|
||||
const aliases = [];
|
||||
prismaMock.receiptAlias.findMany.mockImplementation(async () => aliases);
|
||||
prismaMock.product.findMany.mockResolvedValue([
|
||||
{
|
||||
id: 700,
|
||||
name: 'Arla Mjolk 1l',
|
||||
canonicalName: 'Mjolk',
|
||||
categoryId: 30,
|
||||
categoryRef: { id: 30, name: 'Mejeri' },
|
||||
},
|
||||
]);
|
||||
const first = await service.matchProducts([{ rawName: 'ARLA MJOLK 1L' }], 42);
|
||||
expect(first[0].matchedProductId).toBeUndefined();
|
||||
expect(first[0].suggestedProductId).toBe(700);
|
||||
aliases.push({
|
||||
receiptName: 'arla mjolk 1l',
|
||||
productId: 700,
|
||||
product: {
|
||||
id: 700,
|
||||
name: 'Arla Mjolk 1l',
|
||||
canonicalName: 'Mjolk',
|
||||
categoryId: 30,
|
||||
categoryRef: { id: 30, name: 'Mejeri' },
|
||||
},
|
||||
});
|
||||
const second = await service.matchProducts([{ rawName: 'ARLA MJOLK 1L' }], 42);
|
||||
expect(second[0].matchedProductId).toBe(700);
|
||||
expect(second[0].matchedProductName).toBe('Mjolk');
|
||||
expect(second[0].suggestedProductId).toBeUndefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
//# sourceMappingURL=receipt-import.service.spec.js.map
|
||||
Reference in New Issue
Block a user