import { describe, expect, test } from 'vitest' import { OverlayAction, ActionAck } from '../../../../core/automation/application/ports/IOverlaySyncPort' import { IAutomationEventPublisher, AutomationEvent } from '../../../../core/automation/application/ports/IAutomationEventPublisher' import { IAutomationLifecycleEmitter, LifecycleCallback } from '../../../../core/automation/infrastructure//IAutomationLifecycleEmitter' import { OverlaySyncService } from '../../../../core/automation/application/services/OverlaySyncService' class MockLifecycleEmitter implements IAutomationLifecycleEmitter { private callbacks: Set = new Set() onLifecycle(cb: LifecycleCallback): void { this.callbacks.add(cb) } offLifecycle(cb: LifecycleCallback): void { this.callbacks.delete(cb) } async emit(event: AutomationEvent) { for (const cb of Array.from(this.callbacks)) { // fire without awaiting to simulate async emitter cb(event) } } } describe('OverlaySyncService (unit)', () => { test('startAction resolves as confirmed only after action-started event is emitted', async () => { const emitter = new MockLifecycleEmitter() // create service wiring: pass emitter as dependency (constructor shape expected) const svc = new OverlaySyncService({ lifecycleEmitter: emitter, logger: console as any, publisher: { publish: async () => {} }, }) const action: OverlayAction = { id: 'add-car', label: 'Adding...' } // start the action but don't emit event yet const promise = svc.startAction(action) // wait a small tick to ensure promise hasn't resolved prematurely await new Promise((r) => setTimeout(r, 10)) let resolved = false promise.then(() => (resolved = true)) expect(resolved).toBe(false) // now emit action-started await emitter.emit({ type: 'action-started', actionId: 'add-car', timestamp: Date.now() }) const ack = await promise expect(ack.status).toBe('confirmed') expect(ack.id).toBe('add-car') }) })