fix(api): align frontend with REST contracts

This commit is contained in:
2026-07-30 20:38:52 +08:00
parent b57e58ff87
commit 782363cfb6
65 changed files with 99362 additions and 318 deletions
+103
View File
@@ -0,0 +1,103 @@
import { render, waitFor } from "@testing-library/react";
import { ProjectProvider } from "./ProjectContext";
const mockApiFetch = jest.fn();
const mockUseSession = jest.fn();
const mockSetCurrentProjectId = jest.fn();
jest.mock("next-auth/react", () => ({
useSession: () => mockUseSession(),
}));
jest.mock("next/navigation", () => ({
usePathname: () => "/login",
useRouter: () => ({ push: jest.fn() }),
}));
jest.mock("@/components/project/ProjectSelector", () => ({
ProjectSelector: () => <div>project selector</div>,
}));
jest.mock("@/config/config", () => ({
config: {
BACKEND_URL: "http://backend.test",
MAP_EXTENT: [1, 2, 3, 4],
MAP_WORKSPACE: "default-workspace",
},
NETWORK_NAME: "default-network",
setMapExtent: jest.fn(),
setMapWorkspace: jest.fn(),
setNetworkName: jest.fn(),
}));
jest.mock("@/lib/apiFetch", () => ({
apiFetch: (...args: unknown[]) => mockApiFetch(...args),
}));
jest.mock("@/lib/permissions", () => ({
permissionCodes: { environmentManage: "environment:manage" },
}));
jest.mock("@/store/accessStore", () => ({
useAccessStore: (selector: (state: { permissions: string[] }) => unknown) =>
selector({ permissions: [] }),
}));
jest.mock("@/store/projectStore", () => ({
useProjectStore: (
selector: (state: { setCurrentProjectId: typeof mockSetCurrentProjectId }) => unknown,
) => selector({ setCurrentProjectId: mockSetCurrentProjectId }),
}));
const seedSavedProject = () => {
localStorage.setItem("MAP_WORKSPACE", "fengyang");
localStorage.setItem("NETWORK_NAME", "fengyang");
localStorage.setItem("MAP_EXTENT", "1,2,3,4");
localStorage.setItem(
"active_project",
"a2d67c84-fd9d-4feb-a500-c357244b2760",
);
};
describe("ProjectProvider authentication boundary", () => {
beforeEach(() => {
localStorage.clear();
mockApiFetch.mockReset();
mockSetCurrentProjectId.mockReset();
mockApiFetch.mockResolvedValue({
ok: true,
json: async () => ({}),
});
});
it("does not restore or open a saved project while unauthenticated", () => {
seedSavedProject();
mockUseSession.mockReturnValue({ status: "unauthenticated" });
render(
<ProjectProvider>
<div>login page</div>
</ProjectProvider>,
);
expect(mockSetCurrentProjectId).not.toHaveBeenCalled();
expect(mockApiFetch).not.toHaveBeenCalled();
});
it("restores and opens a saved project after authentication", async () => {
seedSavedProject();
mockUseSession.mockReturnValue({ status: "authenticated" });
render(
<ProjectProvider>
<div>main page</div>
</ProjectProvider>,
);
await waitFor(() => expect(mockApiFetch).toHaveBeenCalledTimes(2));
expect(mockSetCurrentProjectId).toHaveBeenCalledWith(
"a2d67c84-fd9d-4feb-a500-c357244b2760",
);
});
});