63 lines
4.6 KiB
JavaScript
63 lines
4.6 KiB
JavaScript
import { readFile } from 'node:fs/promises'
|
|
import { resolve } from 'node:path'
|
|
|
|
const root = resolve(new URL('..', import.meta.url).pathname)
|
|
const load = (path) => readFile(resolve(root, path), 'utf8').then(JSON.parse)
|
|
const [workflow, inventory, exactPlan] = await Promise.all([
|
|
load('config/freecad-gui-workflow-plan.json'),
|
|
load('config/freecad-gui-command-inventory.json'),
|
|
load('config/freecad-web-exact-parity-plan.json'),
|
|
])
|
|
|
|
const fail = (message) => { throw new Error(`FreeCAD GUI workflow plan: ${message}`) }
|
|
const lockedCommit = '0108fd4b4850cc46e625b60e53cea7a7bbe69f8d'
|
|
if (workflow.schemaVersion !== 1 || workflow.baseline?.freecadVersion !== '1.1.1' || workflow.baseline?.commit !== lockedCommit) fail('baseline mismatch.')
|
|
if (inventory.baseline?.commit !== lockedCommit || exactPlan.baseline?.freecadCommit !== lockedCommit) fail('input baseline mismatch.')
|
|
|
|
const family = workflow.family
|
|
if (!family?.id || !family.title || !family.workbench || !family.primaryCommand || !family.primaryExactTask || !family.selectionReason) fail('selected family metadata is incomplete.')
|
|
if (!Array.isArray(family.commandIds) || family.commandIds.length === 0 || !family.commandIds.includes(family.primaryCommand)) fail('selected command family is incomplete.')
|
|
if (!Array.isArray(family.relatedExactTasks) || family.relatedExactTasks.length === 0) fail('related exact tasks are missing.')
|
|
|
|
const exactTaskIds = new Set(exactPlan.programs.flatMap((program) => program.tasks.map((task) => task.id)))
|
|
for (const taskId of [family.primaryExactTask, ...family.relatedExactTasks]) if (!exactTaskIds.has(taskId)) fail(`selected family references unknown exact task ${taskId}.`)
|
|
|
|
const commandById = new Map(inventory.commands.map((command) => [command.id, command]))
|
|
for (const commandId of family.commandIds) if (!commandById.has(commandId)) fail(`selected command ${commandId} is absent from the runtime inventory.`)
|
|
const primary = commandById.get(family.primaryCommand)
|
|
const sensitivity = workflow.sensitivityEvidence
|
|
if (sensitivity?.source !== 'config/freecad-gui-command-inventory.json' || sensitivity.commandId !== family.primaryCommand) fail('sensitivity provenance is incomplete.')
|
|
if (sensitivity.documentSensitive !== primary.documentSensitive || sensitivity.selectionSensitive !== primary.selectionSensitive) fail('selected sensitivity flags do not match the runtime inventory.')
|
|
for (const caseName of ['noDocument', 'documentNoSelection', 'selectedPartBox']) {
|
|
if (JSON.stringify(sensitivity.cases?.[caseName]) !== JSON.stringify(primary.cases?.[caseName]?.states)) fail(`${caseName} sensitivity evidence is stale.`)
|
|
}
|
|
|
|
const stateNames = ['success', 'disabled', 'failure', 'cancel', 'recovery']
|
|
for (const stateName of stateNames) {
|
|
const recipe = workflow.stateRecipes?.[stateName]
|
|
if (!recipe?.setup || !recipe.action || !recipe.expected) fail(`${stateName} workflow recipe is incomplete.`)
|
|
}
|
|
|
|
const expectedTaskIds = Array.from({ length: 7 }, (_, index) => `ORA-GUI-WF-${String(index).padStart(3, '0')}`)
|
|
if (!Array.isArray(workflow.progress) || workflow.progress.map((entry) => entry.taskId).join(',') !== expectedTaskIds.join(',')) fail('workflow progress must contain ordered ORA-GUI-WF-000 through 006 tasks.')
|
|
const statuses = workflow.progress.map((entry) => entry.status)
|
|
const inProgressCount = statuses.filter((status) => status === 'in_progress').length
|
|
if (inProgressCount > 1 || statuses.some((status) => !['completed', 'in_progress', 'pending'].includes(status))) fail('workflow progress has invalid statuses.')
|
|
const inProgressIndex = statuses.indexOf('in_progress')
|
|
if (inProgressCount === 0 && statuses.some((status) => status !== 'completed')) fail('workflow progress without an in-progress task must be fully completed.')
|
|
if (inProgressCount === 1 && (statuses.slice(0, inProgressIndex).some((status) => status !== 'completed') || statuses.slice(inProgressIndex + 1).some((status) => status !== 'pending'))) fail('workflow progress is not a contiguous serial queue.')
|
|
for (const [index, entry] of workflow.progress.entries()) {
|
|
if (!Array.isArray(entry.evidence)) fail(`${entry.taskId} evidence must be an array.`)
|
|
if (index < inProgressIndex && entry.evidence.length === 0) fail(`${entry.taskId} is completed without evidence.`)
|
|
}
|
|
|
|
console.log(JSON.stringify({
|
|
status: 'freecad-gui-workflow-plan-pass',
|
|
family: family.id,
|
|
primaryCommand: family.primaryCommand,
|
|
primaryExactTask: family.primaryExactTask,
|
|
completed: statuses.filter((status) => status === 'completed').length,
|
|
inProgress: inProgressIndex >= 0 ? workflow.progress[inProgressIndex].taskId : null,
|
|
pending: statuses.filter((status) => status === 'pending').length,
|
|
}, null, 2))
|