Fix euristica BOM, unifica script/GUI su un'unica fonte, aggiungi test integrazione/E2E

Due bug reali trovati confrontando l'estrazione BOM con la BOM ufficiale Creo:
un componente scartato per placement fallito in un file veniva bloccato per
sempre anche quando il file giusto aveva un vero placement (perdendo interi
sotto-alberi come 9256510047), e i codici con suffisso (_C01 ecc.) richiedevano
sempre placement esplicito invece dell'eccezione per occorrenze ripetute già
presente nella GUI. Entrambi corretti.

AssemblyComponentScanner reso pubblico e scripts/BomViaDb.cs, ExtractCreoStrings.cs
riscritti per referenziarlo via "#:project" invece di duplicare l'euristica:
un'unica fonte di verità condivisa da GUI e script standalone.

Aggiunti test di integrazione per Indexer.ScanAsync (mai coperto oltre al
parsing del nome file) e test end-to-end che ricostruiscono l'intera pipeline
disco -> indice -> risoluzione albero componenti senza toccare la UI.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
davideandClaude Sonnet 5 committed 2026-09-21 13:43:54 +02:00
1 parent 353a1028d9
commit 5c9661eb12
8 files changed
+514 -171

No files matched your search

@@ -148,4 +148,54 @@ public class AssemblyComponentScannerTests
}
finally { File.Delete(percorso); }
}
[Fact]
public void Riconosce_codice_con_suffisso_anche_senza_dettaglio_nel_placement()
{
// Regressione: caso reale 9284218024b.asm -> 2546220101_C01.._C04. Il placement
// riporta solo il codice padre senza suffisso ("Componente ID N (2546220101.PRT)"),
// il dettaglio "_C01" compare solo altrove come stringa ripetuta, mai dentro un
// placement esplicito. Un codice nudo nella stessa situazione andrebbe scartato
// (vedi Scarta_codice_ripetuto_senza_un_vero_placement), ma un codice con suffisso
// di variante è considerato "forma nota": bastano le occorrenze ripetute. Prima
// che questa eccezione fosse portata anche in scripts/BomViaDb.cs, lo script
// perdeva l'intero sotto-assieme (vedi docs/formato-asm.md).
string percorso = ScriviAsmFinto(
"Componente ID 1 (2546220101.PRT) x 2546220101_C01 y 2546220101_C01 z");
try
{
var codici = AssemblyComponentScanner.EstraiCodiciReferenziati(percorso);
Assert.Contains("2546220101_C01", codici);
}
finally { File.Delete(percorso); }
}
[Fact]
public void Il_placement_e_indipendente_per_ogni_file_scansionato()
{
// Regressione: caso reale 9244228558.asm -> 9244236104 dentro 9244228558_C01.asm.
// Lo stesso codice compare come riferimento fantasma (nudo, senza placement) nel
// file dell'assieme padre, e con un vero placement nel file del sotto-assieme che
// lo contiene realmente. La proprietà che rende sicura la BFS del chiamante
// (Form1.RisolviAlberoComponenti, scripts/BomViaDb.cs, scripts/ExtractCreoStrings.cs)
// è che EstraiCodiciReferenziati valuta il placement da zero per ogni file, senza
// stato condiviso tra le chiamate: un fallimento nel file padre non deve
// condizionare il risultato quando lo stesso codice viene ripassato per il file
// figlio (vedi docs/formato-asm.md, "Trappola nella BFS").
string filePadre = ScriviAsmFinto("assieme 11112222 padre 11112222 fine");
string fileFiglio = ScriviAsmFinto("Componente ID 1 (11112222.PRT) x 11112222 y");
try
{
Assert.DoesNotContain("11112222", AssemblyComponentScanner.EstraiCodiciReferenziati(filePadre));
Assert.Contains("11112222", AssemblyComponentScanner.EstraiCodiciReferenziati(fileFiglio));
}
finally { File.Delete(filePadre); File.Delete(fileFiglio); }
}
[Theory]
[InlineData("9284208188", "9284208188")]
[InlineData("9284208188_D600", "9284208188")]
[InlineData("9244228558_C03-01", "9244228558")]
public void CodiceRadice_estrae_la_parte_prima_del_primo_underscore(string codice, string radiceAttesa) =>
Assert.Equal(radiceAttesa, AssemblyComponentScanner.CodiceRadice(codice));
}