import { test } from "node:test"; import assert from "node:assert/strict"; import { BOUNDARY_ORIGIN_EPSILON_SECONDS, isBoilerplateChapterTitle, matchBoundaries, normalizeBoundaries, scoreBoundaries, scoreBoundariesAt, } from "./boundaryScore"; test("the origin boundary is dropped, because everything has one", () => { // A boundary at t=0 is free for any segmentation and carries no information // about segmentation skill. If it survived, both arms of an A/B would collect // an unearned true positive. assert.deepEqual(normalizeBoundaries([0, 120, 300]), [120, 300]); assert.deepEqual(normalizeBoundaries([BOUNDARY_ORIGIN_EPSILON_SECONDS, 120]), [120]); assert.deepEqual(normalizeBoundaries([BOUNDARY_ORIGIN_EPSILON_SECONDS + 1, 120]), [6, 120]); }); test("normalization sorts, rounds and de-dups", () => { assert.deepEqual(normalizeBoundaries([300.4, 120.6, 300.2, NaN, 121]), [121, 300]); }); test("a perfect segmentation scores 1.0 at every tolerance", () => { const truth = [120, 300, 600]; const report = scoreBoundaries(truth, truth); for (const s of report.scores) { assert.equal(s.f1, 1); assert.equal(s.precision, 1); assert.equal(s.recall, 1); } assert.equal(report.medianOffsetSeconds, 0); assert.equal(report.withinThirtySeconds, 3); }); test("a disjoint segmentation scores 0", () => { const report = scoreBoundaries([120, 300], [5000, 6000]); for (const s of report.scores) { assert.equal(s.matched, 0); assert.equal(s.f1, 0); } assert.equal(report.withinThirtySeconds, 0); }); test("matching is ONE-TO-ONE, so clustering is not rewarded", () => { // The over-segmentation failure precision exists to punish: ten boundaries // crammed into one 30 s window around a single uploader boundary. A // nearest-neighbour count would score this 10 matches and look excellent. const generated = [115, 116, 117, 118, 119, 120, 121, 122, 123, 124]; const score = scoreBoundariesAt([120], generated, 30); assert.equal(score.matched, 1, "one reference boundary can absorb only one match"); assert.equal(score.recall, 1); assert.equal(score.precision, 1 / 10); assert.ok(score.f1 < 0.2); }); test("closest pair wins when two references contend for one boundary", () => { // 300 is 5 s away, 280 is 15 s away. Greedy-by-distance must give the // generated boundary to 300 and leave 280 unmatched, not the reverse. const matches = matchBoundaries([280, 300], [295], 30); assert.equal(matches.length, 1); assert.deepEqual(matches[0], { reference: 300, generated: 295, distance: 5 }); }); test("tolerance is a hard edge, and the two tolerances can disagree", () => { // 45 s off: missed at 30 s, found at 60 s. A variant that trades tightness // for recall has to be visible as exactly that. const report = scoreBoundaries([300], [345]); const at30 = report.scores.find((s) => s.toleranceSeconds === 30)!; const at60 = report.scores.find((s) => s.toleranceSeconds === 60)!; assert.equal(at30.matched, 0); assert.equal(at60.matched, 1); assert.equal(report.medianOffsetSeconds, 45); }); test("offset is nearest-neighbour and F1 is not, so they fail differently", () => { // One generated boundary sitting near one of three references: a good median // offset would be misleading on its own, and precision/recall is what catches // it. Both are reported for this reason. const report = scoreBoundaries([300, 900, 1500], [305]); assert.equal(report.medianOffsetSeconds, 595); const at30 = report.scores.find((s) => s.toleranceSeconds === 30)!; assert.equal(at30.precision, 1); assert.equal(at30.recall, 1 / 3); }); test("an empty generated set scores zero rather than dividing by zero", () => { const report = scoreBoundaries([120, 300], []); for (const s of report.scores) { assert.equal(s.precision, 0); assert.equal(s.recall, 0); assert.equal(s.f1, 0); } assert.equal(report.medianOffsetSeconds, null); }); test("a video whose only uploader chapter is the origin yields no reference", () => { const report = scoreBoundaries([0], [120, 300]); assert.equal(report.referenceCount, 0); assert.equal(report.medianOffsetSeconds, null); }); test("boilerplate chapter titles are recognized", () => { for (const t of ["Intro", "intro", "Outro", "Sponsor", "Ad read", "Merch", "BRB"]) { assert.ok(isBoilerplateChapterTitle(t), `${t} should be boilerplate`); } for (const t of ["Court filing deadlines", "Introducing the new tariff rules"]) { assert.ok(!isBoilerplateChapterTitle(t), `${t} should NOT be boilerplate`); } });