Skip to content

Testing

BraDypUS ha una suite PHPUnit (unit + integration) e una suite Hurl end-to-end che copre 31+ fasi del ciclo di vita completo dell'applicazione. Entrambe sono orchestrate da un unico entry point: test.sh, che gestisce container Docker usa-e-getta e supporta tutti e tre i motori DB.


Quick start

bash
cd bdus-api

# Menu interattivo: sceglie cosa eseguire
./test.sh

# Crea app + suite CRUD completa (SQLite)
./test.sh --setup --tests

# Crea app + suite + seed demo (per screenshot)
./test.sh --setup --tests --seed --keep

# Solo PHPUnit
./test.sh --unit

# Lista tutte le fasi senza eseguire
./test.sh --list

Interfaccia di test.sh

Flag di modalità (combinabili)

Se non viene passato nessun flag di modalità, test.sh mostra un menu interattivo.

FlagCosa fa
--unitEsegue PHPUnit (unit + integration)
--setupFasi 01–03: crea l'app e configura lo schema
--testsFasi 04–31: suite CRUD e feature completa
--seedFase 19: popola l'app con dati demo
--seed-moreFase 19b: seed esteso (implica --seed)

Controllo delle fasi

FlagCosa fa
--listElenca tutte le fasi con i loro step; non esegue nulla
--from=NEsegue da fase N in poi (es. --from=06)
--only=NEsegue solo le fasi con prefisso N (es. --only=04 include 04, 04b, 04c…)

Dipendenze tra fasi

--from e --only saltano la fase 04 che cattura us_id_1 / us_id_2. Le fasi che dipendono da questi ID (04b, 04c, 04e, 05, 20, 28) ricevono variabili vuote se 04 viene saltata.

Infrastruttura

FlagCosa fa
--resetCancella l'app esistente senza chiedere
--db=sqlite|pgsql|mysqlMotore DB (default: sqlite)
--all-enginesEsegue la suite su tutti e 3 i motori in sequenza
--keepLascia il container Docker in esecuzione dopo i test
--no-dockerSalta Docker; usa il server locale all'indirizzo in vars.env

Workflow comuni

bash
# Suite completa, run pulita (cancella app esistente)
./test.sh --reset --setup --tests

# Test su PostgreSQL
./test.sh --db=pgsql --setup --tests

# CI: tutti i motori
./test.sh --all-engines --setup --tests

# Solo fase 04 e sotto-fasi (04b, 04c, 04d, 04e)
./test.sh --tests --only=04

# Da fase 06 in poi (presuppone app già configurata)
./test.sh --tests --from=06

# Screenshot workflow: seed + container in piedi
./test.sh --reset --setup --tests --seed-more --keep
# → apri http://localhost:8081 nel browser per gli screenshot
# → il prossimo ./test.sh pulisce automaticamente

Configurazione

Le variabili di ambiente sono in tests/api/vars.env. Copia in tests/api/vars.local.env per override locali (git-ignored).

bash
BASE_URL=http://localhost:8080   # usato con --no-docker
APP_NAME=bdus_demo
ADMIN_EMAIL=admin@example.com
ADMIN_PASSWORD=Admin1234!
DB_ENGINE=sqlite

In modalità Docker, test.sh sovrascrive BASE_URL con http://localhost:8081 e inietta le credenziali DB per pgsql/mysql automaticamente.


Attenzione: schema bdus_users nei test

I test PHPUnit che creano bdus_users lo fanno manualmente con CREATE TABLE hardcoded nel metodo createSchema() della singola classe. Quando si aggiunge una colonna a bdus_users tramite migration, bisogna aggiornare tutti i CREATE TABLE bdus_users nei file di test. File da controllare:

tests/Integration/LoginCtrlTest.php
tests/Integration/ConfirmAdminPwdCtrlTest.php
tests/Integration/FreeSqlCtrlTest.php
tests/Integration/SavedQueriesCtrlTest.php
tests/Integration/ChartCtrlTest.php
tests/Integration/UserCtrlTest.php

M022MigrationTest e simili vanno lasciati invariati: testano la migration stessa, quindi partono intenzionalmente da uno schema precedente.


PHPUnit — test unit e integration

PHPUnit usa un database SQLite in-memory creato da zero per ogni classe di test. Nessun I/O di rete, nessun accesso a disco per il DB.

bash
# Direttamente nel container Docker
docker compose exec app php vendor/bin/phpunit --testdox

# Una suite specifica
php vendor/bin/phpunit --testsuite Unit
php vendor/bin/phpunit --testsuite Integration

Configurazione: phpunit.xml. Bootstrap: tests/bootstrap.php.

BdusTestCase — classe base condivisa

Tutti i test di integrazione estendono Tests\Support\BdusTestCase.

tests/
├── bootstrap.php
├── Support/
│   └── BdusTestCase.php   ← SQLite in-memory, DI helpers
├── Unit/                  ← logica pura (nessun DB)
└── Integration/           ← test a livello controller (DB in-memory reale)

Helper chiave:

php
// Instanzia un controller con DI completo (DB, Config, UAC, logger)
$ctrl = $this->makeController(RecordController::class, get: ['tb' => 'us', 'id' => 1]);

// Chiama un metodo e cattura il JSON che produce
$result = $this->callController(LoginController::class, 'auth', post: [
    'email'    => 'admin@example.com',
    'password' => 'secret',
    'app'      => 'testapp',
]);

