feat(platform): harden access scoping and delivery baseline

This commit is contained in:
2026-03-30 00:27:31 +02:00
parent 00b936fa1f
commit 819345acfa
109 changed files with 26142 additions and 8081 deletions
@@ -27,6 +27,32 @@ type ResourceGraphMeta = {
};
};
type ResourceGraphDetail = {
resource: { id: string; eid: string; displayName: string };
availableDomains: string[];
requestedDomain: string | null;
totalNodeCount: number;
totalLinkCount: number;
selectedNodeCount: number;
selectedLinkCount: number;
nodes: Array<{ id: string; domain: string }>;
};
type ProjectGraphDetail = {
project: { id: string; shortCode: string; name: string };
availableDomains: string[];
requestedDomain: string | null;
totalNodeCount: number;
selectedNodeCount: number;
selectedLinkCount: number;
nodes: Array<{ id: string; domain: string }>;
links?: Array<{ source: string; target: string }>;
meta: {
projectName: string;
projectCode: string;
};
};
function createControllerCaller(db: Record<string, unknown>) {
return createCaller({
session: {
@@ -99,6 +125,47 @@ function buildResource(overrides: Record<string, unknown> = {}) {
};
}
function createProjectDb(projectFindImpl: ReturnType<typeof vi.fn>) {
return {
project: {
findUniqueOrThrow: projectFindImpl,
},
estimate: {
findFirst: vi.fn().mockResolvedValue(null),
},
assignment: {
findMany: vi.fn().mockResolvedValue([
{
status: "CONFIRMED",
dailyCostCents: 4_000,
startDate: new Date("2026-01-05T00:00:00.000Z"),
endDate: new Date("2026-01-30T00:00:00.000Z"),
hoursPerDay: 4,
},
]),
},
effortRule: {
count: vi.fn().mockResolvedValue(0),
},
experienceMultiplierRule: {
count: vi.fn().mockResolvedValue(0),
},
};
}
function buildProject(overrides: Record<string, unknown> = {}) {
return {
id: "project_1",
name: "Gelddruckmaschine",
shortCode: "GDM",
budgetCents: 100_000,
winProbability: 75,
startDate: new Date("2026-01-05T00:00:00.000Z"),
endDate: new Date("2026-02-28T00:00:00.000Z"),
...overrides,
};
}
describe("computation graph router", () => {
beforeEach(() => {
vi.clearAllMocks();
@@ -192,4 +259,60 @@ describe("computation graph router", () => {
expect.objectContaining({ name: "Heilige Drei Könige", date: "2026-01-06" }),
]));
});
it("returns a filtered resource detail graph with canonical selection metadata", async () => {
const db = createDb(vi.fn().mockResolvedValue(buildResource({
id: "resource_augsburg",
metroCityId: "city_augsburg",
metroCity: { id: "city_augsburg", name: "Augsburg" },
})));
const caller = createControllerCaller(db);
const result = await caller.getResourceDataDetail({
resourceId: "resource_augsburg",
month: "2026-08",
domain: "SAH",
}) as ResourceGraphDetail;
expect(result.resource).toEqual({
id: "resource_augsburg",
eid: "bruce.banner",
displayName: "Bruce Banner",
});
expect(result.availableDomains).toEqual(expect.arrayContaining(["INPUT", "SAH", "ALLOCATION", "CHARGEABILITY"]));
expect(result.requestedDomain).toBe("SAH");
expect(result.totalNodeCount).toBeGreaterThan(result.selectedNodeCount);
expect(result.totalLinkCount).toBeGreaterThan(0);
expect(result.selectedNodeCount).toBeGreaterThan(0);
expect(result.selectedLinkCount).toBe(0);
expect(result.nodes.every((node) => node.domain === "SAH")).toBe(true);
});
it("returns a filtered project detail graph with canonical project identity", async () => {
const db = createProjectDb(vi.fn().mockResolvedValue(buildProject()));
const caller = createControllerCaller(db);
const result = await caller.getProjectDataDetail({
projectId: "project_1",
domain: "BUDGET",
includeLinks: true,
}) as ProjectGraphDetail;
expect(result.project).toEqual({
id: "project_1",
shortCode: "GDM",
name: "Gelddruckmaschine",
});
expect(result.meta).toEqual({
projectName: "Gelddruckmaschine",
projectCode: "GDM",
});
expect(result.availableDomains).toEqual(expect.arrayContaining(["INPUT", "BUDGET"]));
expect(result.requestedDomain).toBe("BUDGET");
expect(result.totalNodeCount).toBeGreaterThan(result.selectedNodeCount);
expect(result.selectedNodeCount).toBeGreaterThan(0);
expect(result.selectedLinkCount).toBeGreaterThan(0);
expect(result.nodes.every((node) => node.domain === "BUDGET")).toBe(true);
expect(result.links?.length).toBe(result.selectedLinkCount);
});
});