feat(opencode): add tests for read.ts tool copying based on vision connector presence

- Implemented two new test scenarios: one to verify that read.ts is not copied when no vision connector is present, and another to confirm that it is copied when a vision connector is configured.
- Updated the runner logic to conditionally copy custom tools into the OpenCode global config directory based on the presence of a vision connector in the configuration.
This commit is contained in:
Max 2026-04-25 09:48:06 +08:00
parent 12e88943e9
commit 4b97a890fd
2 changed files with 80 additions and 9 deletions

View file

@ -75,15 +75,20 @@ func (r *Runner) Prepare(ctx context.Context, req *types.PrepareRequest) error {
}
}
// 4. Copy custom tools into OpenCode global config dir ($HOME/.config/opencode/tools/).
// Bun resolves symlinks and breaks module resolution (opencode#11001), so use cp.
// HOME is set to workDir (/workspace) at runtime, so this lands in persistent storage.
steps = append(steps, types.PrepareStep{
Action: "exec",
Cmd: "mkdir -p $HOME/.config/opencode/tools && for f in /opt/opencode-tools/*.ts; do [ -f \"$f\" ] && cp -f \"$f\" $HOME/.config/opencode/tools/; done",
Once: true,
IgnoreError: true,
})
// 4. Copy custom tools (e.g. read.ts for vision) into OpenCode global
// config dir ($HOME/.config/opencode/tools/). Only needed when a
// vision connector is configured — the custom read tool overrides the
// built-in read to route image files through the vision API.
if req.Config != nil && req.Config.Runner.Connectors != nil {
if vc, ok := req.Config.Runner.Connectors["vision"]; ok && vc != nil && vc.Connector != "" {
steps = append(steps, types.PrepareStep{
Action: "exec",
Cmd: "mkdir -p $HOME/.config/opencode/tools && for f in /opt/opencode-tools/*.ts; do [ -f \"$f\" ] && cp -f \"$f\" $HOME/.config/opencode/tools/; done",
Once: true,
IgnoreError: true,
})
}
}
// 5. Generate opencode.json (project config at workspace root)
configJSON := buildOpenCodeConfig(req, r.mcpServers)

View file

@ -103,6 +103,72 @@ func TestOpenCode_Session(t *testing.T) {
})
}
// ---------------------------------------------------------------------------
// Scenario 4: No vision connector — read.ts should NOT be copied
// ---------------------------------------------------------------------------
func TestOpenCode_NoVision_ReadToolNotCopied(t *testing.T) {
sandboxtestutils.Prepare(t)
defer sandboxtestutils.Clean(t)
require.NotNil(t, caller.AgentGetterFunc)
const assistantID = "tests.sandbox-v2.opencode-oneshot-cli"
agent, err := caller.AgentGetterFunc(assistantID)
require.NoError(t, err)
chatID := fmt.Sprintf("e2e-novision-%d", time.Now().UnixMilli())
ctx := agentcontext.New(
context.Background(),
&oauthtypes.AuthorizedInfo{TeamID: "test-team-e2e", UserID: "test-user-e2e"},
chatID,
)
resp := streamAndWait(t, agent, ctx,
`Check if the file $HOME/.config/opencode/tools/read.ts exists. `+
`Reply with exactly "READ_EXISTS" if it does, or "READ_MISSING" if it does not. Nothing else.`,
defaultTimeout,
)
require.NotNil(t, resp.Completion)
content := strings.ToLower(contentString(t, resp))
t.Logf("NoVision check: %s", content)
assert.Contains(t, content, "read_missing",
"without vision connector, read.ts should NOT be copied")
}
// ---------------------------------------------------------------------------
// Scenario 5: With vision connector — read.ts SHOULD be copied
// ---------------------------------------------------------------------------
func TestOpenCode_Vision_ReadToolCopied(t *testing.T) {
sandboxtestutils.Prepare(t)
defer sandboxtestutils.Clean(t)
require.NotNil(t, caller.AgentGetterFunc)
const assistantID = "tests.sandbox-v2.opencode-vision-cli"
agent, err := caller.AgentGetterFunc(assistantID)
require.NoError(t, err)
chatID := fmt.Sprintf("e2e-vision-%d", time.Now().UnixMilli())
ctx := agentcontext.New(
context.Background(),
&oauthtypes.AuthorizedInfo{TeamID: "test-team-e2e", UserID: "test-user-e2e"},
chatID,
)
resp := streamAndWait(t, agent, ctx,
`Check if the file $HOME/.config/opencode/tools/read.ts exists. `+
`Reply with exactly "READ_EXISTS" if it does, or "READ_MISSING" if it does not. Nothing else.`,
defaultTimeout,
)
require.NotNil(t, resp.Completion)
content := strings.ToLower(contentString(t, resp))
t.Logf("Vision check: %s", content)
assert.Contains(t, content, "read_exists",
"with vision connector, read.ts SHOULD be copied")
}
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------