/home/techb158/cosmic.abdallabala.com/tests
Edit: /home/techb158/cosmic.abdallabala.com/tests/api-workflow.test.js (7145B)
const assert = require("assert");
const fs = require("fs");
const path = require("path");
const os = require("os");
const { spawn } = require("child_process");
const root = path.join(__dirname, "..");
const sourceDb = path.join(root, "data", "database.json");
const tempDb = path.join(os.tmpdir(), `cosmic-api-db-${Date.now()}.json`);
const port = 8190 + Math.floor(Math.random() * 100);
const baseUrl = `http://127.0.0.1:${port}`;
fs.copyFileSync(sourceDb, tempDb);
function wait(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function request(pathname, options = {}) {
const response = await fetch(`${baseUrl}${pathname}`, Object.assign({
headers: { "Content-Type": "application/json" }
}, options));
const body = await response.json().catch(() => ({}));
if (!response.ok) {
throw new Error(body.error || `HTTP ${response.status}`);
}
return body;
}
async function waitForServer() {
const deadline = Date.now() + 8000;
let lastError;
while (Date.now() < deadline) {
try {
return await request("/api/health");
} catch (error) {
lastError = error;
await wait(150);
}
}
throw lastError || new Error("Server did not start");
}
(async () => {
const child = spawn(process.execPath, ["server.js"], {
cwd: root,
env: Object.assign({}, process.env, {
PORT: String(port),
COSMIC_DB_FILE: tempDb
}),
stdio: ["ignore", "pipe", "pipe"]
});
try {
const health = await waitForServer();
assert.strictEqual(health.ok, true, "Health endpoint should return ok");
const dashboardBefore = await request("/api/dashboard");
assert.strictEqual(dashboardBefore.dashboard.summary.totalRisks, 35, "Seed dashboard should have 35 risks");
const created = await request("/api/projects/cosmic-ai-risk-001/risks", {
method: "POST",
body: JSON.stringify({
title: "API workflow test risk",
dimension: "Technical",
domain: "Technical risks",
lifecyclePhase: "Testing and performance evaluation",
probability: 4,
impact: 4,
detectability: 3,
owner: "API Test Owner",
evidence: "API workflow test evidence"
})
});
assert.ok(created.risk.id, "Risk should be created through API");
const updated = await request(`/api/risks/${encodeURIComponent(created.risk.id)}`, {
method: "PATCH",
body: JSON.stringify({ status: "In mitigation", approvalStatus: "Approved" })
});
assert.strictEqual(updated.risk.status, "In mitigation", "Risk status should update through API");
assert.strictEqual(updated.risk.approvalStatus, "Approved", "Approval status should update through API");
const mitigation = await request(`/api/risks/${encodeURIComponent(created.risk.id)}/mitigations`, {
method: "POST",
body: JSON.stringify({
title: "API workflow mitigation",
status: "Done",
progress_percent: 100,
effectiveness_percent: 80,
owner: "API Test Owner"
})
});
assert.ok(mitigation.mitigation.id, "Mitigation should be created through API");
const mitigationPatch = await request(`/api/mitigations/${encodeURIComponent(mitigation.mitigation.id)}`, {
method: "PATCH",
body: JSON.stringify({
title: "API workflow mitigation",
status: "Done",
progress_percent: 100,
effectiveness_percent: 85,
evidenceTitle: "API mitigation evidence",
evidenceType: "Test"
})
});
assert.strictEqual(mitigationPatch.mitigation.status, "Done", "Mitigation should update through API");
const mitigationRead = await request(`/api/mitigations/${encodeURIComponent(mitigation.mitigation.id)}`);
assert.ok(mitigationRead.mitigation.evidenceArtifacts.length >= 1, "Mitigation should expose linked evidence through API");
const mitigationList = await request("/api/projects/cosmic-ai-risk-001/mitigations");
assert.ok(mitigationList.mitigations.some(item => item.id === mitigation.mitigation.id), "Project mitigation list should include created mitigation");
const gate = await request("/api/projects/cosmic-ai-risk-001/gate/evaluate", {
method: "POST",
body: JSON.stringify({})
});
assert.ok(["Ready", "Warning", "Blocked"].includes(gate.gate.status), "Gate should evaluate through API");
const integrations = await request("/api/projects/cosmic-ai-risk-001/integrations");
assert.strictEqual(integrations.integrations.length, 4, "API should expose Trello, Jira, Asana, and Microsoft Planner integrations");
assert.ok(integrations.integrations.some(item => item.provider === "Microsoft Planner"), "API should expose Microsoft Planner integration");
const jiraIntegration = integrations.integrations.find(item => item.provider === "Jira");
assert.ok(jiraIntegration, "Jira integration should be available through API");
const sync = await request(`/api/integrations/${encodeURIComponent(jiraIntegration.id)}/sync`, {
method: "POST",
body: JSON.stringify({ actorUserId: "USER-INTEGRATION" })
});
assert.strictEqual(sync.syncRun.createdCount, 36, "Jira sync should create mappings for current API test risks");
const mappings = await request("/api/projects/cosmic-ai-risk-001/integration-mappings");
assert.ok(mappings.mappings.some(item => item.provider === "Jira"), "Integration mappings should include Jira items");
const executiveReport = await request("/api/projects/cosmic-ai-risk-001/reports/executive");
assert.ok(executiveReport.report.summary.totalRisks >= 36, "Executive report should include current API workflow risk count");
assert.ok(executiveReport.report.integrationSummary.supportedApplications.includes("Microsoft Planner"), "Executive report should include Planner integration summary");
const riskCsvResponse = await fetch(`${baseUrl}/api/projects/cosmic-ai-risk-001/reports/risk-register.csv`);
assert.strictEqual(riskCsvResponse.ok, true, "Risk CSV report endpoint should respond successfully");
const riskCsv = await riskCsvResponse.text();
assert.ok(riskCsv.includes("API workflow test risk"), "Risk CSV should include current API workflow test risk before deletion");
const htmlResponse = await fetch(`${baseUrl}/api/projects/cosmic-ai-risk-001/reports/executive.html`);
assert.strictEqual(htmlResponse.ok, true, "Executive HTML report endpoint should respond successfully");
const html = await htmlResponse.text();
assert.ok(html.includes("Print or save as PDF"), "Executive HTML report should include print control");
await request(`/api/risks/${encodeURIComponent(created.risk.id)}`, { method: "DELETE" });
const dashboardAfter = await request("/api/dashboard");
assert.strictEqual(dashboardAfter.dashboard.summary.totalRisks, 35, "Risk count should return to seed size after delete");
console.log("All COSMIC AI-Risk API workflow tests passed.");
} finally {
child.kill("SIGTERM");
if (fs.existsSync(tempDb)) fs.unlinkSync(tempDb);
}
})().catch(error => {
console.error(error);
process.exit(1);
});