'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 };