1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
|
package cli
import (
"log/slog"
"os"
"testing"
"github.com/thepeterstone/claudomator/internal/config"
"github.com/thepeterstone/claudomator/internal/executor"
)
// TestRegisterCloudRunners_RegistersAllFiveConfiguredProviders confirms
// registerCloudRunners (used identically by both `serve` and `run`) wires up
// a NativeRunner for every cloud provider that has a non-empty API key —
// including groq/openrouter/openai, which docs/api-keys-setup.md documented
// as "works once configured" before this phase actually configured them.
func TestRegisterCloudRunners_RegistersAllFiveConfiguredProviders(t *testing.T) {
cfg := &config.Config{
SandboxImage: "test-image:latest",
Providers: map[string]config.ProviderConfig{
"anthropic": {APIKey: "ak", DefaultModel: "claude-sonnet-5"},
"google": {APIKey: "gk", DefaultModel: "gemini-2.5-flash"},
"groq": {APIKey: "grk", Endpoint: "https://api.groq.com/openai/v1", DefaultModel: "llama-3.3-70b-versatile"},
"openrouter": {APIKey: "ork", Endpoint: "https://openrouter.ai/api/v1", DefaultModel: "meta-llama/llama-3.3-70b-instruct:free"},
"openai": {APIKey: "oak", Endpoint: "https://api.openai.com/v1", DefaultModel: "gpt-4o-mini"},
},
}
runners := map[string]executor.Runner{}
logger := slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: slog.LevelError}))
registerCloudRunners(runners, cfg, logger, t.TempDir())
for _, name := range []string{"anthropic", "google", "groq", "openrouter", "openai"} {
r, ok := runners[name]
if !ok {
t.Errorf("expected a %q runner to be registered", name)
continue
}
nr, ok := r.(*executor.NativeRunner)
if !ok {
t.Errorf("%q runner should be a *executor.NativeRunner", name)
continue
}
if nr.Provider == nil {
t.Errorf("%q runner has a nil Provider", name)
}
if nr.SandboxKind != "docker" {
t.Errorf("%q runner should use SandboxKind \"docker\", got %q", name, nr.SandboxKind)
}
}
}
// TestRegisterCloudRunners_SkipsProvidersWithoutAPIKeyOrDisabled confirms
// providers are skipped when unconfigured (no api_key) or explicitly
// disabled via [runners], matching the anthropic/google-only behavior this
// generalizes.
func TestRegisterCloudRunners_SkipsProvidersWithoutAPIKeyOrDisabled(t *testing.T) {
f := false
cfg := &config.Config{
Providers: map[string]config.ProviderConfig{
"groq": {APIKey: "", DefaultModel: "x"}, // no key
"openrouter": {APIKey: "ork", DefaultModel: "x"},
},
Runners: config.RunnersConfig{OpenRouter: &f}, // explicitly disabled
}
runners := map[string]executor.Runner{}
logger := slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: slog.LevelError}))
registerCloudRunners(runners, cfg, logger, t.TempDir())
if _, ok := runners["groq"]; ok {
t.Error("groq should not be registered without an api_key")
}
if _, ok := runners["openrouter"]; ok {
t.Error("openrouter should not be registered when explicitly disabled")
}
if _, ok := runners["openai"]; ok {
t.Error("openai should not be registered when absent from cfg.Providers")
}
}
|