feat(FN-1228): complete Step 1 — Backend API Routes modifications
- Modified POST /api/nodes to make type optional (defaults to 'remote') - Changed DELETE /api/nodes/:id to return 204 No Content - Updated GET /api/nodes/:id/metrics to return SystemMetrics from node.systemMetrics - Added GET /api/mesh/state route for full mesh topology state
This commit is contained in:
@@ -588,6 +588,14 @@ export function MissionManager({ isOpen, isInline = false, onClose, addToast, pr
|
||||
void loadMissionHealth(missions);
|
||||
};
|
||||
|
||||
const handleFeatureUpdated = () => {
|
||||
refreshHealth();
|
||||
// Reload the selected mission detail to reflect updated feature status
|
||||
if (selectedMission) {
|
||||
void loadMissionDetail(selectedMission.id);
|
||||
}
|
||||
};
|
||||
|
||||
const handleMissionEvent = (rawEvent: Event) => {
|
||||
refreshHealth();
|
||||
|
||||
@@ -634,13 +642,13 @@ export function MissionManager({ isOpen, isInline = false, onClose, addToast, pr
|
||||
|
||||
eventSource.addEventListener("mission:updated", refreshHealth);
|
||||
eventSource.addEventListener("slice:updated", refreshHealth);
|
||||
eventSource.addEventListener("feature:updated", refreshHealth);
|
||||
eventSource.addEventListener("feature:updated", handleFeatureUpdated);
|
||||
eventSource.addEventListener("mission:event", handleMissionEvent);
|
||||
|
||||
return () => {
|
||||
eventSource.removeEventListener("mission:updated", refreshHealth);
|
||||
eventSource.removeEventListener("slice:updated", refreshHealth);
|
||||
eventSource.removeEventListener("feature:updated", refreshHealth);
|
||||
eventSource.removeEventListener("feature:updated", handleFeatureUpdated);
|
||||
eventSource.removeEventListener("mission:event", handleMissionEvent);
|
||||
eventSource.close();
|
||||
};
|
||||
@@ -649,6 +657,7 @@ export function MissionManager({ isOpen, isInline = false, onClose, addToast, pr
|
||||
eventsFilter,
|
||||
isActive,
|
||||
isActivityScrolledNearBottom,
|
||||
loadMissionDetail,
|
||||
loadMissionHealth,
|
||||
missions,
|
||||
projectId,
|
||||
|
||||
@@ -762,6 +762,53 @@ describe("MissionManager", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("reloads selected mission detail when feature:updated SSE event arrives", async () => {
|
||||
const fetchMock = createDetailFetchMock(mockMissionEvents);
|
||||
globalThis.fetch = fetchMock;
|
||||
globalThis.EventSource = MockEventSource as unknown as typeof globalThis.EventSource;
|
||||
|
||||
render(<MissionManager isOpen={true} onClose={vi.fn()} addToast={vi.fn()} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Build Auth System")).toBeDefined();
|
||||
});
|
||||
|
||||
// Click on the mission to open detail view
|
||||
fireEvent.click(screen.getByText("Build Auth System"));
|
||||
|
||||
await waitFor(() => {
|
||||
// Back button should appear in detail view
|
||||
expect(screen.getByTestId("mission-back-btn")).toBeDefined();
|
||||
});
|
||||
|
||||
// Record initial fetch calls for mission detail
|
||||
const initialFetchCount = fetchMock.mock.calls.filter(
|
||||
(call) => typeof call[0] === "string" && call[0].includes("/api/missions/M-001")
|
||||
).length;
|
||||
expect(initialFetchCount).toBeGreaterThan(0);
|
||||
|
||||
// Emit a feature:updated SSE event
|
||||
await act(async () => {
|
||||
for (const source of MockEventSource.instances) {
|
||||
source.emit("feature:updated", {
|
||||
featureId: "F-001",
|
||||
missionId: "M-001",
|
||||
sliceId: "SL-001",
|
||||
previousStatus: "triaged",
|
||||
newStatus: "in-progress",
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Verify mission detail was reloaded (fetch was called again for the mission)
|
||||
await waitFor(() => {
|
||||
const updatedFetchCount = fetchMock.mock.calls.filter(
|
||||
(call) => typeof call[0] === "string" && call[0].includes("/api/missions/M-001")
|
||||
).length;
|
||||
expect(updatedFetchCount).toBeGreaterThan(initialFetchCount);
|
||||
});
|
||||
});
|
||||
|
||||
it("shows empty state when no missions exist", async () => {
|
||||
globalThis.fetch = vi.fn().mockResolvedValue(mockApiResponse([]));
|
||||
render(<MissionManager isOpen={true} onClose={vi.fn()} addToast={vi.fn()} />);
|
||||
|
||||
Reference in New Issue
Block a user