Files
EVCS/tests/kilometrage/run.mjs

100 lines
7.1 KiB
JavaScript

// Tests du kilometrage (04/10/2026).
// node tests/kilometrage/run.mjs -> fonctions pures
// node tests/kilometrage/run.mjs --vues -> vues SQL, dans une transaction
// ANNULEE (a lancer dans le
// conteneur d'historisation)
import { lireReleves, ajouterReleve, creerSchemaKm } from '../../tools/kilometrage.mjs';
let ok = 0, ko = 0;
const eq = (nom, a, b) => {
const sa = JSON.stringify(a), sb = JSON.stringify(b);
if (sa === sb) { ok++; } else { ko++; console.log(`ECHEC ${nom}\n obtenu ${sa}\n attendu ${sb}`); }
};
const proche = (nom, a, b, tol = 1e-6) => eq(nom, Math.abs(Number(a) - b) <= tol ? b : Number(a), b);
// --- lireReleves
const T = 1759515246;
eq('vide', lireReleves('').releves, []);
eq('unknown', lireReleves('unknown').releves, []);
eq('json illisible', lireReleves('[x').rejets, ['JSON illisible']);
const r1 = lireReleves(JSON.stringify([['jeep', 112694, 54, T, 'w'], ['ZERO', 16469, null, T, 'w']]));
eq('deux releves', r1.releves.map(r => [r.vehicule, r.km, r.niveau, r.ts.getTime() / 1000]),
[['JEEP', 112694, 54, T], ['ZERO', 16469, null, T]]);
eq('km decimal refuse', lireReleves(JSON.stringify([['VW', 1.5, null, T, 'w']])).rejets.length, 1);
eq('niveau 150 refuse', lireReleves(JSON.stringify([['VW', 1000, 150, T, 'w']])).rejets.length, 1);
eq('horodatage absurde refuse', lireReleves(JSON.stringify([['VW', 1000, null, 12, 'w']])).rejets.length, 1);
// --- ajouterReleve : garde les derniers, tient dans 255 caracteres
let b = '';
for (let i = 0; i < 10; i++) b = ajouterReleve(b, { vehicule: 'JEEP', km: 112694 + i, niveau: 54, ts: new Date((T + i) * 1000), source: 'whatsapp' });
eq('255 car max', b.length <= 255, true);
eq('dernier garde', JSON.parse(b).at(-1)[1], 112703);
eq('helper corrompu repart a vide', JSON.parse(ajouterReleve('pas du json', { vehicule: 'VW', km: 5, niveau: null, ts: new Date(T * 1000), source: 'ha' })).length, 1);
if (process.argv.includes('--vues')) {
const pg = (await import('pg')).default;
const c = new pg.Client(); await c.connect();
try {
await c.query('BEGIN');
await creerSchemaKm(c);
// Vehicules fictifs, annee 2020 : rien ne se melange au reel, et tout est annule.
const ins = (v, ts, km, n, cap, auto, elec) => c.query(
'INSERT INTO releve_km (vehicule, ts, km, niveau_pct, source, capacite_kwh, autonomie_ref_km, tout_electrique) VALUES ($1,$2,$3,$4,\'test\',$5,$6,$7)',
[v, ts, km, n, cap, auto, elec]);
await ins('TESTH', '2020-01-01T18:00Z', 1000, 54, 8.7, 50, false);
await ins('TESTH', '2020-01-02T18:00Z', 1030, 20, 8.7, 50, false); // pas de charge : 34 % x 8,7 = 2,958 kWh
await ins('TESTH', '2020-01-04T18:00Z', 1080, 0, 8.7, 50, false); // batterie vide : essence possible
await ins('TESTE', '2020-01-01T18:00Z', 500, 80, 15, 200, true);
await ins('TESTE', '2020-01-03T18:00Z', 600, 0, 15, 200, true); // electrique : 0 % reste valide
await c.query(`INSERT INTO session_charge (debut, fin, terminee, vehicule, vehicule_sur, energie_kwh, energie_station_kwh)
VALUES ('2020-01-03T01:00Z','2020-01-03T03:00Z',true,'TESTH',true,4.0,4.2)`);
const { rows } = await c.query("SELECT * FROM km_intervalle WHERE vehicule LIKE 'TEST%' ORDER BY vehicule, debut");
const h1 = rows.find(r => r.vehicule === 'TESTH' && r.km_parcourus === 30);
proche('H1 kWh consommes = 0 + 0,34 x 8,7', h1.kwh_consommes, 2.958);
proche('H1 km/kWh', h1.km_par_kwh, 30 / 2.958);
proche('ref km/kWh = 50/8,7', h1.km_par_kwh_ref, 50 / 8.7);
eq('H1 valide', h1.statut, 'valide');
const h2 = rows.find(r => r.vehicule === 'TESTH' && r.km_parcourus === 50);
proche('H2 charge de la borne comptee (4,2)', h2.kwh_charges, 4.2);
eq('H2 hybride vide au retour', h2.statut, 'batterie vide au retour : km essence possibles');
const e1 = rows.find(r => r.vehicule === 'TESTE');
proche('E1 kWh = 0,80 x 15', e1.kwh_consommes, 12);
eq('E1 electrique a 0 % valide', e1.statut, 'valide');
// Capacite : branchement 2020-02-01 10:00 -> 14:00, niveau 50 % saisi a
// 10:40 (compteur deja a 101,0 : la voiture chargeait), compteur 100,0 au
// branchement et 104,5 au debranchement, plein vu a 13:00.
const ev = (ts, apres) => c.query(`INSERT INTO evenement (ts, genre, source, resume, detail) VALUES ($1,'vehicule','test','test',$2)`,
[ts, JSON.stringify({ entite: 'binary_sensor.victron_evcs_vehicle_connected', apres })]);
const cpt = (ts, v) => c.query(`INSERT INTO mesure (ts, cle, valeur) VALUES ($1,'sensor.evcs_hq2239dnt7p_total_energy',$2)`, [ts, v]);
await ev('2020-02-01T10:00:00.123456Z', 'on'); await ev('2020-02-01T14:00Z', 'off');
await c.query(`INSERT INTO etiquette_branchement (branchement, label, source_airtria) VALUES ('2020-02-01T10:00:00.123Z','TESTH','test')`);
await c.query(`INSERT INTO niveau_saisi (saisi_a, niveau_pct, ref_kwh) VALUES ('2020-02-01T10:40Z', 50, 101.0)`);
await cpt('2020-02-01T09:59Z', 100.0); await cpt('2020-02-01T13:59Z', 104.5);
await c.query(`INSERT INTO decision (ts, etat) VALUES ('2020-02-01T13:00Z','day_vehicle_full_idle')`);
const capa = async () => (await c.query("SELECT * FROM capacite_estimee WHERE branche >= '2020-02-01' AND branche < '2020-02-02'")).rows[0];
let cap = await capa();
proche('energie depuis le branchement, pas la saisie', cap.kwh_livres, 4.5);
proche('capacite = 4,5 / 0,5', cap.capacite_kwh, 9.0);
eq('vehicule de l etiquette (horodatage a la microseconde pres)', cap.vehicule, 'TESTH');
await c.query(`INSERT INTO session_charge (debut, fin, terminee, vehicule, vehicule_sur, energie_kwh, energie_station_kwh)
VALUES ('2020-02-01T10:01Z','2020-02-01T13:00Z',true,'TESTX',true,4.4,4.5)`);
eq('la session confirmee prime sur l etiquette', (await capa()).vehicule, 'TESTX');
await c.query(`DELETE FROM decision WHERE ts = '2020-02-01T13:00Z'`);
cap = await capa();
eq('pas plein, pas de releve -> pas de capacite', cap.capacite_kwh, null);
await ins('TESTX', '2020-02-01T14:20Z', 777, 80, 15, 200, true);
proche('releve dans l heure : 4,5 / (80-50) %', (await capa()).capacite_kwh, 15.0);
// Saisie ancienne sans compteur historise : somme des sessions du branchement.
await ev('2020-03-01T20:00Z', 'on'); await ev('2020-03-02T04:00Z', 'off');
await c.query(`INSERT INTO session_charge (debut, fin, terminee, vehicule, vehicule_sur, energie_kwh, energie_station_kwh)
VALUES ('2020-03-01T23:25Z','2020-03-02T01:41Z',true,'TESTV',true,7.6,7.7)`);
await c.query(`INSERT INTO niveau_saisi (saisi_a, niveau_pct, ref_kwh, source) VALUES ('2020-03-01T20:00:01Z', 5, NULL, 'test')`);
await c.query(`INSERT INTO decision (ts, etat) VALUES ('2020-03-02T01:42Z','night_recovery_done_idle')`);
const v = (await c.query("SELECT * FROM capacite_estimee WHERE branche >= '2020-03-01' AND branche < '2020-03-02'")).rows[0];
proche('sans compteur : sessions du branchement 7,7 / 0,95', v.capacite_kwh, 7.7 / 0.95);
} finally { await c.query('ROLLBACK'); await c.end(); }
}
console.log(`${ok}/${ok + ko}`);
process.exit(ko ? 1 : 0);