Files
NewTube/server/tests/page_tokens.test.mjs
T
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

85 lines
3.9 KiB
JavaScript

/**
* Phase 3.12 — persistance des `pageToken` YouTube en base.
*
* Le test porte sur le comportement observable, pas sur l'implémentation :
* un jeton écrit par un « process A » doit être relisible par un « process B »
* qui ne partage aucun état mémoire. C'est exactement ce qui échouait avec la
* Map process-local — et ce que le redémarrage en production provocait.
*/
import { test } from 'node:test';
import assert from 'node:assert/strict';
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'newtube-tokens-'));
process.env.NEWTUBE_DB_FILE = path.join(tmpDir, 'tokens.db');
const db = await import('../db.mjs');
test('3.12 — un jeton écrit est relu par un autre process (L2 partagé)', () => {
const key = 'yt|UCtest|pl|24';
assert.equal(db.getCachedPageTokens(key), null, 'clé absente au départ');
assert.equal(db.setCachedPageTokens(key, [undefined, 'TOKEN_PAGE_2', 'TOKEN_PAGE_3']), true);
// On repart d'un cache mémoire vierge : c'est le cas d'un process neuf ou d'un
// pod différent. Seul le L2 peut répondre ici.
const reread = db.getCachedPageTokens(key);
assert.ok(Array.isArray(reread), 'le jeton doit survivre hors du cache mémoire');
// Indexation : `tokens[n]` = jeton pour la page n+2 (l'index 1 est donc la
// page 2). Un trou est matérialisé par `''`, jamais par un décalage : si le
// round-trip compressait le tableau, le jeton de la page 2 se retrouverait à
// l'index 0 et la page 3 renverrait celui de la page 4.
assert.equal(reread[0], '', 'le trou de l\'index 0 reste un trou, pas un décalage');
assert.equal(reread[1], 'TOKEN_PAGE_2', 'tokens[1] = jeton de la page 2');
assert.equal(reread[2], 'TOKEN_PAGE_3', 'tokens[2] = jeton de la page 3');
});
test('3.12 — un jeton absent reste absent (jamais de token inventé)', () => {
assert.equal(db.getCachedPageTokens('yt|UCinconnu|pl|24'), null);
});
test('3.12 — un jeton expiré n\'est pas resservi', () => {
const key = 'yt|UCexpire|pl|24';
db.setCachedPageTokens(key, ['TOK'], 1);
// TTL de 1 ms : la ligne est déjà hors délai au tour suivant.
assert.equal(db.getCachedPageTokens(key), null, 'un jeton expiré doit être traité comme absent');
// Et la ligne doit avoir été retirée, pas laissée traîner.
assert.equal(db.getCachedPageTokens(key), null);
});
test('3.12 — une écriture vide est refusée (pas de ligne inutile)', () => {
assert.equal(db.setCachedPageTokens('yt|vide|pl|24', []), false);
assert.equal(db.getCachedPageTokens('yt|vide|pl|24'), null);
});
test('3.12 — les jetons ne se confondent pas avec les résultats de recherche', () => {
// Namespace distinct : sinon le plafond par fournisseur et les stats de cache
// du fournisseur `yt` seraient faussés par des jetons qui ne sont pas des
// résultats.
const key = 'collision|1';
db.setCachedPageTokens(key, ['TOK_A']);
// `setCachedSearch` n'écrit que des tableaux d'items, jamais sous ce provider.
assert.equal(db.setCachedSearch('yt_tokens', key, 'q', ['item'], 'api'), true);
// Le même cache_key sous deux providers ne se chevauche pas : la lecture
// page_token et la lecture search restent distinctes.
assert.ok(db.getCachedPageTokens(key), 'le jeton reste lisible');
});
test('3.12 — une chaîne de jetons se complète sans écraser les pages précédentes', () => {
const key = 'yt|UCchain|pl|24';
// Page 1 écrite seule, puis page 2 : le tableau doit garder les deux.
db.setCachedPageTokens(key, ['TOK_PAGE_2']);
assert.equal(db.getCachedPageTokens(key)[0], 'TOK_PAGE_2');
db.setCachedPageTokens(key, ['TOK_PAGE_2', 'TOK_PAGE_3']);
const arr = db.getCachedPageTokens(key);
assert.equal(arr.length, 2);
assert.equal(arr[0], 'TOK_PAGE_2', 'la page 2 ne doit pas disparaître');
assert.equal(arr[1], 'TOK_PAGE_3');
});
test.after(() => {
try { fs.rmSync(tmpDir, { recursive: true, force: true }); } catch {}
});