Files
vibn-frontend/vibn-agent-runner/dist/test-execute-hardening.js

140 lines
5.6 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const child_process_1 = require("child_process");
const http_1 = __importDefault(require("http"));
// We will start the runner server on port 3334
const PORT = 3334;
const BASE_URL = `http://localhost:${PORT}`;
console.log("🧪 Starting AgentRunner Hardening Test Suite...");
// Set up environment variables
const env = {
...process.env,
PORT: String(PORT),
AGENT_RUNNER_SECRET: "test-secret-123",
GOOGLE_API_KEY: "dummy-key-for-testing", // Pass dummy key to avoid Gemini API initialization crash
VIBN_API_URL: "http://localhost:3335", // Mock backend
};
// Start mock backend on port 3335 to catch PATCH callbacks and verify headers
let receivedHeaders = null;
let receivedBody = null;
const mockBackend = http_1.default.createServer((req, res) => {
receivedHeaders = req.headers;
let body = "";
req.on("data", (chunk) => {
body += chunk;
});
req.on("end", () => {
try {
receivedBody = JSON.parse(body);
}
catch {
receivedBody = body;
}
res.writeHead(200, { "Content-Type": "application/json" });
res.end(JSON.stringify({ ok: true }));
});
});
mockBackend.listen(3335, () => {
console.log("✓ Mock backend server listening on port 3335");
});
// Spawn the runner server
const serverProcess = (0, child_process_1.spawn)("npx", ["ts-node", "src/server.ts"], {
env,
stdio: "pipe",
});
// Wait for server to start
serverProcess.stdout.on("data", (data) => {
const output = data.toString();
console.log(`[Server Out] ${output.trim()}`);
});
serverProcess.stderr.on("data", (data) => {
console.error(`[Server Err] ${data.toString()}`);
});
// Helper function to sleep
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
async function runTests() {
// Wait 4 seconds for server to boot
await sleep(4000);
let passed = 0;
let failed = 0;
const assert = (condition, message) => {
if (condition) {
console.log(` 🟢 PASSED: ${message}`);
passed++;
}
else {
console.error(` 🔴 FAILED: ${message}`);
failed++;
}
};
try {
// Test 1: Empty appPath should be accepted and fall back to "."
console.log("\n1⃣ Testing appPath empty string fallback...");
const res1 = await fetch(`${BASE_URL}/agent/execute`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
sessionId: "test-session-1",
projectId: "test-project-1",
task: "Test empty appPath",
appPath: "", // Empty string!
giteaRepo: "test-repo",
}),
});
assert(res1.status === 202, `Should return 202, got ${res1.status}`);
const data1 = (await res1.json());
assert(data1.sessionId === "test-session-1", `Should return correct sessionId, got ${data1.sessionId}`);
// Test 2: Missing sessionId should return 400
console.log("\n2⃣ Testing missing required parameters (sessionId)...");
const res2 = await fetch(`${BASE_URL}/agent/execute`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
projectId: "test-project-1",
task: "Test missing sessionId",
appPath: ".",
}),
});
assert(res2.status === 400, `Should return 400, got ${res2.status}`);
// Test 3: Emergency callback headers should include x-agent-runner-secret
console.log("\n3⃣ Testing early failure callback headers...");
// Trigger a clone failure by passing a malformed giteaRepo containing slash,
// which triggers clone instead of default workspace but will fail clone.
console.log("Triggering clone failure on mock Gitea...");
const res3 = await fetch(`${BASE_URL}/agent/execute`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
sessionId: "test-session-3",
projectId: "test-project-3",
task: "Trigger crash",
appPath: ".",
giteaRepo: "invalid_owner/invalid_repo",
}),
});
assert(res3.status === 202, `Should return 202 Accepted, got ${res3.status}`);
// Wait for server to process async task and fail, calling our mock backend PATCH
console.log("Waiting for runner callback on mock backend...");
await sleep(4000);
assert(receivedHeaders !== null, "Should call mock backend PATCH endpoint");
if (receivedHeaders) {
assert(receivedHeaders["x-agent-runner-secret"] === "test-secret-123", `Callback should include secret header 'test-secret-123', got '${receivedHeaders["x-agent-runner-secret"]}'`);
assert(receivedBody && receivedBody.status === "failed", `Callback body should have status 'failed', got '${receivedBody?.status}'`);
}
}
catch (err) {
console.error("Test execution failed with exception:", err);
}
finally {
console.log("\n🧹 Cleaning up test servers...");
serverProcess.kill();
mockBackend.close();
console.log(`\n📊 Tests complete. Passed: ${passed}, Failed: ${failed}`);
process.exit(failed > 0 ? 1 : 0);
}
}
runTests();