Files
NewTube/server/tests/record_provider_shapes.mjs
bruno 665a0f0ebd
CI / build-and-test (push) Successful in 14m43s
feat(providers): phases 7.3/7.4/7.6/8.1 — provenance, health, NDJSON, contrat unique
7.3: capturedAt/source au registre + 6 adaptateurs + module provenance.ts + ?debug=1 (search-transport.mjs). 7.4: ProviderHealthService + badge source degradee. 7.6: squelettes par provider + snapshots progressifs + transport NDJSON /api/search. 8.1: ProviderAdapter unifie (search enveloppe + channelContent/channelMeta/capabilities) via getProviderAdapter + test de contrat offline.
2026-09-30 07:57:11 -04:00

87 lines
3.7 KiB
JavaScript

/**
* Phase 8.4 — enregistreur de fixtures de contrat.
*
* Appelle les 6 adaptateurs **réels** et fige leur sortie dans
* `server/tests/fixtures/provider-suggestions.json`. Les fixtures sont donc
* générées, pas écrites à la main : elles reflètent ce que les providers
* produisent vraiment, y compris leurs champs bizarres.
*
* Réflexe : figer des objets inventés ne prouverait rien — on validerait sa
* propre imagination. Figer la sortie réelle prouve que le code d'aujourd'hui
* respecte le contrat, et le test rejoué à chaque PR détecte toute dérive.
*
* Usage : `npm run fixtures:record` (réseau requis).
* En CI : jamais exécuté. On vérifie seulement que les fixtures gelées sont
* toujours valides (`npm run test:shapes`).
*/
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
const OUT = path.resolve(import.meta.dirname, 'fixtures/provider-suggestions.json');
const QUERY = process.argv[2] || 'tutorial';
const LIMIT = 3;
// BASE DE DONNÉES JETABLE, définie AVANT tout import du registre.
//
// Deux raisons, toutes deux apprises à l'usage :
// 1. `search_cache` est persistant : sans base neuve, le gel rejoue des
// résultats d'un ancien gel et fige une forme PÉRIMÉE. C'est arrivé : le
// correctif de `language` PeerTube n'apparaissait pas dans les fixtures.
// 2. Le gel ne doit pas toucher `db/newtube.db` (ni y appliquer de migration).
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'newtube-record-'));
process.env.NEWTUBE_DB_FILE = path.join(tmpDir, 'record.db');
const { providerRegistry } = await import('../providers/registry.mjs');
/** Champs parasites à ne pas figer : ils changent à chaque appel et ne disent rien du contrat. */
function normalize(item) {
const out = {};
// Tri des clés : deux enregistrements successifs donnent un diff lisible.
for (const k of Object.keys(item).sort()) {
if (item[k] === undefined) continue;
out[k] = item[k];
}
return out;
}
const recorded = {
_comment: 'FIGÉ PAR `npm run fixtures:record`. Ne pas éditer à la main : régénérer.',
_contractVersion: 2,
_recordedAt: new Date().toISOString(),
_query: QUERY,
providers: {},
};
let failures = 0;
for (const id of Object.keys(providerRegistry)) {
process.stdout.write(` ${id} … `);
try {
const items = await providerRegistry[id].search(QUERY, { limit: LIMIT, page: 1, sort: 'relevance' });
const list = (Array.isArray(items) ? items : []).map(normalize);
recorded.providers[id] = list.length
? { items: list }
// Upstream indisponible au moment du gel (Rumble/Cloudflare, Odysee hors
// ligne) : on le note au lieu d'écrire un tableau vide silencieux, pour
// que la couverture manquante reste visible dans le fichier.
: { items: [], note: 'aucun résultat au moment du gel — couverture non testée pour ce provider' };
console.log(`${list.length} item(s)${list.length ? '' : ' ⚠'}`);
if (!list.length) failures++;
} catch (e) {
recorded.providers[id] = { items: [], note: `erreur au gel : ${String(e?.message || e).slice(0, 160)}` };
console.log(`ERREUR ${e?.message || e}`);
failures++;
}
}
fs.mkdirSync(path.dirname(OUT), { recursive: true });
fs.writeFileSync(OUT, `${JSON.stringify(recorded, null, 2)}\n`);
console.log(`\n→ ${path.relative(process.cwd(), OUT)}`);
if (failures) {
console.log(`\n${failures} provider(s) sans fixture exploitable : la couverture du test de contrat est partielle.`);
}
try { fs.rmSync(tmpDir, { recursive: true, force: true }); } catch {}
// Exit 1 : le gel a réussi mais est partiel (upstream HS). Le fichier est
// quand même écrit, pour que le trou de couverture soit visible dans le gel.
process.exit(failures ? 1 : 0);