Files
Obsidian/obsidian-halo/src/services/__tests__/taxonomy-service.test.ts
T
Serendipity 12a7aebeff feat: Enhance HaloClient with retry logic and improve error handling
- 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.
2026-04-28 18:01:26 +08:00

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");
});
});
});