Skip to content

Commit edcca86

Browse files
test(runner): cover remote runner flow
Generated with [codeagent](https://github.com/qbox/codeagent) Co-authored-by: Cai-Tang-www <106404101+Cai-Tang-www@users.noreply.github.com>
1 parent 65a54b9 commit edcca86

8 files changed

Lines changed: 1089 additions & 14 deletions

File tree

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package cli
2+
3+
import (
4+
"context"
5+
"path/filepath"
6+
"strings"
7+
"testing"
8+
)
9+
10+
func TestNewRunnerCommandForwardsFlags(t *testing.T) {
11+
originalRunner := runRunnerCommandFn
12+
t.Cleanup(func() { runRunnerCommandFn = originalRunner })
13+
14+
var captured runnerCommandOptions
15+
runRunnerCommandFn = func(ctx context.Context, options runnerCommandOptions) error {
16+
captured = options
17+
return nil
18+
}
19+
20+
cmd := newRunnerCommand()
21+
cmd.SetArgs([]string{
22+
"--gateway-address", "127.0.0.1:9000",
23+
"--token-file", "/tmp/token",
24+
"--runner-id", "runner-1",
25+
"--runner-name", "Local Runner",
26+
"--workdir", "/tmp/work",
27+
})
28+
if err := cmd.ExecuteContext(context.Background()); err != nil {
29+
t.Fatalf("ExecuteContext() error = %v", err)
30+
}
31+
if captured.GatewayAddress != "127.0.0.1:9000" || captured.TokenFile != "/tmp/token" || captured.RunnerID != "runner-1" || captured.RunnerName != "Local Runner" || captured.Workdir != "/tmp/work" {
32+
t.Fatalf("captured options = %#v", captured)
33+
}
34+
}
35+
36+
func TestDefaultRunRunnerReadsTokenFileError(t *testing.T) {
37+
err := defaultRunRunner(context.Background(), runnerCommandOptions{TokenFile: filepath.Join(t.TempDir(), "missing.token")})
38+
if err == nil || !strings.Contains(err.Error(), "read token file") {
39+
t.Fatalf("defaultRunRunner() error = %v", err)
40+
}
41+
}
42+
43+
func TestRootCommandIncludesRunnerSubcommand(t *testing.T) {
44+
cmd := NewRootCommand()
45+
found := false
46+
for _, child := range cmd.Commands() {
47+
if child.Name() == "runner" {
48+
found = true
49+
break
50+
}
51+
}
52+
if !found {
53+
t.Fatal("runner subcommand not registered on root command")
54+
}
55+
}
56+
57+
func TestNewRunnerCommandAllowsDefaultFlags(t *testing.T) {
58+
originalRunner := runRunnerCommandFn
59+
t.Cleanup(func() { runRunnerCommandFn = originalRunner })
60+
61+
var captured runnerCommandOptions
62+
runRunnerCommandFn = func(ctx context.Context, options runnerCommandOptions) error {
63+
captured = options
64+
return nil
65+
}
66+
67+
cmd := newRunnerCommand()
68+
cmd.SetArgs([]string{})
69+
if err := cmd.ExecuteContext(context.Background()); err != nil {
70+
t.Fatalf("ExecuteContext() error = %v", err)
71+
}
72+
if captured != (runnerCommandOptions{}) {
73+
t.Fatalf("captured options = %#v, want zero-value defaults before runtime resolution", captured)
74+
}
75+
}

internal/config/runner_test.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package config
2+
3+
import (
4+
"testing"
5+
"time"
6+
)
7+
8+
func TestRunnerConfigApplyDefaultsCloneAndDurations(t *testing.T) {
9+
cfg := RunnerConfig{
10+
WorkdirAllowlist: []string{"/tmp/work"},
11+
}
12+
defaults := defaultRunnerConfig()
13+
cfg.ApplyDefaults(defaults)
14+
15+
if cfg.GatewayAddress != DefaultRunnerGatewayAddress {
16+
t.Fatalf("GatewayAddress = %q", cfg.GatewayAddress)
17+
}
18+
if cfg.HeartbeatInterval() != 10*time.Second {
19+
t.Fatalf("HeartbeatInterval() = %s", cfg.HeartbeatInterval())
20+
}
21+
if cfg.ReconnectBackoffMin() != 500*time.Millisecond {
22+
t.Fatalf("ReconnectBackoffMin() = %s", cfg.ReconnectBackoffMin())
23+
}
24+
if cfg.ReconnectBackoffMax() != 10*time.Second {
25+
t.Fatalf("ReconnectBackoffMax() = %s", cfg.ReconnectBackoffMax())
26+
}
27+
if cfg.RequestTimeout() != 30*time.Second {
28+
t.Fatalf("RequestTimeout() = %s", cfg.RequestTimeout())
29+
}
30+
31+
clone := cfg.Clone()
32+
clone.WorkdirAllowlist[0] = "/changed"
33+
if cfg.WorkdirAllowlist[0] != "/tmp/work" {
34+
t.Fatal("Clone() did not deep copy WorkdirAllowlist")
35+
}
36+
}
37+
38+
func TestRunnerConfigValidate(t *testing.T) {
39+
if err := (RunnerConfig{}).Validate(); err != nil {
40+
t.Fatalf("disabled RunnerConfig.Validate() error = %v", err)
41+
}
42+
43+
cases := []RunnerConfig{
44+
{Enabled: true},
45+
{Enabled: true, GatewayAddress: "127.0.0.1:8080", HeartbeatIntervalSec: -1},
46+
{Enabled: true, GatewayAddress: "127.0.0.1:8080", HeartbeatIntervalSec: 1, ReconnectBackoffMinM: -1, ReconnectBackoffMaxM: 1},
47+
{Enabled: true, GatewayAddress: "127.0.0.1:8080", HeartbeatIntervalSec: 1, ReconnectBackoffMinM: 2, ReconnectBackoffMaxM: 1},
48+
{Enabled: true, GatewayAddress: "127.0.0.1:8080", HeartbeatIntervalSec: 1, ReconnectBackoffMinM: 1, ReconnectBackoffMaxM: 2, RequestTimeoutSec: -1},
49+
}
50+
for _, cfg := range cases {
51+
if err := cfg.Validate(); err == nil {
52+
t.Fatalf("Validate() error = nil for %#v", cfg)
53+
}
54+
}
55+
56+
valid := RunnerConfig{
57+
Enabled: true,
58+
GatewayAddress: "127.0.0.1:8080",
59+
HeartbeatIntervalSec: 1,
60+
ReconnectBackoffMinM: 1,
61+
ReconnectBackoffMaxM: 2,
62+
RequestTimeoutSec: 3,
63+
}
64+
if err := valid.Validate(); err != nil {
65+
t.Fatalf("Validate() error = %v", err)
66+
}
67+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package feishuadapter
2+
3+
import "testing"
4+
5+
func TestTranslateRunnerError(t *testing.T) {
6+
cases := map[string]string{
7+
"runner_offline": "本机 Runner 未连接,请在电脑上启动 `neocode runner`",
8+
"capability_denied": "权限不足:当前能力令牌不允许此操作",
9+
"tool_execution_failed: failed": "工具执行失败:tool_execution_failed: failed",
10+
"timed out waiting for runner": "本机 Runner 响应超时,请检查网络连接和 Runner 状态",
11+
"other": "",
12+
}
13+
for input, want := range cases {
14+
if got := translateRunnerError(input); got != want {
15+
t.Fatalf("translateRunnerError(%q) = %q, want %q", input, got, want)
16+
}
17+
}
18+
}

0 commit comments

Comments
 (0)