openvibers/openvibe.media Commits

npm test no longer counts a skipped test as passed (roadmap WS-Q task 7; Shared v1.25.0 openvibe-shared/test-runner). The test script was a shell loop that stopped at the first failure and counted nothing, so a test that printed "⚠️ ffmpeg not found: … skipped" and exited 0 was as good as a pass. test/run.js is now the shared runner: every file, one at a time as before (parallel 1), a 300 s timeout per file, and a summary; a file that prints "<label>: skipped (<why>)" is listed with ○ and not counted as passed, and --strict fails on it. The six tests that skipped in free-form words now print that line: jobs-invariant (split/remux execution), clip-cut-job (clip.cut), jobs-previews (preview jobs), duration-reconcile (measurement checks) and vod-duration (real-file finalize check) without ffmpeg, and trust-proxy (direct-caller check) without a non-loopback interface. openvibe-shared is pinned at v1.25.0 (from v1.24.0), named in the README. npm test (Node 22) 64/64 test files passed.

OP OpenVibers committed 28 Sept 2026 · 47b21bde0b · 10 files, +28 −13


Changes

ddiff --git a/README.md b/README.md
iindex 00726fb..f81d5ac 100644
---- a/README.md
++++ b/README.md
@@@ -107,7 +107,7 @@ server/
vod.duration.reconcile (stored vs measured durations, local and B2/R2),
storage.orphans.scan (monthly storage orphan report, service-wide, report only)
client-ip.js trust proxy = loopback; req.ip is the only client address
--(openvibe-shared v1.24.0, openvibe-contracts v0.71.0, openvibe-sdk v0.12.0: pinned release tarballs, installed by npm)
++(openvibe-shared v1.25.0, openvibe-contracts v0.71.0, openvibe-sdk v0.12.0: pinned release tarballs, installed by npm)
scripts/smoke-test.sh end-to-end smoke test (boots a temp instance)
scripts/backfill-objects.js / reconcile-objects.js / object-invariant.js / no-good-copy-report.js object-model operator tools
scripts/media-jobs.js list jobs, run the size-invariant scan (dry run by default), approve/cancel proposals
ddiff --git a/package-lock.json b/package-lock.json
iindex 6e18fe6..2d14df2 100644
---- a/package-lock.json
++++ b/package-lock.json
@@@ -18,7 +18,7 @@
"multer": "^1.4.5-lts.1",
"openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.71.0",
"openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.12.0",
-- "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.24.0",
++ "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.25.0",
"sharp": "^0.33.4"
}
},
@@@ -1944,9 +1944,9 @@
}
},
"node_modules/openvibe-shared": {
-- "version": "1.24.0",
-- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.24.0",
-- "integrity": "sha512-VPzafrc07Pw0oqmXioOlDRtlX5xu82jNLBYkVYxXvhjkILMpWsyjExItIRt5Y6l2st0ozHm7bDQFTJpKEShMEA==",
++ "version": "1.25.0",
++ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.25.0",
++ "integrity": "sha512-oqZ+PvljrWi8IEbtg6bDYai17ZV2sALFx/ezMptniELSr10TgFUiGUQt+b1IWwiHv0T2Y1U2iSrkirJxMNgjSw==",
"license": "MIT",
"dependencies": {
"jsonwebtoken": "^9.0.0"
ddiff --git a/package.json b/package.json
iindex 0c1a01f..8060472 100644
---- a/package.json
++++ b/package.json
@@@ -7,7 +7,7 @@
"start": "node server/index.js",
"init-db": "node server/db/init.js",
"smoke": "bash scripts/smoke-test.sh",
-- "test": "for f in test/*.test.js; do node \"$f\" || exit 1; done",
++ "test": "node test/run.js",
"n-1:record": "node scripts/n-1-record.js"
},
"license": "MIT",
@@@ -21,7 +21,7 @@
"dotenv": "^16.4.5",
"express": "^4.19.2",
"multer": "^1.4.5-lts.1",
-- "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.24.0",
++ "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.25.0",
"sharp": "^0.33.4"
}
}
ddiff --git a/test/clip-cut-job.test.js b/test/clip-cut-job.test.js
iindex e635454..43da6fe 100644
---- a/test/clip-cut-job.test.js
++++ b/test/clip-cut-job.test.js
@@@ -33,7 +33,7 @@ const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
const waitFor = async (fn, ms = 30000) => { const end = Date.now() + ms; while (Date.now() < end) { if (await fn()) return true; await sleep(25); } return false; };
(async () => {
-- if (spawnSync('ffmpeg', ['-version']).status !== 0) { console.log('⚠️ ffmpeg not found: clip.cut skipped'); process.exit(0); }
++ if (spawnSync('ffmpeg', ['-version']).status !== 0) { console.log('clip.cut: skipped (ffmpeg not found)'); process.exit(0); }
await new Promise((r) => stub.listen(0, '127.0.0.1', r));
const base = `http://127.0.0.1:${stub.address().port}`;
const db = require('../server/db/database');
ddiff --git a/test/duration-reconcile.test.js b/test/duration-reconcile.test.js
iindex 15b1e93..02e8681 100644
---- a/test/duration-reconcile.test.js
++++ b/test/duration-reconcile.test.js
@@@ -93,7 +93,7 @@ const s3 = http.createServer((req, res) => {
let ids = {};
if (!hasFfmpeg) {
-- console.log('⚠️ ffmpeg not found: the measurement checks are skipped (orphan report still runs)');
++ console.log('measurement checks: skipped (ffmpeg not found; the orphan report still runs)');
} else {
const V = process.env.VOD_PATH;
mk(path.join(V, 'a.mp4'), 6);
ddiff --git a/test/jobs-invariant.test.js b/test/jobs-invariant.test.js
iindex e94c4dd..a6d42b2 100644
---- a/test/jobs-invariant.test.js
++++ b/test/jobs-invariant.test.js
@@@ -137,7 +137,7 @@ const hasFfmpeg = spawnSync('ffmpeg', ['-version']).status === 0;
assert.deepStrictEqual([r.status, r.body.code], [400, 'media.job.invalid'], 'parts 2-1000');
if (!hasFfmpeg) {
-- console.log('⚠️ ffmpeg not found: split/remux execution skipped');
++ console.log('split/remux execution: skipped (ffmpeg not found)');
} else {
const src = path.join(process.env.VOD_PATH, 'rec-10.mp4');
const mk6 = spawnSync('ffmpeg', ['-v', 'error', '-y', '-f', 'lavfi', '-i', 'testsrc=duration=6:size=160x120:rate=10', '-f', 'lavfi', '-i', 'sine=frequency=440:duration=6',
ddiff --git a/test/jobs-previews.test.js b/test/jobs-previews.test.js
iindex a2b210c..577c0a1 100644
---- a/test/jobs-previews.test.js
++++ b/test/jobs-previews.test.js
@@@ -41,7 +41,7 @@ const size = (file) => { const r = spawnSync('ffprobe', ['-v', 'error', '-select
assert.deepStrictEqual(spriteLayout(6, { frames: 100, columns: 10, tile_width: 160 }), { count: 3, interval_seconds: 2, columns: 3, rows: 1, tile_width: 160, tile_height: 90 });
assert.deepStrictEqual(spriteLayout(43083, { frames: 100, columns: 10, tile_width: 160 }), { count: 100, interval_seconds: 430.83, columns: 10, rows: 10, tile_width: 160, tile_height: 90 });
assert.strictEqual(spriteLayout(1, { frames: 100, columns: 10, tile_width: 160 }).count, 1);
-- if (!hasFfmpeg) { console.log('⚠️ ffmpeg not found: preview jobs skipped'); process.exit(0); }
++ if (!hasFfmpeg) { console.log('preview jobs: skipped (ffmpeg not found)'); process.exit(0); }
await new Promise((r) => stub.listen(0, '127.0.0.1', r));
const base = `http://127.0.0.1:${stub.address().port}`;
ddiff --git a/test/run.js b/test/run.js
nnew file mode 100644
iindex 0000000..a45fd34
---- /dev/null
++++ b/test/run.js
@@@ -0,0 +1,15 @@
++#!/usr/bin/env node
++/**
++ * Runs every test in test/ — the files named *.test.js — one at a time, each in its own process, and
++ * fails if any of them fails (openvibe-shared/test-runner).
++ *
++ * npm test # everything
++ * npm test -- objects jobs # only files whose name contains one of the words
++ * npm test -- --strict # a skipped test fails the run too
++ *
++ * The tests use temp SQLite databases and local servers on random ports. A few need ffmpeg (clip cuts,
++ * previews, remux, duration measurement) or a non-loopback interface; without them they print
++ * `<label>: skipped (<why>)`, and that file is listed with ○ and not counted as passed.
++ */
++'use strict';
++require('openvibe-shared/test-runner').main({ dir: __dirname, timeoutMs: 300000, pad: 40, parallel: 1, hide: /^\[DB\] / });
ddiff --git a/test/trust-proxy.test.js b/test/trust-proxy.test.js
iindex f167144..b40892f 100644
---- a/test/trust-proxy.test.js
++++ b/test/trust-proxy.test.js
@@@ -43,7 +43,7 @@ const externalAddress = () => {
assert.ok(r.ip.endsWith(ext) && r.client.endsWith(ext), `a direct caller cannot choose its IP (got ${r.ip})`);
console.log('✅ a non-loopback peer\'s X-Forwarded-For / CF-Connecting-IP are ignored');
} else {
-- console.log('⚠️ no non-loopback interface: the direct-caller check is skipped');
++ console.log('direct-caller check: skipped (no non-loopback interface)');
}
server.close();
// The view counter keys on req.ip, not on the headers.
ddiff --git a/test/vod-duration.test.js b/test/vod-duration.test.js
iindex ed154cd..338a8f2 100644
---- a/test/vod-duration.test.js
++++ b/test/vod-duration.test.js
@@@ -158,7 +158,7 @@ function restore() { Object.assign(tools, realTools); }
// ── 5. Real ffmpeg: an orphaned 6 s recording made 2 days ago finalizes to ~6 s (probe) ──
if (spawnSync('ffmpeg', ['-version']).status !== 0) {
-- console.log('⚠️ ffmpeg not found: the real-file finalize check is skipped');
++ console.log('real-file finalize check: skipped (ffmpeg not found)');
} else {
const file = path.join(process.env.VOD_PATH, 'real.mp4');
const mk = spawnSync('ffmpeg', ['-v', 'error', '-y', '-f', 'lavfi', '-i', 'testsrc=duration=6:size=160x120:rate=10',