Skip to content

Commit bf9cdd7

Browse files
committed
test(v1): pin the workspace-actor projection
requireWorkspaceRequestActor is the only place the 'no billed account for this workspace' case is turned into a response, and the table route tests mock the middleware wholesale, so the projection itself needs its own pin.
1 parent 2d20245 commit bf9cdd7

1 file changed

Lines changed: 48 additions & 1 deletion

File tree

apps/sim/app/api/v1/middleware.test.ts

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,15 @@ const {
2929
mockGetRateLimit,
3030
mockGetUserEntityPermissions,
3131
mockGetWorkspaceBillingSettings,
32+
mockGetWorkspaceBilledAccountUserId,
3233
} = vi.hoisted(() => ({
3334
mockAuthenticateV1Request: vi.fn(),
3435
mockGetSubscription: vi.fn(),
3536
mockCheckRateLimit: vi.fn(),
3637
mockGetRateLimit: vi.fn(),
3738
mockGetUserEntityPermissions: vi.fn(),
3839
mockGetWorkspaceBillingSettings: vi.fn(),
40+
mockGetWorkspaceBilledAccountUserId: vi.fn(),
3941
}))
4042

4143
vi.mock('@/app/api/v1/auth', () => ({
@@ -61,7 +63,7 @@ vi.mock('@/lib/workspaces/permissions/utils', () => ({
6163

6264
vi.mock('@/lib/workspaces/utils', () => ({
6365
getWorkspaceBillingSettings: mockGetWorkspaceBillingSettings,
64-
getWorkspaceBilledAccountUserId: vi.fn(async () => 'billed-user'),
66+
getWorkspaceBilledAccountUserId: mockGetWorkspaceBilledAccountUserId,
6567
}))
6668

6769
import { DEFAULT_PERMISSION_GROUP_CONFIG } from '@/lib/permission-groups/fields'
@@ -70,6 +72,7 @@ import {
7072
checkRateLimit,
7173
checkWorkspaceScope,
7274
createRateLimitResponse,
75+
requireWorkspaceRequestActor,
7376
v1ValidationErrorResponse,
7477
} from '@/app/api/v1/middleware'
7578

@@ -421,3 +424,47 @@ describe('checkWorkspaceScope', () => {
421424
expect(response?.status).toBe(403)
422425
})
423426
})
427+
428+
describe('requireWorkspaceRequestActor', () => {
429+
beforeEach(() => {
430+
vi.clearAllMocks()
431+
mockGetWorkspaceBilledAccountUserId.mockResolvedValue('billed-user')
432+
})
433+
434+
it('substitutes the billed account as the system actor for a workspace key', async () => {
435+
const actor = await requireWorkspaceRequestActor(
436+
{ allowed: true, keyType: 'workspace', userId: 'key-creator' } as never,
437+
'workspace-1'
438+
)
439+
440+
expect(actor).toEqual({ ok: true, actorUserId: 'billed-user' })
441+
})
442+
443+
it('keeps the owner for a personal key', async () => {
444+
const actor = await requireWorkspaceRequestActor(
445+
{ allowed: true, keyType: 'personal', userId: 'user-1' } as never,
446+
'workspace-1'
447+
)
448+
449+
expect(actor).toEqual({ ok: true, actorUserId: 'user-1' })
450+
})
451+
452+
/**
453+
* An archived or deleted workspace has no billed account to stand in. That is
454+
* a reachable request about an unreachable workspace, not a server fault: the
455+
* call sites used to throw, and the routes' catch-all reported it as a 500.
456+
*/
457+
it('projects an unresolvable actor onto a 400 rather than throwing', async () => {
458+
mockGetWorkspaceBilledAccountUserId.mockResolvedValue(null)
459+
460+
const actor = await requireWorkspaceRequestActor(
461+
{ allowed: true, keyType: 'workspace', userId: 'key-creator' } as never,
462+
'workspace-gone'
463+
)
464+
465+
expect(actor.ok).toBe(false)
466+
if (actor.ok) throw new Error('expected a refusal')
467+
expect(actor.response.status).toBe(400)
468+
await expect(actor.response.json()).resolves.toEqual({ error: 'Invalid workspace ID' })
469+
})
470+
})

0 commit comments

Comments
 (0)