docs(admin-portal): 新增 nextstep-v2.md 记录下游核查结果

v1 声称完成的下游工作经核查实际未完成:
- api-gateway: /api/admin/graphql 路由未注册,go vet 编译失败
- teacher-bff: resolver 已完成但 schema 未同步(命名空间 vs 扁平)
- iam: proto 缺 BatchGetUsers rpc 声明

v2 记录详细核查证据和修复要求
This commit is contained in:
SpecialX
2026-07-14 08:26:27 +08:00
parent 99580fa13a
commit 0b42302a64
42 changed files with 4804 additions and 637 deletions

View File

@@ -0,0 +1,84 @@
/**
* Vitest 全局测试 setup
*
* - 注册 @testing-library/jest-dom 自定义匹配器
* - mock next/navigationApp Router hooks
* - polyfill BroadcastChannel / WebSocketjsdom 未实现)
* - 启动/重置/关闭 MSW server
*/
import "@testing-library/jest-dom/vitest";
import { afterAll, afterEach, beforeAll, beforeEach, vi } from "vitest";
import { server } from "./src/mocks/server";
// mock next/navigationApp Router hooks 依赖)
vi.mock("next/navigation", () => ({
useParams: () => ({}),
useRouter: () => ({
push: vi.fn(),
replace: vi.fn(),
back: vi.fn(),
forward: vi.fn(),
refresh: vi.fn(),
prefetch: vi.fn(),
}),
useSearchParams: () => new URLSearchParams(),
usePathname: () => "/",
redirect: vi.fn(),
notFound: vi.fn(),
}));
// btoa Unicode 兼容 polyfill 已移至 vitest.polyfills.ts需在 server import 之前生效)
// BroadcastChannel polyfilljsdom 未实现 BroadcastChannel
if (typeof globalThis.BroadcastChannel === "undefined") {
class MockBroadcastChannel {
name: string;
onmessage: ((event: MessageEvent) => void) | null = null;
constructor(name: string) {
this.name = name;
}
addEventListener(): void {}
removeEventListener(): void {}
postMessage(): void {}
close(): void {}
}
// 测试中需要 as 断言将 mock 类挂载到全局
globalThis.BroadcastChannel =
MockBroadcastChannel as unknown as typeof BroadcastChannel;
}
// WebSocket polyfilljsdom 未实现 WebSocket
if (typeof globalThis.WebSocket === "undefined") {
class MockWebSocket {
static CONNECTING = 0;
static OPEN = 1;
static CLOSING = 2;
static CLOSED = 3;
url: string;
onopen: ((event: Event) => void) | null = null;
onmessage: ((event: MessageEvent) => void) | null = null;
onerror: ((event: Event) => void) | null = null;
onclose: ((event: CloseEvent) => void) | null = null;
readyState = 0;
constructor(url: string) {
this.url = url;
}
send(): void {}
close(): void {}
}
globalThis.WebSocket = MockWebSocket as unknown as typeof WebSocket;
}
// 启动 MSW server拦截 HTTP 请求返回 mock 响应)
beforeAll(() => server.listen({ onUnhandledRequest: "bypass" }));
// 每个测试后恢复初始 MSW handlers隔离测试间状态
afterEach(() => server.restoreHandlers());
// 全部测试完成后关闭 MSW server
afterAll(() => server.close());
// 每个测试前清理 localStorage防止测试间状态泄漏
beforeEach(() => {
localStorage.clear();
});