UNPKG

@access-mcp/system-status

Version:

MCP server for ACCESS-CI System Status and Outages API

252 lines (251 loc) 13.1 kB
import { describe, it, expect, beforeEach } from "vitest"; import { SystemStatusServer } from "../server.js"; describe("SystemStatusServer Integration Tests", () => { let server; beforeEach(() => { server = new SystemStatusServer(); }); describe("Real API Integration", () => { it("should fetch current outages from real API", async () => { const result = await server["handleToolCall"]({ method: "tools/call", params: { name: "get_infrastructure_news", arguments: { time: "current", limit: 5 }, }, }); const content = result.content[0]; const responseData = JSON.parse(content.text); expect(responseData).toHaveProperty("total_outages"); expect(responseData).toHaveProperty("affected_resources"); expect(responseData).toHaveProperty("severity_counts"); expect(responseData.severity_counts).toHaveProperty("high"); expect(responseData.severity_counts).toHaveProperty("medium"); expect(responseData.severity_counts).toHaveProperty("low"); expect(responseData.severity_counts).toHaveProperty("unknown"); expect(Array.isArray(responseData.outages)).toBe(true); // Check enhanced fields if (responseData.outages.length > 0) { const outage = responseData.outages[0]; expect(outage).toHaveProperty("severity"); // The API response includes original fields like Subject, OutageStart, OutageEnd, etc. expect(outage).toHaveProperty("Subject"); } }, 10000); it("should fetch scheduled maintenance from real API", async () => { const result = await server["handleToolCall"]({ method: "tools/call", params: { name: "get_infrastructure_news", arguments: { time: "scheduled", limit: 5 }, }, }); const content = result.content[0]; const responseData = JSON.parse(content.text); expect(responseData).toHaveProperty("total_scheduled"); expect(responseData).toHaveProperty("upcoming_24h"); expect(responseData).toHaveProperty("upcoming_week"); expect(responseData).toHaveProperty("affected_resources"); expect(Array.isArray(responseData.maintenance)).toBe(true); // Check enhanced fields if (responseData.maintenance.length > 0) { const maintenance = responseData.maintenance[0]; expect(maintenance).toHaveProperty("hours_until_start"); expect(maintenance).toHaveProperty("has_scheduled_time"); expect(maintenance.hours_until_start).toSatisfy((val) => val === null || typeof val === "number"); } }, 10000); it("should fetch past outages from real API", async () => { const result = await server["handleToolCall"]({ method: "tools/call", params: { name: "get_infrastructure_news", arguments: { time: "past", limit: 5 }, }, }); const content = result.content[0]; const responseData = JSON.parse(content.text); expect(responseData).toHaveProperty("total_past_outages"); expect(responseData).toHaveProperty("recent_outages_30_days"); expect(responseData).toHaveProperty("affected_resources"); expect(responseData).toHaveProperty("outage_types"); expect(responseData).toHaveProperty("average_duration_hours"); expect(Array.isArray(responseData.outages)).toBe(true); // Check enhanced fields if (responseData.outages.length > 0) { const outage = responseData.outages[0]; expect(outage).toHaveProperty("duration_hours"); expect(outage).toHaveProperty("days_ago"); expect(outage).toHaveProperty("outage_type"); expect(outage.days_ago).toSatisfy((val) => val === null || typeof val === "number"); } }, 10000); it("should get comprehensive system announcements", async () => { const result = await server["handleToolCall"]({ method: "tools/call", params: { name: "get_infrastructure_news", arguments: { time: "all", limit: 20 }, }, }); const content = result.content[0]; const responseData = JSON.parse(content.text); expect(responseData).toHaveProperty("total_announcements"); expect(responseData).toHaveProperty("current_outages"); expect(responseData).toHaveProperty("scheduled_maintenance"); expect(responseData).toHaveProperty("recent_past_outages"); expect(responseData).toHaveProperty("categories"); expect(responseData.categories).toHaveProperty("current"); expect(responseData.categories).toHaveProperty("scheduled"); expect(responseData.categories).toHaveProperty("recent_past"); expect(Array.isArray(responseData.announcements)).toBe(true); // Check categorization if (responseData.announcements.length > 0) { const announcement = responseData.announcements[0]; expect(announcement).toHaveProperty("category"); expect(["current", "scheduled", "recent_past"]).toContain(announcement.category); } }, 15000); it("should check resource status with direct method", async () => { // Test with human-readable names - these get resolved to full IDs const result = await server["handleToolCall"]({ method: "tools/call", params: { name: "get_infrastructure_news", arguments: { ids: ["Anvil", "Delta", "Expanse"], use_group_api: false, }, }, }); const content = result.content[0]; const responseData = JSON.parse(content.text); expect(responseData).toHaveProperty("checked_at"); expect(responseData).toHaveProperty("resources_checked", 3); expect(responseData).toHaveProperty("operational"); expect(responseData).toHaveProperty("affected"); expect(responseData).toHaveProperty("api_method", "direct_outages_check"); expect(Array.isArray(responseData.resource_status)).toBe(true); expect(responseData.resource_status).toHaveLength(3); // Check resource status structure - IDs should be resolved to full format responseData.resource_status.forEach((resource) => { expect(resource).toHaveProperty("resource_id"); expect(resource.resource_id).toContain(".access-ci.org"); // Resolved to full ID expect(resource).toHaveProperty("status"); expect(["operational", "affected"]).toContain(resource.status); expect(resource).toHaveProperty("active_outages"); expect(Array.isArray(resource.outage_details)).toBe(true); }); }, 15000); it("should test group API functionality", async () => { // Test group API with a human-readable name const result = await server["handleToolCall"]({ method: "tools/call", params: { name: "get_infrastructure_news", arguments: { ids: ["Anvil"], use_group_api: true, }, }, }); const content = result.content[0]; const responseData = JSON.parse(content.text); expect(responseData).toHaveProperty("api_method", "resource_group_api"); expect(responseData).toHaveProperty("resources_checked", 1); expect(responseData.resource_status).toHaveLength(1); const resourceStatus = responseData.resource_status[0]; expect(resourceStatus.resource_id).toContain("anvil"); // Resolved ID contains anvil expect(resourceStatus.resource_id).toContain(".access-ci.org"); expect(resourceStatus).toHaveProperty("api_method"); expect(["group_specific", "group_specific_failed"]).toContain(resourceStatus.api_method); // If it succeeded, check structure if (resourceStatus.api_method === "group_specific") { expect(resourceStatus).toHaveProperty("status"); expect(["operational", "affected"]).toContain(resourceStatus.status); } // If it failed, check error handling if (resourceStatus.api_method === "group_specific_failed") { expect(resourceStatus.status).toBe("unknown"); expect(resourceStatus).toHaveProperty("error"); } }, 15000); it("should filter outages by resource correctly", async () => { const result = await server["handleToolCall"]({ method: "tools/call", params: { name: "get_infrastructure_news", arguments: { time: "current", query: "anvil" }, }, }); const content = result.content[0]; const responseData = JSON.parse(content.text); expect(responseData).toHaveProperty("total_outages"); // If there are any results, they should match the filter if (responseData.outages.length > 0) { responseData.outages.forEach((outage) => { const matchesFilter = outage.Subject?.toLowerCase().includes("anvil") || outage.AffectedResources?.some((resource) => resource.ResourceName?.toLowerCase().includes("anvil") || resource.ResourceID?.toString().includes("anvil")); expect(matchesFilter).toBe(true); }); } }, 10000); it("should handle resource reads for all endpoints", async () => { const resources = [ "accessci://system-status", "accessci://outages/current", "accessci://outages/scheduled", "accessci://outages/past", ]; for (const uri of resources) { const result = await server["handleResourceRead"]({ method: "resources/read", params: { uri }, }); expect(result.contents).toHaveLength(1); expect(result.contents[0]).toHaveProperty("uri", uri); expect(result.contents[0]).toHaveProperty("mimeType"); expect(result.contents[0]).toHaveProperty("text"); if (uri !== "accessci://system-status") { // JSON resources should have valid JSON const content = result.contents[0]; if ("text" in content) { expect(() => JSON.parse(content.text)).not.toThrow(); } } } }, 15000); }); describe("Edge Cases and Error Handling", () => { it("should handle empty API responses", async () => { // This tests the robustness of our logic with potentially empty responses const result = await server["handleToolCall"]({ method: "tools/call", params: { name: "get_infrastructure_news", arguments: { time: "current", query: "nonexistent-resource-xyz-12345" }, }, }); const content = result.content[0]; const responseData = JSON.parse(content.text); expect(responseData).toHaveProperty("total_outages", 0); expect(responseData.outages).toHaveLength(0); expect(responseData.affected_resources).toHaveLength(0); }, 10000); it("should handle large limit values gracefully", async () => { const result = await server["handleToolCall"]({ method: "tools/call", params: { name: "get_infrastructure_news", arguments: { time: "past", limit: 1000 }, }, }); const content = result.content[0]; const responseData = JSON.parse(content.text); expect(responseData).toHaveProperty("total_past_outages"); // Should not crash or timeout expect(responseData.outages.length).toBeLessThanOrEqual(1000); }, 15000); }); });