Browse Source

test(20-01): add validateWorkflow integration tests

- Empty workflow validation
- nanoBanana node validation (text required, image optional)
- annotation node validation (image or sourceImage required)
- output node validation (image required)
- Valid workflow scenarios (chains, complex, groups)
- Multiple errors reported

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
handoff-20260429-1057
shrimbly 6 months ago
parent
commit
5b6d128fa6
  1. 276
      src/store/__tests__/workflowStore.integration.test.ts

276
src/store/__tests__/workflowStore.integration.test.ts

@ -429,4 +429,280 @@ describe("workflowStore integration tests", () => {
});
});
});
describe("validateWorkflow", () => {
describe("Empty workflow", () => {
it("should return invalid with 'Workflow is empty' error", () => {
useWorkflowStore.setState({
nodes: [],
edges: [],
});
const store = useWorkflowStore.getState();
const result = store.validateWorkflow();
expect(result.valid).toBe(false);
expect(result.errors).toContain("Workflow is empty");
});
});
describe("nanoBanana node validation", () => {
it("should return error when nanoBanana node missing text input", () => {
useWorkflowStore.setState({
nodes: [
createTestNode("nanoBanana-1", "nanoBanana", {}),
],
edges: [],
});
const store = useWorkflowStore.getState();
const result = store.validateWorkflow();
expect(result.valid).toBe(false);
expect(result.errors).toContain('Generate node "nanoBanana-1" missing text input');
});
it("should return valid when text input is connected", () => {
useWorkflowStore.setState({
nodes: [
createTestNode("prompt-1", "prompt", { prompt: "test" }),
createTestNode("nanoBanana-1", "nanoBanana", {}),
],
edges: [createTestEdge("prompt-1", "nanoBanana-1", "text", "text")],
});
const store = useWorkflowStore.getState();
const result = store.validateWorkflow();
expect(result.valid).toBe(true);
expect(result.errors).toHaveLength(0);
});
it("should not require image input (optional)", () => {
useWorkflowStore.setState({
nodes: [
createTestNode("prompt-1", "prompt", { prompt: "test" }),
createTestNode("nanoBanana-1", "nanoBanana", {}),
],
edges: [createTestEdge("prompt-1", "nanoBanana-1", "text", "text")],
});
const store = useWorkflowStore.getState();
const result = store.validateWorkflow();
// Should be valid without image input
expect(result.valid).toBe(true);
});
it("should validate multiple nanoBanana nodes independently", () => {
useWorkflowStore.setState({
nodes: [
createTestNode("prompt-1", "prompt", { prompt: "test" }),
createTestNode("nanoBanana-1", "nanoBanana", {}),
createTestNode("nanoBanana-2", "nanoBanana", {}), // No text input
],
edges: [createTestEdge("prompt-1", "nanoBanana-1", "text", "text")],
});
const store = useWorkflowStore.getState();
const result = store.validateWorkflow();
expect(result.valid).toBe(false);
expect(result.errors).toContain('Generate node "nanoBanana-2" missing text input');
expect(result.errors).not.toContain('Generate node "nanoBanana-1" missing text input');
});
});
describe("annotation node validation", () => {
it("should return error when no image connected and no sourceImage", () => {
useWorkflowStore.setState({
nodes: [
createTestNode("annotation-1", "annotation", { sourceImage: null }),
],
edges: [],
});
const store = useWorkflowStore.getState();
const result = store.validateWorkflow();
expect(result.valid).toBe(false);
expect(result.errors).toContain('Annotation node "annotation-1" missing image input');
});
it("should return valid when image is connected", () => {
useWorkflowStore.setState({
nodes: [
createTestNode("imageInput-1", "imageInput", { image: "data:image/png;base64,test" }),
createTestNode("annotation-1", "annotation", { sourceImage: null }),
],
edges: [createTestEdge("imageInput-1", "annotation-1", "image", "image")],
});
const store = useWorkflowStore.getState();
const result = store.validateWorkflow();
expect(result.valid).toBe(true);
});
it("should return valid when sourceImage is present (manual load)", () => {
useWorkflowStore.setState({
nodes: [
createTestNode("annotation-1", "annotation", {
sourceImage: "data:image/png;base64,manuallyLoadedImage",
}),
],
edges: [],
});
const store = useWorkflowStore.getState();
const result = store.validateWorkflow();
expect(result.valid).toBe(true);
});
});
describe("output node validation", () => {
it("should return error when output node has no image input", () => {
useWorkflowStore.setState({
nodes: [
createTestNode("output-1", "output", {}),
],
edges: [],
});
const store = useWorkflowStore.getState();
const result = store.validateWorkflow();
expect(result.valid).toBe(false);
expect(result.errors).toContain('Output node "output-1" missing image input');
});
it("should return valid when output node has image connected", () => {
useWorkflowStore.setState({
nodes: [
createTestNode("imageInput-1", "imageInput", { image: "data:image/png;base64,test" }),
createTestNode("output-1", "output", {}),
],
edges: [createTestEdge("imageInput-1", "output-1", "image", "image")],
});
const store = useWorkflowStore.getState();
const result = store.validateWorkflow();
expect(result.valid).toBe(true);
});
});
describe("Valid workflow scenarios", () => {
it("should validate simple prompt -> nanoBanana -> output chain", () => {
useWorkflowStore.setState({
nodes: [
createTestNode("prompt-1", "prompt", { prompt: "test prompt" }),
createTestNode("nanoBanana-1", "nanoBanana", {}),
createTestNode("output-1", "output", {}),
],
edges: [
createTestEdge("prompt-1", "nanoBanana-1", "text", "text"),
createTestEdge("nanoBanana-1", "output-1", "image", "image"),
],
});
const store = useWorkflowStore.getState();
const result = store.validateWorkflow();
expect(result.valid).toBe(true);
expect(result.errors).toHaveLength(0);
});
it("should validate complex workflow with multiple node types", () => {
useWorkflowStore.setState({
nodes: [
createTestNode("imageInput-1", "imageInput", { image: "data:image/png;base64,test" }),
createTestNode("prompt-1", "prompt", { prompt: "describe this" }),
createTestNode("llmGenerate-1", "llmGenerate", {}),
createTestNode("nanoBanana-1", "nanoBanana", {}),
createTestNode("annotation-1", "annotation", { sourceImage: null }),
createTestNode("output-1", "output", {}),
],
edges: [
createTestEdge("imageInput-1", "llmGenerate-1", "image", "image"),
createTestEdge("prompt-1", "llmGenerate-1", "text", "text"),
createTestEdge("llmGenerate-1", "nanoBanana-1", "text", "text"),
createTestEdge("nanoBanana-1", "annotation-1", "image", "image"),
createTestEdge("annotation-1", "output-1", "image", "image"),
],
});
const store = useWorkflowStore.getState();
const result = store.validateWorkflow();
expect(result.valid).toBe(true);
expect(result.errors).toHaveLength(0);
});
it("should validate workflow with groups (groups don't affect validation)", () => {
useWorkflowStore.setState({
nodes: [
{ ...createTestNode("prompt-1", "prompt", { prompt: "test" }), groupId: "group-1" },
{ ...createTestNode("nanoBanana-1", "nanoBanana", {}), groupId: "group-1" },
],
edges: [createTestEdge("prompt-1", "nanoBanana-1", "text", "text")],
groups: {
"group-1": {
id: "group-1",
name: "Test Group",
color: "neutral",
position: { x: 0, y: 0 },
size: { width: 400, height: 400 },
},
},
});
const store = useWorkflowStore.getState();
const result = store.validateWorkflow();
expect(result.valid).toBe(true);
});
it("should allow nodes that don't require validation (imageInput, prompt)", () => {
useWorkflowStore.setState({
nodes: [
createTestNode("imageInput-1", "imageInput", { image: null }),
createTestNode("prompt-1", "prompt", { prompt: "" }),
],
edges: [],
});
const store = useWorkflowStore.getState();
const result = store.validateWorkflow();
// These nodes don't have validation rules, so workflow is valid
expect(result.valid).toBe(true);
});
});
describe("Multiple validation errors", () => {
it("should report all validation errors, not just the first", () => {
useWorkflowStore.setState({
nodes: [
createTestNode("nanoBanana-1", "nanoBanana", {}),
createTestNode("nanoBanana-2", "nanoBanana", {}),
createTestNode("annotation-1", "annotation", { sourceImage: null }),
createTestNode("output-1", "output", {}),
],
edges: [],
});
const store = useWorkflowStore.getState();
const result = store.validateWorkflow();
expect(result.valid).toBe(false);
expect(result.errors.length).toBeGreaterThanOrEqual(4);
expect(result.errors).toContain('Generate node "nanoBanana-1" missing text input');
expect(result.errors).toContain('Generate node "nanoBanana-2" missing text input');
expect(result.errors).toContain('Annotation node "annotation-1" missing image input');
expect(result.errors).toContain('Output node "output-1" missing image input');
});
});
});
});

Loading…
Cancel
Save