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>
169 lines
3.8 KiB
Go
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)
|
|
}
|
|
})
|
|
}
|
|
}
|