Skip to content

Commit 237c47c

Browse files
fix: include initiator in edge sync broadcast
1 parent e138cd1 commit 237c47c

File tree

2 files changed

+124
-2
lines changed

2 files changed

+124
-2
lines changed

apps/sim/socket/handlers/operations.test.ts

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,4 +153,126 @@ describe('setupOperationsHandlers', () => {
153153
expect.objectContaining({ operationId: 'op-1', serverTimestamp: expect.any(Number) })
154154
)
155155
})
156+
157+
it('broadcasts generic side-effect edge syncs to the initiating client', async () => {
158+
mockPersistWorkflowOperation.mockResolvedValue({
159+
removedEdgeIds: ['edge-removed'],
160+
addedEdges: [
161+
{
162+
id: 'edge-added',
163+
source: 'container-1',
164+
target: 'block-1',
165+
sourceHandle: 'loop-start-source',
166+
targetHandle: 'target',
167+
type: 'workflowEdge',
168+
},
169+
],
170+
})
171+
172+
const socketEmit = vi.fn()
173+
const socketRoomEmit = vi.fn()
174+
const emitToWorkflow = vi.fn()
175+
const socketHandlers = new Map<string, (data: unknown) => Promise<void>>()
176+
177+
const socket = {
178+
id: 'socket-1',
179+
on: vi.fn((event: string, handler: (data: unknown) => Promise<void>) => {
180+
socketHandlers.set(event, handler)
181+
}),
182+
emit: socketEmit,
183+
to: vi.fn(() => ({
184+
emit: socketRoomEmit,
185+
})),
186+
}
187+
188+
const roomManager = {
189+
io: {} as never,
190+
initialize: vi.fn(),
191+
isReady: vi.fn(() => true),
192+
shutdown: vi.fn(),
193+
addUserToRoom: vi.fn(),
194+
removeUserFromRoom: vi.fn(),
195+
getWorkflowIdForSocket: vi.fn().mockResolvedValue('workflow-1'),
196+
getUserSession: vi.fn().mockResolvedValue({ userId: 'user-1', userName: 'Test User' }),
197+
getWorkflowUsers: vi.fn().mockResolvedValue([
198+
{
199+
socketId: 'socket-1',
200+
userId: 'user-1',
201+
workflowId: 'workflow-1',
202+
userName: 'Test User',
203+
joinedAt: Date.now(),
204+
lastActivity: Date.now(),
205+
role: 'admin',
206+
},
207+
]),
208+
hasWorkflowRoom: vi.fn().mockResolvedValue(true),
209+
updateUserActivity: vi.fn(),
210+
updateRoomLastModified: vi.fn(),
211+
broadcastPresenceUpdate: vi.fn(),
212+
emitToWorkflow,
213+
getUniqueUserCount: vi.fn(),
214+
getTotalActiveConnections: vi.fn(),
215+
handleWorkflowDeletion: vi.fn(),
216+
handleWorkflowRevert: vi.fn(),
217+
handleWorkflowUpdate: vi.fn(),
218+
}
219+
220+
setupOperationsHandlers(socket as never, roomManager)
221+
222+
const workflowOperationHandler = socketHandlers.get('workflow-operation')
223+
224+
expect(workflowOperationHandler).toBeDefined()
225+
226+
await workflowOperationHandler?.({
227+
operationId: 'op-2',
228+
operation: BLOCKS_OPERATIONS.BATCH_TOGGLE_LOCKED,
229+
target: OPERATION_TARGETS.BLOCKS,
230+
payload: {
231+
ids: ['block-1'],
232+
locked: true,
233+
},
234+
timestamp: 456,
235+
})
236+
237+
expect(socketRoomEmit).toHaveBeenCalledWith(
238+
'workflow-operation',
239+
expect.objectContaining({
240+
operation: BLOCKS_OPERATIONS.BATCH_TOGGLE_LOCKED,
241+
target: OPERATION_TARGETS.BLOCKS,
242+
payload: { ids: ['block-1'], locked: true },
243+
})
244+
)
245+
expect(emitToWorkflow).toHaveBeenNthCalledWith(
246+
1,
247+
'workflow-1',
248+
'workflow-operation',
249+
expect.objectContaining({
250+
operation: EDGES_OPERATIONS.BATCH_REMOVE_EDGES,
251+
target: OPERATION_TARGETS.EDGES,
252+
payload: { ids: ['edge-removed'] },
253+
})
254+
)
255+
expect(emitToWorkflow).toHaveBeenNthCalledWith(
256+
2,
257+
'workflow-1',
258+
'workflow-operation',
259+
expect.objectContaining({
260+
operation: EDGES_OPERATIONS.BATCH_ADD_EDGES,
261+
target: OPERATION_TARGETS.EDGES,
262+
payload: {
263+
edges: [
264+
expect.objectContaining({
265+
id: 'edge-added',
266+
source: 'container-1',
267+
target: 'block-1',
268+
}),
269+
],
270+
},
271+
})
272+
)
273+
expect(socketEmit).toHaveBeenCalledWith(
274+
'operation-confirmed',
275+
expect.objectContaining({ operationId: 'op-2', serverTimestamp: expect.any(Number) })
276+
)
277+
})
156278
})

apps/sim/socket/handlers/operations.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -595,7 +595,7 @@ export function setupOperationsHandlers(socket: AuthenticatedSocket, roomManager
595595
// Broadcast edge removals if the operation cleaned up boundary edges
596596
// (e.g., update-parent nesting a block into a container)
597597
if (result?.removedEdgeIds && result.removedEdgeIds.length > 0) {
598-
socket.to(workflowId).emit('workflow-operation', {
598+
roomManager.emitToWorkflow(workflowId, 'workflow-operation', {
599599
operation: EDGES_OPERATIONS.BATCH_REMOVE_EDGES,
600600
target: OPERATION_TARGETS.EDGES,
601601
payload: { ids: result.removedEdgeIds },
@@ -609,7 +609,7 @@ export function setupOperationsHandlers(socket: AuthenticatedSocket, roomManager
609609

610610
// Broadcast auto-connected edges so clients add them to local state
611611
if (result?.addedEdges && result.addedEdges.length > 0) {
612-
socket.to(workflowId).emit('workflow-operation', {
612+
roomManager.emitToWorkflow(workflowId, 'workflow-operation', {
613613
operation: EDGES_OPERATIONS.BATCH_ADD_EDGES,
614614
target: OPERATION_TARGETS.EDGES,
615615
payload: { edges: result.addedEdges },

0 commit comments

Comments
 (0)