feat(FN-4029): expose database health in API and store contracts
Adds database health endpoints to the API layer (FN-4029), exposing a new store health accessor through `api-node.ts` and `api/legacy.ts`, with aligned auth middleware integration tests and updated architecture documentation. Fusion-Task-Id: FN-4029
This commit is contained in:
@@ -33,7 +33,12 @@ describe("api-node", () => {
|
||||
|
||||
describe("fetchRemoteNodeHealth", () => {
|
||||
it("calls proxyApi with correct path and nodeId", async () => {
|
||||
const mockHealth = { status: "online", version: "1.0.0", nodeId: "node_abc" };
|
||||
const mockHealth = {
|
||||
status: "online",
|
||||
version: "1.0.0",
|
||||
nodeId: "node_abc",
|
||||
database: { healthy: true, isRunning: false, lastCheckedAt: null },
|
||||
};
|
||||
mockProxyApi.mockResolvedValueOnce(mockHealth);
|
||||
|
||||
const result = await fetchRemoteNodeHealth("node_abc");
|
||||
@@ -44,7 +49,12 @@ describe("api-node", () => {
|
||||
});
|
||||
|
||||
it("returns remote node health data", async () => {
|
||||
const mockHealth = { status: "offline", version: "2.0.0", nodeId: "node_xyz" };
|
||||
const mockHealth = {
|
||||
status: "offline",
|
||||
version: "2.0.0",
|
||||
nodeId: "node_xyz",
|
||||
database: { healthy: false, isRunning: true, lastCheckedAt: "2026-05-11T10:00:00.000Z" },
|
||||
};
|
||||
mockProxyApi.mockResolvedValueOnce(mockHealth);
|
||||
|
||||
const result = await fetchRemoteNodeHealth("node_xyz");
|
||||
@@ -52,6 +62,11 @@ describe("api-node", () => {
|
||||
expect(result.status).toBe("offline");
|
||||
expect(result.version).toBe("2.0.0");
|
||||
expect(result.nodeId).toBe("node_xyz");
|
||||
expect(result.database).toEqual({
|
||||
healthy: false,
|
||||
isRunning: true,
|
||||
lastCheckedAt: "2026-05-11T10:00:00.000Z",
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -12,6 +12,11 @@ export interface RemoteNodeHealth {
|
||||
status: string;
|
||||
version: string;
|
||||
nodeId: string;
|
||||
database: {
|
||||
healthy: boolean;
|
||||
lastCheckedAt: string | null;
|
||||
isRunning: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
/** Fetch health information from a remote node */
|
||||
|
||||
@@ -199,6 +199,11 @@ export interface DashboardHealthResponse {
|
||||
status: string;
|
||||
version: string;
|
||||
uptime: number;
|
||||
database: {
|
||||
healthy: boolean;
|
||||
lastCheckedAt: string | null;
|
||||
isRunning: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchDashboardHealth(): Promise<DashboardHealthResponse> {
|
||||
|
||||
@@ -66,9 +66,9 @@ class MockStore extends EventEmitter {
|
||||
|
||||
getDatabaseHealth() {
|
||||
return {
|
||||
corruptionDetected: false,
|
||||
integrityCheckPending: false,
|
||||
integrityCheckLastRunAt: null,
|
||||
healthy: true,
|
||||
isRunning: false,
|
||||
lastCheckedAt: null,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -69,9 +69,9 @@ function createMockStore(overrides: Partial<TaskStore> = {}): TaskStore {
|
||||
prepare: vi.fn().mockReturnValue({ run: vi.fn().mockReturnValue({ changes: 0 }), get: vi.fn(), all: vi.fn().mockReturnValue([]) }),
|
||||
}),
|
||||
getDatabaseHealth: vi.fn().mockReturnValue({
|
||||
corruptionDetected: false,
|
||||
integrityCheckPending: false,
|
||||
integrityCheckLastRunAt: null,
|
||||
healthy: true,
|
||||
isRunning: false,
|
||||
lastCheckedAt: null,
|
||||
}),
|
||||
getMissionStore: vi.fn().mockReturnValue({
|
||||
listMissions: vi.fn().mockReturnValue([]),
|
||||
@@ -313,9 +313,9 @@ describe("createServer health and headless mode", () => {
|
||||
version: CLI_PACKAGE_VERSION,
|
||||
uptime: expect.any(Number),
|
||||
database: {
|
||||
corruptionDetected: false,
|
||||
integrityCheckPending: false,
|
||||
integrityCheckLastRunAt: null,
|
||||
healthy: true,
|
||||
isRunning: false,
|
||||
lastCheckedAt: null,
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -323,9 +323,9 @@ describe("createServer health and headless mode", () => {
|
||||
it("reports degraded status when database corruption is detected", async () => {
|
||||
const store = createMockStore({
|
||||
getDatabaseHealth: vi.fn().mockReturnValue({
|
||||
corruptionDetected: true,
|
||||
integrityCheckPending: false,
|
||||
integrityCheckLastRunAt: "2026-05-11T10:00:00.000Z",
|
||||
healthy: false,
|
||||
isRunning: false,
|
||||
lastCheckedAt: new Date("2026-05-11T10:00:00.000Z"),
|
||||
}),
|
||||
});
|
||||
const app = createServer(store);
|
||||
@@ -338,9 +338,9 @@ describe("createServer health and headless mode", () => {
|
||||
version: CLI_PACKAGE_VERSION,
|
||||
uptime: expect.any(Number),
|
||||
database: {
|
||||
corruptionDetected: true,
|
||||
integrityCheckPending: false,
|
||||
integrityCheckLastRunAt: "2026-05-11T10:00:00.000Z",
|
||||
healthy: false,
|
||||
isRunning: false,
|
||||
lastCheckedAt: "2026-05-11T10:00:00.000Z",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1058,7 +1058,7 @@ export function createServer(store: TaskStore, options?: ServerOptions): ReturnT
|
||||
app.get("/api/health", (_req, res) => {
|
||||
const database = store.getDatabaseHealth();
|
||||
res.json({
|
||||
status: database.corruptionDetected ? "degraded" : "ok",
|
||||
status: database.healthy ? "ok" : "degraded",
|
||||
version: cliPackageVersion,
|
||||
uptime: Math.floor(process.uptime()),
|
||||
database,
|
||||
|
||||
Reference in New Issue
Block a user