312 lines
9.9 KiB
TypeScript
312 lines
9.9 KiB
TypeScript
/**
|
|
* E2E Integration Tests for imagen-app ecosystem
|
|
*
|
|
* Tests the full integration between:
|
|
* - @lilith/imajin-app (pipeline configs, prompts, parsing)
|
|
* - @lilith/imajin-prompt-types (type definitions)
|
|
* - @lilith/imajin-prompt-client (HTTP client)
|
|
*
|
|
* These tests verify that all packages work together correctly
|
|
* and that consumers can use the published APIs as intended.
|
|
*/
|
|
|
|
import { describe, it, expect, beforeAll } from 'vitest';
|
|
import * as path from 'path';
|
|
import * as fs from 'fs';
|
|
|
|
// Test that packages can be imported correctly
|
|
describe('Package Imports', () => {
|
|
it('should import @lilith/imajin-app successfully', async () => {
|
|
const core = await import('../core/dist/index.js');
|
|
|
|
expect(core.PIPELINES).toBeDefined();
|
|
expect(core.getPipelineById).toBeDefined();
|
|
expect(core.parseAssistantPrompts).toBeDefined();
|
|
expect(core.SKELETON_PIPELINE).toBeDefined();
|
|
expect(core.ERROR_PAGES_PIPELINE).toBeDefined();
|
|
});
|
|
|
|
it('should export all required types from imagen-core', async () => {
|
|
// Type checking happens at compile time, but we can verify exports exist
|
|
const core = await import('../core/dist/index.js');
|
|
|
|
// These should be the main exports
|
|
const expectedExports = [
|
|
'PIPELINES',
|
|
'getPipelineById',
|
|
'getPipelineIds',
|
|
'isValidPipelineId',
|
|
'parseAssistantPrompts',
|
|
'SKELETON_PIPELINE',
|
|
'ERROR_PAGES_PIPELINE',
|
|
'SKELETON_SYSTEM_PROMPT',
|
|
'ERROR_PAGES_SYSTEM_PROMPT',
|
|
];
|
|
|
|
for (const exportName of expectedExports) {
|
|
expect(core[exportName]).toBeDefined();
|
|
}
|
|
});
|
|
});
|
|
|
|
describe('Pipeline Configurations', () => {
|
|
let core: any;
|
|
|
|
beforeAll(async () => {
|
|
core = await import('../core/dist/index.js');
|
|
});
|
|
|
|
it('should have at least 2 pipelines (skeleton, error-pages)', () => {
|
|
expect(core.PIPELINES.length).toBeGreaterThanOrEqual(2);
|
|
});
|
|
|
|
it('should have skeleton pipeline with correct structure', () => {
|
|
const skeleton = core.getPipelineById('skeleton-anime-girls');
|
|
|
|
expect(skeleton).toBeDefined();
|
|
expect(skeleton.id).toBe('skeleton-anime-girls');
|
|
expect(skeleton.name).toBe('Skeleton Loading Images');
|
|
expect(skeleton.model).toBe('photorealistic');
|
|
expect(skeleton.families).toContain('portrait');
|
|
expect(skeleton.families).toContain('square');
|
|
expect(skeleton.systemPrompt).toBeTruthy();
|
|
expect(skeleton.examplePrompts.length).toBeGreaterThan(0);
|
|
});
|
|
|
|
it('should have error-pages pipeline with correct structure', () => {
|
|
const errorPages = core.getPipelineById('error-pages');
|
|
|
|
expect(errorPages).toBeDefined();
|
|
expect(errorPages.id).toBe('error-pages');
|
|
expect(errorPages.name).toBe('Error Page Images');
|
|
expect(errorPages.model).toBe('anime');
|
|
expect(errorPages.families).toContain('square');
|
|
expect(errorPages.families).toContain('og');
|
|
expect(errorPages.families).toContain('hero');
|
|
expect(errorPages.systemPrompt).toBeTruthy();
|
|
expect(errorPages.examplePrompts.length).toBeGreaterThan(0);
|
|
});
|
|
|
|
it('should have unique pipeline IDs', () => {
|
|
const ids = core.getPipelineIds();
|
|
const uniqueIds = new Set(ids);
|
|
|
|
expect(ids.length).toBe(uniqueIds.size);
|
|
});
|
|
|
|
it('should validate pipeline IDs correctly', () => {
|
|
expect(core.isValidPipelineId('skeleton-anime-girls')).toBe(true);
|
|
expect(core.isValidPipelineId('error-pages')).toBe(true);
|
|
expect(core.isValidPipelineId('nonexistent')).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('Prompt Parsing', () => {
|
|
let core: any;
|
|
|
|
beforeAll(async () => {
|
|
core = await import('../core/dist/index.js');
|
|
});
|
|
|
|
it('should parse JSON prompts from LLM response', () => {
|
|
const response = `Here are some prompts:
|
|
|
|
\`\`\`json
|
|
[
|
|
{
|
|
"name": "cyber-girl-loading",
|
|
"prompt": "cyberpunk anime girl silhouette, neon glow",
|
|
"negativePrompt": "low quality, blurry"
|
|
}
|
|
]
|
|
\`\`\``;
|
|
|
|
const parsed = core.parseAssistantPrompts(response);
|
|
|
|
expect(parsed).toHaveLength(1);
|
|
expect(parsed[0].name).toBe('cyber-girl-loading');
|
|
expect(parsed[0].prompt).toContain('cyberpunk');
|
|
expect(parsed[0].negativePrompt).toContain('low quality');
|
|
});
|
|
|
|
it('should parse multiple prompts', () => {
|
|
const response = `
|
|
[
|
|
{"name": "prompt1", "prompt": "test1", "negativePrompt": "bad1"},
|
|
{"name": "prompt2", "prompt": "test2", "negativePrompt": "bad2"},
|
|
{"name": "prompt3", "prompt": "test3", "negativePrompt": "bad3"}
|
|
]`;
|
|
|
|
const parsed = core.parseAssistantPrompts(response);
|
|
|
|
expect(parsed).toHaveLength(3);
|
|
expect(parsed[0].name).toBe('prompt1');
|
|
expect(parsed[1].name).toBe('prompt2');
|
|
expect(parsed[2].name).toBe('prompt3');
|
|
});
|
|
|
|
it('should handle empty response', () => {
|
|
const parsed = core.parseAssistantPrompts('');
|
|
expect(parsed).toHaveLength(0);
|
|
});
|
|
|
|
it('should handle invalid JSON gracefully', () => {
|
|
const response = 'This is not JSON at all';
|
|
const parsed = core.parseAssistantPrompts(response);
|
|
expect(parsed).toHaveLength(0);
|
|
});
|
|
|
|
it('should handle partial/malformed JSON', () => {
|
|
const response = `[{"name": "incomplete", "prompt": "test"`;
|
|
const parsed = core.parseAssistantPrompts(response);
|
|
// Should either parse what it can or return empty
|
|
expect(Array.isArray(parsed)).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('System Prompts', () => {
|
|
let core: any;
|
|
|
|
beforeAll(async () => {
|
|
core = await import('../core/dist/index.js');
|
|
});
|
|
|
|
it('should have skeleton system prompt with required sections', () => {
|
|
const prompt = core.SKELETON_SYSTEM_PROMPT;
|
|
|
|
expect(prompt).toContain('skeleton');
|
|
expect(prompt).toContain('anime');
|
|
expect(prompt).toContain('cyberpunk');
|
|
expect(prompt).toContain('JSON');
|
|
});
|
|
|
|
it('should have error pages system prompt with required sections', () => {
|
|
const prompt = core.ERROR_PAGES_SYSTEM_PROMPT;
|
|
|
|
expect(prompt).toContain('error');
|
|
expect(prompt).toContain('404');
|
|
expect(prompt).toContain('500');
|
|
expect(prompt).toContain('JSON');
|
|
});
|
|
|
|
it('should have system prompts embedded in pipeline configs', () => {
|
|
for (const pipeline of core.PIPELINES) {
|
|
expect(pipeline.systemPrompt).toBeTruthy();
|
|
expect(pipeline.systemPrompt.length).toBeGreaterThan(100);
|
|
}
|
|
});
|
|
});
|
|
|
|
describe('Package Build Artifacts', () => {
|
|
const coreDistPath = path.join(__dirname, '../core/dist');
|
|
const reactDistPath = path.join(__dirname, '../react/dist');
|
|
|
|
it('should have core dist folder', () => {
|
|
expect(fs.existsSync(coreDistPath)).toBe(true);
|
|
});
|
|
|
|
it('should have core index.js', () => {
|
|
expect(fs.existsSync(path.join(coreDistPath, 'index.js'))).toBe(true);
|
|
});
|
|
|
|
it('should have core type definitions', () => {
|
|
expect(fs.existsSync(path.join(coreDistPath, 'index.d.ts'))).toBe(true);
|
|
});
|
|
|
|
it('should have react dist folder', () => {
|
|
expect(fs.existsSync(reactDistPath)).toBe(true);
|
|
});
|
|
|
|
it('should have react index.js', () => {
|
|
expect(fs.existsSync(path.join(reactDistPath, 'index.js'))).toBe(true);
|
|
});
|
|
|
|
it('should have react type definitions', () => {
|
|
expect(fs.existsSync(path.join(reactDistPath, 'index.d.ts'))).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('Cross-Package Type Compatibility', () => {
|
|
it('should have compatible PipelineConfig between packages', async () => {
|
|
const core = await import('../core/dist/index.js');
|
|
|
|
// Verify pipeline structure matches expected interface
|
|
const pipeline = core.PIPELINES[0];
|
|
|
|
// Required fields
|
|
expect(typeof pipeline.id).toBe('string');
|
|
expect(typeof pipeline.name).toBe('string');
|
|
expect(typeof pipeline.description).toBe('string');
|
|
expect(typeof pipeline.category).toBe('string');
|
|
expect(['photorealistic', 'anime']).toContain(pipeline.model);
|
|
expect(Array.isArray(pipeline.families)).toBe(true);
|
|
expect(typeof pipeline.systemPrompt).toBe('string');
|
|
expect(Array.isArray(pipeline.examplePrompts)).toBe(true);
|
|
});
|
|
|
|
it('should have compatible ParsedPrompt structure', async () => {
|
|
const core = await import('../core/dist/index.js');
|
|
|
|
const response = '[{"name": "test", "prompt": "positive", "negativePrompt": "negative"}]';
|
|
const parsed = core.parseAssistantPrompts(response);
|
|
|
|
expect(parsed[0]).toMatchObject({
|
|
name: expect.any(String),
|
|
prompt: expect.any(String),
|
|
negativePrompt: expect.any(String),
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('Consumer Usage Patterns', () => {
|
|
it('should support pipeline selection flow', async () => {
|
|
const core = await import('../core/dist/index.js');
|
|
|
|
// 1. Get all pipelines
|
|
const pipelines = core.PIPELINES;
|
|
expect(pipelines.length).toBeGreaterThan(0);
|
|
|
|
// 2. Get pipeline IDs for display
|
|
const ids = core.getPipelineIds();
|
|
expect(ids).toContain('skeleton-anime-girls');
|
|
|
|
// 3. Select a pipeline by ID
|
|
const selected = core.getPipelineById('skeleton-anime-girls');
|
|
expect(selected).toBeDefined();
|
|
|
|
// 4. Access pipeline properties for UI
|
|
expect(selected.name).toBeTruthy();
|
|
expect(selected.description).toBeTruthy();
|
|
expect(selected.examplePrompts.length).toBeGreaterThan(0);
|
|
|
|
// 5. Get system prompt for LLM
|
|
expect(selected.systemPrompt).toBeTruthy();
|
|
});
|
|
|
|
it('should support prompt generation flow', async () => {
|
|
const core = await import('../core/dist/index.js');
|
|
|
|
// 1. Get pipeline
|
|
const pipeline = core.getPipelineById('skeleton-anime-girls');
|
|
|
|
// 2. Use system prompt (would be sent to LLM)
|
|
const systemPrompt = pipeline.systemPrompt;
|
|
expect(systemPrompt).toContain('JSON');
|
|
|
|
// 3. Parse LLM response
|
|
const mockLLMResponse = `
|
|
[
|
|
{
|
|
"name": "loading-girl-1",
|
|
"prompt": "cyberpunk anime girl silhouette, pink neon glow, mysterious",
|
|
"negativePrompt": "low quality, blurry, deformed"
|
|
}
|
|
]`;
|
|
|
|
const prompts = core.parseAssistantPrompts(mockLLMResponse);
|
|
|
|
// 4. Use parsed prompts (would be sent to image generation)
|
|
expect(prompts[0].prompt).toBeTruthy();
|
|
expect(prompts[0].negativePrompt).toBeTruthy();
|
|
});
|
|
});
|