// Imposta il privilegio utente simulato
$this->setPrivilege(\UAC\UAC::SUPERADM);   // 1
$this->setPrivilege(\UAC\UAC::READ);       // 30

Hurl — suite E2E API

Le fasi si eseguono in sequenza su un server live. Alcune catturano valori (JWT, ID record) che le fasi successive consumano. Tutte sono gestite da tests/api/_phases.sh (sorgente interna, non eseguire direttamente).

Fasi di setup (run con --setup)

FaseFileCosa testa
0101_create_app.hurlPOST /api/new-app — creazione app (DB parametrico)
0202_login.hurlAuth: cattura JWT, casi negativi, refresh
0303_config_tables.hurlConfig tabelle e campi CRUD; crea crud_test

Fasi di test (run con --tests)

FaseFileCosa testa
0404_records.hurlRecord create/read/update/delete; cattura us_id_1, us_id_2
04b04b_plugin_crud.hurlRighe plugin (repeater) CRUD
04c04c_file_upload.hurlUpload file, attach, sort, delete
04d04d_export.hurlExport CSV, JSON, XLSX
04e04e_manual_links.hurlLink manuali tra record (userlinks)
0505_rs.hurlRelazioni stratigrafiche (Harris Matrix)
0606_search.hurlRicerca semplice, avanzata, SQL expert
0707_charts.hurlChart CRUD + query dati
0808_backup.hurlBackup create/list/delete
0909_users_privileges.hurlGestione utenti + enforcement privilegi
1010_cleanup.hurlDrop crud_test + logout (sempre alla fine di --tests)
1111_versions.hurlVersioni record + restore
1212_import.hurlImport CSV / JSON / GeoJSON
1313_migrations.hurlStato migrazioni
1414_relations.hurlbdus_cfg_relations CRUD
1515_vocabularies.hurlVocabulary CRUD + sort
1616_welcome_search_replace.hurlTesto welcome + search & replace globale
1717_zotero.hurlGestione libreria Zotero + citation links
1818_json_filter.hurlFiltro stile Directus filter[field][op]=value
2020_fuzzy_date.hurlPlugin fuzzy-date end-to-end
2121_chrono_filter.hurlFiltro _chrono_overlap
2222_schema_changes.hurlModifiche strutturali schema (add/drop colonne)
2323_widgets.hurlWidget dashboard CRUD
2424_logs.hurlLog applicazione
2525_api_keys.hurlAPI key management
2626_saved_queries.hurlQuery salvate CRUD
2727_templates.hurlTemplate layout record
2828_geoface_ops.hurlGeoface feature CRUD
2929_history_admin.hurlHistory + gate admin password
3030_file_sort.hurlOrdinamento file drag-and-drop
3131_fk_indexes.hurlIndici FK definiti dall'utente

Fasi di seed (run con --seed)

FaseFileCosa fa
1919_seed_demo.hurlPopola l'app con dati demo (siti, US, saggi, reperti)
19b19b_seed_more.hurlSeed esteso: 15 siti, 375 reperti, geodata (con --seed-more)

Infrastruttura multi-motore

01_create_app.hurl usa variabili per motore e connessione DB. test.sh --db=pgsql / --db=mysql inietta queste variabili e avvia il container del servizio corretto:

MotoreImmagine containerOverride compose
SQLite— (file nel container app)docker-compose.test.yml
PostgreSQLpostgres:16-alpinedocker-compose.test.pg.yml
MariaDBmariadb:11docker-compose.test.mysql.yml

Scrivere nuovi test

Nuovo test di integrazione PHPUnit

php
// tests/Integration/MyFeatureTest.php
namespace Tests\Integration;

use Tests\Support\BdusTestCase;

class MyFeatureTest extends BdusTestCase
{
    public function testItWorks(): void
    {
        $this->setPrivilege(\UAC\UAC::SUPERADM);

        $result = $this->callController(
            \Bdus\Controllers\MyController::class,
            'doSomething',
            post: ['key' => 'value']
        );

        $this->assertEquals('success', $result['status']);
    }
}

Nuova fase Hurl

  1. Crea tests/api/NN_nome.hurl con il numero di fase successivo.
  2. Aggiungi il blocco corrispondente in tests/api/_phases.sh dentro run_tests():
bash
# In _phases.sh → run_tests()
if should_run "NN"; then
  header "Phase NN — Nome feature"
  run_phase "Nome feature" "NN_nome.hurl" \
    --variable "jwt=${JWT}"
fi

Le variabili disponibili via HURL_VARS: base_url, app_name, admin_email, admin_password, db_engine, db_host, db_port, db_name, db_username, db_password.

Regole per codice cross-engine

Qualsiasi codice PHP che tocca il database deve essere engine-agnostic:

  • Usa $db->getEngine() per branching engine-specific solo quando strettamente necessario.
  • Per verificare l'esistenza di una tabella: $manage->tableExists(string $table).
  • Per verificare una colonna: $manage->columnExists(string $table, string $column).
  • Per verificare un indice: $manage->indexExistsPublic(string $table, string $index).
  • Non usare sqlite_master o PRAGMA fuori da blocchi SQLite-guard.
  • Non usare INSERT OR IGNORE / INSERT OR REPLACE (SQLite/MySQL only).
  • Non usare last_insert_rowid() — usa $db->query($sql, $params, 'id').
  • Gli indici parziali (CREATE INDEX ... WHERE ...) non sono supportati su MySQL.