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

96 lines
5.1 KiB
TypeScript

import { describe, expect, it, vi } from "vitest";
vi.mock("@toolctrl/db/entities.server", () => ({
getAllLegacyEntities: vi.fn(),
}));
vi.mock("~/locale/i18next.server", () => ({
getTranslations: vi.fn(async () => ({ t: (value: string) => value, translations: {}, locale: "en" })),
}));
vi.mock("~/utils/url.server", () => ({
getAbsoluteApiUrl: vi.fn((_request: Request, path: string) => `https://api.example.com${path.startsWith("/") ? path : `/${path}`}`),
}));
import { getAllLegacyEntities } from "@toolctrl/db/entities.server";
import ApiSpecsService from "./ApiSpecsService";
const entity = {
id: "entity_1",
slug: "contacts",
title: "Contact",
titlePlural: "Contacts",
hasApi: true,
active: true,
properties: [],
childEntities: [{ id: "child_relation" }],
parentEntities: [],
};
describe("ApiSpecsService", () => {
it("documents canonical API auth and idempotency for generated specs and Postman requests", async () => {
vi.mocked(getAllLegacyEntities).mockResolvedValue([entity] as never);
const specs = await ApiSpecsService.generateSpecs({ request: new Request("https://admin.example.com/api/docs") });
const openApi = specs.openApi;
expect(openApi.servers).toEqual([{ url: "https://api.example.com", description: "API host" }]);
expect(openApi.components.securitySchemes.bearerAuth).toMatchObject({
type: "http",
scheme: "bearer",
bearerFormat: "service-token",
});
expect(openApi.paths["/v1/entities/{entity}/rows"].post.parameters).toContainEqual(expect.objectContaining({ name: "Idempotency-Key", required: true }));
expect(openApi.paths["/v1/entities/{entity}/rows/{rowId}"].patch.parameters).toContainEqual(expect.objectContaining({ name: "Idempotency-Key", required: true }));
expect(openApi.paths["/v1/entities/{entity}/rows/{rowId}"].delete.parameters).toContainEqual(expect.objectContaining({ name: "Idempotency-Key", required: true }));
expect(openApi.paths["/v1/relationships"].post.parameters).toContainEqual(expect.objectContaining({ name: "Idempotency-Key", required: true }));
expect(openApi.paths["/v1/relationships/{id}"].delete.parameters).toContainEqual(expect.objectContaining({ name: "Idempotency-Key", required: true }));
const postmanItems = specs.postmanCollection.item as Array<{
name: string;
request: { header: Array<{ key: string; value?: string }>; url: { raw: string; query?: Array<{ key: string; value: string; disabled?: boolean }> } };
}>;
expect(postmanItems.find((item) => item.name === "GET /v1/session/current-user")?.request.header).toEqual([
expect.objectContaining({ key: "Authorization", value: "Bearer <token>" }),
]);
expect(postmanItems.find((item) => item.name === "POST /v1/api-keys/validate")?.request.header).toEqual([
expect.objectContaining({ key: "x-api-key", value: "<api-key-or-service-token>" }),
]);
expect(postmanItems.some((item) => item.name.startsWith("POST /v1/workflows") || item.name.startsWith("GET /v1/workflows"))).toBe(false);
expect(postmanItems.find((item) => item.name === "GET /v1/entities/:entity/rows")?.request.header).toEqual([
expect.objectContaining({ key: "x-api-key", value: "<api-key>" }),
]);
expect(postmanItems.find((item) => item.name === "GET /v1/entities/:entity/rows")?.request.url.query).toContainEqual(
expect.objectContaining({ key: "tenantId", value: "<tenantId>" })
);
expect(postmanItems.find((item) => item.name === "GET /v1/entities/:entity/rows")?.request.url.raw).toContain("tenantId=%3CtenantId%3E");
expect(postmanItems.find((item) => item.name === "POST /v1/entities/:entity/rows")?.request.header).toEqual([
expect.objectContaining({ key: "x-api-key", value: "<api-key>" }),
expect.objectContaining({ key: "Idempotency-Key" }),
]);
expect(postmanItems.find((item) => item.name === "POST /v1/ai/upload")?.request.header).toContainEqual(
expect.objectContaining({ key: "x-tenant-id", value: "<x-tenant-id>" })
);
expect(postmanItems.find((item) => item.name === "POST /v1/analytics/events")?.request.header).toEqual([
expect.objectContaining({ key: "x-analytics-token" }),
]);
expect(postmanItems.find((item) => item.name === "GET /v1/exports/:exportJobId/download-url")?.request.header).toEqual([
expect.objectContaining({ key: "x-api-key", value: "<api-key>" }),
]);
expect(postmanItems.find((item) => item.name === "POST /v1/billing/stripe/webhooks")?.request.header).toEqual([
expect.objectContaining({ key: "stripe-signature" }),
]);
expect(postmanItems.find((item) => item.name === "POST /v1/events/webhooks/attempts/:id")?.request.header).toEqual([
expect.objectContaining({ key: "Authorization", value: "Basic <base64(mailjet:webhook-secret)>" }),
]);
expect((specs.endpoints ?? []).map((endpoint) => endpoint.route)).toEqual([
"https://api.example.com/v1/entities/contacts/rows",
"https://api.example.com/v1/entities/contacts/rows/:rowId",
"https://api.example.com/v1/entities/contacts/rows",
"https://api.example.com/v1/entities/contacts/rows/:rowId",
"https://api.example.com/v1/entities/contacts/rows/:rowId",
]);
});
});