banatie-service/tests/api/03-flows.ts

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);
});