feat(workbench): integrate runtime data and map tooling

This commit is contained in:
2026-09-22 10:45:46 +08:00
parent bf44cc0f25
commit bebcf2a06c
163 changed files with 15642 additions and 1312 deletions
@@ -0,0 +1,105 @@
import { afterEach, describe, expect, it, vi } from "vitest";
import {
fetchWorkbenchProjectPresentation,
findOwnedWorkbenchProject,
parseProjectMapExtent
} from "./project-client";
describe("workbench project client", () => {
afterEach(() => vi.unstubAllGlobals());
it("selects the active project owned by the configured GeoServer workspace", async () => {
const fetchMock = vi.fn(async (_input: RequestInfo | URL, _init?: RequestInit) => new Response(JSON.stringify({
items: [
{ project_id: "inactive", gs_workspace: "tjwater_next", status: "disabled" },
{ project_id: "supply", gs_workspace: "tjwater_next", status: "active" }
],
total: 2,
limit: 1000,
offset: 0
}), { status: 200 }));
vi.stubGlobal("fetch", fetchMock);
const result = await findOwnedWorkbenchProject(async () => "token");
expect(result.project.project_id).toBe("supply");
const headers = new Headers(fetchMock.mock.calls[0]?.[1]?.headers);
expect(headers.get("Authorization")).toBe("Bearer token");
expect(headers.get("X-Project-Id")).toBeNull();
});
it("does not infer a project when the authorized list has no workspace match", async () => {
vi.stubGlobal("fetch", vi.fn(async () => new Response(JSON.stringify({
items: [], total: 0, limit: 1000, offset: 0
}), { status: 200 })));
await expect(findOwnedWorkbenchProject()).rejects.toThrow("授权项目");
});
it("loads the authoritative project extent and access identity independently", async () => {
const fetchMock = vi.fn(async (input: RequestInfo | URL, init?: RequestInit) => {
const path = new URL(String(input)).pathname;
if (path === "/api/v1/projects") {
return new Response(JSON.stringify({
items: [{
project_id: "supply",
name: "北区供水项目",
code: "north-supply",
gs_workspace: "tjwater_next",
status: "active",
project_role: "member"
}],
total: 1,
limit: 1000,
offset: 0
}), { status: 200 });
}
const headers = new Headers(init?.headers);
expect(headers.get("Authorization")).toBe("Bearer token");
expect(headers.get("X-Project-Id")).toBe("supply");
if (path === "/api/v1/projects/current/metadata") {
return new Response(JSON.stringify({
project_id: "supply",
name: "北区供水项目",
code: "north-supply",
gs_workspace: "tjwater_next",
map_extent: { bbox: [13358338.895, 3503549.844, 13369470.85, 3516410.23], zoom: 13 },
status: "active",
project_role: "member"
}), { status: 200 });
}
return new Response(JSON.stringify({
user_id: "user-1",
username: "north.operator",
system_role: "user",
is_system_admin: false,
project_id: "supply",
project_role: "member",
permissions: ["webgis.view"]
}), { status: 200 });
});
vi.stubGlobal("fetch", fetchMock);
const presentation = await fetchWorkbenchProjectPresentation(async () => "token");
expect(presentation).toMatchObject({
projectId: "supply",
projectName: "北区供水项目",
username: "north.operator",
projectRole: "member",
metadataAvailable: true,
accessContextAvailable: true,
networkView: {
bbox3857: [13358338.895, 3503549.844, 13369470.85, 3516410.23]
}
});
});
it("rejects malformed or reversed project extents without losing the fallback path", () => {
expect(parseProjectMapExtent({ bbox: [10, 20, 5, 30] })).toBeNull();
expect(parseProjectMapExtent({ bbox: [0, 0, 30_000_000, 1] })).toBeNull();
expect(parseProjectMapExtent({ xmin: 1, ymin: 2, xmax: 3, ymax: 4 })).toEqual({
bbox3857: [1, 2, 3, 4]
});
});
});