110 lines
3.8 KiB
TypeScript
110 lines
3.8 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
import { getAdminActiveUserKpiCounts, getAdminTenantKpiCounts } from "@toolctrl/db/kpi.server";
|
|
import { StatChange } from "~/application/dtos/stats/StatChange";
|
|
import { getAdminDashboardStats } from "./adminDashboardService";
|
|
import { getMrr } from "./.server/subscriptionService";
|
|
|
|
vi.mock("@toolctrl/db/kpi.server", () => ({
|
|
getAdminActiveUserKpiCounts: vi.fn(),
|
|
getAdminTenantKpiCounts: vi.fn(),
|
|
}));
|
|
|
|
vi.mock("./.server/subscriptionService", () => ({
|
|
getMrr: vi.fn(),
|
|
}));
|
|
|
|
const translations: Record<string, string> = {
|
|
"admin.kpi.accounts.name": "Translated accounts",
|
|
"admin.kpi.accounts.hint": "Translated total accounts",
|
|
"admin.kpi.mrr.name": "Translated MRR",
|
|
"admin.kpi.mrr.hint": "Translated monthly revenue",
|
|
"admin.kpi.mrr.previousStat": "{{0}} translated accounts",
|
|
"admin.kpi.activeUsers.name": "Translated MAU",
|
|
"admin.kpi.activeUsers.hint": "Translated monthly active users",
|
|
};
|
|
|
|
function t(key: string) {
|
|
return translations[key] ?? key;
|
|
}
|
|
|
|
describe("getAdminDashboardStats", () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
vi.mocked(getMrr).mockResolvedValue({ total: 1234.5, count: 7 });
|
|
});
|
|
|
|
it("builds translated KPI stats from package counts and forwards the selected period", async () => {
|
|
const gte = new Date("2026-01-01T00:00:00.000Z");
|
|
vi.mocked(getAdminTenantKpiCounts).mockResolvedValue({ added: 3, total: 10 });
|
|
vi.mocked(getAdminActiveUserKpiCounts).mockResolvedValue({ added: 2, total: 8 });
|
|
|
|
const stats = await getAdminDashboardStats({ t, gte });
|
|
|
|
expect(getAdminTenantKpiCounts).toHaveBeenCalledTimes(1);
|
|
expect(getAdminTenantKpiCounts).toHaveBeenCalledWith({ gte });
|
|
expect(getAdminActiveUserKpiCounts).toHaveBeenCalledTimes(1);
|
|
expect(getAdminActiveUserKpiCounts).toHaveBeenCalledWith({ gte });
|
|
expect(getMrr).toHaveBeenCalledTimes(1);
|
|
expect(getMrr).toHaveBeenCalledWith("usd");
|
|
expect(stats).toEqual([
|
|
{
|
|
name: "Translated accounts",
|
|
hint: "Translated total accounts",
|
|
stat: "10",
|
|
previousStat: "7",
|
|
change: "+3",
|
|
changeType: StatChange.Increase,
|
|
path: "/accounts",
|
|
},
|
|
{
|
|
name: "Translated MRR",
|
|
hint: "Translated monthly revenue",
|
|
stat: "$1,234.50",
|
|
previousStat: "7 translated accounts",
|
|
},
|
|
{
|
|
name: "Translated MAU",
|
|
hint: "Translated monthly active users",
|
|
stat: "8",
|
|
previousStat: "6",
|
|
change: "+2",
|
|
changeType: StatChange.Increase,
|
|
},
|
|
]);
|
|
});
|
|
|
|
it("keeps zero additions visible with equal change type and missing translations as key text", async () => {
|
|
vi.mocked(getAdminTenantKpiCounts).mockResolvedValue({ added: 0, total: 5 });
|
|
vi.mocked(getAdminActiveUserKpiCounts).mockResolvedValue({ added: 0, total: 0 });
|
|
vi.mocked(getMrr).mockResolvedValue({ total: 0, count: 0 });
|
|
|
|
const stats = await getAdminDashboardStats({ t: (key) => key });
|
|
|
|
expect(getAdminTenantKpiCounts).toHaveBeenCalledWith({ gte: undefined });
|
|
expect(getAdminActiveUserKpiCounts).toHaveBeenCalledWith({ gte: undefined });
|
|
expect(stats[0]).toMatchObject({
|
|
name: "admin.kpi.accounts.name",
|
|
hint: "admin.kpi.accounts.hint",
|
|
stat: "5",
|
|
previousStat: "5",
|
|
change: "+0",
|
|
changeType: StatChange.Equal,
|
|
path: "/accounts",
|
|
});
|
|
expect(stats[1]).toMatchObject({
|
|
name: "admin.kpi.mrr.name",
|
|
hint: "admin.kpi.mrr.hint",
|
|
stat: "$0.00",
|
|
previousStat: "admin.kpi.mrr.previousStat",
|
|
});
|
|
expect(stats[2]).toMatchObject({
|
|
name: "admin.kpi.activeUsers.name",
|
|
hint: "admin.kpi.activeUsers.hint",
|
|
stat: "0",
|
|
previousStat: "0",
|
|
change: "+0",
|
|
changeType: StatChange.Increase,
|
|
});
|
|
});
|
|
});
|