mirror of
https://github.com/hanzoai/desktop.git
synced 2026-10-30 14:58:42 +00:00
220 lines
7.5 KiB
TypeScript
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)`);
|
|
});
|
|
});
|