Files
HiveGrid-Wireframes/hivegrid-cloud/apps/web/app/modules/metrics/services/MetricService.test.ts
T

137 lines
4.5 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from "vitest";
type MetricServiceMocks = {
metricLogGroupBy: ReturnType<typeof vi.fn>;
adminGetAllTenantsIdsAndNames: ReturnType<typeof vi.fn>;
adminGetAllUsersNames: ReturnType<typeof vi.fn>;
};
function getMocks(): MetricServiceMocks {
const store = globalThis as typeof globalThis & { __metricServiceMocks__?: MetricServiceMocks };
store.__metricServiceMocks__ ??= {
metricLogGroupBy: vi.fn(),
adminGetAllTenantsIdsAndNames: vi.fn(),
adminGetAllUsersNames: vi.fn(),
};
return store.__metricServiceMocks__;
}
vi.mock("~/utils/db.server", () => ({
db: {
metricLog: {
groupBy: getMocks().metricLogGroupBy,
},
},
}));
vi.mock("~/utils/db/tenants.db.server", () => ({
adminGetAllTenantsIdsAndNames: getMocks().adminGetAllTenantsIdsAndNames,
}));
vi.mock("~/utils/db/users.db.server", () => ({
adminGetAllUsersNames: getMocks().adminGetAllUsersNames,
}));
import MetricService from "./MetricService";
const mocks = getMocks();
function createDeferred<T>() {
let resolve!: (value: T) => void;
const promise = new Promise<T>((promiseResolve) => {
resolve = promiseResolve;
});
return { promise, resolve };
}
describe("MetricService filters", () => {
beforeEach(() => {
vi.clearAllMocks();
mocks.metricLogGroupBy
.mockResolvedValueOnce([{ route: "/app", _count: { route: 3 } }])
.mockResolvedValueOnce([{ url: "https://app.example.test/app", _count: { url: 2 } }])
.mockResolvedValueOnce([{ function: "loader", _count: { function: 5 } }]);
mocks.adminGetAllTenantsIdsAndNames.mockResolvedValue([]);
mocks.adminGetAllUsersNames.mockResolvedValue([]);
});
it("returns translation keys for static visible metric filter labels", async () => {
const { filterableProperties } = await MetricService.getFilters({
request: new Request("https://admin.example.test/admin/metrics"),
});
expect(filterableProperties.find((filter) => filter.name === "env")).toMatchObject({
title: "admin.metrics.columns.env",
options: [
{ name: "shared.environments.production", value: "production" },
{ name: "shared.environments.staging", value: "staging" },
{ name: "shared.environments.development", value: "development" },
],
});
expect(filterableProperties.find((filter) => filter.name === "type")).toMatchObject({
title: "admin.metrics.columns.type",
options: [
{ name: "admin.metrics.types.loader", value: "loader" },
{ name: "admin.metrics.types.action", value: "action" },
],
});
expect(filterableProperties.find((filter) => filter.name === "function")).toMatchObject({
title: "admin.metrics.columns.function",
});
});
it("runs independent metric filter group queries together with a bounded result size", async () => {
const routeGroupBy = createDeferred([{ route: "/app", _count: { route: 3 } }]);
const urlGroupBy = createDeferred([{ url: "https://app.example.test/app", _count: { url: 2 } }]);
const functionGroupBy = createDeferred([{ function: "loader", _count: { function: 5 } }]);
mocks.metricLogGroupBy
.mockReset()
.mockReturnValueOnce(routeGroupBy.promise)
.mockReturnValueOnce(urlGroupBy.promise)
.mockReturnValueOnce(functionGroupBy.promise);
const filtersPromise = MetricService.getFilters({
request: new Request("https://admin.example.test/admin/metrics"),
});
await Promise.resolve();
expect(mocks.metricLogGroupBy).toHaveBeenCalledTimes(3);
expect(mocks.metricLogGroupBy).toHaveBeenNthCalledWith(
1,
expect.objectContaining({
by: ["route"],
take: 200,
})
);
expect(mocks.metricLogGroupBy).toHaveBeenNthCalledWith(
2,
expect.objectContaining({
by: ["url"],
take: 200,
})
);
expect(mocks.metricLogGroupBy).toHaveBeenNthCalledWith(
3,
expect.objectContaining({
by: ["function"],
take: 200,
})
);
routeGroupBy.resolve([{ route: "/app", _count: { route: 3 } }]);
urlGroupBy.resolve([{ url: "https://app.example.test/app", _count: { url: 2 } }]);
functionGroupBy.resolve([{ function: "loader", _count: { function: 5 } }]);
await expect(filtersPromise).resolves.toMatchObject({
filterableProperties: expect.arrayContaining([
expect.objectContaining({
name: "route",
options: [{ name: "/app (3)", value: "/app" }],
}),
]),
});
});
});