- Add runtime proto for CLI profile messages - Update edge transport server with profile support - Update node adapters factory for profile handling - Regenerate protobuf code
275 lines
7.5 KiB
Go
275 lines
7.5 KiB
Go
package adapters
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"google.golang.org/protobuf/types/known/structpb"
|
|
|
|
iop "iop/proto/gen/iop"
|
|
)
|
|
|
|
func TestCLIConfFromProto_ProfileRuntimeOptions(t *testing.T) {
|
|
m := &iop.CLIAdapterConfig{
|
|
Profiles: map[string]*iop.CLIProfileConfig{
|
|
"claude": {
|
|
Command: "claude",
|
|
Args: []string{"-p"},
|
|
OutputFormat: "claude-json",
|
|
},
|
|
},
|
|
}
|
|
|
|
cfg := cliConfFromProto(m)
|
|
|
|
prof, ok := cfg.Profiles["claude"]
|
|
if !ok {
|
|
t.Fatal("expected claude profile")
|
|
}
|
|
if prof.Command != "claude" {
|
|
t.Fatalf("command: got %q", prof.Command)
|
|
}
|
|
if len(prof.Args) != 1 || prof.Args[0] != "-p" {
|
|
t.Fatalf("args: got %#v", prof.Args)
|
|
}
|
|
if prof.OutputFormat != "claude-json" {
|
|
t.Fatalf("output_format: got %q", prof.OutputFormat)
|
|
}
|
|
}
|
|
|
|
func TestCLIConfFromProto_ClaudeHeadlessBypassProfile(t *testing.T) {
|
|
m := &iop.CLIAdapterConfig{
|
|
Profiles: map[string]*iop.CLIProfileConfig{
|
|
"claude": {
|
|
Command: "claude",
|
|
Args: []string{"-p", "--dangerously-skip-permissions"},
|
|
},
|
|
},
|
|
}
|
|
cfg := cliConfFromProto(m)
|
|
prof, ok := cfg.Profiles["claude"]
|
|
if !ok {
|
|
t.Fatal("expected claude profile")
|
|
}
|
|
if len(prof.Args) != 2 || prof.Args[0] != "-p" || prof.Args[1] != "--dangerously-skip-permissions" {
|
|
t.Fatalf("args: got %#v", prof.Args)
|
|
}
|
|
if prof.Persistent || prof.Terminal {
|
|
t.Fatalf("expected persistent/terminal=false")
|
|
}
|
|
}
|
|
|
|
func TestCLIConfFromProto_GeminiHeadlessYoloProfile(t *testing.T) {
|
|
m := &iop.CLIAdapterConfig{
|
|
Profiles: map[string]*iop.CLIProfileConfig{
|
|
"gemini": {
|
|
Command: "gemini",
|
|
Args: []string{"-p", "--approval-mode", "yolo"},
|
|
},
|
|
},
|
|
}
|
|
cfg := cliConfFromProto(m)
|
|
prof, ok := cfg.Profiles["gemini"]
|
|
if !ok {
|
|
t.Fatal("expected gemini profile")
|
|
}
|
|
if len(prof.Args) != 3 || prof.Args[0] != "-p" || prof.Args[1] != "--approval-mode" || prof.Args[2] != "yolo" {
|
|
t.Fatalf("args: got %#v", prof.Args)
|
|
}
|
|
}
|
|
|
|
func TestCLIConfFromProto_ClineConfigProfile(t *testing.T) {
|
|
m := &iop.CLIAdapterConfig{
|
|
Profiles: map[string]*iop.CLIProfileConfig{
|
|
"cline-dgx": {
|
|
Command: "/config/.npm-global/bin/cline",
|
|
Args: []string{"-y", "--json", "--config", "/config/.cline/profiles/ollama-dgx"},
|
|
OutputFormat: "cline-json",
|
|
},
|
|
},
|
|
}
|
|
cfg := cliConfFromProto(m)
|
|
prof, ok := cfg.Profiles["cline-dgx"]
|
|
if !ok {
|
|
t.Fatal("expected cline-dgx profile")
|
|
}
|
|
if prof.Command != "/config/.npm-global/bin/cline" {
|
|
t.Fatalf("command: got %q", prof.Command)
|
|
}
|
|
if len(prof.Args) != 4 {
|
|
t.Fatalf("args: got %#v", prof.Args)
|
|
}
|
|
if prof.OutputFormat != "cline-json" {
|
|
t.Fatalf("output_format: got %q", prof.OutputFormat)
|
|
}
|
|
}
|
|
|
|
func TestCLIConfFromProto_ProfileCompletionMarker(t *testing.T) {
|
|
m := &iop.CLIAdapterConfig{
|
|
Profiles: map[string]*iop.CLIProfileConfig{
|
|
"codex": {
|
|
Command: "codex",
|
|
Persistent: true,
|
|
CompletionMarker: &iop.CLICompletionMarker{
|
|
Line: "[[DONE]]",
|
|
Regex: `^\[\[DONE\]\]$`,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
cfg := cliConfFromProto(m)
|
|
prof := cfg.Profiles["codex"]
|
|
if prof.CompletionMarker.Line != "[[DONE]]" {
|
|
t.Fatalf("line: got %q", prof.CompletionMarker.Line)
|
|
}
|
|
if prof.CompletionMarker.Regex != `^\[\[DONE\]\]$` {
|
|
t.Fatalf("regex: got %q", prof.CompletionMarker.Regex)
|
|
}
|
|
}
|
|
|
|
func TestCLIConfFromProto_PersistentAndIdleTimeouts(t *testing.T) {
|
|
m := &iop.CLIAdapterConfig{
|
|
Profiles: map[string]*iop.CLIProfileConfig{
|
|
"codex": {
|
|
Command: "codex",
|
|
Persistent: true,
|
|
ResponseIdleTimeoutMs: 1500,
|
|
StartupIdleTimeoutMs: 300,
|
|
},
|
|
},
|
|
}
|
|
cfg := cliConfFromProto(m)
|
|
prof := cfg.Profiles["codex"]
|
|
if !prof.Persistent {
|
|
t.Fatal("persistent")
|
|
}
|
|
if prof.ResponseIdleTimeoutMS != 1500 {
|
|
t.Fatalf("response_idle_timeout_ms: got %d", prof.ResponseIdleTimeoutMS)
|
|
}
|
|
if prof.StartupIdleTimeoutMS != 300 {
|
|
t.Fatalf("startup_idle_timeout_ms: got %d", prof.StartupIdleTimeoutMS)
|
|
}
|
|
}
|
|
|
|
func TestCLIConfFromProto_NilSettings(t *testing.T) {
|
|
cfg := cliConfFromProto(&iop.CLIAdapterConfig{})
|
|
if !cfg.Enabled {
|
|
t.Fatal("expected enabled=true")
|
|
}
|
|
if cfg.Profiles == nil {
|
|
t.Fatal("profiles map should be initialized")
|
|
}
|
|
}
|
|
|
|
// Legacy structpb fallback regression tests — these exercise the path taken
|
|
// when an older edge sends AdapterConfig.settings instead of the typed oneof.
|
|
|
|
func TestOllamaConfFromStruct_LegacyPayload(t *testing.T) {
|
|
st, err := structpb.NewStruct(map[string]any{"base_url": "http://legacy:11434"})
|
|
if err != nil {
|
|
t.Fatalf("structpb: %v", err)
|
|
}
|
|
cfg := ollamaConfFromStruct(st)
|
|
if !cfg.Enabled {
|
|
t.Fatal("expected enabled=true")
|
|
}
|
|
if cfg.BaseURL != "http://legacy:11434" {
|
|
t.Fatalf("base_url: got %q", cfg.BaseURL)
|
|
}
|
|
}
|
|
|
|
func TestVllmConfFromStruct_LegacyPayload(t *testing.T) {
|
|
st, err := structpb.NewStruct(map[string]any{"endpoint": "http://legacy:8000"})
|
|
if err != nil {
|
|
t.Fatalf("structpb: %v", err)
|
|
}
|
|
cfg := vllmConfFromStruct(st)
|
|
if !cfg.Enabled {
|
|
t.Fatal("expected enabled=true")
|
|
}
|
|
if cfg.Endpoint != "http://legacy:8000" {
|
|
t.Fatalf("endpoint: got %q", cfg.Endpoint)
|
|
}
|
|
}
|
|
|
|
func TestCLIConfFromStruct_LegacyPayload(t *testing.T) {
|
|
st, err := structpb.NewStruct(map[string]any{
|
|
"profiles": map[string]any{
|
|
"codex": map[string]any{
|
|
"command": "codex",
|
|
"args": []any{"--legacy"},
|
|
"env": []any{"K=V"},
|
|
"persistent": true,
|
|
"terminal": false,
|
|
"response_idle_timeout_ms": 1500,
|
|
"startup_idle_timeout_ms": 300,
|
|
"output_format": "codex-json",
|
|
"completion_marker": map[string]any{
|
|
"line": "[[DONE]]",
|
|
"regex": `^\[\[DONE\]\]$`,
|
|
},
|
|
},
|
|
},
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("structpb: %v", err)
|
|
}
|
|
cfg := cliConfFromStruct(st)
|
|
prof, ok := cfg.Profiles["codex"]
|
|
if !ok {
|
|
t.Fatal("expected codex profile")
|
|
}
|
|
if prof.Command != "codex" {
|
|
t.Fatalf("command: got %q", prof.Command)
|
|
}
|
|
if len(prof.Args) != 1 || prof.Args[0] != "--legacy" {
|
|
t.Fatalf("args: got %#v", prof.Args)
|
|
}
|
|
if len(prof.Env) != 1 || prof.Env[0] != "K=V" {
|
|
t.Fatalf("env: got %#v", prof.Env)
|
|
}
|
|
if !prof.Persistent {
|
|
t.Fatal("persistent")
|
|
}
|
|
if prof.ResponseIdleTimeoutMS != 1500 {
|
|
t.Fatalf("response_idle_timeout_ms: got %d", prof.ResponseIdleTimeoutMS)
|
|
}
|
|
if prof.StartupIdleTimeoutMS != 300 {
|
|
t.Fatalf("startup_idle_timeout_ms: got %d", prof.StartupIdleTimeoutMS)
|
|
}
|
|
if prof.OutputFormat != "codex-json" {
|
|
t.Fatalf("output_format: got %q", prof.OutputFormat)
|
|
}
|
|
if prof.CompletionMarker.Line != "[[DONE]]" || prof.CompletionMarker.Regex != `^\[\[DONE\]\]$` {
|
|
t.Fatalf("completion_marker: got %+v", prof.CompletionMarker)
|
|
}
|
|
}
|
|
|
|
// End-to-end fallback through BuildFromPayload: oneof empty, settings populated.
|
|
func TestBuildFromPayload_LegacySettingsFallback(t *testing.T) {
|
|
ollamaSt, _ := structpb.NewStruct(map[string]any{"base_url": "http://legacy:11434"})
|
|
vllmSt, _ := structpb.NewStruct(map[string]any{"endpoint": "http://legacy:8000"})
|
|
cliSt, _ := structpb.NewStruct(map[string]any{
|
|
"profiles": map[string]any{
|
|
"codex": map[string]any{
|
|
"command": "codex",
|
|
"args": []any{"--legacy"},
|
|
},
|
|
},
|
|
})
|
|
payload := &iop.NodeConfigPayload{
|
|
Adapters: []*iop.AdapterConfig{
|
|
{Type: "ollama", Enabled: true, Settings: ollamaSt},
|
|
{Type: "vllm", Enabled: true, Settings: vllmSt},
|
|
{Type: "cli", Enabled: true, Settings: cliSt},
|
|
},
|
|
}
|
|
reg, err := BuildFromPayload(payload, nil)
|
|
if err != nil {
|
|
t.Fatalf("build: %v", err)
|
|
}
|
|
for _, name := range []string{"ollama", "vllm", "cli"} {
|
|
if _, ok := reg.Get(name); !ok {
|
|
t.Fatalf("expected %s adapter registered from legacy settings", name)
|
|
}
|
|
}
|
|
}
|