55 lines
5.1 KiB
JavaScript
55 lines
5.1 KiB
JavaScript
import { createHash } from 'node:crypto'
|
|
import { readFile, stat } from 'node:fs/promises'
|
|
import { resolve } from 'node:path'
|
|
import { FREECAD_NAMING_CALLBACKS, FREECAD_NAMING_OPERATIONS } from './freecad-naming-production-matrix.mjs'
|
|
|
|
const root = resolve(new URL('..', import.meta.url).pathname)
|
|
const fail = (message) => { throw new Error(`FreeCAD production naming verification failed: ${message}`) }
|
|
const checkReport = async (reportName, runtime, artifactRoot) => {
|
|
const report = JSON.parse(await readFile(resolve(root, 'config', reportName), 'utf8'))
|
|
if (report.status !== 'pass' || report.runtime !== runtime || report.implementation !== 'freecad-linked' || report.productionWorkerLinked !== true || report.systemExact !== false) fail(`${reportName} has an invalid production boundary.`)
|
|
if (JSON.stringify(report.callbacks) !== JSON.stringify(FREECAD_NAMING_CALLBACKS)) fail(`${reportName} callback list is incomplete.`)
|
|
if (JSON.stringify(report.operations) !== JSON.stringify(FREECAD_NAMING_OPERATIONS) || report.cases?.length !== FREECAD_NAMING_OPERATIONS.length) fail(`${reportName} operation matrix is incomplete.`)
|
|
if (report.cases.some((entry, index) => {
|
|
const native = entry.selectedRecords > 0
|
|
return entry.operation !== FREECAD_NAMING_OPERATIONS[index]
|
|
|| entry.status !== 'pass'
|
|
|| entry.provenanceResults < 1
|
|
|| entry.provenanceResults !== entry.selectedRecords + entry.ambiguousResults
|
|
|| entry.externallySourcedRecords < 1
|
|
|| entry.internalRecordsExcluded < 0
|
|
|| entry.externallySourcedRecords + entry.internalRecordsExcluded !== entry.historyRecords
|
|
|| entry.duplicateRecordsCollapsed < 0
|
|
|| entry.modifiedRelationPrecedence < 0
|
|
|| entry.callbackExecuted !== native
|
|
|| entry.mappedNames !== entry.selectedRecords
|
|
|| (native && (entry.stringHasherEntries < 0 || entry.elementMaps < 1 || !['native-evidence', 'mixed-native-and-ambiguous'].includes(entry.namingStatus)))
|
|
|| (!native && (entry.ambiguousResults < 1 || entry.stringHasherEntries !== 0 || entry.elementMaps !== 0 || entry.namingStatus !== 'ambiguous'))
|
|
})) fail(`${reportName} contains an invalid operation result.`)
|
|
const callbackCases = report.cases.filter(({ callbackExecuted }) => callbackExecuted).length
|
|
const ambiguityOnlyCases = report.cases.filter(({ namingStatus }) => namingStatus === 'ambiguous').length
|
|
if (report.callbackExecutedCases !== callbackCases || report.ambiguityOnlyCases !== ambiguityOnlyCases || callbackCases < 1 || callbackCases + ambiguityOnlyCases !== FREECAD_NAMING_OPERATIONS.length) fail(`${reportName} has invalid callback/ambiguity partitioning.`)
|
|
if (report.isomorphicCorpus?.status !== 'pass' || report.isomorphicCorpus.fixture !== 'coincident-equal-boxes' || report.isomorphicCorpus.uniqueResults !== 0 || report.isomorphicCorpus.ambiguousResults < 1 || report.isomorphicCorpus.arrayOrderFallbackUsed !== false || report.isomorphicCorpus.candidateSets?.some(({ candidates }) => candidates.length < 2)) fail(`${reportName} is missing its symmetric native-provenance corpus.`)
|
|
if (report.invalidHistoryRejected !== true || report.chainedStage?.mappedNames < 1 || report.chainedStage?.stringHasherEntries < 2) fail(`${reportName} is missing chained or fail-closed evidence.`)
|
|
for (const artifact of report.artifacts ?? []) {
|
|
const path = resolve(root, artifactRoot, artifact.name)
|
|
const [content, bytes] = await Promise.all([readFile(path), stat(path).then(({ size }) => size)])
|
|
if (bytes !== artifact.bytes || createHash('sha256').update(content).digest('hex') !== artifact.sha256) fail(`${reportName} is stale for ${artifact.name}.`)
|
|
}
|
|
if (report.artifacts?.length !== 3) fail(`${reportName} must lock JS, WASM and DATA.`)
|
|
const harnessPath = resolve(root, report.harness?.path ?? '')
|
|
const [harnessContent, harnessBytes] = await Promise.all([readFile(harnessPath), stat(harnessPath).then(({ size }) => size)])
|
|
if (report.harness?.path !== 'scripts/freecad-naming-production-matrix.mjs' || harnessBytes !== report.harness.bytes || createHash('sha256').update(harnessContent).digest('hex') !== report.harness.sha256) fail(`${reportName} is stale for the shared production harness.`)
|
|
return { runtime, cases: report.cases.length, generatedAt: report.generatedAt }
|
|
}
|
|
|
|
const requestedRuntime = process.argv.find((argument) => argument.startsWith('--runtime='))?.slice('--runtime='.length) ?? 'all'
|
|
if (!['all', 'node', 'chrome'].includes(requestedRuntime)) fail(`unsupported runtime ${requestedRuntime}.`)
|
|
const specs = [
|
|
['freecad-naming-production-verification.json', 'node', 'native/occt-history/dist'],
|
|
['chrome-freecad-naming-production-verification.json', 'chrome', 'public/native/occt-history'],
|
|
].filter(([, runtime]) => requestedRuntime === 'all' || runtime === requestedRuntime)
|
|
const reports = []
|
|
for (const [reportName, runtime, artifactRoot] of specs) reports.push(await checkReport(reportName, runtime, artifactRoot))
|
|
console.log(JSON.stringify({ status: 'freecad-production-naming-pass', implementation: 'freecad-linked', callbacks: FREECAD_NAMING_CALLBACKS, operations: FREECAD_NAMING_OPERATIONS.length, reports, systemExact: false }, null, 2))
|