8 lines
5.8 KiB
Markdown
8 lines
5.8 KiB
Markdown
---
|
|
title: Lot F côté manager-service : les quatre chantiers de dette backend, en une passe
|
|
---
|
|
<span><code>dotnet test</code> <strong>163 → 200</strong>, aucun test sauté, quatre commits, <code>manager-service</code> seul. <strong>1) Le journal d'audit voyait tout sauf le contenu</strong> : <code>AuditedTypes.Contains(entry.Entity.GetType())</code> exigeait l'égalité <strong>exacte</strong>, or <code>Section</code> est abstraite — aucune des 13 sortes de section n'était journalisée, soit précisément ce que l'écran du 12/08 devait tracer. <code>Resource</code>, <code>Configuration</code>, <code>Device</code>, <code>User</code> et <code>Instance</code> passaient, eux : ils sont concrets, <strong>et c'est ce qui rendait le trou invisible</strong>. <strong>Tranché : normalisation côté serveur</strong> — <code>EntityType</code> porte <code>Section</code>, donc le filtre déjà présent dans l'écran se met à rendre des lignes <strong>sans toucher <code>manager-app</code></strong> ; élargir le filtre front aurait coûté 13 entrées et 39 clés i18n <em>et</em> laissé tout futur sous-type sortir du filtre en silence — la classe de panne exacte qu'on ferme. Le sous-type n'est pas perdu : le discriminateur TPH est sérialisé dans <code>NewValues</code>, un test le vérifie. 8 tests, dont un <strong>par réflexion</strong> sur les 13 sous-types — la liste d'origine n'obligeait personne à la suivre, c'est ce qui l'a laissée devenir fausse.</span>
|
|
<span><strong>2) Plafond de 5 utilisateurs</strong> : <code>CreateUser</code> rend <strong>422</strong>. <strong>5 en dur</strong> (le faire varier par plan serait une colonne, donc une migration après le gel du lot B — dette V1 assumée) et <strong>SuperAdmin exempté</strong>, seule porte de service tant qu'aucun champ ne permet de relever la limite d'un client. Rien à retoucher côté front. <strong>3) <code>GetSummary</code> agrège en SQL</strong> au lieu de charger 13 mois en mémoire ; les six agrégats qui se lisent dans le JSON de <code>Metadata</code> ne remontent plus que <strong>deux colonnes</strong>, pour leur seul type d'événement, et les stats avancées ne sont plus calculées puis effacées pour les plans qui n'y ont pas droit. ⚠️ <strong>La branche « stats avancées » n'était couverte par aucun test</strong> : les cas existants ne seedent pas d'<code>Instance</code>, donc <code>hasAdvancedStats</code> était toujours faux et la moitié de la méthode n'était jamais exécutée.</span>
|
|
<span><strong>4) Le vector store est éprouvé à DEUX instances, sur un vrai Postgres</strong> — 14 tests Testcontainers sur l'image de <code>Dockerfile.postgres</code>, qui se sautent proprement sans démon Docker (186 passés / 14 sautés / 0 échec). pgvector <strong>0.8.6</strong> : le <code>SET hnsw.iterative_scan</code> que pose <code>SearchAsync</code> est accepté — sur une version antérieure, <strong>toute recherche échouait en production</strong>. À 30 contre 1, la recherche rend le bon nombre de résultats, tous de la bonne instance, aucune fuite. ⚠️ <strong>Deux résultats contraires à ce que le plan supposait</strong> : ce qui protège du post-filtrage <strong>n'est pas le parcours itératif mais l'index sur <code>(InstanceId, ContentType)</code></strong> — le planificateur filtre d'abord et trie exactement, l'index HNSW n'est jamais touché (vérifié à 620 lignes et à 22 000) ; et cet index retiré, <code>relaxed_order</code> <strong>ne rattrape rien</strong>, le parcours s'épuisant après ~335 lignes sans atteindre l'instance minoritaire. Le même jeu de données avec l'index HNSW construit <em>après</em> l'insertion rend bien ses 20 lignes : c'est la connectivité du graphe qui décide, et nos migrations créent l'index sur une table vide. <strong>Outillage</strong> : Testcontainers épinglé en 3.10.0 (la 4.x parle l'API Docker 1.44, l'engine local plafonne à 1.43) et image construite par le CLI docker — le constructeur de Testcontainers ne sait pas parser le <code>tag@sha256:</code> du Dockerfile, digest qui protège la base d'un changement de glibc sous ses index et ne se retire pas pour un test.</span>
|
|
<span>⚠️ <strong>Et une régression du point 1, trouvée et corrigée le même jour.</strong> <code>WeatherSyncService</code> écrit <code>section.WeatherResult</code> sur cron, à <strong>6 h et 13 h</strong>. Le job était bénin tant que les sections n'étaient pas auditées ; depuis, chaque rafraîchissement produisait une ligne portant la <strong>prévision OpenWeather complète en avant ET en après</strong> — quelques dizaines de Ko, deux fois par jour, par section météo. Le stockage est le moindre problème : <strong>ce bruit noie les modifications humaines</strong> que l'écran existe pour montrer. Correctif : colonnes machine (<code>WeatherResult</code>, <code>WeatherUpdatedDate</code>, <code>DateUpdate</code>) exclues du journal, et <strong>aucune ligne produite</strong> quand une modification ne touche qu'elles — plutôt qu'une ligne au diff vide, qui aurait déplacé le bruit sans le retirer. <code>DateUpdate</code> y est pour une raison distincte : estampillé à chaque <code>SaveChanges</code>, il figurait dans tous les diffs sans rien y apprendre — <strong>et le signal était déjà là</strong>, un test écrit le matin même devait l'écarter à la main pour rester lisible. <code>AgendaSyncService</code> vérifié au passage : il écrit des <code>EventAgenda</code>, non audités. ⚠️ <strong>Trouvé en cherchant à justifier un index sur <code>Timestamp</code> signalé par réflexe</strong> — la question « pourquoi un index ? » a montré que ce qui faisait grossir la table n'était pas ce que je croyais. <strong>L'index est retiré du backlog</strong> : pas de migration, gel du lot B intact. <code>dotnet test</code> <strong>203/203</strong>.</span>
|