Adds unit + integration + regression tests and a QA checklist for the v2→v1 seed migration feature. - Fixtures: src/__fixtures__/profiles.ts (makeV2Profile, makeV1Profile, makeV2ProfileWithCustom) with realistic categories, keywords, suppliers, transactions, budgets. - Unit: categoryMappingService (100 cases covering every DEFAULT_MAPPINGS entry, 4-pass priority, splits, preserved/custom detection), categoryBackupService (23 cases — Tauri mocks: success, write error, integrity check, PIN-encrypted profile), categoryMigrationService (16 cases — BEGIN/COMMIT/ROLLBACK flow, backup-missing abort, journaling, custom parent creation). - Integration: full plan→backup→migrate→verify flow; rollback via SREF import; backup failure → no DB write; migration SQL failure → ROLLBACK + intact state. - Regression: parameterised v2/v1 fixtures covering auto-categorisation, budget aggregation, splits preservation. - Docs: docs/qa-refonte-seed-categories-ipc.md — manual checklist for UX, system errors, encrypted profile, custom preservation, 90-day banner, restore flow. 331 vitest tests pass (up from 193 baseline). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
180 lines
7.6 KiB
TypeScript
180 lines
7.6 KiB
TypeScript
/**
|
|
* Profile fixtures for the v2 → v1 categories migration tests.
|
|
*
|
|
* These fixtures are intentionally loose — they match the minimal shapes
|
|
* consumed by categoryMappingService.ProfileData and the broader migration
|
|
* test helpers (budget/categorization regression). They are *not* a mock of
|
|
* the full Tauri DB layer; tests that need DB access still mock `getDb`.
|
|
*
|
|
* Three flavours are provided:
|
|
* - `makeV2Profile()` realistic v2-seeded profile (~30 cats)
|
|
* - `makeV1Profile()` same user data but already on v1 taxonomy
|
|
* - `makeV2ProfileWithCustom()` v2 profile with 3 user-created categories
|
|
*/
|
|
import type {
|
|
ProfileData,
|
|
V2CategoryInput,
|
|
V2KeywordInput,
|
|
V2TransactionInput,
|
|
V2SupplierInput,
|
|
} from "../services/categoryMappingService";
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// v2 seed (excerpt matching DEFAULT_MAPPINGS keys)
|
|
// ---------------------------------------------------------------------------
|
|
|
|
const V2_STRUCTURAL_PARENTS: V2CategoryInput[] = [
|
|
{ id: 1, name: "Revenus", parent_id: null },
|
|
{ id: 2, name: "Dépenses récurrentes", parent_id: null },
|
|
{ id: 3, name: "Dépenses ponctuelles", parent_id: null },
|
|
{ id: 4, name: "Maison", parent_id: null },
|
|
{ id: 5, name: "Placements", parent_id: null },
|
|
{ id: 6, name: "Autres", parent_id: null },
|
|
];
|
|
|
|
const V2_SEEDED_CATS: V2CategoryInput[] = [
|
|
// Revenus
|
|
{ id: 10, name: "Paie", parent_id: 1 },
|
|
{ id: 11, name: "Autres revenus", parent_id: 1 },
|
|
// Dépenses récurrentes
|
|
{ id: 20, name: "Loyer", parent_id: 2 },
|
|
{ id: 21, name: "Électricité", parent_id: 2 },
|
|
{ id: 22, name: "Épicerie", parent_id: 2 },
|
|
{ id: 23, name: "Dons", parent_id: 2 },
|
|
{ id: 24, name: "Restaurant", parent_id: 2 },
|
|
{ id: 25, name: "Frais bancaires", parent_id: 2 },
|
|
{ id: 26, name: "Jeux, Films & Livres", parent_id: 2 },
|
|
{ id: 27, name: "Abonnements Musique", parent_id: 2 },
|
|
{ id: 28, name: "Transport en commun", parent_id: 2 },
|
|
{ id: 29, name: "Internet & Télécom", parent_id: 2 },
|
|
{ id: 30, name: "Animaux", parent_id: 2 },
|
|
{ id: 31, name: "Assurances", parent_id: 2 },
|
|
{ id: 32, name: "Pharmacie", parent_id: 2 },
|
|
// Dépenses ponctuelles
|
|
{ id: 40, name: "Voiture", parent_id: 3 },
|
|
{ id: 41, name: "Amazon", parent_id: 3 },
|
|
{ id: 42, name: "Électroniques", parent_id: 3 },
|
|
{ id: 43, name: "Alcool", parent_id: 3 },
|
|
{ id: 44, name: "Cadeaux", parent_id: 3 },
|
|
{ id: 45, name: "Vêtements", parent_id: 3 },
|
|
{ id: 47, name: "Voyage", parent_id: 3 },
|
|
{ id: 48, name: "Sports & Plein air", parent_id: 3 },
|
|
{ id: 49, name: "Spectacles & sorties", parent_id: 3 },
|
|
// Maison
|
|
{ id: 50, name: "Hypothèque", parent_id: 4 },
|
|
{ id: 51, name: "Achats maison", parent_id: 4 },
|
|
{ id: 52, name: "Entretien maison", parent_id: 4 },
|
|
{ id: 53, name: "Électroménagers & Meubles", parent_id: 4 },
|
|
// Autres
|
|
{ id: 70, name: "Impôts", parent_id: 6 },
|
|
{ id: 71, name: "Paiement CC", parent_id: 6 },
|
|
{ id: 72, name: "Retrait cash", parent_id: 6 },
|
|
];
|
|
|
|
const V2_KEYWORDS: V2KeywordInput[] = [
|
|
{ id: 101, keyword: "PAIE DEPOT", category_id: 10 },
|
|
{ id: 102, keyword: "IGA", category_id: 22 },
|
|
{ id: 103, keyword: "METRO PLUS", category_id: 22 },
|
|
{ id: 104, keyword: "STM", category_id: 28 },
|
|
{ id: 105, keyword: "SHELL", category_id: 40 },
|
|
{ id: 106, keyword: "NETFLIX", category_id: 26 },
|
|
{ id: 107, keyword: "PRIMEVIDEO", category_id: 26 },
|
|
{ id: 108, keyword: "AMAZON", category_id: 41 },
|
|
];
|
|
|
|
const V2_SUPPLIERS: V2SupplierInput[] = [
|
|
{ id: 501, name: "STM" },
|
|
{ id: 502, name: "Shell Canada" },
|
|
{ id: 503, name: "Hilton Montreal" },
|
|
{ id: 504, name: "IGA" },
|
|
{ id: 505, name: "Hydro-Québec" },
|
|
];
|
|
|
|
const V2_TRANSACTIONS: V2TransactionInput[] = [
|
|
{ id: 1, description: "DEPOT PAIE MAX", category_id: 10 },
|
|
{ id: 2, description: "IGA #5555", category_id: 22, supplier_id: 504 },
|
|
{ id: 3, description: "SHELL #231 LAVAL", category_id: 40, supplier_id: 502 },
|
|
{ id: 4, description: "STM CARTE OPUS", category_id: 28, supplier_id: 501 },
|
|
{ id: 5, description: "HYDRO-QUEBEC FACTURE", category_id: 21, supplier_id: 505 },
|
|
{ id: 6, description: "NETFLIX.COM", category_id: 26 },
|
|
{ id: 7, description: "HILTON SEATTLE", category_id: 47, supplier_id: 503 },
|
|
{ id: 8, description: "AMAZON.CA *XYZ", category_id: 41 },
|
|
];
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Public builders
|
|
// ---------------------------------------------------------------------------
|
|
|
|
export function makeV2Profile(): ProfileData {
|
|
return {
|
|
v2Categories: [...V2_STRUCTURAL_PARENTS, ...V2_SEEDED_CATS],
|
|
keywords: [...V2_KEYWORDS],
|
|
transactions: [...V2_TRANSACTIONS],
|
|
suppliers: [...V2_SUPPLIERS],
|
|
};
|
|
}
|
|
|
|
export function makeV2ProfileWithCustom(): ProfileData {
|
|
const base = makeV2Profile();
|
|
const custom: V2CategoryInput[] = [
|
|
{ id: 9001, name: "Projet maison", parent_id: 3 },
|
|
{ id: 9002, name: "Activités enfants", parent_id: 3 },
|
|
{ id: 9003, name: "Hobby moto", parent_id: 3 },
|
|
];
|
|
return {
|
|
...base,
|
|
v2Categories: [...base.v2Categories, ...custom],
|
|
};
|
|
}
|
|
|
|
/**
|
|
* v1 "profile" — same user data after the migration has been applied.
|
|
* Category ids follow the v1 taxonomy (`categoryTaxonomyV1.json`) and the
|
|
* DEFAULT_MAPPINGS table from categoryMappingService. Useful for parameterised
|
|
* regression tests where the behaviour must be identical on v1 and v2.
|
|
*/
|
|
export function makeV1Profile(): ProfileData {
|
|
// v1 categories in this shape are just for iteration — the SQL write-over
|
|
// path runs the real v1 taxonomy. We only need a few leaves for keyword /
|
|
// budget tests that don't actually inspect the full tree.
|
|
const v1Cats: V2CategoryInput[] = [
|
|
{ id: 1011, name: "Paie régulière", parent_id: 1010 },
|
|
{ id: 1090, name: "Autres revenus", parent_id: 1000 },
|
|
{ id: 1111, name: "Épicerie régulière", parent_id: 1110 },
|
|
{ id: 1121, name: "Restaurants & sorties", parent_id: 1120 },
|
|
{ id: 1211, name: "Loyer", parent_id: 1210 },
|
|
{ id: 1221, name: "Électricité", parent_id: 1220 },
|
|
{ id: 1521, name: "Autobus & métro", parent_id: 1520 },
|
|
{ id: 1512, name: "Essence", parent_id: 1510 },
|
|
{ id: 1713, name: "Abonnements streaming", parent_id: 1710 },
|
|
{ id: 1533, name: "Hébergement en voyage", parent_id: 1530 },
|
|
{ id: 1946, name: "Achats divers", parent_id: 1940 },
|
|
];
|
|
// Keywords/suppliers/transactions carry v1 category_id values now.
|
|
const v1Keywords: V2KeywordInput[] = [
|
|
{ id: 101, keyword: "PAIE DEPOT", category_id: 1011 },
|
|
{ id: 102, keyword: "IGA", category_id: 1111 },
|
|
{ id: 103, keyword: "METRO PLUS", category_id: 1111 },
|
|
{ id: 104, keyword: "STM", category_id: 1521 },
|
|
{ id: 105, keyword: "SHELL", category_id: 1512 },
|
|
{ id: 106, keyword: "NETFLIX", category_id: 1713 },
|
|
{ id: 107, keyword: "PRIMEVIDEO", category_id: 1713 },
|
|
{ id: 108, keyword: "AMAZON", category_id: 1946 },
|
|
];
|
|
const v1Tx: V2TransactionInput[] = [
|
|
{ id: 1, description: "DEPOT PAIE MAX", category_id: 1011 },
|
|
{ id: 2, description: "IGA #5555", category_id: 1111, supplier_id: 504 },
|
|
{ id: 3, description: "SHELL #231 LAVAL", category_id: 1512, supplier_id: 502 },
|
|
{ id: 4, description: "STM CARTE OPUS", category_id: 1521, supplier_id: 501 },
|
|
{ id: 5, description: "HYDRO-QUEBEC FACTURE", category_id: 1221, supplier_id: 505 },
|
|
{ id: 6, description: "NETFLIX.COM", category_id: 1713 },
|
|
{ id: 7, description: "HILTON SEATTLE", category_id: 1533, supplier_id: 503 },
|
|
{ id: 8, description: "AMAZON.CA *XYZ", category_id: 1946 },
|
|
];
|
|
return {
|
|
v2Categories: v1Cats,
|
|
keywords: v1Keywords,
|
|
transactions: v1Tx,
|
|
suppliers: [...V2_SUPPLIERS],
|
|
};
|
|
}
|