test(daemon): add regression tests for headerless /raw/ reads of workspace-bound projects (#7206)

* test(daemon): add regression tests for headerless raw reads of workspace-bound projects

Closes #7072.

The /raw/ and /preview/ routes call authorizeProjectRequest with
{ mode: 'read', allowNavigationQuery: true }. Before the
enforceLocalProjectDataPlaneRequest refactor, these routes delegated to
enforceVerifiedWorkspaceResourceRead, which required an explicit workspace
context for any workspace-bound project and returned
WORKSPACE_CONTEXT_REQUIRED (400) when headers were absent — breaking
local/desktop preview iframes and MCP preview URLs for projects with a
personal workspace_projects binding.

The two new test cases pin the corrected behaviour: a headerless request
to authorizeProjectRequest with allowNavigationQuery: true must succeed
(return true, not call any remote verifier) for both personal and team
workspace-bound projects, matching exactly what an iframe src or a bare
loopback fetch sends.

* chore: retrigger CI after flaky workspace-restoration P0

* chore: retrigger CI after repeated workspace-restoration flake
This commit is contained in:
mehmet turac
2026-08-23 03:48:14 +00:00
committed by GitHub
parent 1151f4aff8
commit a588cefd2b
@@ -691,4 +691,78 @@ describe('createAuthorizeProjectRequest', () => {
expect.any(String),
);
});
// Regression for https://github.com/nexu-io/open-design/issues/7072:
// A personal project with a stale or valid workspace_projects binding must
// be readable without workspace headers when the caller supplies no
// workspace context at all (e.g. an iframe src, MCP preview URL, or bare
// loopback fetch). The /raw/ and /preview/ routes call authorizeProjectRequest
// with { mode: 'read', allowNavigationQuery: true }; returning
// WORKSPACE_CONTEXT_REQUIRED for these headerless loopback reads broke
// desktop preview for local-only personal projects.
it('allows a headerless iframe raw read of a personal workspace-bound project', async () => {
const row = {
workspaceId: 'workspace-a',
visibility: 'personal',
resourceState: 'active',
createdByWorkspaceMemberId: 'member-a',
syncState: 'local_only',
};
const verify = vi.fn(async () => ({
ok: false as const,
status: 400 as const,
code: 'WORKSPACE_CONTEXT_REQUIRED',
message: 'an explicit workspace context is required',
}));
const sendApiError = vi.fn();
const authorize = createAuthorizeProjectRequest({
db: {},
getWorkspaceProject: () => row,
getWorkspaceProjectByProjectId: () => row,
verifyWorkspaceRequestAuthority: verify,
sendApiError,
});
await expect(authorize(
request({}),
response(),
'project-a',
{ mode: 'read', allowNavigationQuery: true },
)).resolves.toBe(true);
expect(verify).not.toHaveBeenCalled();
expect(sendApiError).not.toHaveBeenCalled();
});
it('allows a headerless iframe raw read of a team workspace-bound project', async () => {
const row = {
workspaceId: 'workspace-a',
visibility: 'team',
resourceState: 'active',
createdByWorkspaceMemberId: 'member-a',
syncState: 'local_only',
};
const verify = vi.fn(async () => ({
ok: false as const,
status: 400 as const,
code: 'WORKSPACE_CONTEXT_REQUIRED',
message: 'an explicit workspace context is required',
}));
const sendApiError = vi.fn();
const authorize = createAuthorizeProjectRequest({
db: {},
getWorkspaceProject: () => row,
getWorkspaceProjectByProjectId: () => row,
verifyWorkspaceRequestAuthority: verify,
sendApiError,
});
await expect(authorize(
request({}),
response(),
'project-a',
{ mode: 'read', allowNavigationQuery: true },
)).resolves.toBe(true);
expect(verify).not.toHaveBeenCalled();
expect(sendApiError).not.toHaveBeenCalled();
});
});