Files
desktop/apps/hanzo-desktop/e2e-tests/engine-integration.spec.ts

220 lines
7.5 KiB
TypeScript

import { test, expect } from '@playwright/test';
/**
* Engine Integration Tests
*
* Tests the integration between Zoo Desktop and zoo-node with embedded Hanzo engine.
* These tests verify:
* - Zoo-node API connectivity
* - Hanzo engine availability and health
* - Model loading and initialization
* - Inference endpoint functionality
*/
const ZOO_NODE_URL = process.env.ZOO_NODE_URL || 'http://localhost:9550';
const TEST_MODEL_PATH = process.env.TEST_MODEL_PATH;
test.describe('Zoo Node API Connectivity', () => {
test('should connect to zoo-node health endpoint', async ({ request }) => {
const response = await request.get(`${ZOO_NODE_URL}/v1/node/health`);
// The node HTTP server must be reachable (status < 500).
// Some builds return 404 on /v1/node/health; that still proves the
// server is up and routing requests.
expect(response.status()).toBeLessThan(500);
console.log(`Zoo-node /v1/node/health: HTTP ${response.status()}`);
if (response.ok()) {
const health = await response.json();
console.log('Zoo-node health:', JSON.stringify(health, null, 2));
}
});
test('should verify zoo-node is running with Hanzo engine', async ({ request }) => {
const response = await request.get(`${ZOO_NODE_URL}/v1/node/available_llm_providers`);
expect(response.status()).toBeLessThan(500);
console.log(`Zoo-node /v1/node/available_llm_providers: HTTP ${response.status()}`);
if (response.ok()) {
const providers = await response.json();
console.log('Available providers:', JSON.stringify(providers, null, 2));
expect(providers).toBeDefined();
}
});
test('should check for available agents', async ({ request }) => {
const response = await request.get(`${ZOO_NODE_URL}/v1/node/available_agents`);
expect(response.status()).toBeLessThan(500);
console.log(`Zoo-node /v1/node/available_agents: HTTP ${response.status()}`);
if (response.ok()) {
const agents = await response.json();
console.log('Available agents:', JSON.stringify(agents, null, 2));
}
});
});
test.describe('Hanzo Engine Integration', () => {
test('should verify Hanzo engine is configured', async ({ request }) => {
// Check if HanzoEngine agent is available
const response = await request.get(`${ZOO_NODE_URL}/v1/node/available_agents`);
expect(response.status()).toBeLessThan(500);
console.log(`Zoo-node /v1/node/available_agents: HTTP ${response.status()}`);
if (response.ok()) {
const agents = await response.json();
console.log('Checking for HanzoEngine agent...');
const hasHanzoEngine = Array.isArray(agents) && agents.some(
(agent: any) => agent.name === 'HanzoEngine' || agent.model_type === 'zoo-backend'
);
if (hasHanzoEngine) {
console.log('HanzoEngine agent found');
} else {
console.log('HanzoEngine agent not found, checking providers...');
}
} else {
console.log('Agents endpoint not available in this build');
}
});
test('should test basic inference with Hanzo engine', async ({ request }) => {
// Skip if test model path not provided
if (!TEST_MODEL_PATH) {
test.skip();
return;
}
console.log(`Using test model: ${TEST_MODEL_PATH}`);
// Attempt to send a simple inference request
const inferencePayload = {
prompt: 'Say "Hello" if you can read this.',
max_tokens: 10,
temperature: 0.1,
};
try {
const response = await request.post(`${ZOO_NODE_URL}/v1/node/text_inference`, {
data: inferencePayload,
timeout: 30000, // 30 second timeout
});
if (response.ok()) {
const result = await response.json();
console.log('Inference result:', JSON.stringify(result, null, 2));
expect(result).toBeDefined();
console.log('✅ Hanzo engine inference successful');
} else {
console.log(`⚠️ Inference request failed with status ${response.status()}`);
const error = await response.text();
console.log('Error response:', error);
}
} catch (error) {
console.log('⚠️ Inference test error:', error);
}
});
});
test.describe('Model Loading', () => {
test('should verify model can be loaded', async ({ request }) => {
// Skip if test model path not provided
if (!TEST_MODEL_PATH) {
test.skip();
return;
}
console.log(`Test model path: ${TEST_MODEL_PATH}`);
// Check if we can query model info
const response = await request.get(`${ZOO_NODE_URL}/v1/node/available_models`);
if (response.ok()) {
const models = await response.json();
console.log('Available models:', JSON.stringify(models, null, 2));
expect(models).toBeDefined();
} else {
console.log(`⚠️ Could not fetch models (status ${response.status()})`);
}
});
test('should handle model configuration requests', async ({ request }) => {
// Test that model configuration endpoints are available
const endpoints = [
'/v1/node/available_llm_providers',
'/v1/node/available_agents',
'/v1/node/health',
];
for (const endpoint of endpoints) {
const response = await request.get(`${ZOO_NODE_URL}${endpoint}`);
console.log(`${endpoint}: ${response.status()}`);
expect(response.status()).toBeLessThan(500);
}
});
});
test.describe('UI Engine Integration', () => {
test('should display Hanzo engine in UI after onboarding', async ({ page }) => {
await page.goto('http://localhost:1420');
await page.waitForLoadState('networkidle');
// Note: This will be blocked by onboarding in a fresh install
const currentUrl = page.url();
console.log('Current URL:', currentUrl);
// Verify the app rendered (root element has content)
const rootContent = await page.locator('#root').innerHTML();
expect(rootContent.length).toBeGreaterThan(0);
console.log('App rendered successfully');
// Check if we can see any engine-related text on the page
const bodyText = await page.locator('body').textContent();
if (bodyText?.includes('Hanzo') || bodyText?.includes('Engine')) {
console.log('Found engine-related text in UI');
} else {
console.log('No engine text found (likely on onboarding screen)');
}
await page.screenshot({ path: '/tmp/engine-integration-ui.png' });
});
test('should verify frontend app loads correctly', async ({ page }) => {
await page.goto('http://localhost:1420');
await page.waitForLoadState('networkidle');
// Verify the React app mounted and rendered content
const rootElement = page.locator('#root');
await expect(rootElement).not.toBeEmpty();
console.log('Frontend app loaded successfully');
await page.screenshot({ path: '/tmp/frontend-loaded.png' });
});
});
test.describe('Error Handling', () => {
test('should handle invalid API endpoints gracefully', async ({ request }) => {
const response = await request.get(`${ZOO_NODE_URL}/v1/node/nonexistent_endpoint`);
// Should return 404 or similar, not crash
expect(response.status()).toBeGreaterThanOrEqual(400);
console.log(`Invalid endpoint returns ${response.status()} (expected)`);
});
test('should handle malformed requests', async ({ request }) => {
const response = await request.post(`${ZOO_NODE_URL}/v1/node/text_inference`, {
data: { invalid: 'payload' },
});
// Should return error status, not crash the server
expect(response.status()).toBeGreaterThanOrEqual(400);
console.log(`Malformed request returns ${response.status()} (expected)`);
});
});