import { describe, expect, it } from 'vitest' import type { LGraph, LGraphNode, Subgraph } from '@/lib/litegraph/src/litegraph' import { collectMissingNodes, graphHasMissingNodes } from '@/workbench/extensions/manager/utils/graphHasMissingNodes' import type { NodeDefLookup } from '@/workbench/extensions/manager/utils/graphHasMissingNodes' import type { ComfyNodeDefImpl } from '@/stores/nodeDefStore' type NodeDefs = NodeDefLookup let nodeIdCounter = 0 const mockNodeDef = {} as ComfyNodeDefImpl const createGraph = (nodes: LGraphNode[] = []): LGraph => { return { nodes } as Partial as LGraph } const createSubgraph = (nodes: LGraphNode[]): Subgraph => { return { nodes } as Partial as Subgraph } const createNode = ( type?: string, subgraphNodes?: LGraphNode[] ): LGraphNode => { return { id: nodeIdCounter++, type, isSubgraphNode: subgraphNodes ? () => true : undefined, subgraph: subgraphNodes ? createSubgraph(subgraphNodes) : undefined } as unknown as LGraphNode } describe('graphHasMissingNodes', () => { it('returns false when graph is null', () => { expect(graphHasMissingNodes(null, {})).toBe(false) }) it('returns false when graph is undefined', () => { expect(graphHasMissingNodes(undefined, {})).toBe(false) }) it('returns false when graph has no nodes', () => { expect(graphHasMissingNodes(createGraph(), {})).toBe(false) }) it('returns false when every node has a definition', () => { const graph = createGraph([createNode('FooNode'), createNode('BarNode')]) const nodeDefs: NodeDefs = { FooNode: mockNodeDef, BarNode: mockNodeDef } expect(graphHasMissingNodes(graph, nodeDefs)).toBe(false) }) it('returns true when at least one node is missing', () => { const graph = createGraph([ createNode('FooNode'), createNode('MissingNode') ]) const nodeDefs: NodeDefs = { FooNode: mockNodeDef } expect(graphHasMissingNodes(graph, nodeDefs)).toBe(true) }) it('checks nodes nested in subgraphs', () => { const graph = createGraph([ createNode('ContainerNode', [createNode('InnerMissing')]) ]) const nodeDefs: NodeDefs = { ContainerNode: mockNodeDef } const missingNodes = collectMissingNodes(graph, nodeDefs) expect(missingNodes).toHaveLength(1) expect(missingNodes[0]?.type).toBe('InnerMissing') }) it('ignores nodes without a type', () => { const graph = createGraph([ createNode(undefined), createNode(null as unknown as string) ]) expect(graphHasMissingNodes(graph, {})).toBe(false) }) it('traverses deeply nested subgraphs', () => { const deepGraph = createGraph([ createNode('Layer1', [ createNode('Layer2', [ createNode('Layer3', [createNode('MissingDeep')]) ]) ]) ]) const nodeDefs: NodeDefs = { Layer1: mockNodeDef, Layer2: mockNodeDef, Layer3: mockNodeDef } const missingNodes = collectMissingNodes(deepGraph, nodeDefs) expect(missingNodes).toHaveLength(1) expect(missingNodes[0]?.type).toBe('MissingDeep') }) })