// tests/api/03-flows.ts // Flow Lifecycle Tests - Lazy and Eager Creation Patterns import { api, log, runTest, saveImage, waitForGeneration, testContext, resolveAlias } from './utils'; import { endpoints } from './config'; async function main() { log.section('FLOW LIFECYCLE TESTS'); // Test 1: Lazy flow pattern - first generation without flowId await runTest('Lazy flow - generation without flowId', async () => { const result = await api(endpoints.generations, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ prompt: 'A red sports car on a mountain road', aspectRatio: '16:9', // NOTE: flowId not provided, should auto-generate }), }); if (!result.data.data.flowId) { throw new Error('No flowId returned'); } testContext.lazyFlowId = result.data.data.flowId; log.detail('Auto-generated flowId', testContext.lazyFlowId); const generation = await waitForGeneration(result.data.data.id); if (generation.status !== 'success') { throw new Error(`Generation failed`); } testContext.firstGenId = generation.id; }); // Test 2: Lazy flow - verify flow doesn't exist yet (Section 4.1) await runTest('Lazy flow - verify flow not created yet', async () => { const result = await api(`${endpoints.flows}/${testContext.lazyFlowId}`, { expectError: true, }); if (result.status !== 404) { throw new Error('Flow should not exist yet (lazy creation)'); } log.detail('Flow correctly does not exist', '✓'); }); // Test 3: Lazy flow - second use creates flow await runTest('Lazy flow - second generation creates flow', async () => { const result = await api(endpoints.generations, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ prompt: 'Same car but blue color', aspectRatio: '16:9', flowId: testContext.lazyFlowId, }), }); const generation = await waitForGeneration(result.data.data.id); if (generation.status !== 'success') { throw new Error(`Generation failed`); } // Now flow should exist const flowResult = await api(`${endpoints.flows}/${testContext.lazyFlowId}`); if (!flowResult.data.data) { throw new Error('Flow should exist after second use'); } log.detail('Flow now exists', '✓'); log.detail('Flow ID', flowResult.data.data.id); }); // Test 4: Eager flow creation with flowAlias await runTest('Eager flow - created immediately with flowAlias', async () => { const result = await api(endpoints.generations, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ prompt: 'A hero banner image', aspectRatio: '21:9', flowAlias: '@hero-flow', }), }); if (!result.data.data.flowId) { throw new Error('No flowId returned'); } testContext.eagerFlowId = result.data.data.flowId; const generation = await waitForGeneration(result.data.data.id); if (generation.status !== 'success') { throw new Error(`Generation failed`); } // Flow should exist immediately const flowResult = await api(`${endpoints.flows}/${testContext.eagerFlowId}`); if (!flowResult.data.data) { throw new Error('Flow should exist immediately (eager creation)'); } if (!flowResult.data.data.aliases || !flowResult.data.data.aliases['@hero-flow']) { throw new Error('Flow alias not set'); } log.detail('Flow exists immediately', '✓'); log.detail('Flow alias', '@hero-flow'); }); // Test 5: List all flows await runTest('List all flows', async () => { const result = await api(endpoints.flows); if (!result.data.data || !Array.isArray(result.data.data)) { throw new Error('No flows array returned'); } const found = result.data.data.filter((f: any) => f.id === testContext.lazyFlowId || f.id === testContext.eagerFlowId ); if (found.length !== 2) { throw new Error('Not all created flows found'); } log.detail('Total flows', result.data.data.length); log.detail('Our flows found', found.length); }); // Test 6: Get flow details with computed counts await runTest('Get flow with computed counts', async () => { const result = await api(`${endpoints.flows}/${testContext.lazyFlowId}`); if (!result.data.data) { throw new Error('Flow not found'); } const flow = result.data.data; if (typeof flow.generationCount !== 'number') { throw new Error('Missing generationCount'); } if (typeof flow.imageCount !== 'number') { throw new Error('Missing imageCount'); } log.detail('Generation count', flow.generationCount); log.detail('Image count', flow.imageCount); log.detail('Aliases', JSON.stringify(flow.aliases)); }); // Test 7: Get flow's generations await runTest('List flow generations', async () => { const result = await api(`${endpoints.flows}/${testContext.lazyFlowId}/generations`); if (!result.data.data || !Array.isArray(result.data.data)) { throw new Error('No generations array returned'); } log.detail('Generations in flow', result.data.data.length); }); // Test 8: Get flow's images await runTest('List flow images', async () => { const result = await api(`${endpoints.flows}/${testContext.lazyFlowId}/images`); if (!result.data.data || !Array.isArray(result.data.data)) { throw new Error('No images array returned'); } log.detail('Images in flow', result.data.data.length); }); // Test 9: Update flow aliases await runTest('Update flow aliases', async () => { // Get a generation to use const flowResult = await api(`${endpoints.flows}/${testContext.lazyFlowId}`); const gens = await api(`${endpoints.flows}/${testContext.lazyFlowId}/generations`); const lastGen = gens.data.data[0]; if (!lastGen.outputImageId) { throw new Error('No output image'); } const result = await api(`${endpoints.flows}/${testContext.lazyFlowId}/aliases`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ aliases: { '@latest': lastGen.outputImageId, '@best': lastGen.outputImageId, }, }), }); if (!result.data.data.aliases) { throw new Error('No aliases returned'); } log.detail('Updated aliases', JSON.stringify(result.data.data.aliases)); }); // Test 10: Remove specific flow alias await runTest('Remove specific flow alias', async () => { await api(`${endpoints.flows}/${testContext.lazyFlowId}/aliases/@best`, { method: 'DELETE', }); const result = await api(`${endpoints.flows}/${testContext.lazyFlowId}`); if ('@best' in result.data.data.aliases) { throw new Error('Alias should be removed'); } if (!('@latest' in result.data.data.aliases)) { throw new Error('Other aliases should remain'); } log.detail('Removed @best', '✓'); log.detail('Kept @latest', '✓'); }); // Test 11: Flow regenerate endpoint await runTest('Regenerate flow (most recent generation)', async () => { const result = await api(`${endpoints.flows}/${testContext.lazyFlowId}/regenerate`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({}), }); if (!result.data.data) { throw new Error('No generation returned'); } log.detail('Regeneration triggered', '✓'); log.detail('Generation ID', result.data.data.id); }); log.section('FLOW LIFECYCLE TESTS COMPLETED'); } main().catch(console.error);