mirror of
https://github.com/deepseek-ai/deepseek-harness
synced 2026-08-15 21:04:50 +00:00
101 lines
5.5 KiB
JavaScript
101 lines
5.5 KiB
JavaScript
// Test locks for the fix/usability-p0-batch cycle. Each locks one of the
|
|
// P0 findings from review-usability: fixture id/catalog match, fusion
|
|
// loadFixture idempotency, rubric-cell-jump listener presence, artifact
|
|
// blob banner on missing-blob chains, artifact seed API surface.
|
|
|
|
'use strict'
|
|
|
|
const test = require('node:test')
|
|
const assert = require('node:assert')
|
|
const fs = require('fs')
|
|
const path = require('path')
|
|
|
|
const RENDERER = path.resolve(__dirname, '..', 'src', 'renderer')
|
|
|
|
// -- P0-3 fusion loadFixture is idempotent ---------------------------------
|
|
test('rubric-fusion loadFixture is idempotent by fixture identity', () => {
|
|
const fusion = require(path.join(RENDERER, 'rubric-fusion-model.js'))
|
|
const store = fusion.create()
|
|
const fixture = {
|
|
rubrics: [{ id: 'r1', name: 'R1', dims: [{ id: 'd1', label: 'D1', type: 'continuous', min: 0, max: 1 }] }],
|
|
events: [{ ts: 1, rubricId: 'r1', dimId: 'd1', sessionId: 's', turnId: 't', score: 0.5 }],
|
|
}
|
|
const first = store.loadFixture(fixture)
|
|
const second = store.loadFixture(fixture)
|
|
const third = store.loadFixture(fixture)
|
|
assert.strictEqual(first.events, 1, 'first load registers events')
|
|
assert.strictEqual(second.events, 0, 'second load is a no-op')
|
|
assert.ok(second.deduped, 'second load reports deduped flag')
|
|
assert.strictEqual(third.events, 0, 'third load is a no-op')
|
|
assert.strictEqual(store.listEvents({}).length, 1, 'event count stays at 1 after 3 loads')
|
|
})
|
|
|
|
// -- P0-1 fixture rubric ids match catalog ids -----------------------------
|
|
test('rubric-fusion fixture covers every catalog rubric id', () => {
|
|
const seedSrc = fs.readFileSync(path.join(RENDERER, 'rubrics-seed.js'), 'utf8')
|
|
// Parse rubric names out of the inlined SKILL.md blobs.
|
|
const catalogIds = []
|
|
for (const m of seedSrc.matchAll(/---\\nname: ([\w-]+)/g)) catalogIds.push(m[1])
|
|
assert.ok(catalogIds.length >= 4, 'catalog seed has at least 4 rubrics')
|
|
const fixtureJson = JSON.parse(fs.readFileSync(
|
|
path.join(__dirname, '..', 'docs', 'rubric-fusion-fixture.json'), 'utf8'))
|
|
const fixtureIds = new Set((fixtureJson.rubrics || []).map(r => r.id))
|
|
const missing = catalogIds.filter(id => !fixtureIds.has(id))
|
|
assert.deepStrictEqual(missing, [],
|
|
'every catalog rubric id has a matching fixture rubric def: missing=' + missing.join(','))
|
|
})
|
|
|
|
// -- P0-2 tracing-page registers rubric-cell-jump listener -----------------
|
|
test('tracing-page wires dsh:rubric-cell-jump → openDrill', () => {
|
|
const src = fs.readFileSync(path.join(RENDERER, 'tracing-page.js'), 'utf8')
|
|
assert.match(src, /addEventListener\(['"]dsh:rubric-cell-jump['"]/,
|
|
'tracing-page must register dsh:rubric-cell-jump listener')
|
|
assert.match(src, /openDrill\(detail\.sessionId\)/,
|
|
'listener must call openDrill(detail.sessionId) so the jump lands')
|
|
})
|
|
|
|
// -- P0-5 artifact-evolution shows blob-missing banner ---------------------
|
|
// Static source-string lock (matches artifact-evolution-board.test.js
|
|
// style for the artifacts.js IIFE — the module lives inside a closure
|
|
// and we already prove the runtime path there. Here we just prove the
|
|
// blob-missing banner path exists and gates on the right predicate).
|
|
test('renderEvolution emits blob-missing banner when any hop lacks blob', () => {
|
|
const src = fs.readFileSync(path.join(RENDERER, 'artifacts-board.js'), 'utf8')
|
|
assert.match(src, /artifact-evolution-banner/, 'source must reference the banner class')
|
|
assert.match(src, /missingBlobs\s*=\s*chain\.some/,
|
|
'banner must gate on chain.some(missing-blob)')
|
|
const css = fs.readFileSync(path.join(RENDERER, 'style.css'), 'utf8')
|
|
assert.match(css, /\.artifact-evolution-banner\s*\{/,
|
|
'style.css must style the new .artifact-evolution-banner selector')
|
|
})
|
|
|
|
// -- P0-4 artifact seed fixture is inline-reachable ------------------------
|
|
test('artifact-board-seed.js inlines fixture on window.__dshArtifactBoardSeed', () => {
|
|
const src = fs.readFileSync(path.join(RENDERER, 'artifact-board-seed.js'), 'utf8')
|
|
assert.match(src, /window\.__dshArtifactBoardSeed\s*=/, 'seed must attach to window')
|
|
// Sanity — has multiple kinds and multiple versions.
|
|
assert.match(src, /"kind":\s*"svg"/, 'seed must include an svg artifact')
|
|
assert.match(src, /"kind":\s*"html"/, 'seed must include an html artifact')
|
|
assert.match(src, /"version":\s*3/, 'seed must include multi-version chain (v3+)')
|
|
// Wired into index.html so the browser actually loads it.
|
|
const html = fs.readFileSync(path.join(RENDERER, 'index.html'), 'utf8')
|
|
assert.match(html, /artifact-board-seed\.js/, 'index.html must include artifact-board-seed.js')
|
|
})
|
|
|
|
// -- P1 Lane A: graph binds click on fork nodes + user nodes ---------------
|
|
test('chat-session-graph binds click on actionable non-turn nodes', () => {
|
|
const src = fs.readFileSync(path.join(RENDERER, 'chat-session-graph.js'), 'utf8')
|
|
assert.match(src, /kind === ['"]fork['"] && node\.childSessionId/,
|
|
'graph must bind click on fork nodes when childSessionId is present')
|
|
assert.match(src, /kind === ['"]user['"] && node\.seq/,
|
|
'graph must bind click on user nodes when seq is present')
|
|
})
|
|
|
|
// -- P1 Lane A: renderer stores chatSelectedTurnId + wires user rows -------
|
|
test('renderer propagates chatSelectedTurnId and wires user rows', () => {
|
|
const src = fs.readFileSync(path.join(RENDERER, 'renderer.js'), 'utf8')
|
|
assert.match(src, /state\.chatSelectedTurnId/, 'renderer must store chatSelectedTurnId in state')
|
|
assert.match(src, /row\.kind === ['"]user['"] && row\.seq/,
|
|
'renderer drawer onSelect must handle user rows (kind==="user" && seq)')
|
|
})
|