Add admin vision lab and LLM vision verification
这个提交包含在:
@@ -816,3 +816,80 @@ describe("notification.markAllRead", () => {
|
||||
await expect(caller.notification.markAllRead()).rejects.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
// ===== VISION LIBRARY TESTS =====
|
||||
|
||||
describe("vision.library", () => {
|
||||
it("requires authentication", async () => {
|
||||
const { ctx } = createMockContext(null);
|
||||
const caller = appRouter.createCaller(ctx);
|
||||
await expect(caller.vision.library()).rejects.toThrow();
|
||||
});
|
||||
|
||||
it("returns seeded references for authenticated users", async () => {
|
||||
const user = createTestUser();
|
||||
const { ctx } = createMockContext(user);
|
||||
const caller = appRouter.createCaller(ctx);
|
||||
const seedSpy = vi.spyOn(db, "seedVisionReferenceImages").mockResolvedValueOnce();
|
||||
const listSpy = vi.spyOn(db, "listVisionReferenceImages").mockResolvedValueOnce([
|
||||
{
|
||||
id: 1,
|
||||
slug: "ref-1",
|
||||
title: "标准图:正手挥拍",
|
||||
exerciseType: "forehand",
|
||||
imageUrl: "https://example.com/forehand.jpg",
|
||||
sourcePageUrl: "https://example.com/source",
|
||||
sourceLabel: "Example",
|
||||
author: null,
|
||||
license: null,
|
||||
expectedFocus: ["肩髋转动"],
|
||||
tags: ["forehand"],
|
||||
notes: null,
|
||||
sortOrder: 1,
|
||||
isPublished: 1,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
},
|
||||
] as any);
|
||||
|
||||
const result = await caller.vision.library();
|
||||
|
||||
expect(seedSpy).toHaveBeenCalledTimes(1);
|
||||
expect(listSpy).toHaveBeenCalledTimes(1);
|
||||
expect(result).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("vision.runs", () => {
|
||||
it("limits regular users to their own vision test runs", async () => {
|
||||
const user = createTestUser({ id: 7, role: "user" });
|
||||
const { ctx } = createMockContext(user);
|
||||
const caller = appRouter.createCaller(ctx);
|
||||
const listSpy = vi.spyOn(db, "listVisionTestRuns").mockResolvedValueOnce([]);
|
||||
|
||||
await caller.vision.runs({ limit: 20 });
|
||||
|
||||
expect(listSpy).toHaveBeenCalledWith(7, 20);
|
||||
});
|
||||
|
||||
it("allows admin users to view all vision test runs", async () => {
|
||||
const admin = createTestUser({ id: 9, role: "admin", name: "H1" });
|
||||
const { ctx } = createMockContext(admin);
|
||||
const caller = appRouter.createCaller(ctx);
|
||||
const listSpy = vi.spyOn(db, "listVisionTestRuns").mockResolvedValueOnce([]);
|
||||
|
||||
await caller.vision.runs({ limit: 30 });
|
||||
|
||||
expect(listSpy).toHaveBeenCalledWith(undefined, 30);
|
||||
});
|
||||
});
|
||||
|
||||
describe("vision.seedLibrary", () => {
|
||||
it("rejects non-admin users", async () => {
|
||||
const user = createTestUser({ role: "user" });
|
||||
const { ctx } = createMockContext(user);
|
||||
const caller = appRouter.createCaller(ctx);
|
||||
|
||||
await expect(caller.vision.seedLibrary()).rejects.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
在新工单中引用
屏蔽一个用户