Skip to content

Commit 418b637

Browse files
committed
fix tests
1 parent b0f7fc8 commit 418b637

3 files changed

Lines changed: 148 additions & 3 deletions

File tree

apps/sim/lib/workflows/persistence/duplicate.test.ts

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,4 +382,97 @@ describe('duplicateWorkflow ordering', () => {
382382
expect(remappedVarId).toBe(newVarIds[0])
383383
expect(copiedSubBlocks.variables.value[0].variableName).toBe('customerName')
384384
})
385+
386+
it('preserves stale variable references instead of failing the duplicate', async () => {
387+
let insertedBlocks: Array<Record<string, unknown>> | null = null
388+
const tx = createMockTx(
389+
[
390+
[
391+
{
392+
id: 'source-workflow-id',
393+
workspaceId: 'workspace-123',
394+
folderId: null,
395+
description: 'source',
396+
color: '#000000',
397+
variables: {
398+
'live-var-id': {
399+
id: 'live-var-id',
400+
workflowId: 'source-workflow-id',
401+
name: 'customerName',
402+
type: 'string',
403+
value: 'Ada',
404+
},
405+
},
406+
},
407+
],
408+
[],
409+
[],
410+
[
411+
{
412+
id: 'source-block-id',
413+
workflowId: 'source-workflow-id',
414+
type: 'agent',
415+
name: 'Agent',
416+
parentId: null,
417+
extent: null,
418+
data: {},
419+
subBlocks: {
420+
variables: {
421+
id: 'variables',
422+
type: 'variables-input',
423+
value: [
424+
{
425+
id: 'assignment-1',
426+
variableId: 'deleted-var-id',
427+
variableName: 'customerName',
428+
type: 'string',
429+
value: 'Grace',
430+
isExisting: true,
431+
},
432+
],
433+
},
434+
},
435+
position: { x: 0, y: 0 },
436+
enabled: true,
437+
horizontalHandles: true,
438+
isWide: false,
439+
height: 0,
440+
advancedMode: false,
441+
triggerMode: false,
442+
locked: false,
443+
createdAt: new Date(),
444+
updatedAt: new Date(),
445+
},
446+
],
447+
[],
448+
[],
449+
],
450+
undefined,
451+
(values) => {
452+
if (Array.isArray(values)) {
453+
insertedBlocks = values as Array<Record<string, unknown>>
454+
}
455+
}
456+
)
457+
458+
mockDb.transaction.mockImplementation(async (callback: (txArg: unknown) => Promise<unknown>) =>
459+
callback(tx)
460+
)
461+
462+
await expect(
463+
duplicateWorkflow({
464+
sourceWorkflowId: 'source-workflow-id',
465+
userId: 'user-123',
466+
name: 'Duplicated',
467+
workspaceId: 'workspace-123',
468+
folderId: null,
469+
requestId: 'req-stale',
470+
})
471+
).resolves.toBeDefined()
472+
473+
expect(insertedBlocks).toHaveLength(1)
474+
const copiedSubBlocks = insertedBlocks?.[0].subBlocks as Record<string, any>
475+
expect(copiedSubBlocks.variables.value[0].variableId).toBe('deleted-var-id')
476+
expect(copiedSubBlocks.variables.value[0].variableName).toBe('customerName')
477+
})
385478
})

apps/sim/lib/workflows/persistence/duplicate.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -134,10 +134,13 @@ function remapVariableAssignment(value: unknown, varIdMap: Map<string, string>):
134134

135135
if (typeof assignment.variableId === 'string') {
136136
const newVarId = varIdMap.get(assignment.variableId)
137-
if (!newVarId) {
138-
throw new Error(`Variable reference ${assignment.variableId} could not be remapped`)
137+
if (newVarId) {
138+
next.variableId = newVarId
139+
} else {
140+
logger.warn('Skipping unknown variable reference during duplication', {
141+
variableId: assignment.variableId,
142+
})
139143
}
140-
next.variableId = newVarId
141144
}
142145

143146
return next

packages/testing/src/mocks/workflow-authz.mock.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,47 @@
11
import { vi } from 'vitest'
22

3+
/**
4+
* Real `WorkflowLockedError` subclass used by tests so `instanceof` checks in
5+
* route handlers behave the same as in production. Mirrors the shape exported
6+
* by `@sim/workflow-authz`.
7+
*/
8+
export class MockWorkflowLockedError extends Error {
9+
readonly status = 423
10+
11+
constructor(message = 'Workflow is locked') {
12+
super(message)
13+
this.name = 'WorkflowLockedError'
14+
}
15+
}
16+
17+
/**
18+
* Real `FolderLockedError` subclass used by tests so `instanceof` checks in
19+
* route handlers behave the same as in production. Mirrors the shape exported
20+
* by `@sim/workflow-authz`.
21+
*/
22+
export class MockFolderLockedError extends Error {
23+
readonly status = 423
24+
25+
constructor(message = 'Folder is locked') {
26+
super(message)
27+
this.name = 'FolderLockedError'
28+
}
29+
}
30+
31+
const unlockedStatus = {
32+
locked: false,
33+
directLocked: false,
34+
inheritedLocked: false,
35+
lockedBy: null as 'workflow' | 'folder' | null,
36+
lockedFolderId: null as string | null,
37+
}
38+
339
/**
440
* Controllable mocks for the `@sim/workflow-authz` package.
541
*
42+
* Defaults assume permissive access (no lock, write allowed). Override with
43+
* `mockResolvedValue` per test when exercising the lock/permission paths.
44+
*
645
* @example
746
* ```ts
847
* import { workflowAuthzMockFns } from '@sim/testing'
@@ -20,6 +59,10 @@ export const workflowAuthzMockFns = {
2059
mockGetActiveWorkflowContext: vi.fn(),
2160
mockGetActiveWorkflowRecord: vi.fn(),
2261
mockAssertActiveWorkflowContext: vi.fn(),
62+
mockGetFolderLockStatus: vi.fn().mockResolvedValue(unlockedStatus),
63+
mockGetWorkflowLockStatus: vi.fn().mockResolvedValue(unlockedStatus),
64+
mockAssertWorkflowMutable: vi.fn().mockResolvedValue(undefined),
65+
mockAssertFolderMutable: vi.fn().mockResolvedValue(undefined),
2366
}
2467

2568
/**
@@ -36,4 +79,10 @@ export const workflowAuthzMock = {
3679
getActiveWorkflowContext: workflowAuthzMockFns.mockGetActiveWorkflowContext,
3780
getActiveWorkflowRecord: workflowAuthzMockFns.mockGetActiveWorkflowRecord,
3881
assertActiveWorkflowContext: workflowAuthzMockFns.mockAssertActiveWorkflowContext,
82+
getFolderLockStatus: workflowAuthzMockFns.mockGetFolderLockStatus,
83+
getWorkflowLockStatus: workflowAuthzMockFns.mockGetWorkflowLockStatus,
84+
assertWorkflowMutable: workflowAuthzMockFns.mockAssertWorkflowMutable,
85+
assertFolderMutable: workflowAuthzMockFns.mockAssertFolderMutable,
86+
WorkflowLockedError: MockWorkflowLockedError,
87+
FolderLockedError: MockFolderLockedError,
3988
}

0 commit comments

Comments
 (0)