import { describe, it, expect } from "vitest"; import { suggestCategory, extractMerchant, looksLikeCardNumber } from "./suggestCategory"; import type { Category } from "@/api/schemas"; function cat(name: string, kind: "income" | "expense" = "expense", depth = 1): Category { return { name, kind, depth }; } const CATEGORIES: Category[] = [ cat("Food & Drink"), cat("Coffee"), cat("Transport"), cat("Shopping"), cat("Bills & Services"), cat("Entertainment"), cat("Groceries"), cat("Insurance"), cat("Electronics"), ]; describe("extractMerchant", () => { it("pulls the real merchant out of a card string's :MCI: segment", () => { expect(extractMerchant("554835******6886:30-07-2026 11:02:10:MCI:ANTHROPIC 1")).toBe("ANTHROPIC"); expect(extractMerchant("554835******6886:13-08-2026 12:13:52:MCI:WWW HOSTI 5")).toBe("WWW HOSTI"); expect(extractMerchant("554835******6886:11-08-2026 10:07:35:MCI:TAOBAO CO 4")).toBe("TAOBAO CO"); }); it("falls back to the head before the first colon when there's no :MCI: segment", () => { expect(extractMerchant("ЛИЗИНГ ХХК")).toBe("ЛИЗИНГ ХХК"); expect(extractMerchant("1234******5678")).toBe("1234******5678"); }); }); describe("suggestCategory — real card charges (:MCI: merchant extraction)", () => { it("suggests Bills & Services for ANTHROPIC (no Software/Subscriptions category present)", () => { expect(suggestCategory("554835******6886:30-07-2026 11:02:10:MCI:ANTHROPIC 1", CATEGORIES)).toBe( "Bills & Services", ); }); it("prefers Software over Bills & Services when the category exists", () => { const withSoftware = [...CATEGORIES, cat("Software")]; expect(suggestCategory("554835******6886:30-07-2026 11:02:10:MCI:ANTHROPIC 1", withSoftware)).toBe("Software"); }); it("suggests Bills & Services for a hosting merchant (WWW HOSTI)", () => { expect(suggestCategory("554835******6886:13-08-2026 12:13:52:MCI:WWW HOSTI 5", CATEGORIES)).toBe( "Bills & Services", ); }); it("suggests Shopping for TAOBAO", () => { expect(suggestCategory("554835******6886:11-08-2026 10:07:35:MCI:TAOBAO CO 4", CATEGORIES)).toBe("Shopping"); }); }); describe("suggestCategory — local merchants", () => { it("matches ТОКИ (non-bank lender) to Loan", () => { const withLoan = [...CATEGORIES, cat("Loan")]; expect(suggestCategory("ТОКИ ББСБ ХХК", withLoan)).toBe("Loan"); }); it("falls back to Bills & Services when Loan doesn't exist", () => { expect(suggestCategory("ЛИЗИНГ ХХК", CATEGORIES)).toBe("Bills & Services"); }); it("matches the Cyrillic ЗЭЭЛ keyword (word-boundary-free, Cyrillic-safe)", () => { const withLoan = [...CATEGORIES, cat("Loan")]; expect(suggestCategory("ХААН ЗЭЭЛ ТӨЛБӨР", withLoan)).toBe("Loan"); }); it("matches TUSHIG to Groceries", () => { expect(suggestCategory("TUSHIG SUPERMARKET", CATEGORIES)).toBe("Groceries"); }); it("matches other grocery keywords", () => { expect(suggestCategory("NOMIN SUPERMARKET", CATEGORIES)).toBe("Groceries"); expect(suggestCategory("CU-24 CONVENIENCE", CATEGORIES)).toBe("Groceries"); expect(suggestCategory("ХҮНСНИЙ ДЭЛГҮҮР", CATEGORIES)).toBe("Groceries"); }); it("matches insurance keywords", () => { expect(suggestCategory("MONGOL ДААТГАЛ LLC", CATEGORIES)).toBe("Insurance"); expect(suggestCategory("SOME INSURANCE CO", CATEGORIES)).toBe("Insurance"); }); it("matches electronics retailers", () => { expect(suggestCategory("MAGIC TECH STORE", CATEGORIES)).toBe("Electronics"); expect(suggestCategory("ITOPIA MALL", CATEGORIES)).toBe("Electronics"); }); it("matches mobile carriers to Bills & Services", () => { expect(suggestCategory("MOBICOM PAYMENT", CATEGORIES)).toBe("Bills & Services"); expect(suggestCategory("UNITEL TOP UP", CATEGORIES)).toBe("Bills & Services"); }); it("matches apparel/shopping brands", () => { expect(suggestCategory("CONVERSE STORE", CATEGORIES)).toBe("Shopping"); expect(suggestCategory("SANT ASAR TRADE", CATEGORIES)).toBe("Shopping"); }); it("matches gaming merchants to Entertainment", () => { expect(suggestCategory("STEAM GAMES", CATEGORIES)).toBe("Entertainment"); expect(suggestCategory("PGAMING WALLET", CATEGORIES)).toBe("Entertainment"); }); it("matches CAFE/КАФЕ to Coffee, not Food & Drink", () => { expect(suggestCategory("КАФЕ МОДЕРН", CATEGORIES)).toBe("Coffee"); expect(suggestCategory("TOM CAFE LLC", CATEGORIES)).toBe("Coffee"); expect(suggestCategory("КОФЕ ЦЭГ", CATEGORIES)).toBe("Coffee"); expect(suggestCategory("STARBUCKS COFFEE", CATEGORIES)).toBe("Coffee"); }); it("matches restaurant/food keywords (no CAFE overlap) to Food & Drink", () => { expect(suggestCategory("KFC ULAANBAATAR", CATEGORIES)).toBe("Food & Drink"); expect(suggestCategory("ХООЛНЫ ГАЗАР", CATEGORIES)).toBe("Food & Drink"); expect(suggestCategory("BURGER KING", CATEGORIES)).toBe("Food & Drink"); }); it("matches transport/fuel keywords", () => { expect(suggestCategory("UBCAB TRIP", CATEGORIES)).toBe("Transport"); expect(suggestCategory("ШАТАХУУНЫ СТАНЦ", CATEGORIES)).toBe("Transport"); }); }); describe("suggestCategory — no signal", () => { it("returns null for a masked card number with no :MCI: merchant segment", () => { expect(suggestCategory("554835******6886:13-08-2026 09:41:22", CATEGORIES)).toBeNull(); expect(suggestCategory("1234******5678", CATEGORIES)).toBeNull(); }); it("returns null when nothing matches", () => { expect(suggestCategory("TLJ CENTR", CATEGORIES)).toBeNull(); expect(suggestCategory("TSENGELDE", CATEGORIES)).toBeNull(); }); it("returns null when every matching rule's candidates are all absent", () => { // The phone-carrier rule has a single candidate ("Bills & Services") with // no fallback, unlike Loan/Insurance/Groceries/Electronics which fall // back to a broader category — removing it leaves nothing to suggest. const noBills = CATEGORIES.filter((c) => c.name !== "Bills & Services"); expect(suggestCategory("MOBICOM PAYMENT", noBills)).toBeNull(); }); it("returns null for empty input", () => { expect(suggestCategory("", CATEGORIES)).toBeNull(); }); }); describe("looksLikeCardNumber", () => { it("recognizes a masked card head as a card number", () => { expect(looksLikeCardNumber("554835******6886")).toBe(true); }); it("does not flag a normal merchant name", () => { expect(looksLikeCardNumber("ANTHROPIC")).toBe(false); expect(looksLikeCardNumber("ТОКИ ББСБ ХХК")).toBe(false); }); });