Files
harbour-henflow/tests/harness/harness.cpp
T
kaneda 248e0db899 schermate di configurazione complete: razze disponibili, mio pollaio, registro galline, clienti, backup/ripristino nel formato dell'app Android, lingua a runtime, introduzione e informazioni
- datastore: export/import JSON con le chiavi camelCase dell'app Android (breedSettings/reservations/customers), scorte e ultimo aggiornamento preservati al ripristino
- settings: flag 'introduzione vista'
- desktop: permesso Documents (dove finisce il backup)
- nuovi controlli: tools/check_qml.py (chiavi di traduzione, simboli JS, pagine, componenti) e qmllint nella suite
- versione 0.2.0
2026-10-09 09:21:47 +02:00

221 lines
9.6 KiB
C++

// Banco di prova del livello dati (fuori dal pacchetto RPM).
// Compila con Qt 5.15 locale e gira con XDG_CONFIG_HOME su una cartella
// temporanea, cosi' NON tocca la configurazione vera:
// sh tests/cpp_test.sh
#include <QCoreApplication>
#include <QDir>
#include <QFile>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QStandardPaths>
#include <QVariantList>
#include <QVariantMap>
#include <QDebug>
#include "../../src/datastore.h"
static int failures = 0;
static void check(const QString &name, bool ok, const QString &detail = QString())
{
if (!ok)
failures++;
qInfo().noquote() << (ok ? "OK " : "FAIL") << name
<< (ok || detail.isEmpty() ? QString() : "-> " + detail);
}
static QVariantList makeBreeds()
{
QVariantList hens;
QVariantMap m1;
m1.insert("id", "m1");
m1.insert("name", "Marta");
m1.insert("birthDate", "2025-01-01T00:00:00.000");
m1.insert("isLaying", true);
hens.append(m1);
QVariantMap marans;
marans.insert("breedId", "marans");
marans.insert("hens", hens);
return QVariantList() << marans;
}
static QVariantList makeReservations()
{
QVariantMap r;
r.insert("id", "r1");
r.insert("customerName", "Cli1");
r.insert("breedId", "araucana");
r.insert("eggsRequested", 2);
r.insert("createdAt", "2026-04-25T09:00:00.000");
r.insert("deliveryDate", "2026-04-28");
r.insert("isDelivered", false);
return QVariantList() << r;
}
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
app.setOrganizationName("harbour");
app.setApplicationName("henflow");
qInfo().noquote() << "config dir:" << QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation);
// --- primo avvio: nessun file, stato vuoto -----------------------------
{
DataStore store;
check("primo avvio: nessuna razza", store.breedSettings().isEmpty());
check("primo avvio: nessuna prenotazione", store.reservations().isEmpty());
check("primo avvio: magazzino vuoto", store.warehouseStock().isEmpty());
check("primo avvio: nessuna data di aggiornamento", store.lastProductionUpdate().isEmpty());
check("il file dati non esiste ancora", !QFile::exists(store.dataFilePath()));
}
// --- scrittura e rilettura --------------------------------------------
DataStore store;
check("salvataggio razze", store.saveBreedSettings(makeBreeds()));
check("salvataggio prenotazioni", store.saveReservations(makeReservations()));
QVariantMap customers;
customers.insert("id", "c1");
customers.insert("firstName", "Anna");
customers.insert("lastName", "Rossi");
customers.insert("phoneNumber", "+39 333 0000000");
check("salvataggio clienti", store.saveCustomers(QVariantList() << customers));
QVariantMap stock;
stock.insert("marans", 6.0);
stock.insert("araucana", 1.5);
check("salvataggio magazzino", store.saveWarehouseStock(stock));
check("salvataggio data aggiornamento", store.saveLastProductionUpdate("2026-10-09"));
check("il file dati esiste", QFile::exists(store.dataFilePath()));
{
DataStore reopened;
check("rilettura razze", reopened.breedSettings().size() == 1);
check("rilettura nidificata (galline)", reopened.breedSettings().at(0).toMap()
.value("hens").toList().size() == 1);
check("rilettura prenotazioni", reopened.reservations().size() == 1);
check("rilettura quantita' uova", reopened.reservations().at(0).toMap()
.value("eggsRequested").toInt() == 2);
check("rilettura flag consegna (bool)", reopened.reservations().at(0).toMap()
.value("isDelivered").toBool() == false);
check("rilettura clienti", reopened.customers().size() == 1 &&
reopened.customers().at(0).toMap().value("lastName").toString() == "Rossi");
check("rilettura magazzino (numeri)", qFuzzyCompare(reopened.warehouseStock()
.value("araucana").toDouble(), 1.5));
check("rilettura data aggiornamento", reopened.lastProductionUpdate() == "2026-10-09");
}
// --- contratto con le chiavi dell'app Flutter --------------------------
{
QFile f(store.dataFilePath());
f.open(QIODevice::ReadOnly);
const QJsonObject root = QJsonDocument::fromJson(f.readAll()).object();
f.close();
check("chiave breed_settings_v2 (compatibile con l'app Android)",
root.contains("breed_settings_v2"));
check("chiave reservations_v2", root.contains("reservations_v2"));
check("chiave customers_v1", root.contains("customers_v1"));
check("chiave warehouse_stock_v1", root.contains("warehouse_stock_v1"));
check("chiave last_production_update_v1", root.contains("last_production_update_v1"));
}
// --- backup nel formato dell'app Android -------------------------------
QString exported;
{
exported = store.exportBackup();
check("export: percorso restituito", !exported.isEmpty(), exported);
check("export: file scritto in Documenti",
exported.contains(QStringLiteral("/Documents/")), exported);
check("export: file esistente", QFile::exists(exported));
QFile f(exported);
f.open(QIODevice::ReadOnly);
const QJsonObject root = QJsonDocument::fromJson(f.readAll()).object();
f.close();
check("backup: chiave breedSettings (come l'app Android)", root.contains("breedSettings"));
check("backup: chiave reservations", root.contains("reservations"));
check("backup: chiave customers", root.contains("customers"));
check("backup: nessuna chiave interna (breed_settings_v2)",
!root.contains("breed_settings_v2"));
check("backup: esattamente 3 chiavi", root.size() == 3);
}
// backup prodotto dall'app Android (scritto a mano come lo esporta lei):
// deve essere accettato e sostituire i dati.
{
const QString android = QStringLiteral(
"{\n"
" \"breedSettings\": [\n"
" {\"breedId\": \"livorno\", \"hens\": [\n"
" {\"id\": \"h9\", \"name\": \"Gina\", \"birthDate\": \"2024-03-01T00:00:00.000\", \"isLaying\": true}\n"
" ]}\n"
" ],\n"
" \"reservations\": [\n"
" {\"id\": \"r9\", \"customerName\": \"Luca\", \"breedId\": \"livorno\", \"eggsRequested\": 6,\n"
" \"createdAt\": \"2026-10-01T08:00:00.000\", \"deliveryDate\": \"2026-10-03\", \"isDelivered\": false}\n"
" ],\n"
" \"customers\": [\n"
" {\"id\": \"c9\", \"firstName\": \"Luca\", \"lastName\": \"Bianchi\", \"phoneNumber\": \"333\"}\n"
" ]\n"
"}\n");
check("backup Android riconosciuto valido", store.isValidBackup(android));
check("import del backup Android", store.importBackup(android));
check("import: razze sostituite", store.breedSettings().size() == 1
&& store.breedSettings().at(0).toMap().value("breedId").toString() == "livorno");
check("import: gallina annidata letta", store.breedSettings().at(0).toMap()
.value("hens").toList().at(0).toMap().value("name").toString() == "Gina");
check("import: prenotazioni sostituite", store.reservations().size() == 1
&& store.reservations().at(0).toMap().value("eggsRequested").toInt() == 6);
check("import: clienti sostituiti", store.customers().size() == 1
&& store.customers().at(0).toMap().value("lastName").toString() == "Bianchi");
// il backup Android non porta scorte ne' ultima produzione: si tengono
check("import: magazzino preservato", !store.warehouseStock().isEmpty());
check("import: data aggiornamento preservata",
store.lastProductionUpdate() == "2026-10-09");
// rilettura da disco: l'import e' stato salvato
DataStore reopened;
check("import: salvato su disco", reopened.reservations().size() == 1
&& reopened.breedSettings().at(0).toMap().value("breedId").toString() == "livorno");
}
// backup senza 'customers' (le versioni vecchie non lo avevano)
{
const QString old = QStringLiteral("{\"breedSettings\": [], \"reservations\": []}");
check("backup senza clienti: valido", store.isValidBackup(old));
check("backup senza clienti: importato", store.importBackup(old));
check("backup senza clienti: liste vuote", store.breedSettings().isEmpty()
&& store.customers().isEmpty());
}
// --- file corrotto: non deve crashare e non deve perdere i dati ---------
{
QFile f(store.dataFilePath());
f.open(QIODevice::WriteOnly | QIODevice::Truncate);
f.write("{ questo non e' json");
f.close();
DataStore broken;
check("file corrotto: non crasha", true);
check("file corrotto: stato vuoto", broken.reservations().isEmpty());
check("import di un JSON non valido rifiutato", !broken.importBackup("{ rotto"));
check("import di un oggetto senza le chiavi richieste rifiutato",
!broken.importBackup("{\"altro\": 1}"));
check("import di un array (non oggetto) rifiutato", !broken.importBackup("[]"));
check("import di testo vuoto rifiutato", !broken.importBackup(" "));
check("isValidBackup su testo vuoto", !broken.isValidBackup(""));
}
QFile::remove(exported);
qInfo().noquote() << (failures == 0 ? "\ntutti i controlli superati"
: QString("\n%1 controlli falliti").arg(failures));
return failures == 0 ? 0 : 1;
}