|
10 | 10 | // used to grade `independent` and now grade `mirror` - is proven at the check level, in |
11 | 11 | // checks.test.mjs, which is where the old behaviour actually existed. |
12 | 12 |
|
13 | | -import test from 'node:test'; |
14 | | -import assert from 'node:assert/strict'; |
15 | | -import fs from 'node:fs'; |
16 | | -import path from 'node:path'; |
17 | 13 | import { fileURLToPath } from 'node:url'; |
| 14 | +import { test, describe, assert, fs, path } from './harness.mjs'; |
18 | 15 |
|
19 | 16 | import { parseCapture } from '../lib/corpus.mjs'; |
20 | 17 | import { |
21 | | - sketch, similarity, shingles, documentGroups, isMirror, |
| 18 | + sketch, similarity, shingles, documentGroups, isMirror, closestPair, |
22 | 19 | MIRROR_THRESHOLD, MIN_SHINGLES, |
23 | 20 | } from '../lib/similarity.mjs'; |
24 | 21 |
|
| 22 | +describe('similarity'); |
| 23 | + |
25 | 24 | const here = path.dirname(fileURLToPath(import.meta.url)); |
26 | 25 | const repo = path.resolve(here, '..', '..'); |
27 | 26 | const seaRaw = path.join(repo, 'docs', 'decisions', '2026-09-21-sea-assets', 'research', 'raw'); |
@@ -106,3 +105,35 @@ test('unjudgeable rows each stand alone rather than merging', () => { |
106 | 105 | // Two nulls must not become one group: "I cannot tell" is not "they are the same". |
107 | 106 | assert.equal(documentGroups([null, null]).length, 2); |
108 | 107 | }); |
| 108 | + |
| 109 | +// ---------------------------------------------------------------- closestPair |
| 110 | + |
| 111 | +test('closestPair reports the tightest pair, and null when nothing is judgeable', () => { |
| 112 | + const canonical = sketch(body(seaRaw, '-nodejs-')); |
| 113 | + const mirrored = sketch(body(seaRaw, 'epac')); |
| 114 | + const other = sketch(body(seaRaw, 'bundling')); |
| 115 | + |
| 116 | + // The mirror is the tightest pair present, so it is what gets reported. |
| 117 | + const three = closestPair([canonical, mirrored, other]); |
| 118 | + assert.ok(three > 0.35 && three <= 1, `expected the mirror to dominate, got ${three}`); |
| 119 | + assert.equal(closestPair([canonical, mirrored]), similarity(canonical, mirrored)); |
| 120 | + |
| 121 | + assert.equal(closestPair([]), null, 'nothing to compare is null, not 0'); |
| 122 | + assert.equal(closestPair([canonical]), null, 'one document has no pair'); |
| 123 | + assert.equal(closestPair([null, null]), null, 'unjudgeable pairs report null, never 0'); |
| 124 | +}); |
| 125 | + |
| 126 | +test('closestPair is REPORTED, not acted on - the verdict does not move with it', () => { |
| 127 | + // The measured reason, recorded so nobody "fixes" this into a blocking rule later: |
| 128 | + // containment - the obvious repair for a size-mismatched mirror - ranks a genuinely |
| 129 | + // different pair (0.506) ABOVE a real mirror (0.380). A check that tried harder would |
| 130 | + // report real second sources as copies, and the remedy a reviewer reaches for is to drop |
| 131 | + // one. So the number informs and the grade is unchanged. |
| 132 | + const canonical = sketch(body(seaRaw, '-nodejs-')); |
| 133 | + const other = sketch(body(seaRaw, 'bundling')); |
| 134 | + const score = closestPair([canonical, other]); |
| 135 | + assert.ok(score !== null); |
| 136 | + assert.ok(score < MIRROR_THRESHOLD, 'this pair must remain below the mirror line'); |
| 137 | + // Two distinct documents stay two distinct documents whatever the number says. |
| 138 | + assert.equal(documentGroups([canonical, other]).length, 2); |
| 139 | +}); |
0 commit comments