// tests/api/03-aliases.ts import { join } from 'path'; import { api, log, runTest, saveImage, uploadFile, waitForGeneration, testContext } from './utils'; import { config, endpoints } from './config'; import { fileURLToPath } from 'url'; import { dirname } from 'path'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); async function main() { log.section('ALIAS TESTS'); // Test 1: Upload with project-scoped alias await runTest('Upload with project alias', async () => { const fixturePath = join(__dirname, config.fixturesDir, 'test-image.png'); const response = await uploadFile(fixturePath, { alias: '@brand-logo', description: 'Brand logo for project-wide use', }); if (!response.image.alias || response.image.alias !== '@brand-logo') { throw new Error('Alias not set correctly'); } log.detail('Image ID', response.image.id); log.detail('Project alias', response.image.alias); log.detail('Flow ID', response.image.flowId || 'null (project-scoped)'); }); // Test 2: Upload with flow-scoped alias await runTest('Upload with flow alias', async () => { const fixturePath = join(__dirname, config.fixturesDir, 'test-image.png'); const response = await uploadFile(fixturePath, { flowAlias: '@temp-logo', flowId: testContext.flowId, description: 'Temporary logo for flow use', }); if (!response.flow || !response.flow.aliases['@temp-logo']) { throw new Error('Flow alias not set'); } log.detail('Image ID', response.image.id); log.detail('Flow ID', response.image.flowId); log.detail('Flow aliases', JSON.stringify(response.flow.aliases)); }); // Test 3: Upload with BOTH project and flow aliases await runTest('Upload with dual aliases', async () => { const fixturePath = join(__dirname, config.fixturesDir, 'test-image.png'); const response = await uploadFile(fixturePath, { alias: '@global-asset', flowAlias: '@flow-asset', flowId: testContext.flowId, description: 'Image with both alias types', }); if (!response.image.alias || response.image.alias !== '@global-asset') { throw new Error('Project alias not set'); } if (!response.flow || !response.flow.aliases['@flow-asset']) { throw new Error('Flow alias not set'); } log.detail('Image ID', response.image.id); log.detail('Project alias', response.image.alias); log.detail('Flow alias', '@flow-asset'); }); // Test 4: Resolve project-scoped alias await runTest('Resolve project alias', async () => { const result = await api(`${endpoints.images}/resolve/@brand-logo`); if (!result.data.image) { throw new Error('Image not resolved'); } if (result.data.scope !== 'project') { throw new Error(`Wrong scope: ${result.data.scope}`); } log.detail('Image ID', result.data.image.id); log.detail('Scope', result.data.scope); log.detail('Alias', result.data.image.alias); }); // Test 5: Resolve flow-scoped alias await runTest('Resolve flow alias', async () => { const result = await api(`${endpoints.images}/resolve/@temp-logo?flowId=${testContext.flowId}`); if (!result.data.image) { throw new Error('Image not resolved'); } if (result.data.scope !== 'flow') { throw new Error(`Wrong scope: ${result.data.scope}`); } log.detail('Image ID', result.data.image.id); log.detail('Scope', result.data.scope); log.detail('Flow ID', result.data.flow?.id); }); // Test 6: Resolve @last technical alias await runTest('Resolve @last technical alias', async () => { const result = await api(`${endpoints.images}/resolve/@last?flowId=${testContext.flowId}`); if (!result.data.image) { throw new Error('Image not resolved'); } if (result.data.scope !== 'technical') { throw new Error(`Wrong scope: ${result.data.scope}`); } log.detail('Image ID', result.data.image.id); log.detail('Scope', result.data.scope); log.detail('Technical alias', '@last'); }); // Test 7: Resolve @first technical alias await runTest('Resolve @first technical alias', async () => { const result = await api(`${endpoints.images}/resolve/@first?flowId=${testContext.flowId}`); if (!result.data.image) { throw new Error('Image not resolved'); } if (result.data.scope !== 'technical') { throw new Error(`Wrong scope: ${result.data.scope}`); } log.detail('Image ID', result.data.image.id); log.detail('Scope', result.data.scope); }); // Test 8: Resolve @upload technical alias await runTest('Resolve @upload technical alias', async () => { const result = await api(`${endpoints.images}/resolve/@upload?flowId=${testContext.flowId}`); if (!result.data.image) { throw new Error('Image not resolved'); } if (result.data.scope !== 'technical') { throw new Error(`Wrong scope: ${result.data.scope}`); } log.detail('Image ID', result.data.image.id); log.detail('Scope', result.data.scope); log.detail('Source', result.data.image.source); }); // Test 9: Generate with assignAlias (project-scoped) await runTest('Generate with project alias assignment', async () => { const result = await api(endpoints.generations, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ prompt: 'A minimalist logo design', aspectRatio: '1:1', assignAlias: '@generated-logo', }), }); log.info('Waiting for generation to complete...'); const generation = await waitForGeneration(result.data.generation.id); if (generation.status !== 'success') { throw new Error(`Generation failed: ${generation.errorMessage}`); } // Check if alias was assigned const imageResult = await api(`${endpoints.images}/${generation.outputImageId}`); if (imageResult.data.image.alias !== '@generated-logo') { throw new Error('Alias not assigned to generated image'); } log.detail('Output image', generation.outputImageId); log.detail('Assigned alias', imageResult.data.image.alias); // Save image const imageUrl = imageResult.data.image.storageUrl; const imageResponse = await fetch(imageUrl); const imageBuffer = await imageResponse.arrayBuffer(); await saveImage(imageBuffer, 'generated-with-alias.png'); }); // Test 10: Generate with assignFlowAlias (flow-scoped) await runTest('Generate with flow alias assignment', async () => { const result = await api(endpoints.generations, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ prompt: 'A vibrant abstract pattern', aspectRatio: '1:1', flowId: testContext.flowId, assignFlowAlias: '@pattern', }), }); log.info('Waiting for generation to complete...'); const generation = await waitForGeneration(result.data.generation.id); if (generation.status !== 'success') { throw new Error(`Generation failed: ${generation.errorMessage}`); } // Check if flow alias was assigned const flowResult = await api(`${endpoints.flows}/${testContext.flowId}`); if (!flowResult.data.flow.aliases['@pattern']) { throw new Error('Flow alias not assigned'); } log.detail('Output image', generation.outputImageId); log.detail('Flow alias assigned', '@pattern'); }); // Test 11: Alias precedence (flow > project) await runTest('Test alias precedence', async () => { // Create project alias @test const fixturePath = join(__dirname, config.fixturesDir, 'test-image.png'); const projectResponse = await uploadFile(fixturePath, { alias: '@precedence-test', }); const projectImageId = projectResponse.image.id; // Create flow alias @test (different image) const flowResponse = await uploadFile(fixturePath, { flowAlias: '@precedence-test', flowId: testContext.flowId, }); const flowImageId = flowResponse.image.id; // Resolve without flowId (should get project alias) const withoutFlow = await api(`${endpoints.images}/resolve/@precedence-test`); if (withoutFlow.data.image.id !== projectImageId) { throw new Error('Should resolve to project alias'); } log.detail('Without flow context', 'resolved to project alias ✓'); // Resolve with flowId (should get flow alias) const withFlow = await api(`${endpoints.images}/resolve/@precedence-test?flowId=${testContext.flowId}`); if (withFlow.data.image.id !== flowImageId) { throw new Error('Should resolve to flow alias'); } log.detail('With flow context', 'resolved to flow alias ✓'); }); log.section('ALIAS TESTS COMPLETED'); } main().catch(console.error);