CoolFace
Apppublic

vonargo/mosAIc

sourceHugging Facemitupdated 2mo agoView on Hugging Face
1likes
subject-rail.test.js116 linesDownload Raw Back to test
1// test/subject-rail.test.js — the rail's pure core: overlap ranking, candidate indexing2// (views + okf concept tiles), the rescore-only rerank invariant, escaped HTML.3import { test } from 'node:test';4import assert from 'node:assert/strict';5import { tokens, overlapScore, candidateIndex, rankBySubject, applyRerank, railHtml, railRowHtml, RAIL_K } from '../js/subject-rail.js';6import { TASKS } from '../js/demo.js';7 8const VIEWS = [9  { id: 'oauth', title: 'OAuth flow', heading: 'OAuth 2.0 flow', tesserae: [{ type: 'markdown', title: 'Token exchange', body: 'x' }] },10  { id: 'tokens', title: 'Access tokens', heading: 'Access tokens explained', tesserae: [{ type: 'markdown', title: 'Token lifetime', body: 'x' }] },11  { id: 'recipes', title: 'Pasta recipes', heading: 'Weeknight pasta', tesserae: [{ type: 'markdown', title: 'Carbonara', body: 'x' }] },12  { id: 'kb', title: 'Concepts', tesserae: [13    { type: 'markdown', title: 'OAuth scopes', body: 'scope grants', okf: { tags: ['oauth', 'auth'], description: 'scopes' } },14    { type: 'markdown', title: 'Béchamel', body: 'butter flour milk', okf: { tags: ['cooking'], description: 'sauce' } },15  ] },16];17 18test('tokens: lowercase 3+ alnum runs, stopwords dropped', () => {19  const t = tokens('The OAuth 2.0 Flow and the token');20  assert.ok(t.has('oauth') && t.has('flow') && t.has('token'));21  assert.ok(!t.has('the') && !t.has('and') && !t.has('2'));22});23 24test('overlapScore: 0 on disjoint/empty; symmetric; clamped', () => {25  assert.equal(overlapScore(tokens('alpha beta'), tokens('gamma delta')), 0);26  assert.equal(overlapScore(new Set(), tokens('alpha')), 0);27  const a = tokens('oauth token flow'), b = tokens('token flow lifetime');28  assert.ok(overlapScore(a, b) > 0 && overlapScore(a, b) <= 1);29  assert.equal(overlapScore(a, b), overlapScore(b, a));30});31 32test('candidateIndex: other views + okf concept tiles (ref viewId::i); active excluded; non-okf tiles are not candidates', () => {33  const c = candidateIndex(VIEWS, 'oauth');34  const refs = c.map(x => x.ref);35  assert.ok(!refs.includes('oauth'));36  assert.ok(refs.includes('tokens') && refs.includes('recipes') && refs.includes('kb'));37  assert.ok(refs.includes('kb::0') && refs.includes('kb::1'));       // concepts38  assert.equal(c.find(x => x.ref === 'kb::0').kind, 'concept');39  assert.ok(!refs.includes('tokens::0'));                             // plain tile ≠ candidate40});41 42test('rankBySubject v2: related ranks above unrelated; zero-score dropped; sorted desc; field weight shows', () => {43  const { scorer, ranked } = rankBySubject(VIEWS[0], VIEWS);44  assert.equal(scorer, 'lexical v2');45  const refs = ranked.map(r => r.ref);46  assert.ok(refs.indexOf('tokens') >= 0, 'token-overlapping view ranks');47  assert.ok(refs.indexOf('kb::0') >= 0, 'oauth concept ranks');48  assert.equal(refs[0], 'kb::0', 'title+tag match outranks a body-ish match (field weighting)');49  assert.ok(!refs.includes('recipes'), 'pasta does not rank for OAuth');   // zero-score dropped50  for (let i = 1; i < ranked.length; i++) assert.ok(ranked[i - 1].score >= ranked[i].score);51});52 53// ── scorer v2 specifics (field weights · IDF · the Unicode tokenizer) ──54test('tokenizer v2: 2-letter acronyms live, Cyrillic tokenizes, compound tech tokens survive whole', () => {55  const t = tokens('The AI UI runs on node.js with OAuth2 — Настройка памяти');56  assert.ok(t.has('ai') && t.has('ui'), 'acronyms no longer invisible');57  assert.ok(t.has('node.js'), 'compound tech token kept whole');58  assert.ok(t.has('oauth2'), 'alnum tech token kept');59  assert.ok(t.has('настройка') && t.has('памяти'), 'Cyrillic tokenizes instead of vanishing');60  assert.ok(!t.has('the') && !t.has('on'), 'stopwords (incl. 2-letter) still dropped');61});62 63test('field weighting: a TITLE match outranks the same term buried in a BODY', () => {64  const active = { id: 'a', title: 'Provenance', tesserae: [] };65  const views = [active,66    { id: 'in-title', title: 'Provenance ledger', tesserae: [] },67    { id: 'in-body', title: 'Notes', subtitle: 'provenance provenance provenance', tesserae: [] },68  ];69  const { ranked } = rankBySubject(active, views);70  assert.equal(ranked[0].ref, 'in-title');71});72 73test('IDF: a rare shared term outranks a term every candidate carries', () => {74  const active = { id: 'a', title: 'verdigris surface', tesserae: [] };75  const views = [active,76    { id: 'rare', title: 'verdigris surface', tesserae: [] },        // shares rare + common77    { id: 'common1', title: 'surface one', tesserae: [] },           // 'surface' is everywhere78    { id: 'common2', title: 'surface two', tesserae: [] },79    { id: 'common3', title: 'surface three', tesserae: [] },80  ];81  const { ranked } = rankBySubject(active, views);82  assert.equal(ranked[0].ref, 'rare');83  assert.ok(ranked[0].score > ranked[1].score * 1.5, 'the rare-term match wins decisively, not marginally');84});85 86test('applyRerank: rescore-only — unknown refs ignored, no rows added/removed, clamped, resorted', () => {87  const ranked = [{ kind: 'view', ref: 'a', title: 'A', score: 0.9 }, { kind: 'view', ref: 'b', title: 'B', score: 0.5 }];88  const out = applyRerank(ranked, [{ ref: 'b', score: 5 }, { ref: 'ghost', score: 1 }, { ref: 'a', score: 0.1 }]);89  assert.equal(out.length, 2);                        // no minted rows90  assert.equal(out[0].ref, 'b');                      // clamped to 1 → first91  assert.equal(out[0].score, 1);92  assert.equal(out[1].score, 0.1);93  assert.ok(!out.some(r => r.ref === 'ghost'));94});95 96test('the demo showcase lights the rail: every multi-view example ranks ≥1 related row for its first view', () => {97  for (const task of TASKS) {98    const views = task.overlay.views || [];99    if (views.length < 2) continue;100    const { ranked } = rankBySubject(views[0], views);101    assert.ok(ranked.length >= 1, `example "${task.id}" leaves the rail dark`);102  }103});104 105test('railHtml: caps at RAIL_K with a +N more toggle; escapes hostile titles; empty → hidden', () => {106  const many = { scorer: 'overlap', ranked: Array.from({ length: RAIL_K + 3 }, (_, i) => ({ kind: 'view', ref: 'v' + i, title: 'V' + i, score: 1 - i * 0.05 })) };107  const html = railHtml(many, {});108  assert.match(html, /+3 more/);109  assert.equal((html.match(/srail-row/g) || []).length, RAIL_K);110  assert.match(railHtml(many, { expanded: true }), /− less/);111  assert.equal(railHtml({ scorer: 'overlap', ranked: [] }, {}), '');112  const evil = railRowHtml({ kind: 'view', ref: 'x', title: '<img src=x onerror=alert(1)>', score: 0.5 });113  assert.doesNotMatch(evil, /<img src=x/);114  assert.match(evil, /&lt;img/);115});116