refactor: 统一项目名称从OpenFang到ZCLAW
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
重构所有代码和文档中的项目名称,将OpenFang统一更新为ZCLAW。包括: - 配置文件中的项目名称 - 代码注释和文档引用 - 环境变量和路径 - 类型定义和接口名称 - 测试用例和模拟数据 同时优化部分代码结构,移除未使用的模块,并更新相关依赖项。
This commit is contained in:
243
desktop/tests/e2e/zclaw-compat/specs/api-endpoints.spec.ts
Normal file
243
desktop/tests/e2e/zclaw-compat/specs/api-endpoints.spec.ts
Normal file
@@ -0,0 +1,243 @@
|
||||
/**
|
||||
* ZCLAW API 端点兼容性测试
|
||||
*
|
||||
* 验证 ZCLAW 前端与 ZCLAW 后端的 REST API 兼容性。
|
||||
*/
|
||||
|
||||
import { test, expect, Page } from '@playwright/test';
|
||||
import { zclawResponses } from '../fixtures/zclaw-responses';
|
||||
|
||||
const BASE_URL = 'http://localhost:1420';
|
||||
|
||||
async function setupMockAPI(page: Page) {
|
||||
await page.route('**/api/health', async route => {
|
||||
await route.fulfill({ json: zclawResponses.health });
|
||||
});
|
||||
|
||||
await page.route('**/api/status', async route => {
|
||||
await route.fulfill({ json: zclawResponses.status });
|
||||
});
|
||||
|
||||
await page.route('**/api/agents', async route => {
|
||||
if (route.request().method() === 'GET') {
|
||||
await route.fulfill({ json: zclawResponses.agents });
|
||||
} else if (route.request().method() === 'POST') {
|
||||
await route.fulfill({ json: { clone: { id: 'new-agent-001', name: 'New Agent' } } });
|
||||
}
|
||||
});
|
||||
|
||||
await page.route('**/api/agents/*', async route => {
|
||||
await route.fulfill({ json: zclawResponses.agent });
|
||||
});
|
||||
|
||||
await page.route('**/api/models', async route => {
|
||||
await route.fulfill({ json: zclawResponses.models });
|
||||
});
|
||||
|
||||
await page.route('**/api/hands', async route => {
|
||||
await route.fulfill({ json: zclawResponses.hands });
|
||||
});
|
||||
|
||||
await page.route('**/api/hands/*', async route => {
|
||||
if (route.request().method() === 'GET') {
|
||||
await route.fulfill({ json: zclawResponses.hand });
|
||||
} else if (route.request().url().includes('/activate')) {
|
||||
await route.fulfill({ json: zclawResponses.handActivation });
|
||||
}
|
||||
});
|
||||
|
||||
await page.route('**/api/workflows', async route => {
|
||||
await route.fulfill({ json: zclawResponses.workflows });
|
||||
});
|
||||
|
||||
await page.route('**/api/workflows/*', async route => {
|
||||
await route.fulfill({ json: zclawResponses.workflow });
|
||||
});
|
||||
|
||||
await page.route('**/api/sessions', async route => {
|
||||
await route.fulfill({ json: zclawResponses.sessions });
|
||||
});
|
||||
|
||||
await page.route('**/api/config', async route => {
|
||||
await route.fulfill({ json: zclawResponses.config });
|
||||
});
|
||||
|
||||
await page.route('**/api/channels', async route => {
|
||||
await route.fulfill({ json: zclawResponses.channels });
|
||||
});
|
||||
|
||||
await page.route('**/api/skills', async route => {
|
||||
await route.fulfill({ json: zclawResponses.skills });
|
||||
});
|
||||
}
|
||||
|
||||
test.describe('ZCLAW API 端点兼容性测试', () => {
|
||||
|
||||
test.describe('API-01: Health 端点', () => {
|
||||
test('应返回正确的健康状态', async ({ page }) => {
|
||||
await setupMockAPI(page);
|
||||
const response = await page.evaluate(async () => {
|
||||
const res = await fetch('/api/health');
|
||||
return res.json();
|
||||
});
|
||||
expect(response.status).toBe('ok');
|
||||
expect(response.version).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('API-02: Agents 端点', () => {
|
||||
test('应返回 Agent 列表', async ({ page }) => {
|
||||
await setupMockAPI(page);
|
||||
const response = await page.evaluate(async () => {
|
||||
const res = await fetch('/api/agents');
|
||||
return res.json();
|
||||
});
|
||||
expect(Array.isArray(response)).toBe(true);
|
||||
expect(response[0]).toHaveProperty('id');
|
||||
expect(response[0]).toHaveProperty('name');
|
||||
expect(response[0]).toHaveProperty('state');
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('API-03: Create Agent 端点', () => {
|
||||
test('应创建新 Agent', async ({ page }) => {
|
||||
await setupMockAPI(page);
|
||||
const response = await page.evaluate(async () => {
|
||||
const res = await fetch('/api/agents', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ name: 'Test Agent', model: 'qwen3.5-plus' }),
|
||||
});
|
||||
return res.json();
|
||||
});
|
||||
expect(response.clone).toHaveProperty('id');
|
||||
expect(response.clone).toHaveProperty('name');
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('API-04: Hands 端点', () => {
|
||||
test('应返回 Hands 列表', async ({ page }) => {
|
||||
await setupMockAPI(page);
|
||||
const response = await page.evaluate(async () => {
|
||||
const res = await fetch('/api/hands');
|
||||
return res.json();
|
||||
});
|
||||
expect(response).toHaveProperty('hands');
|
||||
expect(Array.isArray(response.hands)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('API-05: Hand Activation 端点', () => {
|
||||
test('应激活 Hand 并返回 instance_id', async ({ page }) => {
|
||||
await setupMockAPI(page);
|
||||
const response = await page.evaluate(async () => {
|
||||
const res = await fetch('/api/hands/Browser/activate', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({}),
|
||||
});
|
||||
return res.json();
|
||||
});
|
||||
expect(response).toHaveProperty('instance_id');
|
||||
expect(response).toHaveProperty('status');
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('API-06: Workflows 端点', () => {
|
||||
test('应返回工作流列表', async ({ page }) => {
|
||||
await setupMockAPI(page);
|
||||
const response = await page.evaluate(async () => {
|
||||
const res = await fetch('/api/workflows');
|
||||
return res.json();
|
||||
});
|
||||
expect(response).toHaveProperty('workflows');
|
||||
expect(Array.isArray(response.workflows)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('API-07: Sessions 端点', () => {
|
||||
test('应返回会话列表', async ({ page }) => {
|
||||
await setupMockAPI(page);
|
||||
const response = await page.evaluate(async () => {
|
||||
const res = await fetch('/api/sessions');
|
||||
return res.json();
|
||||
});
|
||||
expect(response).toHaveProperty('sessions');
|
||||
expect(Array.isArray(response.sessions)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('API-08: Models 端点', () => {
|
||||
test('应返回模型列表', async ({ page }) => {
|
||||
await setupMockAPI(page);
|
||||
const response = await page.evaluate(async () => {
|
||||
const res = await fetch('/api/models');
|
||||
return res.json();
|
||||
});
|
||||
expect(Array.isArray(response)).toBe(true);
|
||||
expect(response[0]).toHaveProperty('id');
|
||||
expect(response[0]).toHaveProperty('name');
|
||||
expect(response[0]).toHaveProperty('provider');
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('API-09: Config 端点', () => {
|
||||
test('应返回配置信息', async ({ page }) => {
|
||||
await setupMockAPI(page);
|
||||
const response = await page.evaluate(async () => {
|
||||
const res = await fetch('/api/config');
|
||||
return res.json();
|
||||
});
|
||||
expect(response).toHaveProperty('data_dir');
|
||||
expect(response).toHaveProperty('default_model');
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('API-10: Channels 端点', () => {
|
||||
test('应返回通道列表', async ({ page }) => {
|
||||
await setupMockAPI(page);
|
||||
const response = await page.evaluate(async () => {
|
||||
const res = await fetch('/api/channels');
|
||||
return res.json();
|
||||
});
|
||||
expect(response).toHaveProperty('channels');
|
||||
expect(Array.isArray(response.channels)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('API-11: Skills 端点', () => {
|
||||
test('应返回技能列表', async ({ page }) => {
|
||||
await setupMockAPI(page);
|
||||
const response = await page.evaluate(async () => {
|
||||
const res = await fetch('/api/skills');
|
||||
return res.json();
|
||||
});
|
||||
expect(response).toHaveProperty('skills');
|
||||
expect(Array.isArray(response.skills)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('API-12: Error Handling', () => {
|
||||
test('应正确处理 404 错误', async ({ page }) => {
|
||||
await page.route('**/api/nonexistent', async route => {
|
||||
await route.fulfill({ status: 404, json: { error: 'Not found' } });
|
||||
});
|
||||
const response = await page.evaluate(async () => {
|
||||
const res = await fetch('/api/nonexistent');
|
||||
return { status: res.status, body: await res.json() };
|
||||
});
|
||||
expect(response.status).toBe(404);
|
||||
});
|
||||
|
||||
test('应正确处理 500 错误', async ({ page }) => {
|
||||
await page.route('**/api/error', async route => {
|
||||
await route.fulfill({ status: 500, json: { error: 'Internal server error' } });
|
||||
});
|
||||
const response = await page.evaluate(async () => {
|
||||
const res = await fetch('/api/error');
|
||||
return { status: res.status, body: await res.json() };
|
||||
});
|
||||
expect(response.status).toBe(500);
|
||||
});
|
||||
});
|
||||
});
|
||||
109
desktop/tests/e2e/zclaw-compat/specs/protocol-compat.spec.ts
Normal file
109
desktop/tests/e2e/zclaw-compat/specs/protocol-compat.spec.ts
Normal file
@@ -0,0 +1,109 @@
|
||||
/**
|
||||
* ZCLAW 协议兼容性测试
|
||||
*
|
||||
* 验证 ZCLAW 前端与 ZCLAW 后端的协议兼容性。
|
||||
*/
|
||||
|
||||
import { test, expect } from '@playwright/test';
|
||||
import { zclawResponses, streamEvents, gatewayFrames } from '../fixtures/zclaw-responses';
|
||||
|
||||
const BASE_URL = 'http://localhost:1420';
|
||||
|
||||
test.describe('ZCLAW 协议兼容性测试', () => {
|
||||
|
||||
test.describe('PROTO-01: 流事件类型解析', () => {
|
||||
test('应正确解析 text_delta 事件', () => {
|
||||
const event = streamEvents.textDelta('Hello World');
|
||||
expect(event.type).toBe('text_delta');
|
||||
expect(event.content).toBe('Hello World');
|
||||
});
|
||||
|
||||
test('应正确解析 phase 事件', () => {
|
||||
const doneEvent = streamEvents.phaseDone;
|
||||
expect(doneEvent.type).toBe('phase');
|
||||
expect(doneEvent.phase).toBe('done');
|
||||
});
|
||||
|
||||
test('应正确解析 tool_call 和 tool_result 事件', () => {
|
||||
const toolCall = streamEvents.toolCall('search', { query: 'test' });
|
||||
expect(toolCall.type).toBe('tool_call');
|
||||
expect(toolCall.tool).toBe('search');
|
||||
|
||||
const toolResult = streamEvents.toolResult('search', { results: [] });
|
||||
expect(toolResult.type).toBe('tool_result');
|
||||
});
|
||||
|
||||
test('应正确解析 hand 事件', () => {
|
||||
const handEvent = streamEvents.hand('Browser', 'completed', { pages: 5 });
|
||||
expect(handEvent.type).toBe('hand');
|
||||
expect(handEvent.hand_name).toBe('Browser');
|
||||
expect(handEvent.hand_status).toBe('completed');
|
||||
});
|
||||
|
||||
test('应正确解析 error 事件', () => {
|
||||
const errorEvent = streamEvents.error('TIMEOUT', 'Request timed out');
|
||||
expect(errorEvent.type).toBe('error');
|
||||
expect(errorEvent.code).toBe('TIMEOUT');
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('PROTO-02: Gateway 帧格式兼容', () => {
|
||||
test('应正确构造请求帧', () => {
|
||||
const frame = gatewayFrames.request(1, 'chat', { message: 'Hello' });
|
||||
expect(frame.type).toBe('req');
|
||||
expect(frame.id).toBe(1);
|
||||
expect(frame.method).toBe('chat');
|
||||
});
|
||||
|
||||
test('应正确构造响应帧', () => {
|
||||
const frame = gatewayFrames.response(1, { status: 'ok' });
|
||||
expect(frame.type).toBe('res');
|
||||
expect(frame.id).toBe(1);
|
||||
});
|
||||
|
||||
test('应正确构造事件帧', () => {
|
||||
const frame = gatewayFrames.event({ type: 'text_delta', content: 'test' });
|
||||
expect(frame.type).toBe('event');
|
||||
});
|
||||
|
||||
test('应正确构造 pong 帧', () => {
|
||||
const frame = gatewayFrames.pong(1);
|
||||
expect(frame.type).toBe('pong');
|
||||
expect(frame.id).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('PROTO-03: 连接状态管理', () => {
|
||||
const validStates = ['disconnected', 'connecting', 'handshaking', 'connected', 'reconnecting'];
|
||||
|
||||
test('连接状态应为有效值', () => {
|
||||
validStates.forEach(state => {
|
||||
expect(['disconnected', 'connecting', 'handshaking', 'connected', 'reconnecting']).toContain(state);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('PROTO-04: 心跳机制', () => {
|
||||
test('心跳帧格式正确', () => {
|
||||
const pingFrame = { type: 'ping' };
|
||||
expect(pingFrame.type).toBe('ping');
|
||||
});
|
||||
|
||||
test('pong 响应格式正确', () => {
|
||||
const pongFrame = gatewayFrames.pong(1);
|
||||
expect(pongFrame.type).toBe('pong');
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('PROTO-05: 设备认证流程', () => {
|
||||
test('设备认证响应格式', () => {
|
||||
const authResponse = {
|
||||
status: 'authenticated',
|
||||
device_id: 'device-001',
|
||||
token: 'jwt-token-here',
|
||||
};
|
||||
expect(authResponse.status).toBe('authenticated');
|
||||
expect(authResponse.device_id).toBeDefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user