Files
Mimante/Mimante/Tests/OperationalDatabaseTests.cs
T
Alby96andClaude Fable 5.1 9177b31bd5 Due database, interruttore dell'apprendimento, barre a icone; via kill-switch, esportazioni e voci deprecate
Tutto ciò che l'applicazione registra sta ora in due file SQLite in
%LocalAppData%\AutoBidder\Database: autobidder.sqlite per le osservazioni e
esercizio.sqlite per i documenti d'esercizio (aste nel monitor, prodotti, promozioni,
modelli appresi con prefisso ml/) e per i registri applicativo e del riscatto puntate.
Il motore comune è SqliteDatabase; i file JSON e la cartella Apprendimento delle
versioni precedenti vengono importati la prima volta e lasciati dove sono. La cartella
si cambia dalle Impostazioni: al salvataggio si chiede se spostare i file, e si riavvia.
Le cartelle Dati/Statistiche/Registri, i registri su file, le misure dell'anticipo e
le impostazioni morte (LogBids, AutoApplyProductDefaults, NewAuctionLimitsPriority…)
non esistono più.

Il kill-switch se ne va: al suo posto un solo interruttore in barra, accanto ad
Avvia/Osserva/Ferma, che spegne l'apprendimento. Spento, il motore punta solo entro i
limiti dell'utente (prezzo, puntate, budget, fascia oraria, rischio) con l'anticipo
fisso: niente valore atteso, regime, duello, bandit, anticipo adattivo. Le aste vengono
comunque registrate e studiate.

Tre difetti visti osservando il motore dal vivo per un'ora, in Osserva:
- la copertura «Compralo Ora» con V lasciato al listino azzerava la perdita coperta e
  il motore diceva BID a ogni scadenza con P dell'1%: ora vale solo per i prodotti con
  «Valore reale €» scritto, altrimenti la puntata si conta persa e P decide;
- la chiusura di un'asta arrivava due volte (fine, poi rimozione dal monitor) e di
  nuovo al riavvio: modello, profilo, bandit e statistiche per prodotto contavano ogni
  asta due volte. Il monitor la comunica una volta sola, l'apprendimento salta le aste
  già apprese, e le schede prodotto vengono ricostruite una volta dallo storico;
- la pagina delle ricompense veniva scambiata per la pagina di accesso perché porta un
  collegamento a /login.php: prima si guarda se è la pagina delle ricompense.

Interfaccia: la scheda Esporta e tutto il suo codice sono tolti; le esportazioni
chiedono sempre dove salvare. Prodotti, Storico e Apprendimento hanno barre a sole
icone con tooltip che dicono esattamente cosa succede, colorate come il monitor, con
i nuovi pulsanti di pulizia (spegni tutte le stelline, togli i non seguiti, pulizia
completa; seleziona tutte / elimina le selezionate / svuota lo storico). Impostazioni:
sezione Database, Manutenzione con «Elimina tutti i dati (tranne la login)» e
«Impostazioni di fabbrica», esportazione dei registri in testo. Modifiche.txt, il
prompt ormai realizzato, esce dal repository.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-08 13:58:08 +02:00

111 lines
3.8 KiB
C#

using System;
using System.IO;
using System.Linq;
using AutoBidder.Data;
using AutoBidder.Utilities;
using Xunit;
namespace AutoBidder.Tests;
/// <summary>
/// Il database d'esercizio: documenti per chiave, importazione una tantum dei vecchi
/// file JSON, registri che si scrivono, si rileggono, si esportano e scadono.
/// </summary>
[Collection("Database")]
public class OperationalDatabaseTests
{
private static OperationalDatabase Db => OperationalDatabase.Instance;
[Fact]
public void Un_documento_si_scrive_e_si_rilegge()
{
var key = "test/" + Guid.NewGuid().ToString("N");
Db.SetDocument(key, "{\"a\":1}");
Assert.Equal("{\"a\":1}", Db.GetDocument(key));
Db.SetDocument(key, "{\"a\":2}");
Assert.Equal("{\"a\":2}", Db.GetDocument(key));
Db.DeleteDocument(key);
Db.Flush();
Assert.Null(Db.GetDocument(key));
}
[Fact]
public void Il_vecchio_file_si_importa_una_volta_sola()
{
var key = "test/import-" + Guid.NewGuid().ToString("N");
var legacy = Path.Combine(Path.GetTempPath(), $"legacy-{Guid.NewGuid():N}.json");
File.WriteAllText(legacy, "{\"vecchio\":true}");
try
{
Assert.Equal("{\"vecchio\":true}", Db.GetDocumentOrImport(key, legacy));
// Il file cambia, ma il documento è già nel database: comanda il database.
File.WriteAllText(legacy, "{\"vecchio\":false}");
Assert.Equal("{\"vecchio\":true}", Db.GetDocumentOrImport(key, legacy));
Assert.True(File.Exists(legacy)); // resta dov'è
}
finally { File.Delete(legacy); }
}
[Fact]
public void Senza_documento_ne_file_si_ha_null()
{
Assert.Null(Db.GetDocumentOrImport("test/niente-" + Guid.NewGuid().ToString("N"), Path.Combine(Path.GetTempPath(), "non-esiste.json")));
}
[Fact]
public void Il_registro_si_scrive_si_rilegge_e_si_esporta()
{
var marker = "riga-" + Guid.NewGuid().ToString("N");
Db.WriteLog(OperationalDatabase.AppLog, "INFO", marker);
Db.WriteLog(OperationalDatabase.AppLog, "WARN", marker + "-due");
var rows = Db.ReadLog(OperationalDatabase.AppLog, 10);
Assert.Contains(rows, r => r.Message == marker && r.Level == "INFO");
Assert.Equal(marker + "-due", rows[0].Message); // dalla più recente
var file = Path.Combine(Path.GetTempPath(), $"log-{Guid.NewGuid():N}.txt");
try
{
var n = Db.ExportLog(OperationalDatabase.AppLog, file);
Assert.True(n >= 2);
var text = File.ReadAllText(file);
Assert.Contains(marker, text);
Assert.Contains("[WARN]", text);
}
finally { File.Delete(file); }
}
[Fact]
public void Le_righe_vecchie_scadono()
{
var old = SqliteDatabase.Iso(DateTime.Now.AddDays(-200));
Db.ExecuteNow($"INSERT INTO {OperationalDatabase.FreeBidsLog}(ts, level, message) VALUES(?1, 'INFO', 'antica')", old);
Db.WriteLog(OperationalDatabase.FreeBidsLog, "INFO", "recente");
var removed = Db.PurgeLogs(90);
Assert.True(removed >= 1);
var rows = Db.ReadLog(OperationalDatabase.FreeBidsLog, 100);
Assert.DoesNotContain(rows, r => r.Message == "antica");
Assert.Contains(rows, r => r.Message == "recente");
}
[Fact]
public void Le_aste_nel_monitor_vivono_nel_documento()
{
PersistenceManager.SaveAuctions(new[]
{
new Models.AuctionInfo { AuctionId = "doc-1", Name = "Uno" },
new Models.AuctionInfo { AuctionId = "doc-2", Name = "Due" }
});
var back = PersistenceManager.LoadAuctions();
Assert.Equal(2, back.Count);
Assert.Contains(back, a => a.AuctionId == "doc-2" && a.Name == "Due");
}
}