import { test } from 'brittle'
import { resolve, dirname } from 'path'
import { fileURLToPath } from 'url'
import { scan } from '../lib/scanner.js'
import { parseSpecs } from '../lib/doc-parser.js'
import { validate } from '../lib/validator.js'
const HERE = dirname(fileURLToPath(import.meta.url))
const HYPER_FLEEKY = resolve(HERE, '../../')
const SPEC_DIR = resolve(HYPER_FLEEKY, 'pear-git-v3/spec')
test('doc-parser: parse hyper-orgs module spec', (t) => {
const specs = parseSpecs(SPEC_DIR)
t.ok(specs.modules['hyper-orgs'], 'found hyper-orgs spec')
const spec = specs.modules['hyper-orgs']
// Parser invariants — check that fields are extracted and well-typed.
// Don't pin to specific spec-snapshot values; editing the spec must
// not break codetree's own tests (invariant I1).
t.ok(typeof spec.location === 'string' && spec.location.length > 0, 'location is a non-empty string')
t.ok(spec.location.includes('hyper-orgs'), 'location mentions the module name')
t.ok(typeof spec.testCount === 'number' && spec.testCount >= 0, 'testCount is a number')
t.ok(/^\d{4}-\d{2}-\d{2}$/.test(spec.sourceVerified), 'sourceVerified is a yyyy-mm-dd date string')
t.ok(typeof spec.fileLOC['hyper-orgs.js'] === 'number', 'LOC claim for hyper-orgs.js is a number')
t.ok(spec.fileLOC['hyper-orgs.js'] > 0, 'LOC claim for hyper-orgs.js is positive')
t.ok(typeof spec.fileLOC['schema.js'] === 'number', 'LOC claim for schema.js is a number')
// Semantic invariants — stable facts about hyper-orgs as a module.
t.ok(spec.description, 'has description')
t.ok(spec.dependencies.includes('hyper-rooms'), 'dependency on hyper-rooms')
t.ok(spec.dependents.includes('pear-git-v3'), 'dependent pear-git-v3')
t.ok(spec.claimedExports.includes('HyperOrgs'), 'claims HyperOrgs export')
t.ok(spec.claimedMethods.length > 10, spec.claimedMethods.length + ' claimed methods')
t.ok(spec.claimedMethods.includes('create'), 'claims create method')
t.ok(spec.claimedMethods.includes('checkAccess'), 'claims checkAccess method')
})
test('doc-parser: parse SYSTEM.md', (t) => {
const specs = parseSpecs(SPEC_DIR)
t.ok(specs.system, 'system spec parsed')
// Parser invariant — a date was extracted. Don't pin to one value.
t.ok(/^\d{4}-\d{2}-\d{2}$/.test(specs.system.lastVerified), 'lastVerified is yyyy-mm-dd')
t.ok(specs.system.modules.length > 5, specs.system.modules.length + ' modules documented')
const orgsEntry = specs.system.modules.find(m => m.name === 'hyper-orgs')
t.ok(orgsEntry, 'hyper-orgs in system module list')
t.ok(orgsEntry.location.includes('hyper-orgs'), 'location includes hyper-orgs')
t.ok(orgsEntry.purpose, 'has purpose')
// Built items can be empty in theory if a project hasn't shipped anything,
// so only require >= 0. missingItems similarly — a well-maintained spec
// could have neither. Both arrays must exist; counts shouldn't be pinned.
t.ok(Array.isArray(specs.system.builtItems), 'builtItems is an array')
t.ok(Array.isArray(specs.system.missingItems), 'missingItems is an array')
})
test('validator: hyper-orgs spec vs code', async (t) => {
const tree = await scan(HYPER_FLEEKY, { maxDepth: 1 })
const specs = parseSpecs(SPEC_DIR)
const report = validate(tree, specs)
t.ok(report.modules['hyper-orgs'], 'has hyper-orgs validation')
const modReport = report.modules['hyper-orgs']
t.ok(modReport.checks.length > 0, modReport.checks.length + ' checks performed')
// Print the report for inspection
for (const check of modReport.checks) {
const icon = check.status === 'pass' ? 'PASS' : check.status === 'fail' ? 'FAIL' : 'WARN'
t.comment(icon + ': ' + check.claim + (check.detail ? ' — ' + check.detail : ''))
}
// Should have some passes (the module exists, deps are correct)
const passes = modReport.checks.filter(c => c.status === 'pass')
t.ok(passes.length > 0, passes.length + ' checks passed')
})
test('validator: system spec vs code', async (t) => {
const tree = await scan(HYPER_FLEEKY, { maxDepth: 1 })
const specs = parseSpecs(SPEC_DIR)
const report = validate(tree, specs)
t.ok(report.system, 'has system validation')
t.ok(report.system.checks.length > 0, report.system.checks.length + ' system checks')
for (const check of report.system.checks) {
const icon = check.status === 'pass' ? 'PASS' : check.status === 'fail' ? 'FAIL' : 'WARN'
t.comment(icon + ': ' + check.claim + (check.detail ? ' — ' + check.detail : ''))
}
})
test('validator: summary counts', async (t) => {
const tree = await scan(HYPER_FLEEKY, { maxDepth: 1 })
const specs = parseSpecs(SPEC_DIR)
const report = validate(tree, specs)
t.comment('Total: ' + report.summary.total + ' checks')
t.comment('Pass: ' + report.summary.pass)
t.comment('Fail: ' + report.summary.fail)
t.comment('Warn: ' + report.summary.warn)
t.ok(report.summary.total > 0, 'has checks')
t.ok(report.summary.pass > 0, 'some passed')
})