Visitar URL original
fix(mothership): clear errors and a working direct path for CLI version, function and grep commands by waleedlatif1 · Pull Request #8736 · simstudioai/sim · GitHub
Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
test(function): prove direct tool calls against the real function-exe…
…cution policy
  • Loading branch information
waleedlatif1 committed Oct 7, 2026
commit 738e955ac3a3d145343505f657fe86f1e13f8c24
69 changes: 69 additions & 0 deletions apps/sim/lib/internal/function/execute-direct-call.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { createPersonalApiKeyPrincipal } from '@sim/testing/factories/principal.factory'
import { workspaceAuthzMock, workspaceAuthzMockFns } from '@sim/testing/mocks/workspace-authz.mock'
import {
workspaceContextMock,
workspaceContextMockFns,
} from '@sim/testing/mocks/workspace-context.mock'
import { beforeEach, describe, expect, it, vi } from 'vitest'

const mocks = vi.hoisted(() => ({ runSandbox: vi.fn() }))

vi.mock('@/lib/function-execution/execute-request', () => ({
executeFunctionRequest: mocks.runSandbox,
}))
vi.mock('@/lib/workspaces/application/workspace-context', () => workspaceContextMock)
vi.mock('@sim/platform-authz/workspace', () => workspaceAuthzMock)

import type { Principal } from '@sim/auth/principal'
import { markCopilotWorkspaceInvocation } from '@/lib/core/application/copilot-workspace-invocation'
import { PrincipalKindAuthorizationError } from '@/lib/core/application/workspace-authorization'
import { executeFunctionTool } from '@/lib/internal/function/execute'
import { createCopilotChatPrincipal } from '@/lib/mothership/auth/application-delegation'
import { TOOL_EXECUTION_DELEGATION_AUDIENCE } from '@/lib/tool-execution/application/operations'

/**
* `POST /api/v2/tools/{id}/execute` (the embedded CLI's `tools execute`) runs `function_execute`
* with no workflow behind it, so no executor origin exists. The real function-execution policy
* decides from the authenticated caller; only the sandbox is stubbed.
*/
function runDirect(callerPrincipal: Principal) {
return executeFunctionTool({
body: { code: 'return 1' },
headers: new Headers(),
requestId: 'request-1',
context: { workflowId: '', workspaceId: 'workspace-1', userId: 'user-1', callerPrincipal },
})
}

describe('executeFunctionTool direct tool calls', () => {
beforeEach(() => {
workspaceContextMockFns.mockResolveActiveWorkspaceApplicationContext.mockResolvedValue({
workspaceId: 'workspace-1',
workspaceOrganizationId: null,
billedAccountUserId: 'user-1',
allowPersonalApiKeys: true,
})
workspaceAuthzMockFns.mockResolveEffectiveWorkspacePermission.mockResolvedValue('write')
mocks.runSandbox.mockResolvedValue(Response.json({ success: true, output: { result: 1 } }))
})

it('runs the code for an admitted Mothership caller', async () => {
const caller = createCopilotChatPrincipal(
{ userId: 'user-1', workspaceId: 'workspace-1', chatId: 'chat-1' },
TOOL_EXECUTION_DELEGATION_AUDIENCE
)
markCopilotWorkspaceInvocation(caller)

const response = await runDirect(caller)

expect(response.status).toBe(200)
expect(await response.json()).toEqual({ success: true, output: { result: 1 } })
})

it('still refuses a personal API key before any code runs', async () => {
await expect(runDirect(createPersonalApiKeyPrincipal({ keyId: 'key-1' }))).rejects.toThrow(
PrincipalKindAuthorizationError
)
expect(mocks.runSandbox).not.toHaveBeenCalled()
})
})
60 changes: 1 addition & 59 deletions apps/sim/lib/internal/function/execute.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import type { Principal } from '@sim/auth/principal'
import { createPersonalApiKeyPrincipal } from '@sim/testing/factories/principal.factory'
import {
executorPrincipalMock,
executorPrincipalMockFns,
Expand All @@ -14,18 +12,15 @@ const mocks = vi.hoisted(() => ({
vi.mock('@/lib/internal/principals/executor', () => executorPrincipalMock)

vi.mock('@/lib/function-execution/application/execute-function', () => ({
executeFunction: { execute: mocks.execute, delegationAudience: 'sim:function-executions' },
executeFunction: { execute: mocks.execute },
}))

vi.mock('@/lib/function-execution/application/execute-chat-function', () => ({
executeChatFunction: { execute: mocks.executeChat },
}))

import { markCopilotWorkspaceInvocation } from '@/lib/core/application/copilot-workspace-invocation'
import { FUNCTION_EXECUTION_DELEGATION_AUDIENCE } from '@/lib/function-execution/application/authorization'
import { executeFunctionTool } from '@/lib/internal/function/execute'
import { createCopilotChatPrincipal } from '@/lib/mothership/auth/application-delegation'
import { TOOL_EXECUTION_DELEGATION_AUDIENCE } from '@/lib/tool-execution/application/operations'
import { ResolvedSecretTraceRegistry } from '@/executor/utils/resolved-secret-trace-registry'

const { mockCreateExecutorPrincipalFromExecutionContext } = executorPrincipalMockFns
Expand Down Expand Up @@ -174,57 +169,4 @@ describe('executeFunctionTool', () => {
expect(mocks.executeChat).not.toHaveBeenCalled()
expect(mocks.execute).not.toHaveBeenCalled()
})

describe('direct tool calls (POST /api/v2/tools/{id}/execute)', () => {
function directContext(callerPrincipal: Principal) {
return {
workflowId: '',
workspaceId: 'workspace-1',
userId: 'user-1',
callerPrincipal,
}
}

it('rebinds an admitted Mothership caller to the function-execution audience', async () => {
const caller = createCopilotChatPrincipal(
{ userId: 'user-1', workspaceId: 'workspace-1', chatId: 'chat-1' },
TOOL_EXECUTION_DELEGATION_AUDIENCE
)
markCopilotWorkspaceInvocation(caller)

await executeFunctionTool({
body: { code: 'return 1' },
headers: new Headers(),
requestId: 'request-1',
context: directContext(caller),
})

expect(mockCreateExecutorPrincipalFromExecutionContext).not.toHaveBeenCalled()
expect(mocks.execute).toHaveBeenCalledWith(
expect.objectContaining({
principal: expect.objectContaining({
kind: 'delegated',
serviceId: 'copilot',
subjectUserId: 'user-1',
workspaceId: 'workspace-1',
audience: FUNCTION_EXECUTION_DELEGATION_AUDIENCE,
}),
})
)
})

it('hands any other caller to the function-execution policy unchanged', async () => {
const caller = createPersonalApiKeyPrincipal({ keyId: 'personal-key-1' })

await executeFunctionTool({
body: { code: 'return 1' },
headers: new Headers(),
requestId: 'request-1',
context: directContext(caller),
})

expect(mockCreateExecutorPrincipalFromExecutionContext).not.toHaveBeenCalled()
expect(mocks.execute).toHaveBeenCalledWith(expect.objectContaining({ principal: caller }))
})
})
})
Loading