d25d482dc2
Publish CLI Package / publish-npm (push) Waiting to run
Publish Python SDK / publish-pypi (push) Waiting to run
Publish TypeScript SDK / publish-npm (push) Waiting to run
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
246 lines
6.9 KiB
TypeScript
246 lines
6.9 KiB
TypeScript
import { expect } from 'vitest'
|
|
|
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
|
|
/**
|
|
* Asserts that a block exists in the workflow.
|
|
*
|
|
* @example
|
|
* ```ts
|
|
* const workflow = createLinearWorkflow(3)
|
|
* expectBlockExists(workflow.blocks, 'block-0')
|
|
* expectBlockExists(workflow.blocks, 'block-0', 'starter')
|
|
* ```
|
|
*/
|
|
export function expectBlockExists(
|
|
blocks: Record<string, any>,
|
|
blockId: string,
|
|
expectedType?: string
|
|
): void {
|
|
expect(blocks[blockId], `Block "${blockId}" should exist`).toBeDefined()
|
|
expect(blocks[blockId].id).toBe(blockId)
|
|
if (expectedType) {
|
|
expect(blocks[blockId].type, `Block "${blockId}" should be type "${expectedType}"`).toBe(
|
|
expectedType
|
|
)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Asserts that a block does NOT exist in the workflow.
|
|
*
|
|
* @example
|
|
* ```ts
|
|
* expectBlockNotExists(workflow.blocks, 'deleted-block')
|
|
* ```
|
|
*/
|
|
export function expectBlockNotExists(blocks: Record<string, any>, blockId: string): void {
|
|
expect(blocks[blockId], `Block "${blockId}" should not exist`).toBeUndefined()
|
|
}
|
|
|
|
/**
|
|
* Asserts that an edge connects two blocks.
|
|
*
|
|
* @example
|
|
* ```ts
|
|
* expectEdgeConnects(workflow.edges, 'block-0', 'block-1')
|
|
* ```
|
|
*/
|
|
export function expectEdgeConnects(edges: any[], sourceId: string, targetId: string): void {
|
|
const edge = edges.find((e) => e.source === sourceId && e.target === targetId)
|
|
expect(edge, `Edge from "${sourceId}" to "${targetId}" should exist`).toBeDefined()
|
|
}
|
|
|
|
/**
|
|
* Asserts that no edge connects two blocks.
|
|
*
|
|
* @example
|
|
* ```ts
|
|
* expectNoEdgeBetween(workflow.edges, 'block-1', 'block-0') // No reverse edge
|
|
* ```
|
|
*/
|
|
export function expectNoEdgeBetween(edges: any[], sourceId: string, targetId: string): void {
|
|
const edge = edges.find((e) => e.source === sourceId && e.target === targetId)
|
|
expect(edge, `Edge from "${sourceId}" to "${targetId}" should not exist`).toBeUndefined()
|
|
}
|
|
|
|
/**
|
|
* Asserts that a block has a specific parent.
|
|
*
|
|
* @example
|
|
* ```ts
|
|
* expectBlockHasParent(workflow.blocks, 'child-block', 'loop-1')
|
|
* ```
|
|
*/
|
|
export function expectBlockHasParent(
|
|
blocks: Record<string, any>,
|
|
childId: string,
|
|
expectedParentId: string
|
|
): void {
|
|
const block = blocks[childId]
|
|
expect(block, `Child block "${childId}" should exist`).toBeDefined()
|
|
expect(block.data?.parentId, `Block "${childId}" should have parent "${expectedParentId}"`).toBe(
|
|
expectedParentId
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Asserts that a workflow has a specific number of blocks.
|
|
*
|
|
* @example
|
|
* ```ts
|
|
* expectBlockCount(workflow, 5)
|
|
* ```
|
|
*/
|
|
export function expectBlockCount(workflow: any, expectedCount: number): void {
|
|
const actualCount = Object.keys(workflow.blocks).length
|
|
expect(actualCount, `Workflow should have ${expectedCount} blocks`).toBe(expectedCount)
|
|
}
|
|
|
|
/**
|
|
* Asserts that a workflow has a specific number of edges.
|
|
*
|
|
* @example
|
|
* ```ts
|
|
* expectEdgeCount(workflow, 4)
|
|
* ```
|
|
*/
|
|
export function expectEdgeCount(workflow: any, expectedCount: number): void {
|
|
expect(workflow.edges.length, `Workflow should have ${expectedCount} edges`).toBe(expectedCount)
|
|
}
|
|
|
|
/**
|
|
* Asserts that a block is at a specific position.
|
|
*
|
|
* @example
|
|
* ```ts
|
|
* expectBlockPosition(workflow.blocks, 'block-1', { x: 200, y: 0 })
|
|
* ```
|
|
*/
|
|
export function expectBlockPosition(
|
|
blocks: Record<string, any>,
|
|
blockId: string,
|
|
expectedPosition: { x: number; y: number }
|
|
): void {
|
|
const block = blocks[blockId]
|
|
expect(block, `Block "${blockId}" should exist`).toBeDefined()
|
|
expect(block.position.x, `Block "${blockId}" x position`).toBeCloseTo(expectedPosition.x, 0)
|
|
expect(block.position.y, `Block "${blockId}" y position`).toBeCloseTo(expectedPosition.y, 0)
|
|
}
|
|
|
|
/**
|
|
* Asserts that a block is enabled.
|
|
*
|
|
* @example
|
|
* ```ts
|
|
* expectBlockEnabled(workflow.blocks, 'block-1')
|
|
* ```
|
|
*/
|
|
export function expectBlockEnabled(blocks: Record<string, any>, blockId: string): void {
|
|
const block = blocks[blockId]
|
|
expect(block, `Block "${blockId}" should exist`).toBeDefined()
|
|
expect(block.enabled, `Block "${blockId}" should be enabled`).toBe(true)
|
|
}
|
|
|
|
/**
|
|
* Asserts that a block is disabled.
|
|
*
|
|
* @example
|
|
* ```ts
|
|
* expectBlockDisabled(workflow.blocks, 'disabled-block')
|
|
* ```
|
|
*/
|
|
export function expectBlockDisabled(blocks: Record<string, any>, blockId: string): void {
|
|
const block = blocks[blockId]
|
|
expect(block, `Block "${blockId}" should exist`).toBeDefined()
|
|
expect(block.enabled, `Block "${blockId}" should be disabled`).toBe(false)
|
|
}
|
|
|
|
/**
|
|
* Asserts that a workflow has a loop with specific configuration.
|
|
*
|
|
* @example
|
|
* ```ts
|
|
* expectLoopExists(workflow, 'loop-1', { iterations: 5, loopType: 'for' })
|
|
* ```
|
|
*/
|
|
export function expectLoopExists(
|
|
workflow: any,
|
|
loopId: string,
|
|
expectedConfig?: { iterations?: number; loopType?: string; nodes?: string[] }
|
|
): void {
|
|
const loop = workflow.loops[loopId]
|
|
expect(loop, `Loop "${loopId}" should exist`).toBeDefined()
|
|
|
|
if (expectedConfig) {
|
|
if (expectedConfig.iterations !== undefined) {
|
|
expect(loop.iterations).toBe(expectedConfig.iterations)
|
|
}
|
|
if (expectedConfig.loopType !== undefined) {
|
|
expect(loop.loopType).toBe(expectedConfig.loopType)
|
|
}
|
|
if (expectedConfig.nodes !== undefined) {
|
|
expect(loop.nodes).toEqual(expectedConfig.nodes)
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Asserts that a workflow has a parallel block with specific configuration.
|
|
*
|
|
* @example
|
|
* ```ts
|
|
* expectParallelExists(workflow, 'parallel-1', { count: 3 })
|
|
* ```
|
|
*/
|
|
export function expectParallelExists(
|
|
workflow: any,
|
|
parallelId: string,
|
|
expectedConfig?: { count?: number; parallelType?: string; nodes?: string[] }
|
|
): void {
|
|
const parallel = workflow.parallels[parallelId]
|
|
expect(parallel, `Parallel "${parallelId}" should exist`).toBeDefined()
|
|
|
|
if (expectedConfig) {
|
|
if (expectedConfig.count !== undefined) {
|
|
expect(parallel.count).toBe(expectedConfig.count)
|
|
}
|
|
if (expectedConfig.parallelType !== undefined) {
|
|
expect(parallel.parallelType).toBe(expectedConfig.parallelType)
|
|
}
|
|
if (expectedConfig.nodes !== undefined) {
|
|
expect(parallel.nodes).toEqual(expectedConfig.nodes)
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Asserts that the workflow state is empty.
|
|
*
|
|
* @example
|
|
* ```ts
|
|
* const workflow = createWorkflowState()
|
|
* expectEmptyWorkflow(workflow)
|
|
* ```
|
|
*/
|
|
export function expectEmptyWorkflow(workflow: any): void {
|
|
expect(Object.keys(workflow.blocks).length, 'Workflow should have no blocks').toBe(0)
|
|
expect(workflow.edges.length, 'Workflow should have no edges').toBe(0)
|
|
expect(Object.keys(workflow.loops).length, 'Workflow should have no loops').toBe(0)
|
|
expect(Object.keys(workflow.parallels).length, 'Workflow should have no parallels').toBe(0)
|
|
}
|
|
|
|
/**
|
|
* Asserts that blocks are connected in a linear chain.
|
|
*
|
|
* @example
|
|
* ```ts
|
|
* expectLinearChain(workflow.edges, ['start', 'step1', 'step2', 'end'])
|
|
* ```
|
|
*/
|
|
export function expectLinearChain(edges: any[], blockIds: string[]): void {
|
|
for (let i = 0; i < blockIds.length - 1; i++) {
|
|
expectEdgeConnects(edges, blockIds[i], blockIds[i + 1])
|
|
}
|
|
}
|