Files
16gagent/test/benchmark/smoke-one.mjs
T
2026-06-06 10:40:48 +08:00

147 lines
5.5 KiB
JavaScript

#!/usr/bin/env node
/**
* Quick single-domain end-to-end smoke test
* Usage: node test/benchmark/smoke-one.mjs
*/
import { execSync, spawn } from "node:child_process";
import { existsSync, rmSync, mkdirSync } from "node:fs";
import * as http from "node:http";
const TMP = "/tmp/smoke-test";
async function main() {
console.log("=== Smoke Test: PetCare end-to-end ===\n");
rmSync(TMP, { recursive: true, force: true });
// 1. Generate
console.log("1. Generate fullstack...");
const { composeFullstack, writeFullstack } = await import("../../scripts/fullstack-composer-agent.mjs");
const prd = { projectName: "PetCare", domain: "pet", summary: "宠物管理", features: [], pages: [{ name: "首页", route: "/home" }], personas: [], userStories: [] };
const arch = {
databaseSchema: [{
table: "pets", fields: [
{ name: "id", type: "TEXT", constraints: "PK" },
{ name: "owner_id", type: "TEXT", constraints: "NOT NULL, FK → users.id" },
{ name: "name", type: "TEXT", constraints: "NOT NULL" },
{ name: "species", type: "TEXT" },
{ name: "created_at", type: "TEXT" }
]
}],
apiDesign: [{ resource: "pets", basePath: "/api/pets", endpoints: [
{ method: "GET", path: "/api/pets" }, { method: "POST", path: "/api/pets" },
{ method: "GET", path: "/api/pets/:id" }, { method: "PUT", path: "/api/pets/:id" }, { method: "DELETE", path: "/api/pets/:id" }
]}]
};
const result = await composeFullstack(prd, arch);
writeFullstack(result, TMP);
console.log(`${result.stats.totalFiles} files`);
// 2. Install
console.log("2. npm install...");
execSync("npm install --no-audit --no-fund", { cwd: TMP, encoding: "utf-8", timeout: 120000, stdio: "pipe" });
// Also rm any stale db
try { rmSync(TMP + "/apps/api/data", { recursive: true, force: true }); } catch {}
console.log(" ✅");
// 3. Build
console.log("3. tsc --noEmit...");
execSync("npx tsc --noEmit", { cwd: TMP + "/apps/api", encoding: "utf-8", timeout: 60000, stdio: "pipe" });
console.log(" ✅");
// 4. Start server
console.log("4. Start server...");
const proc = spawn("npx", ["tsx", "src/index.ts"], {
cwd: TMP + "/apps/api",
env: { ...process.env, JWT_SECRET: "test", DATABASE_URL: ":memory:", PORT: "5678" },
stdio: "pipe",
});
await waitFor("http://localhost:5678/api/health", 15000);
console.log(" ✅");
// 5. Auth
console.log("5. Register...");
const regResult = await postJson("http://localhost:5678/api/auth/register", { username: `u_${Date.now()}`, password: "123456" });
const token = regResult.token;
const userId = regResult.user?.id;
if (!token || !userId) throw new Error(`Register failed: ${JSON.stringify(regResult)}`);
console.log(` ✅ user=${userId.slice(0, 12)}`);
console.log("6. Login...");
const { token: token2 } = await postJson("http://localhost:5678/api/auth/login", { username: "u1", password: "123456" });
const authToken = token2 || token;
console.log(``);
// 6. CRUD
console.log("7. Create pet...");
const { data: pet } = await postJson("http://localhost:5678/api/pets", { name: "毛球", species: "猫", ownerId: userId }, authToken);
console.log(`${pet.id.slice(0, 12)}`);
console.log("8. List pets...");
const list = await getJson("http://localhost:5678/api/pets", authToken);
console.log(`${list.data.length} pet(s)`);
console.log("9. Update pet...");
await putJson(`http://localhost:5678/api/pets/${pet.id}`, { species: "狗" }, authToken);
console.log(" ✅");
console.log("10. Delete pet...");
const delCode = await deleteReq(`http://localhost:5678/api/pets/${pet.id}`, authToken);
console.log(` ✅ HTTP ${delCode}`);
proc.kill("SIGTERM");
console.log("\n🎉 ALL SMOKE TESTS PASSED");
}
// --- HTTP helpers ---
function waitFor(url, timeout) {
return new Promise((resolve, reject) => {
const start = Date.now();
function check() {
http.get(url, (res) => { res.resume(); res.on("end", resolve); }).on("error", () => {
if (Date.now() - start > timeout) reject(new Error("Server never started"));
else setTimeout(check, 500);
});
}
check();
});
}
function httpReq({ url, method, body, token }) {
return new Promise((resolve, reject) => {
const u = new URL(url);
const payload = body ? JSON.stringify(body) : undefined;
const headers = { "Content-Type": "application/json" };
if (payload) headers["Content-Length"] = Buffer.byteLength(payload);
if (token) headers["Authorization"] = `Bearer ${token}`;
const req = http.request({ hostname: u.hostname, port: u.port, path: u.pathname, method, headers, timeout: 10000 }, (res) => {
let d = "";
res.on("data", c => d += c);
res.on("end", () => resolve({ status: res.statusCode, body: d }));
});
req.on("error", reject);
if (payload) req.write(payload);
req.end();
});
}
async function postJson(url, body, token) {
const { body: b } = await httpReq({ url, method: "POST", body, token });
return JSON.parse(b);
}
async function getJson(url, token) {
const { body: b } = await httpReq({ url, method: "GET", token });
return JSON.parse(b);
}
async function putJson(url, body, token) {
const { body: b } = await httpReq({ url, method: "PUT", body, token });
return JSON.parse(b);
}
async function deleteReq(url, token) {
const { status } = await httpReq({ url, method: "DELETE", token });
return status;
}
main().catch(err => { console.error("FAIL:", err.message); process.exit(1); });