import { test, expect } from "@playwright/test"; const BACKEND_URL = process.env.BACKEND_URL || "http://backend:8000"; const PLUGIN_SERVER_URL = process.env.PLUGIN_SERVER_URL || "https://vps.t1:8000"; const TEST_TIMEOUT = 120000; test.describe("Real OpenClaw Plugin Integration", () => { test.setTimeout(TEST_TIMEOUT); test.beforeAll(async () => { console.log("🔌 Testing real plugin integration..."); console.log(" Backend: " + BACKEND_URL); console.log(" Plugin Server: " + PLUGIN_SERVER_URL); }); async function loginAsAdmin(request: any) { const loginRes = await request.post(BACKEND_URL + "/auth/token", { form: { username: "admin", password: "admin123" }, headers: { "Content-Type": "application/x-www-form-urlencoded" }, }); expect(loginRes.ok()).toBeTruthy(); const loginData = await loginRes.json(); return loginData.access_token; } test("should register server and generate API key", async ({ request }) => { const token = await loginAsAdmin(request); const serverRes = await request.post(BACKEND_URL + "/monitor/admin/servers", { headers: { Authorization: "Bearer " + token }, data: { identifier: "test-real-plugin-vps-t1", display_name: "Test Real Plugin on vps.t1", }, }); expect(serverRes.ok()).toBeTruthy(); const serverData = await serverRes.json(); const serverId = serverData.id; console.log("✅ Server registered: ID=" + serverId); const apiKeyRes = await request.post(BACKEND_URL + "/monitor/admin/servers/" + serverId + "/api-key", { headers: { Authorization: "Bearer " + token }, }); expect(apiKeyRes.ok()).toBeTruthy(); const apiKeyData = await apiKeyRes.json(); expect(apiKeyData.api_key).toBeDefined(); expect(apiKeyData.api_key.length).toBeGreaterThan(30); console.log("✅ API Key generated: " + apiKeyData.api_key.substring(0, 10) + "..."); process.env.TEST_SERVER_ID = String(serverId); process.env.TEST_API_KEY = apiKeyData.api_key; }); test("should receive heartbeat from real plugin", async ({ request }) => { const apiKey = process.env.TEST_API_KEY; const serverId = process.env.TEST_SERVER_ID; expect(apiKey).toBeDefined(); expect(serverId).toBeDefined(); const heartbeatRes = await request.post(BACKEND_URL + "/monitor/server/heartbeat-v2", { headers: { "X-API-Key": apiKey }, data: { identifier: "test-real-plugin-vps-t1", openclaw_version: "0.1.0-test", agents: [{ id: "agent-1", name: "TestAgent", status: "active" }], cpu_pct: 45.5, mem_pct: 60.2, disk_pct: 70.1, swap_pct: 10.0, load_avg: [1.2, 0.8, 0.5], uptime_seconds: 3600, }, }); expect(heartbeatRes.ok()).toBeTruthy(); const heartbeatData = await heartbeatRes.json(); expect(heartbeatData.ok).toBe(true); expect(heartbeatData.server_id).toBe(parseInt(serverId)); expect(heartbeatData.identifier).toBe("test-real-plugin-vps-t1"); expect(heartbeatData.last_seen_at).toBeDefined(); console.log("✅ Heartbeat accepted by backend"); }); test("should persist telemetry data to database", async ({ request }) => { const apiKey = process.env.TEST_API_KEY; const serverId = process.env.TEST_SERVER_ID; await new Promise(resolve => setTimeout(resolve, 1000)); const token = await loginAsAdmin(request); const stateRes = await request.get(BACKEND_URL + "/monitor/admin/servers", { headers: { Authorization: "Bearer " + token }, }); expect(stateRes.ok()).toBeTruthy(); const servers = await stateRes.json(); const testServer = servers.find((s: any) => s.id === parseInt(serverId)); expect(testServer).toBeDefined(); expect(testServer.openclaw_version).toBe("0.1.0-test"); expect(testServer.cpu_pct).toBeCloseTo(45.5, 1); expect(testServer.mem_pct).toBeCloseTo(60.2, 1); expect(testServer.last_seen_at).toBeDefined(); console.log("✅ Telemetry data persisted"); }); test("should reject heartbeat with invalid API key", async ({ request }) => { const heartbeatRes = await request.post(BACKEND_URL + "/monitor/server/heartbeat-v2", { headers: { "X-API-Key": "invalid-api-key-12345" }, data: { identifier: "test-real-plugin-vps-t1", openclaw_version: "0.1.0-test", }, }); expect(heartbeatRes.status()).toBe(401); const errorData = await heartbeatRes.json(); expect(errorData.detail).toContain("Invalid"); console.log("✅ Invalid API key correctly rejected (401)"); }); test("should reject heartbeat without API key", async ({ request }) => { const heartbeatRes = await request.post(BACKEND_URL + "/monitor/server/heartbeat-v2", { data: { identifier: "test-real-plugin-vps-t1", openclaw_version: "0.1.0-test", }, }); expect(heartbeatRes.status()).toBe(422); console.log("✅ Missing API key correctly rejected (422)"); }); test("should revoke API key and reject subsequent heartbeats", async ({ request }) => { const apiKey = process.env.TEST_API_KEY; const serverId = process.env.TEST_SERVER_ID; const token = await loginAsAdmin(request); const revokeRes = await request.delete(BACKEND_URL + "/monitor/admin/servers/" + serverId + "/api-key", { headers: { Authorization: "Bearer " + token }, }); expect(revokeRes.status()).toBe(204); console.log("✅ API key revoked"); const heartbeatRes = await request.post(BACKEND_URL + "/monitor/server/heartbeat-v2", { headers: { "X-API-Key": apiKey }, data: { identifier: "test-real-plugin-vps-t1", openclaw_version: "0.1.0-test", }, }); expect(heartbeatRes.status()).toBe(401); console.log("✅ Revoked API key correctly rejected"); }); test.afterAll(async ({ request }) => { const serverId = process.env.TEST_SERVER_ID; if (!serverId) return; try { const token = await loginAsAdmin(request); await request.delete(BACKEND_URL + "/monitor/admin/servers/" + serverId, { headers: { Authorization: "Bearer " + token }, }); console.log("✅ Test server " + serverId + " cleaned up"); } catch (e) { console.log("⚠️ Cleanup warning: " + e); } }); });