OverviewFilesCommitsIssuesDocsReleasesPeers

codetree / test / validator.test.js

108 lines · javascript ref:
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')
})