import assert from 'node:assert/strict' import { readFile } from 'node:fs/promises' import test from 'node:test' const report = JSON.parse(await readFile(new URL('../config/freecad-cam-path-oracle.json', import.meta.url), 'utf8')) const commandGcode = (object) => object.commands.map((command) => command.gcode) const objectByName = (objects, name) => objects.find((object) => object.name === name) test('native Profile oracle is a locked FreeCAD algorithm result', () => { assert.equal(report.profileAlgorithm.operationTypeId, 'Path::FeaturePython') assert.equal(report.profileAlgorithm.pathPropertyType, 'Path::PropertyPath') assert.equal(report.profileAlgorithm.commandCount, 32) assert.equal(report.profileAlgorithm.cuttingCommandCount, 20) assert.equal(report.profileAlgorithm.normalizationToleranceMm, 0.01) assert.equal(report.profileAlgorithm.commandSha256, '5fcc36e3fc3c022599096f4110c890401f285b5878030de7c481786470a7a455') assert.deepEqual(report.profileAlgorithm.variants.map((variant) => ({ id: variant.id, useComp: variant.useComp, hash: variant.commandSha256 })), [ { id: 'outside-cw-tool-comp', useComp: true, hash: '5fcc36e3fc3c022599096f4110c890401f285b5878030de7c481786470a7a455' }, { id: 'outside-cw-no-comp', useComp: false, hash: 'c4c8bc58c4b21bc7be8796ff5d074fbe02656e31a26cc91ee65a67537a7d240a' }, ]) }) test('native Helix oracle locks all four cut-direction combinations', () => { assert.equal(report.helixAlgorithm.fixture, 'Mod/CAM/CAMTests/test_holes00.fcstd') assert.equal(report.helixAlgorithm.baseSubElementCount, 9) assert.equal(report.helixAlgorithm.toolDiameterMm, 0.9) assert.deepEqual(report.helixAlgorithm.scenarios.map((scenario) => ({ id: scenario.id, direction: scenario.direction, arcs: scenario.arcNames, arcCount: scenario.arcCommandCount, hash: scenario.commandSha256, })), [ { id: 'inside-conventional', direction: 'CW', arcs: ['G2'], arcCount: 1260, hash: '6499583c26dbf01694abc0a98fdbeb9d240cce0f73d32634284ddfa132030e0b' }, { id: 'outside-climb', direction: 'CW', arcs: ['G2'], arcCount: 1260, hash: '37ee85fe360bfa82235f1b7aefce31cb7150900bb8d6f08751741fce6df18626' }, { id: 'inside-climb', direction: 'CCW', arcs: ['G3'], arcCount: 1260, hash: 'f2fd26ca11be5efb037f809fb65949d24956f6ce330f87d152d2674770a27bd8' }, { id: 'outside-conventional', direction: 'CCW', arcs: ['G3'], arcCount: 1260, hash: '0220f90245a74a50c1a54cb501439d14e7370d9d9d34fcc89dccdaff345688b7' }, ]) }) test('Path Feature and FeaturePython survive native save-open-mutate-save-open', () => { for (const name of ['NativePath', 'PythonPath']) { const before = objectByName(report.fcstdRoundTrip.sourceBeforeClose, name) const reopened = objectByName(report.fcstdRoundTrip.sourceReopened, name) const mutated = objectByName(report.fcstdRoundTrip.mutatedReopened, name) assert.deepEqual(commandGcode(reopened), commandGcode(before)) assert.deepEqual(commandGcode(mutated).slice(0, 5), commandGcode(before)) assert.equal(mutated.commandCount, 6) assert.equal(mutated.commands.at(-1).name, name === 'NativePath' ? 'M5' : 'M2') } }) test('Web byte-transparent proxy reopens natively without changing Path commands', () => { assert.equal(report.fcstdRoundTrip.webTransparentPreservation.byteExact, true) assert.equal(report.fcstdRoundTrip.webTransparentPreservation.nativeReopenVerified, true) for (const name of ['NativePath', 'PythonPath']) { const native = objectByName(report.fcstdRoundTrip.sourceReopened, name) const webPreserved = objectByName(report.fcstdRoundTrip.webTransparentPreservation.objects, name) assert.deepEqual(commandGcode(webPreserved), commandGcode(native)) } assert.equal(report.claims.webEditablePathPropertyCodec, 'verified-native-path-feature') assert.equal(report.boundary.exactParityClaim, false) }) test('Web Path::PropertyPath edit is reopened by FreeCAD while FeaturePython stays blocked', () => { const editable = report.fcstdRoundTrip.webEditablePathProperty assert.equal(editable.objectName, 'NativePath') assert.equal(editable.sourceCommandCount, 5) assert.equal(editable.editedCommandCount, 6) assert.deepEqual(editable.lastCommand, { name: 'M3', parameters: { S: 12000 } }) assert.equal(editable.nativeReopenVerified, true) assert.equal(editable.featurePythonExecutionBlocked, true) }) test('FeaturePython Path::PropertyPath resource editing is explicit and non-executing', () => { const editable = report.fcstdRoundTrip.webEditableFeaturePythonPathProperty assert.equal(editable.objectName, 'PythonPath') assert.equal(editable.sourceCommandCount, 5) assert.equal(editable.editedCommandCount, 6) assert.deepEqual(editable.lastCommand, { name: 'M2', parameters: {} }) assert.equal(editable.nativeReopenVerified, true) assert.equal(editable.scriptExecution, 'blocked') assert.equal(editable.optInRequired, true) assert.equal(report.claims.webEditableFeaturePythonPathResourceCodec, 'verified-safe-resource-only-opt-in') }) test('Qt oracle records live CAM actions from the active workbench', () => { assert.equal(report.qtDynamicOracle.activeWorkbench, 'CAMWorkbench') assert.ok(report.qtDynamicOracle.camCommandCount >= 50) assert.ok(report.qtDynamicOracle.requiredCommands.every((command) => ( command.selectedModel.registered && command.selectedModel.actionCount >= 1 && command.selectedModel.text.length > 0 && command.selectedModel.iconPresent ))) assert.ok(report.qtDynamicOracle.requiredCommands.some((command) => command.selectionChangesEnabledState)) }) test('Qt oracle records CAM Task panel open, focus fields, and OK/Cancel close semantics', () => { const lifecycles = report.qtDynamicOracle.taskLifecycles assert.equal(report.qtDynamicOracle.taskLifecycleVerified, true) assert.ok(lifecycles.length >= 3) assert.ok(lifecycles.every((lifecycle) => lifecycle.dialogOpened && lifecycle.dialogClosed && lifecycle.buttonClicked)) assert.ok(lifecycles.every((lifecycle) => lifecycle.active.buttonBoxCount === 1 && lifecycle.active.fieldCount >= 10)) assert.ok(lifecycles.every((lifecycle) => lifecycle.active.buttonTexts.some((button) => button.text === 'OK') && lifecycle.active.buttonTexts.some((button) => button.text === 'Cancel'))) assert.deepEqual(new Set(lifecycles.map((lifecycle) => lifecycle.action)), new Set(['accept', 'cancel'])) })