From 891b47c7fd0ad7b9f902bd51cd0fdb8a0ae04666 Mon Sep 17 00:00:00 2001 From: Dotta Date: Thu, 8 Oct 2026 07:25:06 -0500 Subject: [PATCH] test: bootstrap real issue routes before auth assertions Import the existing route graph once within bounded setup. Keep fresh app actors, reset service state, preserve both denial assertions and the test timeout, and add a board control. Co-Authored-By: Paperclip --- .../issue-workspace-command-authz.test.ts | 53 ++++++++++++++++--- 1 file changed, 46 insertions(+), 7 deletions(-) diff --git a/server/src/__tests__/issue-workspace-command-authz.test.ts b/server/src/__tests__/issue-workspace-command-authz.test.ts index 8b5e09d7a2b..afcc85f124c 100644 --- a/server/src/__tests__/issue-workspace-command-authz.test.ts +++ b/server/src/__tests__/issue-workspace-command-authz.test.ts @@ -1,6 +1,6 @@ import express from "express"; import request from "supertest"; -import { beforeEach, describe, expect, it, vi } from "vitest"; +import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; const mockIssueService = vi.hoisted(() => ({ addComment: vi.fn(), @@ -44,6 +44,7 @@ const mockHeartbeatService = vi.hoisted(() => ({ const mockInstanceSettingsService = vi.hoisted(() => ({ get: vi.fn(), + getExperimental: vi.fn(), listCompanyIds: vi.fn(), })); @@ -151,11 +152,10 @@ function registerRouteMocks() { })); } -async function createApp(actor: Record) { - const [{ errorHandler }, { issueRoutes }] = await Promise.all([ - vi.importActual("../middleware/index.js"), - vi.importActual("../routes/issues.js"), - ]); +let errorHandler: typeof import("../middleware/index.js").errorHandler; +let issueRoutes: typeof import("../routes/issues.js").issueRoutes; + +function createApp(actor: Record) { const app = express(); app.use(express.json()); app.use((req, _res, next) => { @@ -190,7 +190,9 @@ function makeIssue(overrides: Record = {}) { } describe("issue workspace command authorization", () => { - beforeEach(() => { + // Transform the real route graph within the existing bounded setup hook. + // Every case still creates a fresh app/actor and resets its service mocks. + beforeAll(async () => { vi.resetModules(); vi.doUnmock("../services/access.js"); vi.doUnmock("../services/activity-log.js"); @@ -206,6 +208,13 @@ describe("issue workspace command authorization", () => { vi.doUnmock("../routes/authz.js"); vi.doUnmock("../middleware/index.js"); registerRouteMocks(); + [{ errorHandler }, { issueRoutes }] = await Promise.all([ + vi.importActual("../middleware/index.js"), + vi.importActual("../routes/issues.js"), + ]); + }); + + beforeEach(() => { vi.clearAllMocks(); mockIssueService.addComment.mockResolvedValue(null); mockIssueService.create.mockResolvedValue(makeIssue()); @@ -249,6 +258,7 @@ describe("issue workspace command authorization", () => { feedbackDataSharingPreference: "prompt", }, }); + mockInstanceSettingsService.getExperimental.mockResolvedValue({ enableExternalObjects: false }); mockInstanceSettingsService.listCompanyIds.mockResolvedValue(["company-1"]); mockLogActivity.mockResolvedValue(undefined); mockRoutineService.syncRunStatusForIssue.mockResolvedValue(undefined); @@ -263,6 +273,35 @@ describe("issue workspace command authorization", () => { })); }); + it("allows board callers to create workspace commands with fresh actor and service state", async () => { + const app = createApp({ + type: "board", + userId: "board-user", + companyIds: ["company-1"], + source: "local_implicit", + }); + + const res = await request(app) + .post("/api/companies/company-1/issues") + .send({ + title: "Board workspace", + executionWorkspaceSettings: { + workspaceStrategy: { + type: "git_worktree", + runtimeProvisionCommand: "touch /tmp/paperclip-rce", + }, + }, + }); + + expect(res.status).toBe(201); + expect(mockIssueService.create).toHaveBeenCalledOnce(); + expect(mockIssueService.create).toHaveBeenCalledWith("company-1", expect.objectContaining({ + executionWorkspaceSettings: expect.objectContaining({ + workspaceStrategy: expect.objectContaining({ runtimeProvisionCommand: "touch /tmp/paperclip-rce" }), + }), + })); + }); + it("rejects agent callers that create issue workspace runtime provision commands", async () => { const app = await createApp({ type: "agent",