import { test } from 'node:test' import assert from 'node:assert/strict' import { createMockFacade } from '../src/facade/mockFacade' import { PROJECT_SCHEMA_MIGRATIONS, PROJECT_SCHEMA_SQL, PROJECT_SCHEMA_VERSION } from '../src/facade/projectSchema' import { PersistenceWriteQueue } from '../src/facade/projectStore' test('facade exposes a stable initial document projection', () => { const facade = createMockFacade() const state = facade.getState() assert.equal(state.apiVersion, '0.1') assert.equal(state.document.id, 'doc-pump-housing') assert.equal(state.document.tree.find((item) => item.id === 'body')?.type, 'body') assert.equal(facade.gui.workbench.getActive(), 'Part Design') }) test('project schema is versioned and covers the FreeCAD document graph', () => { assert.equal(PROJECT_SCHEMA_VERSION, 1) assert.equal(PROJECT_SCHEMA_MIGRATIONS[0].version, PROJECT_SCHEMA_VERSION) for (const table of ['projects', 'documents', 'objects', 'object_properties', 'dependencies', 'transactions', 'resources']) assert.match(PROJECT_SCHEMA_SQL, new RegExp(`CREATE TABLE IF NOT EXISTS ${table}`)) assert.match(PROJECT_SCHEMA_SQL, /CREATE INDEX IF NOT EXISTS objects_document_ordinal/) }) test('persistence writes are serialized and continue after a failed write', async () => { const queue = new PersistenceWriteQueue() let inFlight = 0 let maximumInFlight = 0 const order: string[] = [] const write = (id: string, shouldFail = false) => queue.run(async () => { inFlight += 1 maximumInFlight = Math.max(maximumInFlight, inFlight) await new Promise((resolve) => setTimeout(resolve, 2)) order.push(id) inFlight -= 1 if (shouldFail) throw new Error('expected write failure') return id }) const results = await Promise.allSettled([write('one'), write('two', true), write('three')]) assert.equal(maximumInFlight, 1) assert.deepEqual(order, ['one', 'two', 'three']) assert.equal(results[0].status, 'fulfilled') assert.equal(results[1].status, 'rejected') assert.equal(results[2].status, 'fulfilled') }) test('workbench and selection changes are event driven', () => { const facade = createMockFacade() const events: string[] = [] const unsubscribe = facade.subscribe((event) => events.push(event.type)) facade.gui.workbench.setActive('Sketcher') facade.selection.select('sketch') unsubscribe() assert.equal(facade.getState().activeWorkbench, 'Sketcher') assert.equal(facade.selection.getObjectId(), 'sketch') assert.ok(events.includes('state.changed')) assert.ok(events.includes('notice')) }) test('commands and tasks preserve apply/cancel lifecycle', () => { const facade = createMockFacade() facade.gui.command.execute({ commandId: 'create-sketch' }) assert.equal(facade.task.getActive()?.status, 'preview') facade.task.update({ support: 'XY_Plane' }) assert.equal(facade.task.getActive()?.draft.support, 'XY_Plane') facade.task.cancel() assert.equal(facade.task.getActive()?.status, 'cancelled') }) test('disabled commands return a reason instead of mutating the document', () => { const facade = createMockFacade() const before = facade.getState().document.version const state = facade.gui.command.getState('pad') assert.equal(state.status, 'enabled') facade.selection.clear() facade.gui.workbench.setActive('Sketcher') const disabled = facade.gui.command.getState('pad') assert.equal(disabled.status, 'disabled') assert.match(disabled.reason || '', /Part Design|compatible object/) facade.gui.command.execute({ commandId: 'pad' }) assert.equal(facade.getState().diagnostics.at(-1)?.code, 'COMMAND_DISABLED') assert.equal(facade.getState().document.version, before) }) test('command events carry a document-scoped request context', () => { const facade = createMockFacade() const events: string[] = [] let requestId = '' facade.subscribe((event) => { events.push(event.type); if (event.type === 'command.started') requestId = event.context.requestId }) facade.gui.command.execute({ commandId: 'create-sketch' }) assert.ok(events.includes('command.started')) assert.ok(events.includes('command.completed')) assert.match(requestId, /^req-/) }) test('document mutations use the facade history boundary', () => { const facade = createMockFacade() facade.gui.command.execute({ commandId: 'new-document' }) assert.equal(facade.app.document.getActive().label, 'Untitled document') assert.equal(facade.history.canUndo(), true) facade.history.undo() assert.equal(facade.app.document.getActive().label, 'Pump Housing') assert.equal(facade.history.canRedo(), true) facade.history.redo() assert.equal(facade.app.document.getActive().label, 'Untitled document') }) test('project persistence remains behind the facade contract', async () => { const facade = createMockFacade() const saved = await facade.project.save() assert.equal(saved.documentId, 'doc-pump-housing') const loaded = await facade.project.load('doc-pump-housing') assert.equal(loaded?.label, 'Pump Housing') assert.equal(facade.project.capabilities().mode, 'sqlite-memory') }) test('project resources use content identity and reference counting', async () => { const facade = createMockFacade() const bytes = new Uint8Array([1, 2, 3, 5, 8]) const first = await facade.project.resource.put(bytes, 'application/octet-stream') const second = await facade.project.resource.put(bytes, 'application/octet-stream') assert.equal(first.hash, second.hash) assert.equal(second.refCount, 2) bytes[0] = 99 assert.deepEqual([...((await facade.project.resource.get(first.hash)) || [])], [1, 2, 3, 5, 8]) await facade.project.resource.release(first.hash) assert.deepEqual([...((await facade.project.resource.get(first.hash)) || [])], [1, 2, 3, 5, 8]) await facade.project.resource.release(first.hash) assert.equal(await facade.project.resource.get(first.hash), null) }) test('Part Design feature tasks commit a document object and remain undoable', () => { const facade = createMockFacade() const before = facade.app.document.getActive() facade.gui.command.execute({ commandId: 'pad' }) assert.equal(facade.task.getActive()?.commandId, 'pad') facade.task.update({ length: 42, reversed: false }) facade.task.apply() const committed = facade.app.document.getActive() const created = committed.tree.find((item) => item.id === 'pad001') assert.equal(created?.label, 'Pad') assert.equal(committed.version, before.version + 1) assert.equal(facade.selection.getObjectId(), 'pad001') assert.equal(committed.dirty, true) facade.history.undo() assert.equal(facade.app.document.getActive().tree.some((item) => item.id === 'pad001'), false) facade.history.redo() assert.equal(facade.app.document.getActive().tree.some((item) => item.id === 'pad001'), true) })