test(cli): harden media treatment parity

This commit is contained in:
ukimsanov
2026-07-26 01:29:05 -07:00
parent 9b504045f7
commit 20f4bde46a
2 changed files with 45 additions and 5 deletions
@@ -1,6 +1,6 @@
import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"; import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os"; import { tmpdir } from "node:os";
import { join } from "node:path"; import { basename, join } from "node:path";
import { runCommand } from "citty"; import { runCommand } from "citty";
import { describe, expect, it, vi } from "vitest"; import { describe, expect, it, vi } from "vitest";
import { import {
@@ -211,11 +211,13 @@ describe("applyMediaTreatmentToHtml", () => {
it("resolves nested composition media through the shared project-root contract", () => { it("resolves nested composition media through the shared project-root contract", () => {
const project = mkdtempSync(join(tmpdir(), "hf-media-treatment-assets-")); const project = mkdtempSync(join(tmpdir(), "hf-media-treatment-assets-"));
const escapedAsset = join(project, "..", `${basename(project)}-escape.mp4`);
mkdirSync(join(project, "capture"), { recursive: true }); mkdirSync(join(project, "capture"), { recursive: true });
mkdirSync(join(project, "assets"), { recursive: true }); mkdirSync(join(project, "assets"), { recursive: true });
writeFileSync(join(project, "capture", "talking-head.mp4"), ""); writeFileSync(join(project, "capture", "talking-head.mp4"), "");
writeFileSync(join(project, "assets", "photo.webp"), ""); writeFileSync(join(project, "assets", "photo.webp"), "");
writeFileSync(join(project, "assets", "My Clip.mp4"), ""); writeFileSync(join(project, "assets", "My Clip.mp4"), "");
writeFileSync(escapedAsset, "");
try { try {
expect( expect(
@@ -248,8 +250,16 @@ describe("applyMediaTreatmentToHtml", () => {
expect(() => expect(() =>
resolveMediaTreatmentSource(project, "compositions/scene.html", "missing.mp4"), resolveMediaTreatmentSource(project, "compositions/scene.html", "missing.mp4"),
).toThrow(/Media file not found/); ).toThrow(/Media file not found/);
expect(() =>
resolveMediaTreatmentSource(
project,
"compositions/scene.html",
`../../${basename(escapedAsset)}`,
),
).toThrow(/Media file not found/);
} finally { } finally {
rmSync(project, { recursive: true, force: true }); rmSync(project, { recursive: true, force: true });
rmSync(escapedAsset, { force: true });
} }
}); });
@@ -28,6 +28,26 @@ lavfi.signalstats.UAVG=125
lavfi.signalstats.VAVG=135 lavfi.signalstats.VAVG=135
lavfi.signalstats.SATAVG=60`; lavfi.signalstats.SATAVG=60`;
const UNDEREXPOSED_SIGNALSTATS = `frame:0 pts:0 pts_time:0
lavfi.signalstats.YMIN=2
lavfi.signalstats.YLOW=8
lavfi.signalstats.YAVG=50
lavfi.signalstats.YHIGH=120
lavfi.signalstats.YMAX=135
lavfi.signalstats.UAVG=128
lavfi.signalstats.VAVG=128
lavfi.signalstats.SATAVG=30`;
const BLOWN_HIGHLIGHT_SIGNALSTATS = `frame:0 pts:0 pts_time:0
lavfi.signalstats.YMIN=70
lavfi.signalstats.YLOW=90
lavfi.signalstats.YAVG=200
lavfi.signalstats.YHIGH=248
lavfi.signalstats.YMAX=255
lavfi.signalstats.UAVG=128
lavfi.signalstats.VAVG=128
lavfi.signalstats.SATAVG=30`;
const PROBE: GradeMediaProbe = { const PROBE: GradeMediaProbe = {
duration: 4, duration: 4,
colorSpace: "bt2020nc", colorSpace: "bt2020nc",
@@ -36,8 +56,8 @@ const PROBE: GradeMediaProbe = {
pixelFormat: "yuv420p10le", pixelFormat: "yuv420p10le",
}; };
function productResult() { function productResult(signalStats = SIGNALSTATS) {
const frames = parseMediaTreatmentSignalStats(SIGNALSTATS); const frames = parseMediaTreatmentSignalStats(signalStats);
const summary = summarizeMediaTreatmentAnalysis(PROBE, frames); const summary = summarizeMediaTreatmentAnalysis(PROBE, frames);
return { return {
frames, frames,
@@ -47,13 +67,13 @@ function productResult() {
}; };
} }
async function vendoredResult() { async function vendoredResult(signalStats = SIGNALSTATS) {
const moduleUrl = new URL( const moduleUrl = new URL(
"../../../skills/media-use/scripts/lib/grade-analyzer.mjs", "../../../skills/media-use/scripts/lib/grade-analyzer.mjs",
import.meta.url, import.meta.url,
); );
const analyzer = await import(moduleUrl.href); const analyzer = await import(moduleUrl.href);
const frames = analyzer.parseMediaTreatmentSignalStats(SIGNALSTATS); const frames = analyzer.parseMediaTreatmentSignalStats(signalStats);
const summary = analyzer.summarizeMediaTreatmentAnalysis(PROBE, frames); const summary = analyzer.summarizeMediaTreatmentAnalysis(PROBE, frames);
return { return {
frames, frames,
@@ -67,4 +87,14 @@ describe("vendored media grade analyzer parity", () => {
it("keeps the standalone skill copy aligned with the typed product module", async () => { it("keeps the standalone skill copy aligned with the typed product module", async () => {
expect(await vendoredResult()).toEqual(productResult()); expect(await vendoredResult()).toEqual(productResult());
}); });
it.each([
["underexposed", UNDEREXPOSED_SIGNALSTATS, 1],
["blown-highlight", BLOWN_HIGHLIGHT_SIGNALSTATS, -1],
])("covers the %s exposure branch", async (_, signalStats, expectedSign) => {
const product = productResult(signalStats);
expect(Math.sign(product.adjust.adjust.exposure)).toBe(expectedSign);
expect(await vendoredResult(signalStats)).toEqual(product);
});
}); });