using PrepStep; using Microsoft.Data.Sqlite; namespace PrepStep.Tests; /// Ricrea lo schema reale del DB (stessa costante SQL usata da Indexer.ScanAsync) e /// verifica, con dati inseriti a mano, i due comportamenti che in passato hanno causato bug /// silenziosi: il confronto case-insensitive su codice/tipo_file e la vista /// files_ultima_versione che tiene solo l'ultima versione per cartella_radice+codice+tipo. public class CaseInsensitiveLookupIntegrationTests : IDisposable { private readonly string _dbPath = Path.Combine(Path.GetTempPath(), $"cadsearch_test_{Guid.NewGuid():N}.db"); private readonly SqliteConnection _connection; public CaseInsensitiveLookupIntegrationTests() { _connection = new SqliteConnection($"Data Source={_dbPath}"); _connection.Open(); using var schemaCmd = _connection.CreateCommand(); schemaCmd.CommandText = Indexer.Schema; schemaCmd.ExecuteNonQuery(); } public void Dispose() { _connection.Dispose(); SqliteConnection.ClearPool(_connection); File.Delete(_dbPath); } private void InserisciFile(string percorso, string cartella, string codice, string tipo, int versione, string dataModifica) { using var cmd = _connection.CreateCommand(); cmd.CommandText = """ INSERT INTO files (percorso_completo, percorso_relativo, nome_file, cartella_radice, famiglia, codice, tipo_file, versione, dimensione_byte, data_modifica, scansionato_il) VALUES (@p, @p, @p, @cartella, NULL, @codice, @tipo, @versione, 1, @modifica, @modifica) """; cmd.Parameters.AddWithValue("@p", percorso); cmd.Parameters.AddWithValue("@cartella", cartella); cmd.Parameters.AddWithValue("@codice", codice); cmd.Parameters.AddWithValue("@tipo", tipo); cmd.Parameters.AddWithValue("@versione", versione); cmd.Parameters.AddWithValue("@modifica", dataModifica); cmd.ExecuteNonQuery(); } /// Stessa query (LOWER su entrambi i lati) usata da Form1 per risolvere un codice /// referenziato in un .asm: la scansione euristica del binario può restituire maiuscole /// diverse da quelle del file reale su disco (es. "_D100" estratto vs "_d100" su X:\). private List<(string Tipo, string Percorso)> CercaCasoInsensitive(string codiceCercato) { using var cmd = _connection.CreateCommand(); cmd.CommandText = "SELECT tipo_file, percorso_completo FROM files_ultima_versione " + "WHERE LOWER(codice) = LOWER(@codice) AND LOWER(tipo_file) IN ('prt', 'asm') " + "ORDER BY tipo_file, data_modifica DESC"; cmd.Parameters.AddWithValue("@codice", codiceCercato); using var reader = cmd.ExecuteReader(); var risultati = new List<(string, string)>(); while (reader.Read()) risultati.Add((reader.GetString(0), reader.GetString(1))); return risultati; } [Fact] public void Trova_il_file_anche_se_la_maiuscola_del_codice_non_corrisponde() { InserisciFile(@"X:\__Corpi\442\M_B\9244228573_d100.asm.16", "__Corpi", "9244228573_d100", "asm", 16, "2018-08-22T14:51:19Z"); var risultati = CercaCasoInsensitive("9244228573_D100"); Assert.Single(risultati); Assert.Equal("asm", risultati[0].Tipo); } [Fact] public void Trova_il_file_anche_se_il_tipo_e_maiuscolo() { InserisciFile(@"X:\Commerciali\Elettrici\1504240807.PRT.1", "Commerciali", "1504240807", "PRT", 1, "2020-01-01T00:00:00Z"); var risultati = CercaCasoInsensitive("1504240807"); Assert.Single(risultati); Assert.Equal("PRT", risultati[0].Tipo); } [Fact] public void Tra_due_cartelle_radice_diverse_tiene_solo_la_versione_piu_recente() { // Stesso codice+tipo duplicato su due cartelle radice diverse (capitato con temp_paris // vs __Corpi): la vista files_ultima_versione deve restituire il più recente per // cartella, ma la query applicativa a valle deve poi tenerne solo uno in assoluto // (comportamento verificato qui a livello di dati grezzi restituiti dalla vista). InserisciFile(@"X:\temp_paris\gabbia45_modelli_copia\9244228573.asm.1", "temp_paris", "9244228573", "asm", 1, "2014-06-25T07:38:13Z"); InserisciFile(@"X:\__Corpi\442\M_B\9244228573.asm.41", "__Corpi", "9244228573", "asm", 41, "2019-05-08T13:27:23Z"); var risultati = CercaCasoInsensitive("9244228573"); // La vista non deduplica tra cartelle radice diverse: la query applicativa ordina per // data_modifica DESC e tiene solo la prima per tipo, cioè quella più recente. Assert.Equal(2, risultati.Count); Assert.Equal(@"X:\__Corpi\442\M_B\9244228573.asm.41", risultati[0].Percorso); } [Fact] public void La_vista_ultima_versione_scarta_le_versioni_precedenti_nella_stessa_cartella() { InserisciFile(@"X:\__Corpi\442\M_B\9244228573.asm.40", "__Corpi", "9244228573", "asm", 40, "2018-08-22T14:51:27Z"); InserisciFile(@"X:\__Corpi\442\M_B\9244228573.asm.41", "__Corpi", "9244228573", "asm", 41, "2019-05-08T13:27:23Z"); var risultati = CercaCasoInsensitive("9244228573"); Assert.Single(risultati); Assert.Contains(".asm.41", risultati[0].Percorso); } [Fact] public void Nessun_risultato_per_un_codice_non_indicizzato() { InserisciFile(@"X:\__Corpi\442\M_B\9244228573.asm.41", "__Corpi", "9244228573", "asm", 41, "2019-05-08T13:27:23Z"); Assert.Empty(CercaCasoInsensitive("0000000000")); } }