250 lines
7.7 KiB
TypeScript
250 lines
7.7 KiB
TypeScript
// tests/api/03-flows.ts
|
|
// Flow Lifecycle Tests - Lazy and Eager Creation Patterns
|
|
|
|
import { api, log, runTest, saveImage, waitForGeneration, testContext, resolveAlias, exitWithTestResults } 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()
|
|
.then(() => exitWithTestResults())
|
|
.catch((error) => {
|
|
console.error('Unexpected error:', error);
|
|
process.exit(1);
|
|
});
|