test(04-01): RED — OSA distance vectors (D1-D9) + disjoint-tier scorer suite (S1-S7)
- 9 hand-computed Damerau-Levenshtein vectors (transpositions, empties, kitten/sitting sanity, the corpus's klcc/kclc + sunway/sunwya typos) - 7 scorer laws: tier literals + disjointness, min-length guard before exact, exact-outranks-fuzzy, mid-word PREFIX vs token-boundary TOKEN_SET, fuzzy residual 0.75*0.60 arithmetic, the 0.72 admission boundary (Penang/Padang 0.667 refuses), matchedAlias raw provenance All 16 new cases fail while src/distance.ts + src/score.ts are absent.
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
#!/usr/bin/env node
|
||||
/*
|
||||
* distance.test.mjs — OSA Damerau-Levenshtein vector suite (SRCH-02).
|
||||
* -------------------------------------------------------------------
|
||||
* RED phase (plan 04-01 task 2): every case fails while
|
||||
* ../src/distance.ts does not exist (lazy import => per-case FAIL).
|
||||
*
|
||||
* The vectors are the hand-computed ones the plan pins (RESEARCH
|
||||
* §"The Damerau-Levenshtein (OSA) distance"). The 3-band row rotation
|
||||
* is the classic implementation trap — these vectors are written FIRST
|
||||
* so a wrong rotation shows up as a red suite, not a silent mis-rank.
|
||||
*
|
||||
* D1 adjacent transposition ab -> ba = 1
|
||||
* D2 two transpositions a cat -> an act = 2
|
||||
* D3 empty vs empty = 0
|
||||
* D4 empty vs abc = 3
|
||||
* D5 abc vs empty = 3
|
||||
* D6 identical strings = 0
|
||||
* D7 KLCC transposition typo klcc -> kclc = 1
|
||||
* D8 real corpus typo sunway -> sunwya = 1
|
||||
* D9 classic Levenshtein sanity kitten -> sitting = 3
|
||||
*
|
||||
* Run: npm run search:test (exit 0 = all green)
|
||||
*/
|
||||
import { strict as assert } from "node:assert";
|
||||
import { test } from "./harness.mjs";
|
||||
|
||||
// Lazy distance load — missing module => per-case ERR_MODULE_NOT_FOUND (the RED state).
|
||||
let distancePromise = null;
|
||||
function loadDamerau() {
|
||||
if (!distancePromise) {
|
||||
distancePromise = import("../src/distance.ts").then((m) => m.damerau);
|
||||
}
|
||||
return distancePromise;
|
||||
}
|
||||
|
||||
test("D1 adjacent transposition: damerau('ab', 'ba') === 1", async () => {
|
||||
const damerau = await loadDamerau();
|
||||
assert.equal(damerau("ab", "ba"), 1);
|
||||
});
|
||||
|
||||
test("D2 two transpositions: damerau('a cat', 'an act') === 2", async () => {
|
||||
const damerau = await loadDamerau();
|
||||
assert.equal(damerau("a cat", "an act"), 2);
|
||||
});
|
||||
|
||||
test("D3 damerau('', '') === 0", async () => {
|
||||
const damerau = await loadDamerau();
|
||||
assert.equal(damerau("", ""), 0);
|
||||
});
|
||||
|
||||
test("D4 damerau('', 'abc') === 3", async () => {
|
||||
const damerau = await loadDamerau();
|
||||
assert.equal(damerau("", "abc"), 3);
|
||||
});
|
||||
|
||||
test("D5 damerau('abc', '') === 3", async () => {
|
||||
const damerau = await loadDamerau();
|
||||
assert.equal(damerau("abc", ""), 3);
|
||||
});
|
||||
|
||||
test("D6 identical strings: damerau('same', 'same') === 0", async () => {
|
||||
const damerau = await loadDamerau();
|
||||
assert.equal(damerau("same", "same"), 0);
|
||||
});
|
||||
|
||||
test("D7 KLCC transposition typo: damerau('klcc', 'kclc') === 1", async () => {
|
||||
const damerau = await loadDamerau();
|
||||
assert.equal(damerau("klcc", "kclc"), 1);
|
||||
});
|
||||
|
||||
test("D8 real corpus typo: damerau('sunway', 'sunwya') === 1", async () => {
|
||||
const damerau = await loadDamerau();
|
||||
assert.equal(damerau("sunway", "sunwya"), 1);
|
||||
});
|
||||
|
||||
test("D9 classic Levenshtein sanity: damerau('kitten', 'sitting') === 3", async () => {
|
||||
const damerau = await loadDamerau();
|
||||
assert.equal(damerau("kitten", "sitting"), 3);
|
||||
});
|
||||
Reference in New Issue
Block a user