170 lines
5.1 KiB
JavaScript
170 lines
5.1 KiB
JavaScript
import assert from 'node:assert/strict'
|
|
import test from 'node:test'
|
|
import { apply } from '../bundle/index.js'
|
|
import { collectSelfCheck } from '../bundle/lib/selfcheck.js'
|
|
import { registerCheckCommand } from '../bundle/command-check.js'
|
|
|
|
const FEATURES = ['guards', 'bootstrap', 'commands', 'workingDirectory']
|
|
|
|
/** A ctx-like with every service the bundle depends on. */
|
|
function fullCtx() {
|
|
return {
|
|
tools: { guard() {} },
|
|
on() {},
|
|
inject() {},
|
|
workingDirectory: { get() {} },
|
|
}
|
|
}
|
|
|
|
test('collectSelfCheck ok on full ctx-like', () => {
|
|
const report = collectSelfCheck(fullCtx())
|
|
assert.deepEqual(
|
|
report.map((entry) => entry.feature),
|
|
FEATURES,
|
|
)
|
|
for (const entry of report) {
|
|
assert.equal(entry.ok, true, `${entry.feature} ok`)
|
|
assert.equal(typeof entry.detail, 'string', `${entry.feature} detail is a string`)
|
|
assert.ok(entry.detail.length > 0, `${entry.feature} detail is non-empty`)
|
|
}
|
|
})
|
|
|
|
test('collectSelfCheck flags missing tools.guard', () => {
|
|
const ctx = fullCtx()
|
|
ctx.tools = {}
|
|
const report = collectSelfCheck(ctx)
|
|
|
|
const guards = report.find((entry) => entry.feature === 'guards')
|
|
assert.equal(guards.ok, false)
|
|
assert.match(guards.detail, /tools\.guard/)
|
|
|
|
for (const entry of report.filter((entry) => entry.feature !== 'guards')) {
|
|
assert.equal(entry.ok, true, `${entry.feature} stays ok`)
|
|
}
|
|
})
|
|
|
|
test('collectSelfCheck flags missing on()', () => {
|
|
const ctx = fullCtx()
|
|
ctx.on = undefined
|
|
const report = collectSelfCheck(ctx)
|
|
|
|
const bootstrap = report.find((entry) => entry.feature === 'bootstrap')
|
|
assert.equal(bootstrap.ok, false)
|
|
|
|
for (const entry of report.filter((entry) => entry.feature !== 'bootstrap')) {
|
|
assert.equal(entry.ok, true, `${entry.feature} stays ok`)
|
|
}
|
|
})
|
|
|
|
test('collectSelfCheck skips features disabled by config', () => {
|
|
const report = collectSelfCheck(fullCtx(), {
|
|
guards: false,
|
|
bootstrap: false,
|
|
roleCommand: false,
|
|
checkCommand: false,
|
|
})
|
|
assert.deepEqual(
|
|
report.map((entry) => entry.feature),
|
|
['workingDirectory'],
|
|
)
|
|
})
|
|
|
|
test('collectSelfCheck does not flag a disabled feature that is also missing', () => {
|
|
const ctx = fullCtx()
|
|
ctx.tools = {}
|
|
ctx.on = undefined
|
|
const report = collectSelfCheck(ctx, { guards: false, bootstrap: false })
|
|
assert.deepEqual(
|
|
report.map((entry) => entry.feature),
|
|
['commands', 'workingDirectory'],
|
|
)
|
|
assert.ok(report.every((entry) => entry.ok), 'nothing not-ok in the report')
|
|
|
|
const noInject = fullCtx()
|
|
noInject.inject = undefined
|
|
const commandsOff = collectSelfCheck(noInject, { roleCommand: false, checkCommand: false })
|
|
assert.ok(!commandsOff.some((entry) => entry.feature === 'commands'), 'commands skipped')
|
|
assert.ok(commandsOff.every((entry) => entry.ok), 'missing inject not flagged')
|
|
|
|
const roleOnly = collectSelfCheck(noInject, { roleCommand: false })
|
|
assert.ok(roleOnly.some((entry) => entry.feature === 'commands'), 'commands still checked')
|
|
assert.equal(
|
|
roleOnly.find((entry) => entry.feature === 'commands').ok,
|
|
false,
|
|
'checkCommand alone still needs ctx.inject',
|
|
)
|
|
})
|
|
|
|
test('registerCheckCommand builds report text', async () => {
|
|
let captured
|
|
const commands = {
|
|
register(def) {
|
|
captured = def
|
|
},
|
|
}
|
|
|
|
registerCheckCommand({ commands, collect: () => collectSelfCheck(fullCtx()) })
|
|
assert.equal(captured.name, 'dsh-harness-check')
|
|
|
|
const okResult = await captured.handler({})
|
|
assert.match(okResult.text, /guards: ok/)
|
|
assert.match(okResult.text, /all ok/)
|
|
|
|
const brokenCtx = fullCtx()
|
|
brokenCtx.tools = {}
|
|
registerCheckCommand({ commands, collect: () => collectSelfCheck(brokenCtx) })
|
|
|
|
const badResult = await captured.handler({})
|
|
assert.match(badResult.text, /guards: not-ok/)
|
|
assert.match(badResult.text, /problem/)
|
|
})
|
|
|
|
/** Logger fake that interpolates `%s` and records error lines. */
|
|
function recordingLogger(errors) {
|
|
return {
|
|
info() {},
|
|
warn() {},
|
|
error(fmt, ...args) {
|
|
const line = fmt.replace(/%s/g, () => String(args.shift() ?? ''))
|
|
errors.push(line)
|
|
},
|
|
}
|
|
}
|
|
|
|
test('apply does not throw when ctx.on and ctx.inject are missing', () => {
|
|
const calls = { guard: 0 }
|
|
const errors = []
|
|
const ctx = {
|
|
tools: { guard() { calls.guard += 1 } },
|
|
workingDirectory: { get() {} },
|
|
logger: recordingLogger(errors),
|
|
}
|
|
|
|
assert.doesNotThrow(() => apply(ctx))
|
|
assert.equal(calls.guard, 1, 'guards feature stays wired')
|
|
assert.ok(
|
|
errors.some((line) => line.includes('self-check: bootstrap not-ok')),
|
|
'bootstrap not-ok is logged',
|
|
)
|
|
assert.ok(
|
|
errors.some((line) => line.includes('self-check: commands not-ok')),
|
|
'commands not-ok is logged',
|
|
)
|
|
})
|
|
|
|
test('apply wires guards, bootstrap, and commands with a full ctx', () => {
|
|
const calls = { guard: 0, on: 0, inject: 0 }
|
|
const ctx = {
|
|
tools: { guard() { calls.guard += 1 } },
|
|
on() { calls.on += 1 },
|
|
inject() { calls.inject += 1 },
|
|
workingDirectory: { get() {} },
|
|
logger: { info() {}, warn() {}, error() {} },
|
|
}
|
|
|
|
assert.doesNotThrow(() => apply(ctx))
|
|
assert.equal(calls.guard, 1, 'guards wired')
|
|
assert.equal(calls.on, 1, 'bootstrap wired')
|
|
assert.equal(calls.inject, 1, 'commands wired')
|
|
})
|