mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2026-09-01 03:08:41 +01:00
Added enriched metrics (#2432)
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
import { configContext, Platform } from "../ConfigContext";
|
||||
import { getAuthorizationHeader } from "../Utils/AuthorizationUtils";
|
||||
import { fetchWithTimeout } from "../Utils/FetchWithTimeout";
|
||||
import MetricScenario, { reportHealthy, reportUnhealthy } from "./MetricEvents";
|
||||
import { MetricScenario } from "./Constants";
|
||||
import { reportMetric } from "./MetricEvents";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const { Response } = require("node-fetch");
|
||||
@@ -21,11 +22,16 @@ describe("MetricEvents", () => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
test("reportHealthy success includes auth header", async () => {
|
||||
test("reportMetric success includes auth header", async () => {
|
||||
const mockResponse = new Response(null, { status: 200 });
|
||||
mockFetchWithTimeout.mockResolvedValue(mockResponse);
|
||||
|
||||
const result = await reportHealthy(MetricScenario.ApplicationLoad, Platform.Portal, "SQL");
|
||||
const result = await reportMetric({
|
||||
platform: Platform.Portal,
|
||||
api: "SQL",
|
||||
scenario: MetricScenario.ApplicationLoad,
|
||||
healthy: true,
|
||||
});
|
||||
|
||||
expect(result).toBeInstanceOf(Response);
|
||||
expect(result.ok).toBe(true);
|
||||
@@ -47,39 +53,77 @@ describe("MetricEvents", () => {
|
||||
expect(getAuthorizationHeader).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test("reportUnhealthy failure status", async () => {
|
||||
const mockResponse = new Response("Failure", { status: 500 });
|
||||
test("reportMetric sends full enriched payload", async () => {
|
||||
const mockResponse = new Response(null, { status: 200 });
|
||||
mockFetchWithTimeout.mockResolvedValue(mockResponse);
|
||||
|
||||
const result = await reportUnhealthy(MetricScenario.ApplicationLoad, Platform.Portal, "SQL");
|
||||
const event = {
|
||||
platform: Platform.Portal,
|
||||
api: "SQL" as const,
|
||||
scenario: MetricScenario.ApplicationLoad,
|
||||
healthy: true,
|
||||
durationMs: 2847,
|
||||
timedOut: false,
|
||||
documentHidden: false,
|
||||
hasExpectedFailure: false,
|
||||
completedPhases: ["ExplorerInitialized", "Interactive"],
|
||||
failedPhases: [] as string[],
|
||||
phaseTimings: {
|
||||
ExplorerInitialized: { endTimeISO: "2026-03-13T10:00:02.500Z", durationMs: 2500 },
|
||||
Interactive: { endTimeISO: "2026-03-13T10:00:02.847Z", durationMs: 2847 },
|
||||
},
|
||||
startTimeISO: "2026-03-13T10:00:00.000Z",
|
||||
endTimeISO: "2026-03-13T10:00:02.847Z",
|
||||
vitals: { lcp: 1850, inp: 120, cls: 0.05, fcp: 980, ttfb: 340 },
|
||||
};
|
||||
|
||||
expect(result).toBeInstanceOf(Response);
|
||||
expect(result.ok).toBe(false);
|
||||
expect(result.status).toBe(500);
|
||||
const result = await reportMetric(event);
|
||||
expect(result.status).toBe(200);
|
||||
|
||||
const callArgs = mockFetchWithTimeout.mock.calls[0];
|
||||
const body = JSON.parse(callArgs[1]?.body as string);
|
||||
expect(body.healthy).toBe(true);
|
||||
expect(body.durationMs).toBe(2847);
|
||||
expect(body.timedOut).toBe(false);
|
||||
expect(body.documentHidden).toBe(false);
|
||||
expect(body.hasExpectedFailure).toBe(false);
|
||||
expect(body.completedPhases).toEqual(["ExplorerInitialized", "Interactive"]);
|
||||
expect(body.failedPhases).toEqual([]);
|
||||
expect(body.phaseTimings.ExplorerInitialized.durationMs).toBe(2500);
|
||||
expect(body.startTimeISO).toBe("2026-03-13T10:00:00.000Z");
|
||||
expect(body.endTimeISO).toBe("2026-03-13T10:00:02.847Z");
|
||||
expect(body.vitals.lcp).toBe(1850);
|
||||
expect(body.vitals.cls).toBe(0.05);
|
||||
});
|
||||
|
||||
test("reportMetric omits undefined optional fields", async () => {
|
||||
const mockResponse = new Response(null, { status: 200 });
|
||||
mockFetchWithTimeout.mockResolvedValue(mockResponse);
|
||||
|
||||
const event = {
|
||||
platform: Platform.Portal,
|
||||
api: "SQL" as const,
|
||||
scenario: MetricScenario.ApplicationLoad,
|
||||
healthy: false,
|
||||
};
|
||||
|
||||
await reportMetric(event);
|
||||
|
||||
const callArgs = mockFetchWithTimeout.mock.calls[0];
|
||||
const body = JSON.parse(callArgs[1]?.body as string);
|
||||
expect(body.healthy).toBe(false);
|
||||
});
|
||||
|
||||
test("helpers healthy/unhealthy", async () => {
|
||||
mockFetchWithTimeout.mockResolvedValue(new Response(null, { status: 201 }));
|
||||
|
||||
const healthyResult = await reportHealthy(MetricScenario.ApplicationLoad, Platform.Portal, "SQL");
|
||||
const unhealthyResult = await reportUnhealthy(MetricScenario.ApplicationLoad, Platform.Portal, "SQL");
|
||||
|
||||
expect(healthyResult.status).toBe(201);
|
||||
expect(unhealthyResult.status).toBe(201);
|
||||
expect(mockFetchWithTimeout).toHaveBeenCalledTimes(2);
|
||||
expect(body.durationMs).toBeUndefined();
|
||||
expect(body.vitals).toBeUndefined();
|
||||
expect(body.completedPhases).toBeUndefined();
|
||||
});
|
||||
|
||||
test("throws when backend endpoint missing", async () => {
|
||||
const original = configContext.PORTAL_BACKEND_ENDPOINT;
|
||||
(configContext as { PORTAL_BACKEND_ENDPOINT: string }).PORTAL_BACKEND_ENDPOINT = "";
|
||||
|
||||
await expect(reportHealthy(MetricScenario.ApplicationLoad, Platform.Portal, "SQL")).rejects.toThrow(
|
||||
"baseUri is null or empty",
|
||||
);
|
||||
await expect(
|
||||
reportMetric({ platform: Platform.Portal, api: "SQL", scenario: MetricScenario.ApplicationLoad, healthy: true }),
|
||||
).rejects.toThrow("baseUri is null or empty");
|
||||
|
||||
expect(mockFetchWithTimeout).not.toHaveBeenCalled();
|
||||
(configContext as { PORTAL_BACKEND_ENDPOINT: string }).PORTAL_BACKEND_ENDPOINT = original;
|
||||
@@ -88,17 +132,17 @@ describe("MetricEvents", () => {
|
||||
test("propagates fetch errors", async () => {
|
||||
mockFetchWithTimeout.mockRejectedValue(new Error("Network error"));
|
||||
|
||||
await expect(reportHealthy(MetricScenario.ApplicationLoad, Platform.Portal, "SQL")).rejects.toThrow(
|
||||
"Network error",
|
||||
);
|
||||
await expect(
|
||||
reportMetric({ platform: Platform.Portal, api: "SQL", scenario: MetricScenario.ApplicationLoad, healthy: true }),
|
||||
).rejects.toThrow("Network error");
|
||||
});
|
||||
|
||||
test("propagates timeout errors", async () => {
|
||||
const abortError = new DOMException("The operation was aborted", "AbortError");
|
||||
mockFetchWithTimeout.mockRejectedValue(abortError);
|
||||
|
||||
await expect(reportUnhealthy(MetricScenario.ApplicationLoad, Platform.Portal, "SQL")).rejects.toThrow(
|
||||
"The operation was aborted",
|
||||
);
|
||||
await expect(
|
||||
reportMetric({ platform: Platform.Portal, api: "SQL", scenario: MetricScenario.ApplicationLoad, healthy: false }),
|
||||
).rejects.toThrow("The operation was aborted");
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user