Files
HiveGrid-Wireframes/hivegrid-cloud/apps/web/app/modules/knowledgeBase/service/KnowledgeBaseService.server.test.ts
T

149 lines
6.1 KiB
TypeScript

import { beforeEach, describe, expect, test, vi } from "vitest";
const getAllKnowledgeBaseArticlesMock = vi.fn();
const createKnowledgeBaseArticleMock = vi.fn();
const getAllKnowledgeBaseCategoriesMock = vi.fn();
const getFixedTMock = vi.fn();
const knowledgeBaseArticleCountMock = vi.fn();
const knowledgeBaseCategoryCountMock = vi.fn();
const knowledgeBaseDeleteMock = vi.fn();
const cachifiedMock = vi.fn();
vi.mock("../db/kbArticles.db.server", () => ({
createKnowledgeBaseArticle: (...args: unknown[]) => createKnowledgeBaseArticleMock(...args),
getAllKnowledgeBaseArticles: (...args: unknown[]) => getAllKnowledgeBaseArticlesMock(...args),
getFeaturedKnowledgeBaseArticles: vi.fn(),
getKbArticleById: vi.fn(),
getKbArticleByIdInKnowledgeBase: vi.fn(),
getKbArticleBySlug: vi.fn(),
}));
vi.mock("../db/kbCategories.db.server", () => ({
createKnowledgeBaseCategory: vi.fn(),
getAllKnowledgeBaseCategories: (...args: unknown[]) => getAllKnowledgeBaseCategoriesMock(...args),
getKbCategoryById: vi.fn(),
getKbCategoryBySlug: vi.fn(),
}));
vi.mock("../db/knowledgeBase.db.server", () => ({
getAllKnowledgeBases: vi.fn(),
getKnowledgeBaseById: vi.fn(),
getKnowledgeBaseBySlug: vi.fn(),
}));
vi.mock("../services/kbArticleSearch.server", () => ({
searchKnowledgeBaseArticles: vi.fn(),
}));
vi.mock("~/locale/i18next.server", () => ({
remixI18Next: {
getFixedT: (...args: unknown[]) => getFixedTMock(...args),
},
}));
vi.mock("~/utils/cache.server", () => ({
cachified: (...args: unknown[]) => cachifiedMock(...args),
}));
vi.mock("~/utils/db.server", () => ({
db: {
knowledgeBaseArticle: { count: (...args: unknown[]) => knowledgeBaseArticleCountMock(...args) },
knowledgeBaseCategory: { count: (...args: unknown[]) => knowledgeBaseCategoryCountMock(...args) },
knowledgeBase: { delete: (...args: unknown[]) => knowledgeBaseDeleteMock(...args) },
},
}));
describe("KnowledgeBaseServiceServer", () => {
beforeEach(() => {
vi.clearAllMocks();
getAllKnowledgeBaseArticlesMock.mockResolvedValue([
{
id: "article-1",
slug: "untitled",
order: 1,
},
]);
getAllKnowledgeBaseCategoriesMock.mockResolvedValue([]);
createKnowledgeBaseArticleMock.mockImplementation(async (input) => ({ id: "created", ...input }));
getFixedTMock.mockImplementation(async (locale: string) => (key: string, vars?: Record<string, unknown>) => `${locale}:${key}:${vars?.number ?? ""}`);
knowledgeBaseArticleCountMock.mockResolvedValue(0);
knowledgeBaseCategoryCountMock.mockResolvedValue(0);
knowledgeBaseDeleteMock.mockResolvedValue({ id: "kb-1" });
cachifiedMock.mockImplementation(async ({ getFreshValue }: { getFreshValue: () => unknown }) => getFreshValue());
});
test("newArticle localizes generated untitled titles and persists the resolved language", async () => {
const KnowledgeBaseServiceServer = (await import("./KnowledgeBaseService.server")).default;
const created = await KnowledgeBaseServiceServer.newArticle({
kb: {
id: "kb-1",
slug: "docs",
defaultLanguage: "de",
} as never,
params: {},
userId: "user-1",
position: "last",
});
expect(getFixedTMock).toHaveBeenCalledWith("de", "translations");
expect(createKnowledgeBaseArticleMock).toHaveBeenCalledWith(expect.objectContaining({ title: "de:knowledgeBase.adminCategories.untitledNumbered:1", language: "de" }));
expect(created).toMatchObject({ title: "de:knowledgeBase.adminCategories.untitledNumbered:1", language: "de" });
});
test("del exposes coded delete guard errors for localized route handling", async () => {
const { default: KnowledgeBaseServiceServer, getKnowledgeBaseActionErrorKey } = await import("./KnowledgeBaseService.server");
knowledgeBaseArticleCountMock.mockResolvedValueOnce(1);
try {
await KnowledgeBaseServiceServer.del({ id: "kb-1" } as never);
throw new Error("Expected delete to reject");
} catch (error) {
expect(error).toMatchObject({ code: "cannot-delete-with-articles" });
expect(getKnowledgeBaseActionErrorKey(error)).toBe("knowledgeBase.admin.errors.cannotDeleteWithArticles");
}
});
test("duplicate and new article validation errors map to semantic i18n keys", async () => {
const { default: KnowledgeBaseServiceServer, getKnowledgeBaseActionErrorKey } = await import("./KnowledgeBaseService.server");
await expect(
KnowledgeBaseServiceServer.duplicateArticle({ kb: { id: "kb-1", slug: "docs", defaultLanguage: "en" } as never, language: "en", articleId: "missing" })
).rejects.toMatchObject({ code: "invalid-article" });
try {
await KnowledgeBaseServiceServer.newArticle({
kb: { id: "kb-1", slug: "docs", defaultLanguage: "en" } as never,
params: {},
userId: "user-1",
position: "last",
categoryId: "missing",
});
} catch (error) {
expect(getKnowledgeBaseActionErrorKey(error)).toBe("knowledgeBase.adminCategories.errors.categoryNotFound");
}
});
test("public lookup errors map to public i18n keys", async () => {
const { default: KnowledgeBaseServiceServer, getKnowledgeBasePublicErrorKey } = await import("./KnowledgeBaseService.server");
cachifiedMock.mockResolvedValueOnce(null);
try {
await KnowledgeBaseServiceServer.get({ slug: "missing", enabled: true, request: new Request("https://example.com/help") });
throw new Error("Expected lookup to reject");
} catch (error) {
expect(error).toMatchObject({ code: "not-found" });
expect(getKnowledgeBasePublicErrorKey(error)).toBe("knowledgeBase.public.errors.notFound");
}
cachifiedMock.mockResolvedValueOnce({ title: "Draft docs", enabled: false });
try {
await KnowledgeBaseServiceServer.get({ slug: "draft", enabled: true, request: new Request("https://example.com/help") });
throw new Error("Expected disabled knowledge base to reject");
} catch (error) {
expect(error).toMatchObject({ code: "disabled" });
expect(getKnowledgeBasePublicErrorKey(error)).toBe("knowledgeBase.public.errors.disabled");
}
});
});