134 行
3.8 KiB
TypeScript
134 行
3.8 KiB
TypeScript
import { describe, expect, it, vi, afterEach } from "vitest";
|
|
import { appRouter } from "./routers";
|
|
import type { TrpcContext } from "./_core/context";
|
|
import * as db from "./db";
|
|
|
|
type AuthenticatedUser = NonNullable<TrpcContext["user"]>;
|
|
|
|
function createTestUser(overrides?: Partial<AuthenticatedUser>): AuthenticatedUser {
|
|
return {
|
|
id: 7,
|
|
openId: "market-user-7",
|
|
email: "market@example.com",
|
|
name: "MarketTester",
|
|
loginMethod: "username",
|
|
role: "user",
|
|
skillLevel: "beginner",
|
|
trainingGoals: null,
|
|
ntrpRating: 1.5,
|
|
manualNtrpRating: null,
|
|
manualNtrpCapturedAt: null,
|
|
heightCm: null,
|
|
weightKg: null,
|
|
sprintSpeedScore: null,
|
|
explosivePowerScore: null,
|
|
agilityScore: null,
|
|
enduranceScore: null,
|
|
flexibilityScore: null,
|
|
coreStabilityScore: null,
|
|
shoulderMobilityScore: null,
|
|
hipMobilityScore: null,
|
|
assessmentNotes: null,
|
|
totalSessions: 0,
|
|
totalMinutes: 0,
|
|
totalShots: 0,
|
|
currentStreak: 0,
|
|
longestStreak: 0,
|
|
createdAt: new Date(),
|
|
updatedAt: new Date(),
|
|
lastSignedIn: new Date(),
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
function createMockContext(user: AuthenticatedUser | null): TrpcContext {
|
|
return {
|
|
user,
|
|
sessionSid: user ? "market-session" : null,
|
|
req: {
|
|
protocol: "https",
|
|
headers: {},
|
|
} as TrpcContext["req"],
|
|
res: {
|
|
clearCookie: vi.fn(),
|
|
cookie: vi.fn(),
|
|
} as TrpcContext["res"],
|
|
};
|
|
}
|
|
|
|
afterEach(() => {
|
|
vi.restoreAllMocks();
|
|
});
|
|
|
|
describe("market.watchRuleCreate", () => {
|
|
it("creates a rule and queues a refresh task", async () => {
|
|
const user = createTestUser();
|
|
const caller = appRouter.createCaller(createMockContext(user));
|
|
|
|
vi.spyOn(db, "createRacketWatchRule").mockResolvedValueOnce(88);
|
|
vi.spyOn(db, "createBackgroundTask").mockResolvedValueOnce("task-market-1");
|
|
vi.spyOn(db, "getBackgroundTaskById").mockResolvedValueOnce({
|
|
id: "task-market-1",
|
|
userId: user.id,
|
|
type: "market_watch_refresh",
|
|
status: "queued",
|
|
title: "Yonex ≤ ¥500 刷新",
|
|
message: "监控规则已创建,后台开始抓取对应平台价格",
|
|
progress: 0,
|
|
payload: {},
|
|
result: null,
|
|
error: null,
|
|
attempts: 0,
|
|
maxAttempts: 3,
|
|
workerId: null,
|
|
runAfter: new Date(),
|
|
lockedAt: null,
|
|
startedAt: null,
|
|
completedAt: null,
|
|
createdAt: new Date(),
|
|
updatedAt: new Date(),
|
|
} as Awaited<ReturnType<typeof db.getBackgroundTaskById>>);
|
|
|
|
const result = await caller.market.watchRuleCreate({
|
|
brand: "Yonex",
|
|
targetPrice: 500,
|
|
modelKeyword: "98",
|
|
pushEnabled: true,
|
|
});
|
|
|
|
expect(result.ruleId).toBe(88);
|
|
expect(result.taskId).toBeTruthy();
|
|
expect(db.createRacketWatchRule).toHaveBeenCalledWith(expect.objectContaining({
|
|
userId: user.id,
|
|
brand: "Yonex",
|
|
targetPrice: 500,
|
|
pushEnabled: 1,
|
|
isActive: 1,
|
|
}));
|
|
expect(db.createBackgroundTask).toHaveBeenCalledWith(expect.objectContaining({
|
|
userId: user.id,
|
|
type: "market_watch_refresh",
|
|
}));
|
|
});
|
|
});
|
|
|
|
describe("market.pushConfigUpdate", () => {
|
|
it("updates the default webhook for admins", async () => {
|
|
const admin = createTestUser({ role: "admin", id: 1, name: "Admin" });
|
|
const caller = appRouter.createCaller(createMockContext(admin));
|
|
|
|
vi.spyOn(db, "updateAppSetting").mockResolvedValueOnce(undefined);
|
|
vi.spyOn(db, "createAdminAuditLog").mockResolvedValueOnce(undefined);
|
|
|
|
const result = await caller.market.pushConfigUpdate({
|
|
webhookUrl: "https://open.larksuite.com/open-apis/bot/v2/hook/demo",
|
|
});
|
|
|
|
expect(result).toEqual({ success: true });
|
|
expect(db.updateAppSetting).toHaveBeenCalledWith("market_default_feishu_webhook", {
|
|
value: "https://open.larksuite.com/open-apis/bot/v2/hook/demo",
|
|
type: "string",
|
|
});
|
|
});
|
|
});
|