rdev/internal/domain/code_agent_test.go
jordan 39df51defd feat: Add multi-provider code agent interface with Claude Code and OpenCode adapters
Implements weeks 1-4 of the multi-provider architecture:

Week 1 - Foundation:
- Add domain models (AgentProvider, AgentRequest, AgentEvent, AgentResult)
- Define CodeAgent port interface with Execute, Cancel, Capabilities
- Create thread-safe provider registry with first-registered default

Week 2 - Claude Code Adapter:
- Extract kubectl exec logic into CodeAgent implementation
- Parse stream-json output format (init, message, tool_use, result)
- Support session continuation via --resume flag

Week 3 - OpenCode Adapter:
- HTTP/SSE client for opencode serve API
- Session management (create, send message, abort)
- Event streaming with documented buffer rationale

Week 4 - Quality & Polish:
- Fix race condition in OpenCode Cancel method
- Add AgentRequest.Validate() with ErrPromptRequired, ErrInvalidTimeout
- Document DefaultAvailabilityTimeout constants
- Add HTTP error context for debugging

Also includes:
- Work queue system with PostgreSQL adapter
- Credential store for infrastructure secrets
- Project templates with Woodpecker CI integration
- Comprehensive test coverage

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-27 09:25:51 -07:00

169 lines
3.8 KiB
Go

package domain
import (
"errors"
"testing"
)
func TestAgentProvider_IsValid(t *testing.T) {
tests := []struct {
provider AgentProvider
want bool
}{
{AgentProviderClaudeCode, true},
{AgentProviderOpenCode, true},
{"unknown", false},
{"", false},
}
for _, tt := range tests {
t.Run(string(tt.provider), func(t *testing.T) {
if got := tt.provider.IsValid(); got != tt.want {
t.Errorf("IsValid() = %v, want %v", got, tt.want)
}
})
}
}
func TestAgentProvider_String(t *testing.T) {
if got := AgentProviderClaudeCode.String(); got != "claudecode" {
t.Errorf("String() = %q, want %q", got, "claudecode")
}
if got := AgentProviderOpenCode.String(); got != "opencode" {
t.Errorf("String() = %q, want %q", got, "opencode")
}
}
func TestParseAgentProvider(t *testing.T) {
tests := []struct {
input string
want AgentProvider
wantErr bool
}{
{"claudecode", AgentProviderClaudeCode, false},
{"opencode", AgentProviderOpenCode, false},
{"unknown", "", true},
{"", "", true},
{"CLAUDECODE", "", true}, // case sensitive
}
for _, tt := range tests {
t.Run(tt.input, func(t *testing.T) {
got, err := ParseAgentProvider(tt.input)
if (err != nil) != tt.wantErr {
t.Errorf("ParseAgentProvider() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("ParseAgentProvider() = %v, want %v", got, tt.want)
}
if tt.wantErr && !errors.Is(err, ErrInvalidAgentProvider) {
t.Errorf("expected ErrInvalidAgentProvider, got %v", err)
}
})
}
}
func TestValidAgentProviders(t *testing.T) {
providers := ValidAgentProviders()
if len(providers) != 2 {
t.Errorf("expected 2 providers, got %d", len(providers))
}
found := make(map[AgentProvider]bool)
for _, p := range providers {
found[p] = true
}
if !found[AgentProviderClaudeCode] {
t.Error("expected claudecode in valid providers")
}
if !found[AgentProviderOpenCode] {
t.Error("expected opencode in valid providers")
}
}
func TestAgentRequest_Validate(t *testing.T) {
tests := []struct {
name string
request AgentRequest
wantErr error
}{
{
name: "valid request",
request: AgentRequest{Prompt: "Hello"},
wantErr: nil,
},
{
name: "empty prompt",
request: AgentRequest{Prompt: ""},
wantErr: ErrPromptRequired,
},
{
name: "negative timeout",
request: AgentRequest{Prompt: "Hello", Timeout: -1},
wantErr: ErrInvalidTimeout,
},
{
name: "zero timeout is valid",
request: AgentRequest{Prompt: "Hello", Timeout: 0},
wantErr: nil,
},
{
name: "positive timeout is valid",
request: AgentRequest{Prompt: "Hello", Timeout: 5},
wantErr: nil,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := tt.request.Validate()
if tt.wantErr != nil {
if !errors.Is(err, tt.wantErr) {
t.Errorf("Validate() error = %v, want %v", err, tt.wantErr)
}
} else if err != nil {
t.Errorf("Validate() unexpected error = %v", err)
}
})
}
}
func TestAgentResult_Success(t *testing.T) {
tests := []struct {
name string
result AgentResult
expected bool
}{
{
name: "success with zero exit code",
result: AgentResult{ExitCode: 0, Error: nil},
expected: true,
},
{
name: "failure with non-zero exit code",
result: AgentResult{ExitCode: 1, Error: nil},
expected: false,
},
{
name: "failure with error",
result: AgentResult{ExitCode: 0, Error: errors.New("test error")},
expected: false,
},
{
name: "failure with both error and exit code",
result: AgentResult{ExitCode: 1, Error: errors.New("test error")},
expected: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := tt.result.Success(); got != tt.expected {
t.Errorf("Success() = %v, want %v", got, tt.expected)
}
})
}
}