Files
Tdarr-plugin/tests/mock-tdarr-runtime.js
PickleRick c2074be826 feat: Tdarr Local Flow Plugin Suite v1.0
7 atomic plugins for automated x265 transcoding pipeline:
- utility/mediaAnalyzerGOP    : GOP calc, FPS detection, film/series/anime routing
- utility/setAnimationFlag    : manual animation flag override
- utility/extractChaptersXml  : mkvextract chapters to XML
- audio/audioSelectorClamping : codec scoring, clamped Opus bitrates, 2 outputs
- audio/backupSourceAudio     : lossless DTS/TrueHD ITA backup (-c:a copy)
- subtitles/extractSubtitlesAndOcr : SRT extract + SubtitleEdit OCR for PGS
- video/transcode2PassX265    : 2-pass x265, Lanczos 1080p bound, Opus mux

Test suite: 34 unit tests + 3 integration tests (FFmpeg real files)
FlowHelpers mock for local testing included
Docs: integration-guide.md with canvas wiring and deploy instructions
2026-09-18 23:23:05 +02:00

110 lines
2.9 KiB
JavaScript

'use strict';
/**
* mock-tdarr-runtime.js
* Creates a complete mock of the Tdarr args object for isolated plugin testing.
* Supports loading real ffprobe data from actual MKV files.
*/
const path = require('path');
const os = require('os');
const fs = require('fs');
const { execSync } = require('child_process');
/**
* Run ffprobe on a file and return parsed JSON.
* Falls back to a minimal synthetic structure if ffprobe is unavailable.
*/
function probeFile(filePath) {
try {
const raw = execSync(
`ffprobe -v quiet -print_format json -show_streams -show_format "${filePath}"`,
{ encoding: 'utf8', timeout: 15000 },
);
return JSON.parse(raw);
} catch (err) {
console.warn(`[mock] ffprobe failed for ${filePath}: ${err.message}`);
return { streams: [], format: {} };
}
}
/**
* Build a mock Tdarr args object.
*
* @param {object} opts
* @param {string} opts.filePath - Path to the input MKV file
* @param {object} [opts.inputs] - Plugin input overrides (UI inputs)
* @param {object} [opts.variables] - Pre-populated variables
* @param {string} [opts.ffmpegPath] - Override ffmpeg path
* @param {string} [opts.workDir] - Override work directory
*/
function buildMockArgs(opts) {
const {
filePath,
inputs = {},
variables = {},
ffmpegPath = process.env.FFMPEG_PATH || 'ffmpeg',
workDir = fs.mkdtempSync(path.join(os.tmpdir(), 'tdarr-test-')),
} = opts;
const ffProbeData = probeFile(filePath);
const logs = [];
return {
inputFileObj: {
_id: filePath,
file: filePath,
ffProbeData,
mediaInfo: { track: [] },
container: path.extname(filePath).replace('.', ''),
},
variables: { ...variables },
inputs,
workDir,
ffmpegPath,
ffprobePath: process.env.FFPROBE_PATH || 'ffprobe',
mkvpropeditPath: process.env.MKVPROPEDIT_PATH || 'mkvpropedit',
userVariables: { global: {}, library: {} },
jobLog: (msg) => {
process.stdout.write(msg);
logs.push(msg);
},
_logs: logs,
_workDir: workDir,
};
}
/**
* Cleanup a workDir created by buildMockArgs.
*/
function cleanupWorkDir(mockArgs) {
const dir = mockArgs._workDir || mockArgs.workDir;
if (dir && dir.startsWith(os.tmpdir()) && fs.existsSync(dir)) {
fs.rmSync(dir, { recursive: true, force: true });
}
}
/**
* Simple assertion helper.
*/
function assert(condition, message) {
if (!condition) throw new Error(`ASSERTION FAILED: ${message}`);
console.log(` [OK] ${message}`);
}
function assertEqual(actual, expected, label) {
assert(
actual === expected,
`${label}: expected "${expected}", got "${actual}"`,
);
}
function assertLessOrEqual(actual, max, label) {
assert(
parseInt(actual, 10) <= max,
`${label}: ${actual} <= ${max}`,
);
}
module.exports = { buildMockArgs, cleanupWorkDir, assert, assertEqual, assertLessOrEqual };