feat(per-instance-hook-scoping): scope observability + backup hooks by instance
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
import { describe, it, expect, vi } from "vitest";
|
||||
import { renderHook } from "@testing-library/react";
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
import { createElement, type ReactNode } from "react";
|
||||
import { useBackupJobs, useBackupRuns, useBackupAlerts } from "../useBackups";
|
||||
import { useAlertmanagerAlerts, usePrometheusStatus } from "../useObservability";
|
||||
|
||||
vi.mock("../../api/client", () => ({
|
||||
fetchAlertmanagerAlerts: vi.fn(),
|
||||
fetchAlertmanagerStatus: vi.fn(),
|
||||
fetchPrometheusStatus: vi.fn(),
|
||||
fetchPrometheusTargets: vi.fn(),
|
||||
fetchMonitoringMachines: vi.fn(),
|
||||
}));
|
||||
vi.mock("../../api/backups", () => ({
|
||||
fetchBackupJobs: vi.fn(),
|
||||
fetchBackupRuns: vi.fn(),
|
||||
fetchBackupAlerts: vi.fn(),
|
||||
fetchBackupDashboard: vi.fn(),
|
||||
fetchBackupJob: vi.fn(),
|
||||
acknowledgeBackupAlert: vi.fn(),
|
||||
}));
|
||||
|
||||
function createWrapper() {
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: { queries: { retry: false } },
|
||||
});
|
||||
return ({ children }: { children: ReactNode }) =>
|
||||
createElement(QueryClientProvider, { client: queryClient }, children);
|
||||
}
|
||||
|
||||
describe("per-instance hook queryKey isolation", () => {
|
||||
it("useBackupJobs produces different keys for different serviceIds", () => {
|
||||
const wrapper = createWrapper();
|
||||
const { result: a } = renderHook(() => useBackupJobs("svc-a"), { wrapper });
|
||||
const { result: b } = renderHook(() => useBackupJobs("svc-b"), { wrapper });
|
||||
expect(a).toBeDefined();
|
||||
expect(b).toBeDefined();
|
||||
// Different serviceId → different query → different cache slot
|
||||
expect(a).not.toBe(b);
|
||||
});
|
||||
|
||||
it("useBackupJobs with undefined serviceId is stable (same key)", () => {
|
||||
const wrapper = createWrapper();
|
||||
const { result: a } = renderHook(() => useBackupJobs(), { wrapper });
|
||||
const { result: b } = renderHook(() => useBackupJobs(), { wrapper });
|
||||
expect(a).toBeDefined();
|
||||
expect(b).toBeDefined();
|
||||
});
|
||||
|
||||
it("useBackupRuns includes serviceId in queryKey", () => {
|
||||
const wrapper = createWrapper();
|
||||
const { result: a } = renderHook(
|
||||
() => useBackupRuns(undefined, undefined, "svc-a"),
|
||||
{ wrapper },
|
||||
);
|
||||
expect(a).toBeDefined();
|
||||
});
|
||||
|
||||
it("useBackupAlerts includes serviceId in queryKey", () => {
|
||||
const wrapper = createWrapper();
|
||||
const { result: a } = renderHook(
|
||||
() => useBackupAlerts(undefined, false, undefined, "svc-a"),
|
||||
{ wrapper },
|
||||
);
|
||||
expect(a).toBeDefined();
|
||||
});
|
||||
|
||||
it("useAlertmanagerAlerts includes serviceId in queryKey", () => {
|
||||
const wrapper = createWrapper();
|
||||
const { result: a } = renderHook(
|
||||
() => useAlertmanagerAlerts("svc-a"),
|
||||
{ wrapper },
|
||||
);
|
||||
expect(a).toBeDefined();
|
||||
});
|
||||
|
||||
it("usePrometheusStatus includes serviceId in queryKey", () => {
|
||||
const wrapper = createWrapper();
|
||||
const { result: a } = renderHook(() => usePrometheusStatus("svc-a"), {
|
||||
wrapper,
|
||||
});
|
||||
expect(a).toBeDefined();
|
||||
});
|
||||
});
|
||||
@@ -8,10 +8,10 @@ import {
|
||||
fetchBackupRuns,
|
||||
} from "../api/backups";
|
||||
|
||||
export function useBackupJobs() {
|
||||
export function useBackupJobs(serviceId?: string) {
|
||||
return useQuery({
|
||||
queryKey: ["backups", "jobs"],
|
||||
queryFn: fetchBackupJobs,
|
||||
queryKey: ["backups", "jobs", serviceId ?? ""],
|
||||
queryFn: () => fetchBackupJobs(serviceId),
|
||||
refetchInterval: 30_000,
|
||||
});
|
||||
}
|
||||
@@ -24,18 +24,18 @@ export function useBackupJob(jobId: string) {
|
||||
});
|
||||
}
|
||||
|
||||
export function useBackupRuns(jobId?: string, status?: string) {
|
||||
export function useBackupRuns(jobId?: string, status?: string, serviceId?: string) {
|
||||
return useQuery({
|
||||
queryKey: ["backups", "runs", jobId, status],
|
||||
queryFn: () => fetchBackupRuns(jobId, status),
|
||||
queryKey: ["backups", "runs", jobId, status, serviceId ?? ""],
|
||||
queryFn: () => fetchBackupRuns(jobId, status, serviceId),
|
||||
refetchInterval: 15_000,
|
||||
});
|
||||
}
|
||||
|
||||
export function useBackupAlerts(jobId?: string, acknowledged?: boolean, severity?: string) {
|
||||
export function useBackupAlerts(jobId?: string, acknowledged?: boolean, severity?: string, serviceId?: string) {
|
||||
return useQuery({
|
||||
queryKey: ["backups", "alerts", jobId, acknowledged, severity],
|
||||
queryFn: () => fetchBackupAlerts(jobId, acknowledged, severity),
|
||||
queryKey: ["backups", "alerts", jobId, acknowledged, severity, serviceId ?? ""],
|
||||
queryFn: () => fetchBackupAlerts(jobId, acknowledged, severity, serviceId),
|
||||
refetchInterval: 30_000,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -7,30 +7,30 @@ import {
|
||||
fetchMonitoringMachines,
|
||||
} from "../api/client";
|
||||
|
||||
export function useAlertmanagerAlerts() {
|
||||
export function useAlertmanagerAlerts(serviceId?: string) {
|
||||
return useQuery({
|
||||
queryKey: ["observability", "alerts"],
|
||||
queryFn: fetchAlertmanagerAlerts,
|
||||
queryKey: ["observability", "alerts", serviceId ?? ""],
|
||||
queryFn: () => fetchAlertmanagerAlerts(serviceId),
|
||||
retry: 2,
|
||||
staleTime: 10_000,
|
||||
refetchInterval: 15_000,
|
||||
});
|
||||
}
|
||||
|
||||
export function useAlertmanagerStatus() {
|
||||
export function useAlertmanagerStatus(serviceId?: string) {
|
||||
return useQuery({
|
||||
queryKey: ["observability", "alertmanager-status"],
|
||||
queryFn: fetchAlertmanagerStatus,
|
||||
queryKey: ["observability", "alertmanager-status", serviceId ?? ""],
|
||||
queryFn: () => fetchAlertmanagerStatus(serviceId),
|
||||
retry: 2,
|
||||
staleTime: 10_000,
|
||||
refetchInterval: 30_000,
|
||||
});
|
||||
}
|
||||
|
||||
export function usePrometheusStatus() {
|
||||
export function usePrometheusStatus(serviceId?: string) {
|
||||
return useQuery({
|
||||
queryKey: ["observability", "prometheus-status"],
|
||||
queryFn: fetchPrometheusStatus,
|
||||
queryKey: ["observability", "prometheus-status", serviceId ?? ""],
|
||||
queryFn: () => fetchPrometheusStatus(serviceId),
|
||||
retry: 2,
|
||||
staleTime: 10_000,
|
||||
refetchInterval: 30_000,
|
||||
|
||||
Reference in New Issue
Block a user