Files
config-center/__tests__/validate-data-sources.test.mjs

68 lines
4.3 KiB
JavaScript

import { test } from 'node:test'
import assert from 'node:assert/strict'
import { cpSync, mkdtempSync, readFileSync, writeFileSync, rmSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { fileURLToPath } from 'node:url'
import { spawnSync } from 'node:child_process'
import { validateDataSources } from '../scripts/validate-data-sources.mjs'
const repo = fileURLToPath(new URL('../', import.meta.url))
function fixture(run) {
const root = mkdtempSync(join(tmpdir(), 'data-contract-'))
for (const path of ['docs/data-sources', 'api-providers', 'compute/providers']) cpSync(join(repo, path), join(root, path), { recursive: true })
for (const file of ['pricing.json', 'service-map.json']) cpSync(join(repo, 'compute', file), join(root, 'compute', file))
try { run(root) } finally { rmSync(root, { recursive: true, force: true }) }
}
function change(root, path, mutate) {
const file = join(root, path), data = JSON.parse(readFileSync(file, 'utf8'))
mutate(data)
writeFileSync(file, JSON.stringify(data))
}
test('non-model source records cover current policy and API configuration', () => {
assert.deepEqual(validateDataSources(repo), { errors: [], records: 8 })
})
test('API authentication and result mapping changes require contract review', () => fixture(root => {
change(root, 'api-providers/web-search/tavily.json', data => { data.auth.type = 'none'; data.response.resultsPath = 'wrong' })
assert.match(validateDataSources(root).errors.join('\n'), /contract drift: auth/)
}))
test('new API provider must have its own source record', () => fixture(root => {
cpSync(join(root, 'api-providers/web-search/tavily.json'), join(root, 'api-providers/web-search/new.json'))
assert.match(validateDataSources(root).errors.join('\n'), /missing source record: api-providers\/web-search\/new.json/)
}))
test('pricing assumptions cannot drift without policy record update', () => fixture(root => {
change(root, 'compute/pricing.json', data => { data.usdToCny = 8 })
assert.match(validateDataSources(root).errors.join('\n'), /contract drift: usdToCny/)
}))
test('default mappings cannot reference absent models', () => fixture(root => {
change(root, 'compute/service-map.json', data => { data.chat.modelName = 'nonexistent' })
assert.match(validateDataSources(root).errors.join('\n'), /unresolved route chat/)
}))
test('changing a default to an existing model still requires a policy record update', () => fixture(root => {
change(root, 'compute/service-map.json', data => { data.chat.modelName = 'gpt-5.5' })
assert.match(validateDataSources(root).errors.join('\n'), /routing policy drift/)
}))
test('third-party hosts cannot be presented as official evidence', () => fixture(root => {
const path = join(root, 'docs/data-sources/api-providers/tavily.md')
writeFileSync(path, readFileSync(path, 'utf8').replace('https://docs.tavily.com/', 'https://third-party.example/'))
assert.match(validateDataSources(root).errors.join('\n'), /unapproved source URL/)
}))
test('an official entry cannot redirect verification to a third-party site', () => fixture(root => {
const path = join(root, 'docs/data-sources/api-providers/tavily.md')
writeFileSync(path, readFileSync(path, 'utf8').replace('"resolvedUrl": "https://docs.tavily.com/', '"resolvedUrl": "https://third-party.example/'))
assert.match(validateDataSources(root).errors.join('\n'), /unapproved resolved source URL/)
}))
test('a page shell cannot prove verified API fields', () => fixture(root => {
const path = join(root, 'docs/data-sources/api-providers/tavily.md')
writeFileSync(path, readFileSync(path, 'utf8').replace('"retrieval": "fetched"', '"retrieval": "shell"'))
assert.match(validateDataSources(root).errors.join('\n'), /verified field without fetched source/)
}))
test('retired ZenMux tools fail before reading or writing data', () => {
for (const entry of ['batch_update_model_jsons', 'apply_all_updates_and_build_diff_report', 'generate_current_audit_artifacts', 'generate_field_tables_per_json']) {
const result = spawnSync(process.execPath, [join(repo, 'tools', `${entry}.mjs`)], { encoding: 'utf8', env: { ...process.env, CONFIG_CENTER_ROOT: '/nonexistent-root', ZENMUX_FILE: '/nonexistent-snapshot' } })
assert.equal(result.status, 1)
assert.match(result.stderr, /retired/)
assert.doesNotMatch(result.stderr, /ENOENT/)
}
})