CI / build-and-test (push) Successful in 14m43s
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.
85 lines
3.9 KiB
JavaScript
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 {}
|
|
});
|