Files
gnoma/internal/provider/subprocess/provider_test.go
T
vikingowl 44d0bdc032 feat(provider): subprocess CLI provider for claude, gemini, vibe
Adds internal/provider/subprocess — a provider.Provider that spawns CLI
agents (claude, gemini, vibe) as subprocesses and streams their output.

- FormatParser interface + three parsers for claude-stream-json,
  gemini-stream-json, and vibe-streaming formats; fixtures captured from
  real binaries
- subprocessStream: pull-based stream.Stream over subprocess stdout with
  bounded stderr capture (8KB) and guarded reap() to prevent double-Wait
- DiscoverCLIAgents: parallel PATH scan with 10s timeout, stable ordering
- Provider: only the last user message is passed as --prompt; all other
  request fields (history, tools, system prompt) are intentionally ignored
  (see package doc)
- main.go: discover and register CLI arms at startup; TODO(P0c) for
  tier-based routing to enforce preference order explicitly
2026-05-07 14:29:34 +02:00

80 lines
1.8 KiB
Go

package subprocess
import (
"context"
"testing"
"somegit.dev/Owlibou/gnoma/internal/provider"
)
func TestProvider_NameAndDefaultModel(t *testing.T) {
agent := DiscoveredAgent{
CLIAgent: CLIAgent{
Name: "testcli",
DisplayName: "Test CLI",
Format: FormatVibeStreaming,
Capabilities: provider.Capabilities{ContextWindow: 32000},
},
Path: "/usr/bin/testcli",
Version: "1.0.0",
}
p := New(agent)
if p.Name() != "subprocess" {
t.Errorf("Name() = %q, want %q", p.Name(), "subprocess")
}
if p.DefaultModel() != "testcli" {
t.Errorf("DefaultModel() = %q, want %q", p.DefaultModel(), "testcli")
}
}
func TestProvider_Models(t *testing.T) {
agent := DiscoveredAgent{
CLIAgent: CLIAgent{
Name: "claude",
DisplayName: "Claude Code",
Format: FormatClaudeStreamJSON,
Capabilities: provider.Capabilities{
ContextWindow: 200000,
},
},
Path: "/usr/bin/claude",
Version: "2.1.0",
}
p := New(agent)
models, err := p.Models(context.Background())
if err != nil {
t.Fatal(err)
}
if len(models) != 1 {
t.Fatalf("Models() returned %d models, want 1", len(models))
}
m := models[0]
if m.Provider != "subprocess" {
t.Errorf("model.Provider = %q, want %q", m.Provider, "subprocess")
}
if m.Capabilities.ContextWindow != 200000 {
t.Errorf("ContextWindow = %d, want 200000", m.Capabilities.ContextWindow)
}
}
func TestProvider_Stream_MissingBinary(t *testing.T) {
agent := DiscoveredAgent{
CLIAgent: CLIAgent{
Name: "nonexistentcli",
DisplayName: "Nonexistent",
Format: FormatVibeStreaming,
PromptArgs: func(p string) []string { return []string{p} },
},
Path: "/nonexistent/cli",
}
p := New(agent)
req := provider.Request{}
_, err := p.Stream(context.Background(), req)
if err == nil {
t.Error("expected error for nonexistent binary, got nil")
}
}