302 lines
10 KiB
TypeScript
302 lines
10 KiB
TypeScript
/**
|
|
* Platform-Admin Integration Tests
|
|
*
|
|
* These tests verify that @lilith/imajin-app integrates correctly
|
|
* with platform-admin's actual usage patterns.
|
|
*
|
|
* Based on real code from:
|
|
* - platform-admin/frontend-admin/src/pages/image-generation/ImagePipelinesPage.tsx
|
|
* - platform-admin/frontend-admin/src/pages/image-generation/ImageGenAssistant.tsx
|
|
*/
|
|
|
|
import { describe, it, expect, beforeAll } from 'vitest';
|
|
|
|
describe('Platform-Admin Integration', () => {
|
|
let core: any;
|
|
|
|
beforeAll(async () => {
|
|
core = await import('../core/dist/index.js');
|
|
});
|
|
|
|
describe('ImagePipelinesPage pattern', () => {
|
|
// Tests the import pattern used in platform-admin
|
|
// import { PIPELINES, type PipelineConfig } from '@lilith/imajin-app';
|
|
|
|
it('should import PIPELINES array', () => {
|
|
expect(core.PIPELINES).toBeDefined();
|
|
expect(Array.isArray(core.PIPELINES)).toBe(true);
|
|
});
|
|
|
|
it('should use PipelineConfig type correctly', () => {
|
|
const pipeline = core.PIPELINES[0];
|
|
|
|
// Platform-admin accesses these fields:
|
|
expect(pipeline.id).toBeDefined();
|
|
expect(pipeline.name).toBeDefined();
|
|
expect(pipeline.description).toBeDefined();
|
|
expect(pipeline.category).toBeDefined();
|
|
expect(pipeline.model).toBeDefined();
|
|
expect(pipeline.families).toBeDefined();
|
|
});
|
|
|
|
it('should support pipeline card rendering pattern', () => {
|
|
// platform-admin maps over PIPELINES to render cards
|
|
const cards = core.PIPELINES.map((pipeline: any) => ({
|
|
key: pipeline.id,
|
|
name: pipeline.name,
|
|
description: pipeline.description,
|
|
category: pipeline.category,
|
|
}));
|
|
|
|
expect(cards.length).toBeGreaterThan(0);
|
|
cards.forEach((card: any) => {
|
|
expect(card.key).toBeTruthy();
|
|
expect(card.name).toBeTruthy();
|
|
expect(card.description).toBeTruthy();
|
|
});
|
|
});
|
|
|
|
it('should support gallery category filtering', () => {
|
|
// platform-admin uses pipeline.category for gallery filtering
|
|
const categories = [...new Set(core.PIPELINES.map((p: any) => p.category))];
|
|
|
|
expect(categories.length).toBeGreaterThan(0);
|
|
categories.forEach((cat: any) => {
|
|
expect(typeof cat).toBe('string');
|
|
expect(cat.length).toBeGreaterThan(0);
|
|
});
|
|
});
|
|
|
|
it('should support families for derivative generation', () => {
|
|
// platform-admin uses pipeline.families to create image derivatives
|
|
for (const pipeline of core.PIPELINES) {
|
|
expect(Array.isArray(pipeline.families)).toBe(true);
|
|
expect(pipeline.families.length).toBeGreaterThan(0);
|
|
|
|
// Verify family names are strings
|
|
pipeline.families.forEach((family: any) => {
|
|
expect(typeof family).toBe('string');
|
|
});
|
|
}
|
|
});
|
|
});
|
|
|
|
describe('ImageGenAssistant pattern', () => {
|
|
// Tests the import pattern used in ImageGenAssistant
|
|
// import type { PipelineConfig } from '@lilith/imajin-app';
|
|
// import { parseAssistantPrompts } from '@lilith/imajin-app';
|
|
|
|
it('should import parseAssistantPrompts function', () => {
|
|
expect(core.parseAssistantPrompts).toBeDefined();
|
|
expect(typeof core.parseAssistantPrompts).toBe('function');
|
|
});
|
|
|
|
it('should access pipeline.systemPrompt for LLM', () => {
|
|
// ImageGenAssistant passes systemPrompt to Ollama
|
|
for (const pipeline of core.PIPELINES) {
|
|
expect(pipeline.systemPrompt).toBeDefined();
|
|
expect(typeof pipeline.systemPrompt).toBe('string');
|
|
expect(pipeline.systemPrompt.length).toBeGreaterThan(100);
|
|
}
|
|
});
|
|
|
|
it('should access pipeline.examplePrompts for UI buttons', () => {
|
|
// ImageGenAssistant renders example prompts as clickable buttons
|
|
for (const pipeline of core.PIPELINES) {
|
|
expect(Array.isArray(pipeline.examplePrompts)).toBe(true);
|
|
expect(pipeline.examplePrompts.length).toBeGreaterThan(0);
|
|
|
|
pipeline.examplePrompts.forEach((example: string) => {
|
|
expect(typeof example).toBe('string');
|
|
expect(example.length).toBeGreaterThan(0);
|
|
});
|
|
}
|
|
});
|
|
|
|
it('should parse JSON prompts from LLM response', () => {
|
|
// Simulates actual LLM response parsing
|
|
const llmResponse = `Here are some prompts for skeleton loading images:
|
|
|
|
\`\`\`json
|
|
[
|
|
{
|
|
"name": "cyber-girl-1",
|
|
"prompt": "cyberpunk anime girl silhouette, pink neon glow, mysterious dark background",
|
|
"negativePrompt": "low quality, blurry, deformed"
|
|
},
|
|
{
|
|
"name": "cyber-girl-2",
|
|
"prompt": "holographic anime girl figure, blue circuit patterns, futuristic",
|
|
"negativePrompt": "low quality, blurry, ugly"
|
|
}
|
|
]
|
|
\`\`\`
|
|
|
|
These prompts focus on the cyberpunk aesthetic that works well for skeleton loaders.`;
|
|
|
|
const parsed = core.parseAssistantPrompts(llmResponse);
|
|
|
|
expect(parsed).toHaveLength(2);
|
|
expect(parsed[0].name).toBe('cyber-girl-1');
|
|
expect(parsed[0].prompt).toContain('cyberpunk');
|
|
expect(parsed[0].negativePrompt).toContain('low quality');
|
|
});
|
|
|
|
it('should build CreateVariationRequest from parsed prompts', () => {
|
|
// ImageGenAssistant builds these requests for image-generator API
|
|
const pipeline = core.PIPELINES[0];
|
|
const llmResponse = `[{"name": "test", "prompt": "test prompt", "negativePrompt": "bad quality"}]`;
|
|
const parsed = core.parseAssistantPrompts(llmResponse);
|
|
|
|
// Simulate the request building pattern from ImageGenAssistant
|
|
const request = {
|
|
name: `${pipeline.category}-${parsed[0].name}-${Date.now()}-0`,
|
|
category: pipeline.category,
|
|
families: pipeline.families,
|
|
generation: {
|
|
prompt: parsed[0].prompt,
|
|
negativePrompt: parsed[0].negativePrompt,
|
|
model: pipeline.model,
|
|
seed: 42424242,
|
|
guidanceScale: 7.5,
|
|
inferenceSteps: 30,
|
|
},
|
|
};
|
|
|
|
expect(request.name).toContain(pipeline.category);
|
|
expect(request.families).toEqual(pipeline.families);
|
|
expect(request.generation.model).toBe(pipeline.model);
|
|
expect(['photorealistic', 'anime']).toContain(request.generation.model);
|
|
});
|
|
});
|
|
|
|
describe('API Types Compatibility', () => {
|
|
// These tests verify that the types exported from imagen-core
|
|
// are compatible with what platform-admin expects
|
|
|
|
it('should have ImageModel type compatible values', () => {
|
|
// platform-admin uses ImageModel type for generation params
|
|
const validModels = ['photorealistic', 'anime'];
|
|
|
|
for (const pipeline of core.PIPELINES) {
|
|
expect(validModels).toContain(pipeline.model);
|
|
}
|
|
});
|
|
|
|
it('should have PipelineConfig with all required fields', () => {
|
|
const requiredFields = [
|
|
'id',
|
|
'name',
|
|
'description',
|
|
'category',
|
|
'model',
|
|
'families',
|
|
'systemPrompt',
|
|
'examplePrompts',
|
|
];
|
|
|
|
for (const pipeline of core.PIPELINES) {
|
|
for (const field of requiredFields) {
|
|
expect(pipeline[field]).toBeDefined();
|
|
}
|
|
}
|
|
});
|
|
|
|
it('should have ParsedPrompt with all required fields', () => {
|
|
const response = '[{"name": "test", "prompt": "positive", "negativePrompt": "negative"}]';
|
|
const parsed = core.parseAssistantPrompts(response);
|
|
|
|
expect(parsed[0]).toHaveProperty('name');
|
|
expect(parsed[0]).toHaveProperty('prompt');
|
|
expect(parsed[0]).toHaveProperty('negativePrompt');
|
|
});
|
|
});
|
|
|
|
describe('Pipeline Selection Flow', () => {
|
|
// Tests the full flow platform-admin uses for pipeline selection
|
|
|
|
it('should support selecting pipeline by ID', () => {
|
|
// User clicks on a pipeline card, gets full pipeline config
|
|
const pipelineId = core.PIPELINES[0].id;
|
|
const selected = core.getPipelineById(pipelineId);
|
|
|
|
expect(selected).toBeDefined();
|
|
expect(selected.id).toBe(pipelineId);
|
|
});
|
|
|
|
it('should validate pipeline IDs correctly', () => {
|
|
// For URL routing and validation
|
|
const validIds = core.getPipelineIds();
|
|
|
|
for (const id of validIds) {
|
|
expect(core.isValidPipelineId(id)).toBe(true);
|
|
}
|
|
|
|
expect(core.isValidPipelineId('invalid-pipeline-id')).toBe(false);
|
|
});
|
|
|
|
it('should support modal state pattern', () => {
|
|
// platform-admin uses useState<PipelineConfig | null> for modal
|
|
let selectedPipeline: any = null;
|
|
|
|
// Simulate clicking "Open Assistant"
|
|
selectedPipeline = core.getPipelineById('skeleton-anime-girls');
|
|
expect(selectedPipeline).not.toBeNull();
|
|
expect(selectedPipeline.name).toBe('Skeleton Loading Images');
|
|
|
|
// Simulate closing modal
|
|
selectedPipeline = null;
|
|
expect(selectedPipeline).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe('Queue Stats Integration', () => {
|
|
// Tests that pipeline.category is usable for queue stats queries
|
|
|
|
it('should provide category for queue stats queries', () => {
|
|
// platform-admin queries: getQueueStats(pipeline.category)
|
|
for (const pipeline of core.PIPELINES) {
|
|
expect(pipeline.category).toBeDefined();
|
|
expect(typeof pipeline.category).toBe('string');
|
|
expect(pipeline.category.length).toBeGreaterThan(0);
|
|
}
|
|
});
|
|
|
|
it('should have unique categories per pipeline', () => {
|
|
// Each pipeline should have its own category for isolation
|
|
const categoryCounts: Record<string, number> = {};
|
|
|
|
for (const pipeline of core.PIPELINES) {
|
|
categoryCounts[pipeline.category] = (categoryCounts[pipeline.category] || 0) + 1;
|
|
}
|
|
|
|
// Currently we expect skeleton and error-pages categories
|
|
expect(Object.keys(categoryCounts).length).toBeGreaterThanOrEqual(2);
|
|
});
|
|
});
|
|
|
|
describe('Gallery Integration', () => {
|
|
// Tests gallery-related pipeline features
|
|
|
|
it('should support family-based derivative selection', () => {
|
|
// platform-admin selects best derivative: portrait > square > first
|
|
const skeletonPipeline = core.getPipelineById('skeleton-anime-girls');
|
|
|
|
expect(skeletonPipeline.families).toContain('portrait');
|
|
expect(skeletonPipeline.families).toContain('square');
|
|
});
|
|
|
|
it('should support image naming convention', () => {
|
|
// platform-admin creates names like: category-promptName-timestamp-index
|
|
const pipeline = core.PIPELINES[0];
|
|
const promptName = 'test-image';
|
|
const timestamp = Date.now();
|
|
const index = 0;
|
|
|
|
const imageName = `${pipeline.category}-${promptName}-${timestamp}-${index}`;
|
|
|
|
expect(imageName).toContain(pipeline.category);
|
|
expect(imageName).toContain(promptName);
|
|
});
|
|
});
|
|
});
|