test: renderPlan + dispatchRemote + statusRemote — agentic 71.5%, 559 tests
- renderPlan: test with real embedded templates (bug-fix, new-feature) - dispatchRemote: full MCP roundtrip with httptest mock - statusRemote: validation + unreachable + full roundtrip Coverage: agentic 67.8% → 71.5% (+3.7pp) Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
parent
1e12b062dd
commit
d30b07e34f
2 changed files with 262 additions and 0 deletions
186
pkg/agentic/remote_dispatch_test.go
Normal file
186
pkg/agentic/remote_dispatch_test.go
Normal file
|
|
@ -0,0 +1,186 @@
|
|||
// SPDX-License-Identifier: EUPL-1.2
|
||||
|
||||
package agentic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// --- dispatchRemote ---
|
||||
|
||||
func TestDispatchRemote_Bad_MissingHost(t *testing.T) {
|
||||
s := &PrepSubsystem{
|
||||
backoff: make(map[string]time.Time),
|
||||
failCount: make(map[string]int),
|
||||
}
|
||||
_, _, err := s.dispatchRemote(context.Background(), nil, RemoteDispatchInput{
|
||||
Repo: "go-io", Task: "do it",
|
||||
})
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "host is required")
|
||||
}
|
||||
|
||||
func TestDispatchRemote_Bad_MissingRepo(t *testing.T) {
|
||||
s := &PrepSubsystem{
|
||||
backoff: make(map[string]time.Time),
|
||||
failCount: make(map[string]int),
|
||||
}
|
||||
_, _, err := s.dispatchRemote(context.Background(), nil, RemoteDispatchInput{
|
||||
Host: "charon", Task: "do it",
|
||||
})
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "repo is required")
|
||||
}
|
||||
|
||||
func TestDispatchRemote_Bad_MissingTask(t *testing.T) {
|
||||
s := &PrepSubsystem{
|
||||
backoff: make(map[string]time.Time),
|
||||
failCount: make(map[string]int),
|
||||
}
|
||||
_, _, err := s.dispatchRemote(context.Background(), nil, RemoteDispatchInput{
|
||||
Host: "charon", Repo: "go-io",
|
||||
})
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "task is required")
|
||||
}
|
||||
|
||||
func TestDispatchRemote_Good_FullRoundtrip(t *testing.T) {
|
||||
callCount := 0
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
callCount++
|
||||
w.Header().Set("Mcp-Session-Id", "test-session")
|
||||
w.Header().Set("Content-Type", "text/event-stream")
|
||||
|
||||
switch callCount {
|
||||
case 1:
|
||||
// Initialize response
|
||||
fmt.Fprintf(w, "data: {\"result\":{}}\n\n")
|
||||
case 2:
|
||||
// Initialized notification — just accept
|
||||
w.WriteHeader(200)
|
||||
case 3:
|
||||
// Tool call response
|
||||
result := map[string]any{
|
||||
"result": map[string]any{
|
||||
"content": []map[string]any{
|
||||
{"text": `{"success":true,"agent":"codex","repo":"go-io","workspace_dir":"/ws/go-io","pid":12345}`},
|
||||
},
|
||||
},
|
||||
}
|
||||
data, _ := json.Marshal(result)
|
||||
fmt.Fprintf(w, "data: %s\n\n", data)
|
||||
}
|
||||
}))
|
||||
t.Cleanup(srv.Close)
|
||||
|
||||
// Override resolveHost to use our test server
|
||||
t.Setenv("AGENT_TOKEN_TESTHOST", "test-token")
|
||||
|
||||
s := &PrepSubsystem{
|
||||
backoff: make(map[string]time.Time),
|
||||
failCount: make(map[string]int),
|
||||
}
|
||||
|
||||
_, out, err := s.dispatchRemote(context.Background(), nil, RemoteDispatchInput{
|
||||
Host: srv.Listener.Addr().String(),
|
||||
Repo: "go-io",
|
||||
Task: "Fix tests",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.True(t, out.Success)
|
||||
assert.Equal(t, "go-io", out.Repo)
|
||||
}
|
||||
|
||||
func TestDispatchRemote_Bad_InitFails(t *testing.T) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(500)
|
||||
}))
|
||||
t.Cleanup(srv.Close)
|
||||
|
||||
s := &PrepSubsystem{
|
||||
backoff: make(map[string]time.Time),
|
||||
failCount: make(map[string]int),
|
||||
}
|
||||
|
||||
_, _, err := s.dispatchRemote(context.Background(), nil, RemoteDispatchInput{
|
||||
Host: srv.Listener.Addr().String(),
|
||||
Repo: "go-io",
|
||||
Task: "test",
|
||||
})
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "MCP initialize failed")
|
||||
}
|
||||
|
||||
// --- statusRemote ---
|
||||
|
||||
func TestStatusRemote_Bad_MissingHost(t *testing.T) {
|
||||
s := &PrepSubsystem{
|
||||
backoff: make(map[string]time.Time),
|
||||
failCount: make(map[string]int),
|
||||
}
|
||||
_, _, err := s.statusRemote(context.Background(), nil, RemoteStatusInput{})
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "host is required")
|
||||
}
|
||||
|
||||
func TestStatusRemote_Good_Unreachable(t *testing.T) {
|
||||
s := &PrepSubsystem{
|
||||
backoff: make(map[string]time.Time),
|
||||
failCount: make(map[string]int),
|
||||
}
|
||||
// Use a port that's not listening
|
||||
_, out, err := s.statusRemote(context.Background(), nil, RemoteStatusInput{
|
||||
Host: "127.0.0.1:1",
|
||||
})
|
||||
assert.NoError(t, err) // returns output, not error
|
||||
assert.Contains(t, out.Error, "unreachable")
|
||||
}
|
||||
|
||||
func TestStatusRemote_Good_FullRoundtrip(t *testing.T) {
|
||||
callCount := 0
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
callCount++
|
||||
w.Header().Set("Mcp-Session-Id", "test-session")
|
||||
w.Header().Set("Content-Type", "text/event-stream")
|
||||
|
||||
switch callCount {
|
||||
case 1:
|
||||
fmt.Fprintf(w, "data: {\"result\":{}}\n\n")
|
||||
case 2:
|
||||
w.WriteHeader(200)
|
||||
case 3:
|
||||
result := map[string]any{
|
||||
"result": map[string]any{
|
||||
"content": []map[string]any{
|
||||
{"text": `{"total":5,"running":2,"completed":3,"failed":0}`},
|
||||
},
|
||||
},
|
||||
}
|
||||
data, _ := json.Marshal(result)
|
||||
fmt.Fprintf(w, "data: %s\n\n", data)
|
||||
}
|
||||
}))
|
||||
t.Cleanup(srv.Close)
|
||||
|
||||
s := &PrepSubsystem{
|
||||
backoff: make(map[string]time.Time),
|
||||
failCount: make(map[string]int),
|
||||
}
|
||||
|
||||
_, out, err := s.statusRemote(context.Background(), nil, RemoteStatusInput{
|
||||
Host: srv.Listener.Addr().String(),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.True(t, out.Success)
|
||||
assert.Equal(t, 5, out.Stats.Total)
|
||||
assert.Equal(t, 2, out.Stats.Running)
|
||||
}
|
||||
76
pkg/agentic/render_plan_test.go
Normal file
76
pkg/agentic/render_plan_test.go
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
// SPDX-License-Identifier: EUPL-1.2
|
||||
|
||||
package agentic
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// --- renderPlan ---
|
||||
|
||||
func TestRenderPlan_Good_BugFix(t *testing.T) {
|
||||
s := &PrepSubsystem{
|
||||
backoff: make(map[string]time.Time),
|
||||
failCount: make(map[string]int),
|
||||
}
|
||||
result := s.renderPlan("bug-fix", nil, "Fix the auth bug")
|
||||
if result == "" {
|
||||
t.Skip("bug-fix template not available in embedded assets")
|
||||
}
|
||||
assert.Contains(t, result, "Bug Fix")
|
||||
assert.Contains(t, result, "Fix the auth bug")
|
||||
assert.Contains(t, result, "Phase 1")
|
||||
assert.Contains(t, result, "Investigation")
|
||||
}
|
||||
|
||||
func TestRenderPlan_Good_WithVariables(t *testing.T) {
|
||||
s := &PrepSubsystem{
|
||||
backoff: make(map[string]time.Time),
|
||||
failCount: make(map[string]int),
|
||||
}
|
||||
vars := map[string]string{
|
||||
"bug_description": "Login fails on mobile",
|
||||
"location": "pkg/auth/handler.go",
|
||||
}
|
||||
result := s.renderPlan("bug-fix", vars, "Fix login")
|
||||
if result == "" {
|
||||
t.Skip("bug-fix template not available")
|
||||
}
|
||||
assert.Contains(t, result, "Fix login")
|
||||
}
|
||||
|
||||
func TestRenderPlan_Bad_UnknownTemplate(t *testing.T) {
|
||||
s := &PrepSubsystem{
|
||||
backoff: make(map[string]time.Time),
|
||||
failCount: make(map[string]int),
|
||||
}
|
||||
result := s.renderPlan("nonexistent-template-slug", nil, "task")
|
||||
assert.Empty(t, result)
|
||||
}
|
||||
|
||||
func TestRenderPlan_Good_NoTask(t *testing.T) {
|
||||
s := &PrepSubsystem{
|
||||
backoff: make(map[string]time.Time),
|
||||
failCount: make(map[string]int),
|
||||
}
|
||||
result := s.renderPlan("bug-fix", nil, "")
|
||||
if result == "" {
|
||||
t.Skip("template not available")
|
||||
}
|
||||
assert.NotContains(t, result, "**Task:**")
|
||||
}
|
||||
|
||||
func TestRenderPlan_Good_NewFeature(t *testing.T) {
|
||||
s := &PrepSubsystem{
|
||||
backoff: make(map[string]time.Time),
|
||||
failCount: make(map[string]int),
|
||||
}
|
||||
result := s.renderPlan("new-feature", nil, "Add caching")
|
||||
if result == "" {
|
||||
t.Skip("new-feature template not available")
|
||||
}
|
||||
assert.Contains(t, result, "Add caching")
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue