Files
no-whatever/src/app/api/location/search/route.test.ts
T
kurihada 6bb0e65d4c refactor(P1): 5 项代码质量改进 — 消除重复、拆分巨型组件、统一基础设施
Task 4: 统一 amap.ts 为完整 API 客户端
- 扩展 amap.ts 为统一客户端(amapFetch 8s 超时 + 错误处理)
- 导出 searchPlaceText/searchPlaceAround/getInputTips/reverseGeocode/getTransitDirection
- 精简 4 个 location route 为单行调用,blindboxPlanGen 删除 ~80 行内联 API 代码

Task 2: 抽取 ShareCardShell 消除三兄弟重复
- 新建 ShareCardShell.tsx 共享外框/背景/品牌头/QR 底部
- RestaurantShareCard 406→268 行,BlindboxShareCard 341→173 行,BlindboxPlanShareCard 277→159 行

Task 3: 拆分 BlindboxPlan.tsx (742→371 行)
- 提取 planUtils.ts (guessCategory + formatDuration)
- 提取 PoiSearchField / SortablePlanItem / PlanItemEditModal 三个独立组件

Task 1: 拆分 blindbox/[code]/page.tsx 上帝组件 (1300→509 行)
- 提取 useBlindboxRoom / useBlindboxIdeas / useBlindboxPlan / useBlindboxDraw 四个 hooks
- 提取 BlindboxPoolPhase / BlindboxRevealPhase 两个子组件
- 主页面仅保留 phase 协调 + hook 组装 + 子组件渲染

Task 5: 统一 SWR 数据获取层
- 新建 fetcher.ts (FetchError 携带 status,401 不重试)
- 新建 useBlindboxRooms / useAchievements / useFavorites SWR hooks
- useRoomPolling 改用共享 fetcher
- blindbox 大厅/成就/个人中心页面删除手写 fetch 样板代码
- JWT 过期时自动弹出登录框而非反复重试
2026-03-02 18:05:06 +08:00

68 lines
2.0 KiB
TypeScript

import { describe, it, expect, vi, beforeEach } from "vitest";
import { createRequest, parseJsonResponse } from "@/__tests__/helpers/api-test-utils";
vi.mock("@/lib/prisma", () => ({ prisma: {} }));
const mockSearchPlaceText = vi.fn();
vi.mock("@/lib/amap", () => ({
searchPlaceText: (...args: unknown[]) => mockSearchPlaceText(...args),
}));
import { GET } from "./route";
const mockCtx = { params: Promise.resolve({}) };
beforeEach(() => {
vi.clearAllMocks();
});
describe("GET /api/location/search", () => {
it("returns search results", async () => {
mockSearchPlaceText.mockResolvedValue([
{
id: "poi-1",
name: "星巴克",
address: "南京路1号",
lat: 31.2,
lng: 121.4,
rating: 4.5,
cost: 40,
},
]);
const req = createRequest("/api/location/search?keywords=星巴克");
const res = await GET(req, mockCtx);
const { status, data } = await parseJsonResponse(res);
expect(status).toBe(200);
expect(data).toHaveLength(1);
expect(data[0].name).toBe("星巴克");
expect(data[0].lat).toBe(31.2);
expect(data[0].lng).toBe(121.4);
});
it("returns empty when no results", async () => {
mockSearchPlaceText.mockResolvedValue([]);
const req = createRequest("/api/location/search?keywords=不存在的地方");
const res = await GET(req, mockCtx);
const { data } = await parseJsonResponse(res);
expect(data).toEqual([]);
});
it("returns 400 when keywords missing", async () => {
const req = createRequest("/api/location/search");
const res = await GET(req, mockCtx);
expect(res.status).toBe(400);
});
it("returns 503 when API unavailable", async () => {
const { ApiError } = await import("@/lib/api");
mockSearchPlaceText.mockRejectedValue(new ApiError("位置服务暂时不可用,请稍后重试", 503));
const req = createRequest("/api/location/search?keywords=test");
const res = await GET(req, mockCtx);
expect(res.status).toBe(503);
});
});