12a7aebeff
- Added retry options to HaloClient for handling transient errors. - Refactored request methods in HaloClient to utilize retry logic. - Updated HaloService to include logging for error handling. - Introduced ApiPaths utility for managing API endpoints. - Implemented logger utility for consistent logging across services. - Added tests for ContentService, Error handling, and TaxonomyService. - Created retry utility for managing retry logic with exponential backoff. - Updated types to include additional properties for better API response handling.
242 lines
7.8 KiB
TypeScript
242 lines
7.8 KiB
TypeScript
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
import { TaxonomyService } from "../taxonomy-service";
|
|
import type { HaloClient } from "../client";
|
|
import type { Category, Tag } from "../types";
|
|
|
|
describe("TaxonomyService", () => {
|
|
let service: TaxonomyService;
|
|
let mockClient: HaloClient;
|
|
|
|
beforeEach(() => {
|
|
mockClient = {
|
|
get: vi.fn(),
|
|
post: vi.fn(),
|
|
put: vi.fn(),
|
|
delete: vi.fn(),
|
|
} as unknown as HaloClient;
|
|
service = new TaxonomyService(mockClient);
|
|
});
|
|
|
|
describe("getCategories", () => {
|
|
it("should return categories from API", async () => {
|
|
const mockCategories: Category[] = [
|
|
{
|
|
metadata: { name: "cat-1" },
|
|
spec: { displayName: "技术", slug: "tech", description: "", cover: "", template: "", priority: 0, children: [] },
|
|
},
|
|
{
|
|
metadata: { name: "cat-2" },
|
|
spec: { displayName: "生活", slug: "life", description: "", cover: "", template: "", priority: 1, children: [] },
|
|
},
|
|
];
|
|
|
|
vi.mocked(mockClient.get).mockResolvedValue({ items: mockCategories });
|
|
|
|
const result = await service.getCategories();
|
|
|
|
expect(result).toEqual(mockCategories);
|
|
expect(mockClient.get).toHaveBeenCalledWith("/apis/content.halo.run/v1alpha1/categories");
|
|
});
|
|
});
|
|
|
|
describe("getTags", () => {
|
|
it("should return tags from API", async () => {
|
|
const mockTags: Tag[] = [
|
|
{
|
|
metadata: { name: "tag-1" },
|
|
spec: { displayName: "Python", slug: "python", color: "#3776AB", cover: "" },
|
|
},
|
|
];
|
|
|
|
vi.mocked(mockClient.get).mockResolvedValue({ items: mockTags });
|
|
|
|
const result = await service.getTags();
|
|
|
|
expect(result).toEqual(mockTags);
|
|
expect(mockClient.get).toHaveBeenCalledWith("/apis/content.halo.run/v1alpha1/tags");
|
|
});
|
|
});
|
|
|
|
describe("getCategoryNames", () => {
|
|
it("should return existing category names", async () => {
|
|
const mockCategories: Category[] = [
|
|
{
|
|
metadata: { name: "cat-1" },
|
|
spec: { displayName: "技术", slug: "tech", description: "", cover: "", template: "", priority: 0, children: [] },
|
|
},
|
|
];
|
|
|
|
vi.mocked(mockClient.get).mockResolvedValue({ items: mockCategories });
|
|
|
|
const result = await service.getCategoryNames(["技术"]);
|
|
|
|
expect(result).toContain("cat-1");
|
|
});
|
|
|
|
it("should create new categories if not exist", async () => {
|
|
const mockCategories: Category[] = [];
|
|
const newCategory: Category = {
|
|
metadata: { name: "new-cat" },
|
|
spec: { displayName: "新分类", slug: "xin-fen-lei", description: "", cover: "", template: "", priority: 0, children: [] },
|
|
};
|
|
|
|
vi.mocked(mockClient.get).mockResolvedValue({ items: mockCategories });
|
|
vi.mocked(mockClient.post).mockResolvedValue(newCategory);
|
|
|
|
const result = await service.getCategoryNames(["新分类"]);
|
|
|
|
expect(result).toContain("new-cat");
|
|
expect(mockClient.post).toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe("getTagNames", () => {
|
|
it("should return existing tag names", async () => {
|
|
const mockTags: Tag[] = [
|
|
{
|
|
metadata: { name: "tag-1" },
|
|
spec: { displayName: "Python", slug: "python", color: "#3776AB", cover: "" },
|
|
},
|
|
];
|
|
|
|
vi.mocked(mockClient.get).mockResolvedValue({ items: mockTags });
|
|
|
|
const result = await service.getTagNames(["Python"]);
|
|
|
|
expect(result).toContain("tag-1");
|
|
});
|
|
|
|
it("should create new tags if not exist", async () => {
|
|
const mockTags: Tag[] = [];
|
|
const newTag: Tag = {
|
|
metadata: { name: "new-tag" },
|
|
spec: { displayName: "新标签", slug: "xin-biao-qian", color: "#ffffff", cover: "" },
|
|
};
|
|
|
|
vi.mocked(mockClient.get).mockResolvedValue({ items: mockTags });
|
|
vi.mocked(mockClient.post).mockResolvedValue(newTag);
|
|
|
|
const result = await service.getTagNames(["新标签"]);
|
|
|
|
expect(result).toContain("new-tag");
|
|
expect(mockClient.post).toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe("getCategoryDisplayNames", () => {
|
|
it("should convert category names to display names", async () => {
|
|
const mockCategories: Category[] = [
|
|
{
|
|
metadata: { name: "cat-1" },
|
|
spec: { displayName: "技术", slug: "tech", description: "", cover: "", template: "", priority: 0, children: [] },
|
|
},
|
|
];
|
|
|
|
vi.mocked(mockClient.get).mockResolvedValue({ items: mockCategories });
|
|
|
|
const result = await service.getCategoryDisplayNames(["cat-1"]);
|
|
|
|
expect(result).toEqual(["技术"]);
|
|
});
|
|
|
|
it("should return empty array for empty input", async () => {
|
|
const result = await service.getCategoryDisplayNames([]);
|
|
|
|
expect(result).toEqual([]);
|
|
});
|
|
|
|
it("should filter out unknown category names", async () => {
|
|
const mockCategories: Category[] = [
|
|
{
|
|
metadata: { name: "cat-1" },
|
|
spec: { displayName: "技术", slug: "tech", description: "", cover: "", template: "", priority: 0, children: [] },
|
|
},
|
|
];
|
|
|
|
vi.mocked(mockClient.get).mockResolvedValue({ items: mockCategories });
|
|
|
|
const result = await service.getCategoryDisplayNames(["cat-1", "unknown-cat"]);
|
|
|
|
expect(result).toEqual(["技术"]);
|
|
});
|
|
});
|
|
|
|
describe("getTagDisplayNames", () => {
|
|
it("should convert tag names to display names", async () => {
|
|
const mockTags: Tag[] = [
|
|
{
|
|
metadata: { name: "tag-1" },
|
|
spec: { displayName: "Python", slug: "python", color: "#3776AB", cover: "" },
|
|
},
|
|
];
|
|
|
|
vi.mocked(mockClient.get).mockResolvedValue({ items: mockTags });
|
|
|
|
const result = await service.getTagDisplayNames(["tag-1"]);
|
|
|
|
expect(result).toEqual(["Python"]);
|
|
});
|
|
});
|
|
|
|
describe("createCategory", () => {
|
|
it("should create a new category", async () => {
|
|
const newCategory: Category = {
|
|
metadata: { name: "cat-created" },
|
|
spec: { displayName: "新分类", slug: "xin-fen-lei", description: "", cover: "", template: "", priority: 0, children: [] },
|
|
};
|
|
|
|
vi.mocked(mockClient.post).mockResolvedValue(newCategory);
|
|
|
|
const result = await service.createCategory("新分类", "xin-fen-lei", 0);
|
|
|
|
expect(result).toEqual(newCategory);
|
|
expect(mockClient.post).toHaveBeenCalledWith(
|
|
"/apis/content.halo.run/v1alpha1/categories",
|
|
expect.objectContaining({
|
|
spec: expect.objectContaining({ displayName: "新分类", slug: "xin-fen-lei" }),
|
|
}),
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("createTag", () => {
|
|
it("should create a new tag", async () => {
|
|
const newTag: Tag = {
|
|
metadata: { name: "tag-created" },
|
|
spec: { displayName: "新标签", slug: "xin-biao-qian", color: "#ffffff", cover: "" },
|
|
};
|
|
|
|
vi.mocked(mockClient.post).mockResolvedValue(newTag);
|
|
|
|
const result = await service.createTag("新标签", "xin-biao-qian", "#ffffff");
|
|
|
|
expect(result).toEqual(newTag);
|
|
expect(mockClient.post).toHaveBeenCalledWith(
|
|
"/apis/content.halo.run/v1alpha1/tags",
|
|
expect.objectContaining({
|
|
spec: expect.objectContaining({ displayName: "新标签", slug: "xin-biao-qian", color: "#ffffff" }),
|
|
}),
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("deleteCategory", () => {
|
|
it("should delete a category", async () => {
|
|
vi.mocked(mockClient.delete).mockResolvedValue();
|
|
|
|
await service.deleteCategory("cat-1");
|
|
|
|
expect(mockClient.delete).toHaveBeenCalledWith("/apis/content.halo.run/v1alpha1/categories/cat-1");
|
|
});
|
|
});
|
|
|
|
describe("deleteTag", () => {
|
|
it("should delete a tag", async () => {
|
|
vi.mocked(mockClient.delete).mockResolvedValue();
|
|
|
|
await service.deleteTag("tag-1");
|
|
|
|
expect(mockClient.delete).toHaveBeenCalledWith("/apis/content.halo.run/v1alpha1/tags/tag-1");
|
|
});
|
|
});
|
|
}); |