Simpl-Resultat/src/__fixtures__/profiles.ts
le king fu 12d1877870
All checks were successful
PR Check / rust (push) Successful in 22m48s
PR Check / frontend (push) Successful in 2m20s
PR Check / rust (pull_request) Successful in 22m51s
PR Check / frontend (pull_request) Successful in 2m21s
test(categories): complete test coverage for migration flow (#123)
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>
2026-04-21 19:25:13 -04:00

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],
};
}