test(api): harden estimate races and user auth boundaries

This commit is contained in:
2026-03-30 12:32:51 +02:00
parent 3c4894a966
commit 019c267435
3 changed files with 192 additions and 1 deletions
@@ -2889,6 +2889,113 @@ describe("assistant import/export and dispo tools", () => {
});
});
it("returns stable assistant errors when estimate creation loses referenced records mid-write", async () => {
const cases = [
{
name: "missing resource reference",
payload: {
name: "Delivery Estimate",
demandLines: [
{
resourceId: "resource_1",
lineType: "LABOR",
name: "Animation",
hours: 40,
costRateCents: 0,
billRateCents: 0,
currency: "EUR",
costTotalCents: 0,
priceTotalCents: 0,
monthlySpread: {},
staffingAttributes: {},
metadata: {},
},
],
},
rejection: {
code: "P2003",
message: "Foreign key constraint failed",
meta: { field_name: "EstimateDemandLine_resourceId_fkey" },
},
expected: "Resource not found with the given criteria.",
},
{
name: "missing scope item reference",
payload: {
name: "Delivery Estimate",
scopeItems: [
{
sequenceNo: 1,
scopeType: "SHOT",
name: "Shot 010",
technicalSpec: {},
metadata: {},
},
],
demandLines: [
{
scopeItemId: "scope_item_missing",
lineType: "LABOR",
name: "Lighting",
hours: 24,
costRateCents: 0,
billRateCents: 0,
currency: "EUR",
costTotalCents: 0,
priceTotalCents: 0,
monthlySpread: {},
staffingAttributes: {},
metadata: {},
},
],
},
rejection: {
code: "P2003",
message: "Foreign key constraint failed",
meta: { field_name: "EstimateDemandLine_scopeItemId_fkey" },
},
expected: "Estimate scope item not found with the given criteria.",
},
{
name: "generic referenced record race",
payload: { name: "Delivery Estimate" },
rejection: {
code: "P2025",
message: "Record to create no longer references a valid row",
meta: { cause: "Dependent record disappeared during nested estimate create" },
},
expected: "One of the referenced project, role, resource, or scope items no longer exists.",
},
] as const;
for (const testCase of cases) {
const ctx = createToolContext(
{
estimate: {
create: vi.fn().mockRejectedValue(testCase.rejection),
},
rateCardLine: {
findMany: vi.fn().mockResolvedValue([]),
},
},
{
userRole: SystemRole.MANAGER,
permissions: [PermissionKey.MANAGE_PROJECTS],
},
);
const result = await executeTool(
"create_estimate",
JSON.stringify(testCase.payload),
ctx,
);
expect(JSON.parse(result.content)).toEqual({
error: testCase.expected,
});
}
});
it("returns stable assistant errors for estimate mutation tools backed by estimate application use-cases", async () => {
const cases = [
{