78 lines
7.6 KiB
JavaScript
78 lines
7.6 KiB
JavaScript
import { readFile } from 'node:fs/promises'
|
|
import { resolve } from 'node:path'
|
|
import { elementMap2SemanticDigest, parseElementMap2, validateElementMap2, writeElementMap2 } from '../src/facade/elementMap2.ts'
|
|
import { validateNativeNamingEvidence } from '../src/facade/nativeNamingEvidence.ts'
|
|
import { migrateStringHasherSchema, parseStringHasherTable, validateStringHasherTable, writeStringHasherTable } from '../src/facade/stringHasher.ts'
|
|
|
|
const root = resolve(new URL('..', import.meta.url).pathname)
|
|
const report = JSON.parse(await readFile(resolve(root, 'config/freecad-composite-history-elementmap-oracle.json'), 'utf8'))
|
|
if (report.schemaVersion !== 1 || report.baselineId !== 'freecad-1.1.1-composite-history-elementmap2' || report.freecadVersion !== '1.1.1' || report.status !== 'pass' || report.gitCommit !== '0108fd4b4850cc46e625b60e53cea7a7bbe69f8d') throw new Error('FreeCAD composite history ElementMap2 oracle baseline is invalid.')
|
|
if (report.summary?.cases !== 30 || report.summary?.passed !== 30 || report.summary?.failed !== 0 || !Array.isArray(report.cases) || report.cases.length !== 30) throw new Error('FreeCAD composite oracle must contain exactly 30 passing cases.')
|
|
let parsedResources = 0
|
|
let parsedStringHasherResources = 0
|
|
let namingEvidenceStages = 0
|
|
let namingEvidenceMissing = 0
|
|
let namingEvidenceBoundaryViolations = 0
|
|
let nativeEvidenceValidatedStages = 0
|
|
let nativeMappedNameStages = 0
|
|
let nativeIndexedNameStages = 0
|
|
let privateTokenEvidenceCompleteStages = 0
|
|
let internalBuilderEvidenceStages = 0
|
|
let internalBuilderEvidenceMissingStages = 0
|
|
for (const fixture of report.cases) {
|
|
if (fixture.status !== 'pass' || !fixture.finalObject || !fixture.stages?.length) throw new Error(`FreeCAD composite fixture ${fixture.id} has no valid feature history stages.`)
|
|
if (fixture.roundtripNameDrift !== 0) throw new Error(`FreeCAD composite fixture ${fixture.id} changed mapped names after FreeCAD reopen.`)
|
|
const names = new Set()
|
|
let historyStages = 0
|
|
for (const stage of fixture.stages) {
|
|
if (!stage.shape?.valid || !stage.names?.length) throw new Error(`FreeCAD composite fixture ${fixture.id}/${stage.name} has invalid topology.`)
|
|
namingEvidenceStages += 1
|
|
if (!['final-shape-only', 'opaque-preserved', 'native-evidence', 'ambiguous', 'missing'].includes(stage.namingEvidenceStatus)) namingEvidenceMissing += 1
|
|
if (stage.namingEvidenceStatus === 'opaque-preserved' && !fixture.stringHasherResource?.text) namingEvidenceBoundaryViolations += 1
|
|
if (!stage.nativeEvidence || stage.nativeEvidence.stageId !== stage.name || stage.nativeEvidence.resultObjectId !== stage.name) throw new Error(`FreeCAD composite fixture ${fixture.id}/${stage.name} has incomplete native naming evidence identity.`)
|
|
if (stage.nativeEvidence.status !== stage.namingEvidenceStatus) throw new Error(`FreeCAD composite fixture ${fixture.id}/${stage.name} has inconsistent naming evidence status.`)
|
|
const nativeEvidence = { ...stage.nativeEvidence }
|
|
if (fixture.stringHasherResource?.text) nativeEvidence.stringHasher = migrateStringHasherSchema(parseStringHasherTable(fixture.stringHasherResource.text))
|
|
const nativeValidation = validateNativeNamingEvidence(nativeEvidence)
|
|
if (!nativeValidation.valid) throw new Error(`FreeCAD native naming evidence validation failed for ${fixture.id}/${stage.name}: ${nativeValidation.issues[0].path}: ${nativeValidation.issues[0].message}`)
|
|
nativeEvidenceValidatedStages += 1
|
|
if (stage.nativeEvidence.mappedNameApiEntries > 0) nativeMappedNameStages += 1
|
|
if (stage.nativeEvidence.indexedNameApiEntries > 0) nativeIndexedNameStages += 1
|
|
if (stage.nativeEvidence.privateTokenEvidenceComplete === true) {
|
|
if (stage.nativeEvidence.mappedNameApiEntries !== stage.nativeEvidence.mappedNames.length) throw new Error(`FreeCAD composite fixture ${fixture.id}/${stage.name} claims incomplete private token evidence as complete.`)
|
|
privateTokenEvidenceCompleteStages += 1
|
|
}
|
|
if (stage.nativeEvidence.status === 'native-evidence' && stage.nativeEvidence.internalBuilderEvidence === true) internalBuilderEvidenceStages += 1
|
|
else if (stage.nativeEvidence.status === 'native-evidence' && stage.nativeEvidence.internalBuilderEvidence !== true && /^(Part::(Fuse|Cut|Common|Extrusion|Revolution|Loft|Sweep|Fillet|Chamfer)|PartDesign::(Pad|Pocket|Revolution|Groove|AdditiveLoft|SubtractiveLoft|AdditivePipe|SubtractivePipe|Fillet|Chamfer|Draft|Thickness|Mirrored|MultiTransform|LinearPattern|PolarPattern|Hole))$/.test(stage.typeId || '')) internalBuilderEvidenceMissingStages += 1
|
|
const stageHasHistory = stage.names.some((entry) => Array.isArray(entry.history) && entry.history.length > 0)
|
|
if (stageHasHistory) historyStages += 1
|
|
for (const entry of stage.names) {
|
|
if (names.has(`${stage.name}:${entry.name}`)) throw new Error(`Duplicate ElementMap name ${fixture.id}/${stage.name}/${entry.name}.`)
|
|
names.add(`${stage.name}:${entry.name}`)
|
|
}
|
|
}
|
|
if (historyStages === 0) throw new Error(`Missing FreeCAD getElementHistory chain for ${fixture.id}.`)
|
|
if (fixture.stringHasherResource?.text !== null && fixture.stringHasherResource?.text !== undefined) {
|
|
const table = migrateStringHasherSchema(parseStringHasherTable(fixture.stringHasherResource.text))
|
|
const validation = validateStringHasherTable(table)
|
|
if (!validation.valid) throw new Error(`StringHasher semantic validation failed for ${fixture.id}: ${validation.issues[0].path}: ${validation.issues[0].message}`)
|
|
const reparsed = migrateStringHasherSchema(parseStringHasherTable(writeStringHasherTable(table)))
|
|
if (JSON.stringify(reparsed) !== JSON.stringify(table)) throw new Error(`StringHasher semantic roundtrip changed native naming evidence for ${fixture.id}.`)
|
|
parsedStringHasherResources += 1
|
|
}
|
|
for (const [path, resource] of Object.entries(fixture.elementMapResources || {})) {
|
|
const document = parseElementMap2(resource.text)
|
|
const validation = validateElementMap2(document)
|
|
if (!validation.valid) throw new Error(`ElementMap2 semantic validation failed for ${fixture.id}/${path}: ${validation.issues[0].path}: ${validation.issues[0].message}`)
|
|
const canonical = writeElementMap2(document)
|
|
const reparsed = parseElementMap2(canonical)
|
|
if (reparsed.maps.length !== document.maps.length || reparsed.postfixes.length !== document.postfixes.length) throw new Error(`ElementMap2 schema roundtrip changed counts for ${fixture.id}/${path}.`)
|
|
if (elementMap2SemanticDigest(reparsed) !== elementMap2SemanticDigest(document)) throw new Error(`ElementMap2 semantic roundtrip changed native naming history for ${fixture.id}/${path}.`)
|
|
parsedResources += 1
|
|
}
|
|
}
|
|
if (parsedResources === 0) throw new Error('FreeCAD composite oracle did not capture any ElementMap2 resources.')
|
|
if (nativeIndexedNameStages !== namingEvidenceStages) throw new Error(`FreeCAD composite oracle must retain direct indexed-name evidence for every stage, found ${nativeIndexedNameStages}/${namingEvidenceStages}.`)
|
|
if (namingEvidenceMissing !== 0 || namingEvidenceBoundaryViolations !== 0) throw new Error(`FreeCAD naming evidence boundary is incomplete: missing=${namingEvidenceMissing}, violations=${namingEvidenceBoundaryViolations}.`)
|
|
console.log(JSON.stringify({ status: 'freecad-composite-history-elementmap-pass', cases: report.summary.cases, parsedResources, parsedStringHasherResources, namingEvidenceStages, nativeEvidenceValidatedStages, nativeIndexedNameStages, nativeMappedNameStages, indexedOnlyStages: nativeIndexedNameStages - nativeMappedNameStages, privateTokenEvidenceCompleteStages, namingEvidenceMissing, namingEvidenceBoundaryViolations, internalBuilderEvidenceStages, internalBuilderEvidenceMissingStages }, null, 2))
|