test(ax): align provider and runner path specs
Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
parent
2f7ca89d80
commit
bf3aae5766
3 changed files with 436 additions and 70 deletions
|
|
@ -4,22 +4,30 @@ package brain
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
core "dappco.re/go/core"
|
||||
"forge.lthn.ai/core/go-ws"
|
||||
"forge.lthn.ai/core/mcp/pkg/mcp/ide"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type bridgeCapture struct {
|
||||
err error
|
||||
msg ide.BridgeMessage
|
||||
}
|
||||
|
||||
func init() {
|
||||
gin.SetMode(gin.TestMode)
|
||||
}
|
||||
|
||||
// setupRouter creates a gin engine with provider routes registered.
|
||||
func setupRouter(p *BrainProvider) *gin.Engine {
|
||||
r := gin.New()
|
||||
g := r.Group(p.BasePath())
|
||||
|
|
@ -27,7 +35,6 @@ func setupRouter(p *BrainProvider) *gin.Engine {
|
|||
return r
|
||||
}
|
||||
|
||||
// providerRequest performs an HTTP request against the provider router and returns the recorder.
|
||||
func providerRequest(t *testing.T, p *BrainProvider, method, path string, body []byte) *httptest.ResponseRecorder {
|
||||
t.Helper()
|
||||
r := setupRouter(p)
|
||||
|
|
@ -43,111 +50,413 @@ func providerRequest(t *testing.T, p *BrainProvider, method, path string, body [
|
|||
return w
|
||||
}
|
||||
|
||||
// --- Provider construction ---
|
||||
func connectedBridge(t *testing.T) (*ide.Bridge, <-chan bridgeCapture, func()) {
|
||||
t.Helper()
|
||||
|
||||
captures := make(chan bridgeCapture, 4)
|
||||
upgrader := websocket.Upgrader{CheckOrigin: func(*http.Request) bool { return true }}
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
conn, err := upgrader.Upgrade(w, r, nil)
|
||||
if err != nil {
|
||||
captures <- bridgeCapture{err: err}
|
||||
return
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
for {
|
||||
_, data, err := conn.ReadMessage()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var msg ide.BridgeMessage
|
||||
if result := core.JSONUnmarshal(data, &msg); !result.OK {
|
||||
parseErr, _ := result.Value.(error)
|
||||
captures <- bridgeCapture{err: parseErr}
|
||||
return
|
||||
}
|
||||
captures <- bridgeCapture{msg: msg}
|
||||
}
|
||||
}))
|
||||
|
||||
bridge := ide.NewBridge(nil, ide.Config{
|
||||
LaravelWSURL: core.Replace(server.URL, "http://", "ws://"),
|
||||
ReconnectInterval: 10 * time.Millisecond,
|
||||
MaxReconnectInterval: 20 * time.Millisecond,
|
||||
})
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
bridge.Start(ctx)
|
||||
require.Eventually(t, bridge.Connected, time.Second, 10*time.Millisecond)
|
||||
|
||||
cleanup := func() {
|
||||
cancel()
|
||||
bridge.Shutdown()
|
||||
server.Close()
|
||||
}
|
||||
|
||||
return bridge, captures, cleanup
|
||||
}
|
||||
|
||||
func providerResponseData(t *testing.T, w *httptest.ResponseRecorder) map[string]any {
|
||||
t.Helper()
|
||||
var response map[string]any
|
||||
require.True(t, core.JSONUnmarshal(w.Body.Bytes(), &response).OK)
|
||||
data, ok := response["data"].(map[string]any)
|
||||
require.True(t, ok)
|
||||
return data
|
||||
}
|
||||
|
||||
func receiveBridgeMessage(t *testing.T, captures <-chan bridgeCapture) ide.BridgeMessage {
|
||||
t.Helper()
|
||||
select {
|
||||
case capture := <-captures:
|
||||
require.NoError(t, capture.err)
|
||||
return capture.msg
|
||||
case <-time.After(time.Second):
|
||||
t.Fatal("timed out waiting for bridge message")
|
||||
return ide.BridgeMessage{}
|
||||
}
|
||||
}
|
||||
|
||||
func providerRouteSignatures(r *gin.Engine) []string {
|
||||
routes := r.Routes()
|
||||
signatures := make([]string, 0, len(routes))
|
||||
for _, route := range routes {
|
||||
signatures = append(signatures, core.Concat(route.Method, " ", route.Path))
|
||||
}
|
||||
return signatures
|
||||
}
|
||||
|
||||
func TestProvider_NewProvider_Good(t *testing.T) {
|
||||
bridge := ide.NewBridge(nil, ide.Config{})
|
||||
hub := ws.NewHub()
|
||||
|
||||
p := NewProvider(bridge, hub)
|
||||
|
||||
require.NotNil(t, p)
|
||||
assert.Same(t, bridge, p.bridge)
|
||||
assert.Same(t, hub, p.hub)
|
||||
}
|
||||
|
||||
func TestProvider_NewProvider_Bad_NilDependencies(t *testing.T) {
|
||||
p := NewProvider(nil, nil)
|
||||
assert.NotNil(t, p)
|
||||
|
||||
require.NotNil(t, p)
|
||||
assert.Nil(t, p.bridge)
|
||||
assert.Nil(t, p.hub)
|
||||
}
|
||||
|
||||
func TestProvider_BrainProvider_Good_Name(t *testing.T) {
|
||||
func TestProvider_NewProvider_Ugly_MixedDependencies(t *testing.T) {
|
||||
bridge := ide.NewBridge(nil, ide.Config{})
|
||||
|
||||
p := NewProvider(bridge, nil)
|
||||
|
||||
require.NotNil(t, p)
|
||||
assert.Same(t, bridge, p.bridge)
|
||||
assert.Nil(t, p.hub)
|
||||
}
|
||||
|
||||
func TestProvider_Name_Good(t *testing.T) {
|
||||
assert.Equal(t, "brain", NewProvider(nil, nil).Name())
|
||||
}
|
||||
|
||||
func TestProvider_BrainProvider_Good_BasePath(t *testing.T) {
|
||||
func TestProvider_Name_Bad_ZeroValueReceiver(t *testing.T) {
|
||||
assert.Equal(t, "brain", (&BrainProvider{}).Name())
|
||||
}
|
||||
|
||||
func TestProvider_Name_Ugly_NilReceiver(t *testing.T) {
|
||||
var p *BrainProvider
|
||||
assert.Equal(t, "brain", p.Name())
|
||||
}
|
||||
|
||||
func TestProvider_BasePath_Good(t *testing.T) {
|
||||
assert.Equal(t, "/api/brain", NewProvider(nil, nil).BasePath())
|
||||
}
|
||||
|
||||
func TestProvider_BrainProvider_Good_Channels(t *testing.T) {
|
||||
func TestProvider_BasePath_Bad_ZeroValueReceiver(t *testing.T) {
|
||||
assert.Equal(t, "/api/brain", (&BrainProvider{}).BasePath())
|
||||
}
|
||||
|
||||
func TestProvider_BasePath_Ugly_NilReceiver(t *testing.T) {
|
||||
var p *BrainProvider
|
||||
assert.Equal(t, "/api/brain", p.BasePath())
|
||||
}
|
||||
|
||||
func TestProvider_Channels_Good(t *testing.T) {
|
||||
channels := NewProvider(nil, nil).Channels()
|
||||
|
||||
assert.Equal(t, []string{
|
||||
"brain.remember.complete",
|
||||
"brain.recall.complete",
|
||||
"brain.forget.complete",
|
||||
}, channels)
|
||||
}
|
||||
|
||||
func TestProvider_Channels_Bad_ZeroValueReceiver(t *testing.T) {
|
||||
channels := (&BrainProvider{}).Channels()
|
||||
assert.Len(t, channels, 3)
|
||||
assert.Contains(t, channels, "brain.remember.complete")
|
||||
assert.Contains(t, channels, "brain.recall.complete")
|
||||
assert.Contains(t, channels, "brain.forget.complete")
|
||||
}
|
||||
|
||||
func TestProvider_BrainProvider_Good_Element(t *testing.T) {
|
||||
el := NewProvider(nil, nil).Element()
|
||||
assert.Equal(t, "core-brain-panel", el.Tag)
|
||||
assert.Equal(t, "/assets/brain-panel.js", el.Source)
|
||||
func TestProvider_Channels_Ugly_ReturnSliceIsDetached(t *testing.T) {
|
||||
channels := NewProvider(nil, nil).Channels()
|
||||
channels[0] = "changed"
|
||||
|
||||
assert.Equal(t, "brain.remember.complete", NewProvider(nil, nil).Channels()[0])
|
||||
}
|
||||
|
||||
func TestProvider_BrainProvider_Good_Describe(t *testing.T) {
|
||||
descs := NewProvider(nil, nil).Describe()
|
||||
assert.Len(t, descs, 5)
|
||||
func TestProvider_Element_Good(t *testing.T) {
|
||||
element := NewProvider(nil, nil).Element()
|
||||
|
||||
paths := make([]string, len(descs))
|
||||
for i, d := range descs {
|
||||
paths[i] = d.Method + " " + d.Path
|
||||
}
|
||||
assert.Contains(t, paths, "POST /remember")
|
||||
assert.Contains(t, paths, "POST /recall")
|
||||
assert.Contains(t, paths, "POST /forget")
|
||||
assert.Contains(t, paths, "GET /list")
|
||||
assert.Contains(t, paths, "GET /status")
|
||||
assert.Equal(t, "core-brain-panel", element.Tag)
|
||||
assert.Equal(t, "/assets/brain-panel.js", element.Source)
|
||||
}
|
||||
|
||||
// --- Handler: status ---
|
||||
func TestProvider_Element_Bad_ZeroValueReceiver(t *testing.T) {
|
||||
element := (&BrainProvider{}).Element()
|
||||
|
||||
assert.Equal(t, "core-brain-panel", element.Tag)
|
||||
assert.Equal(t, "/assets/brain-panel.js", element.Source)
|
||||
}
|
||||
|
||||
func TestProvider_Element_Ugly_ReturnValueIsDetached(t *testing.T) {
|
||||
element := NewProvider(nil, nil).Element()
|
||||
element.Tag = "changed"
|
||||
|
||||
assert.Equal(t, "core-brain-panel", NewProvider(nil, nil).Element().Tag)
|
||||
}
|
||||
|
||||
func TestProvider_RegisterRoutes_Good(t *testing.T) {
|
||||
signatures := providerRouteSignatures(setupRouter(NewProvider(nil, nil)))
|
||||
|
||||
assert.ElementsMatch(t, []string{
|
||||
"POST /api/brain/remember",
|
||||
"POST /api/brain/recall",
|
||||
"POST /api/brain/forget",
|
||||
"GET /api/brain/list",
|
||||
"GET /api/brain/status",
|
||||
}, signatures)
|
||||
}
|
||||
|
||||
func TestProvider_RegisterRoutes_Bad_ZeroValueProvider(t *testing.T) {
|
||||
provider := &BrainProvider{}
|
||||
|
||||
status := providerRequest(t, provider, "GET", "/api/brain/status", nil)
|
||||
list := providerRequest(t, provider, "GET", "/api/brain/list", nil)
|
||||
|
||||
assert.Equal(t, http.StatusOK, status.Code)
|
||||
assert.Equal(t, http.StatusServiceUnavailable, list.Code)
|
||||
}
|
||||
|
||||
func TestProvider_RegisterRoutes_Ugly_CustomGroup(t *testing.T) {
|
||||
r := gin.New()
|
||||
NewProvider(nil, nil).RegisterRoutes(r.Group("/v1/brain"))
|
||||
|
||||
assert.ElementsMatch(t, []string{
|
||||
"POST /v1/brain/remember",
|
||||
"POST /v1/brain/recall",
|
||||
"POST /v1/brain/forget",
|
||||
"GET /v1/brain/list",
|
||||
"GET /v1/brain/status",
|
||||
}, providerRouteSignatures(r))
|
||||
}
|
||||
|
||||
func TestProvider_Describe_Good(t *testing.T) {
|
||||
descriptions := NewProvider(nil, nil).Describe()
|
||||
|
||||
assert.Len(t, descriptions, 5)
|
||||
assert.Equal(t, "POST", descriptions[0].Method)
|
||||
assert.Equal(t, "/remember", descriptions[0].Path)
|
||||
assert.Equal(t, "GET", descriptions[4].Method)
|
||||
assert.Equal(t, "/status", descriptions[4].Path)
|
||||
}
|
||||
|
||||
func TestProvider_Describe_Bad_ZeroValueReceiver(t *testing.T) {
|
||||
descriptions := (&BrainProvider{}).Describe()
|
||||
|
||||
assert.Len(t, descriptions, 5)
|
||||
assert.Equal(t, "/list", descriptions[3].Path)
|
||||
}
|
||||
|
||||
func TestProvider_Describe_Ugly_ReturnSliceIsDetached(t *testing.T) {
|
||||
descriptions := NewProvider(nil, nil).Describe()
|
||||
descriptions[0].Path = "/changed"
|
||||
|
||||
assert.Equal(t, "/remember", NewProvider(nil, nil).Describe()[0].Path)
|
||||
}
|
||||
|
||||
func TestProvider_Status_Good(t *testing.T) {
|
||||
p := NewProvider(nil, nil)
|
||||
w := providerRequest(t, p, "GET", "/api/brain/status", nil)
|
||||
bridge, _, cleanup := connectedBridge(t)
|
||||
defer cleanup()
|
||||
|
||||
w := providerRequest(t, NewProvider(bridge, nil), "GET", "/api/brain/status", nil)
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
var resp map[string]any
|
||||
require.True(t, core.JSONUnmarshal(w.Body.Bytes(), &resp).OK)
|
||||
data, _ := resp["data"].(map[string]any)
|
||||
assert.Equal(t, false, data["connected"])
|
||||
assert.Equal(t, true, providerResponseData(t, w)["connected"])
|
||||
}
|
||||
|
||||
// --- Nil bridge handlers return 503 ---
|
||||
func TestProvider_Status_Bad_NoBridge(t *testing.T) {
|
||||
w := providerRequest(t, NewProvider(nil, nil), "GET", "/api/brain/status", nil)
|
||||
|
||||
func TestProvider_RememberHandler_Bad(t *testing.T) {
|
||||
body := []byte(core.JSONMarshalString(map[string]any{"content": "test memory", "type": "observation"}))
|
||||
w := providerRequest(t, NewProvider(nil, nil), "POST", "/api/brain/remember", body)
|
||||
assert.Equal(t, http.StatusServiceUnavailable, w.Code)
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
assert.Equal(t, false, providerResponseData(t, w)["connected"])
|
||||
}
|
||||
|
||||
func TestProvider_RememberHandlerInvalid_Bad(t *testing.T) {
|
||||
// nil bridge returns 503 before JSON validation.
|
||||
w := providerRequest(t, NewProvider(nil, nil), "POST", "/api/brain/remember", []byte("not json"))
|
||||
assert.Equal(t, http.StatusServiceUnavailable, w.Code)
|
||||
func TestProvider_Status_Ugly_DisconnectedBridge(t *testing.T) {
|
||||
w := providerRequest(t, NewProvider(&ide.Bridge{}, nil), "GET", "/api/brain/status", nil)
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
assert.Equal(t, false, providerResponseData(t, w)["connected"])
|
||||
}
|
||||
|
||||
func TestProvider_RecallHandler_Bad(t *testing.T) {
|
||||
body := []byte(core.JSONMarshalString(map[string]any{"query": "test"}))
|
||||
w := providerRequest(t, NewProvider(nil, nil), "POST", "/api/brain/recall", body)
|
||||
assert.Equal(t, http.StatusServiceUnavailable, w.Code)
|
||||
func TestProvider_Remember_Good(t *testing.T) {
|
||||
bridge, captures, cleanup := connectedBridge(t)
|
||||
defer cleanup()
|
||||
|
||||
body := []byte(core.JSONMarshalString(map[string]any{
|
||||
"content": "Use core.Env for system paths.",
|
||||
"type": "convention",
|
||||
"project": "agent",
|
||||
"confidence": 0.9,
|
||||
"tags": []string{"ax", "paths"},
|
||||
}))
|
||||
w := providerRequest(t, NewProvider(bridge, nil), "POST", "/api/brain/remember", body)
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
assert.Equal(t, true, providerResponseData(t, w)["success"])
|
||||
|
||||
msg := receiveBridgeMessage(t, captures)
|
||||
data, ok := msg.Data.(map[string]any)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, "brain_remember", msg.Type)
|
||||
assert.Equal(t, "Use core.Env for system paths.", data["content"])
|
||||
assert.Equal(t, "convention", data["type"])
|
||||
assert.Equal(t, "agent", data["project"])
|
||||
}
|
||||
|
||||
func TestProvider_ForgetHandler_Bad(t *testing.T) {
|
||||
body := []byte(core.JSONMarshalString(map[string]any{"id": "mem-123"}))
|
||||
w := providerRequest(t, NewProvider(nil, nil), "POST", "/api/brain/forget", body)
|
||||
assert.Equal(t, http.StatusServiceUnavailable, w.Code)
|
||||
}
|
||||
|
||||
func TestProvider_ListHandler_Bad(t *testing.T) {
|
||||
w := providerRequest(t, NewProvider(nil, nil), "GET", "/api/brain/list", nil)
|
||||
assert.Equal(t, http.StatusServiceUnavailable, w.Code)
|
||||
}
|
||||
|
||||
func TestProvider_RememberHandler_Bad_InvalidInput(t *testing.T) {
|
||||
func TestProvider_Remember_Bad_InvalidInput(t *testing.T) {
|
||||
w := providerRequest(t, NewProvider(&ide.Bridge{}, nil), "POST", "/api/brain/remember", []byte("not json"))
|
||||
assert.Equal(t, http.StatusBadRequest, w.Code)
|
||||
}
|
||||
|
||||
func TestProvider_ListHandler_Bad_InvalidLimit(t *testing.T) {
|
||||
func TestProvider_Remember_Ugly_DisconnectedBridge(t *testing.T) {
|
||||
body := []byte(core.JSONMarshalString(map[string]any{"content": "test memory", "type": "observation"}))
|
||||
w := providerRequest(t, NewProvider(&ide.Bridge{}, nil), "POST", "/api/brain/remember", body)
|
||||
assert.Equal(t, http.StatusInternalServerError, w.Code)
|
||||
}
|
||||
|
||||
func TestProvider_Recall_Good(t *testing.T) {
|
||||
bridge, captures, cleanup := connectedBridge(t)
|
||||
defer cleanup()
|
||||
|
||||
body := []byte(core.JSONMarshalString(map[string]any{
|
||||
"query": "workspace path helpers",
|
||||
"top_k": 3,
|
||||
"filter": map[string]any{
|
||||
"project": "agent",
|
||||
"type": "convention",
|
||||
},
|
||||
}))
|
||||
w := providerRequest(t, NewProvider(bridge, nil), "POST", "/api/brain/recall", body)
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
data := providerResponseData(t, w)
|
||||
assert.Equal(t, true, data["success"])
|
||||
assert.Equal(t, 0.0, data["count"])
|
||||
|
||||
msg := receiveBridgeMessage(t, captures)
|
||||
payload, ok := msg.Data.(map[string]any)
|
||||
require.True(t, ok)
|
||||
filter, ok := payload["filter"].(map[string]any)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, "brain_recall", msg.Type)
|
||||
assert.Equal(t, "workspace path helpers", payload["query"])
|
||||
assert.Equal(t, 3.0, payload["top_k"])
|
||||
assert.Equal(t, "agent", filter["project"])
|
||||
assert.Equal(t, "convention", filter["type"])
|
||||
}
|
||||
|
||||
func TestProvider_Recall_Bad_InvalidInput(t *testing.T) {
|
||||
w := providerRequest(t, NewProvider(&ide.Bridge{}, nil), "POST", "/api/brain/recall", []byte("not json"))
|
||||
assert.Equal(t, http.StatusBadRequest, w.Code)
|
||||
}
|
||||
|
||||
func TestProvider_Recall_Ugly_DisconnectedBridge(t *testing.T) {
|
||||
body := []byte(core.JSONMarshalString(map[string]any{"query": "test"}))
|
||||
w := providerRequest(t, NewProvider(&ide.Bridge{}, nil), "POST", "/api/brain/recall", body)
|
||||
assert.Equal(t, http.StatusInternalServerError, w.Code)
|
||||
}
|
||||
|
||||
func TestProvider_Forget_Good(t *testing.T) {
|
||||
bridge, captures, cleanup := connectedBridge(t)
|
||||
defer cleanup()
|
||||
|
||||
body := []byte(core.JSONMarshalString(map[string]any{"id": "mem-123", "reason": "superseded"}))
|
||||
w := providerRequest(t, NewProvider(bridge, nil), "POST", "/api/brain/forget", body)
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
assert.Equal(t, "mem-123", providerResponseData(t, w)["forgotten"])
|
||||
|
||||
msg := receiveBridgeMessage(t, captures)
|
||||
data, ok := msg.Data.(map[string]any)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, "brain_forget", msg.Type)
|
||||
assert.Equal(t, "mem-123", data["id"])
|
||||
assert.Equal(t, "superseded", data["reason"])
|
||||
}
|
||||
|
||||
func TestProvider_Forget_Bad_InvalidInput(t *testing.T) {
|
||||
w := providerRequest(t, NewProvider(&ide.Bridge{}, nil), "POST", "/api/brain/forget", []byte("not json"))
|
||||
assert.Equal(t, http.StatusBadRequest, w.Code)
|
||||
}
|
||||
|
||||
func TestProvider_Forget_Ugly_DisconnectedBridge(t *testing.T) {
|
||||
body := []byte(core.JSONMarshalString(map[string]any{"id": "mem-123"}))
|
||||
w := providerRequest(t, NewProvider(&ide.Bridge{}, nil), "POST", "/api/brain/forget", body)
|
||||
assert.Equal(t, http.StatusInternalServerError, w.Code)
|
||||
}
|
||||
|
||||
func TestProvider_List_Good(t *testing.T) {
|
||||
bridge, captures, cleanup := connectedBridge(t)
|
||||
defer cleanup()
|
||||
|
||||
w := providerRequest(t, NewProvider(bridge, nil), "GET", "/api/brain/list?project=agent&type=convention&agent_id=codex&limit=2", nil)
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
data := providerResponseData(t, w)
|
||||
assert.Equal(t, true, data["success"])
|
||||
assert.Equal(t, 0.0, data["count"])
|
||||
|
||||
msg := receiveBridgeMessage(t, captures)
|
||||
payload, ok := msg.Data.(map[string]any)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, "brain_list", msg.Type)
|
||||
assert.Equal(t, "agent", payload["project"])
|
||||
assert.Equal(t, "convention", payload["type"])
|
||||
assert.Equal(t, "codex", payload["agent_id"])
|
||||
assert.Equal(t, 2.0, payload["limit"])
|
||||
}
|
||||
|
||||
func TestProvider_List_Bad_InvalidLimit(t *testing.T) {
|
||||
w := providerRequest(t, NewProvider(&ide.Bridge{}, nil), "GET", "/api/brain/list?limit=abc", nil)
|
||||
assert.Equal(t, http.StatusBadRequest, w.Code)
|
||||
}
|
||||
|
||||
// --- emitEvent ---
|
||||
func TestProvider_List_Ugly_DisconnectedBridge(t *testing.T) {
|
||||
w := providerRequest(t, NewProvider(&ide.Bridge{}, nil), "GET", "/api/brain/list?limit=2", nil)
|
||||
assert.Equal(t, http.StatusInternalServerError, w.Code)
|
||||
}
|
||||
|
||||
func TestProvider_EmitEvent_Good(t *testing.T) {
|
||||
p := NewProvider(nil, nil)
|
||||
p.emitEvent("brain.test", map[string]any{"foo": "bar"})
|
||||
assert.NotPanics(t, func() {
|
||||
NewProvider(nil, ws.NewHub()).emitEvent("brain.test", map[string]any{"foo": "bar"})
|
||||
})
|
||||
}
|
||||
|
||||
func TestProvider_EmitEvent_Bad_EmptyChannel(t *testing.T) {
|
||||
assert.NotPanics(t, func() {
|
||||
NewProvider(nil, ws.NewHub()).emitEvent("", map[string]any{"foo": "bar"})
|
||||
})
|
||||
}
|
||||
|
||||
func TestProvider_EmitEvent_Ugly_NilHub(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -18,6 +18,25 @@ func ExampleWorkspaceRoot() {
|
|||
// Output: true
|
||||
}
|
||||
|
||||
func ExampleReadStatus() {
|
||||
fsys := (&core.Fs{}).NewUnrestricted()
|
||||
dir := fsys.TempDir("runner-paths-read")
|
||||
defer fsys.DeleteAll(dir)
|
||||
|
||||
WriteStatus(dir, &WorkspaceStatus{
|
||||
Status: "completed",
|
||||
Agent: "codex",
|
||||
Repo: "go-io",
|
||||
})
|
||||
|
||||
st, err := ReadStatus(dir)
|
||||
core.Println(err == nil)
|
||||
core.Println(st.Repo)
|
||||
// Output:
|
||||
// true
|
||||
// go-io
|
||||
}
|
||||
|
||||
func ExampleWriteStatus() {
|
||||
fsys := (&core.Fs{}).NewUnrestricted()
|
||||
dir := fsys.TempDir("runner-paths")
|
||||
|
|
|
|||
|
|
@ -12,18 +12,18 @@ import (
|
|||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestPaths_CoreRoot_Good_EnvVar(t *testing.T) {
|
||||
func TestPaths_CoreRoot_Good(t *testing.T) {
|
||||
t.Setenv("CORE_WORKSPACE", "/tmp/core-root")
|
||||
assert.Equal(t, "/tmp/core-root", CoreRoot())
|
||||
}
|
||||
|
||||
func TestPaths_CoreRoot_Bad_Fallback(t *testing.T) {
|
||||
func TestPaths_CoreRoot_Bad(t *testing.T) {
|
||||
t.Setenv("CORE_WORKSPACE", "")
|
||||
home := core.Env("DIR_HOME")
|
||||
assert.Equal(t, home+"/Code/.core", CoreRoot())
|
||||
}
|
||||
|
||||
func TestPaths_CoreRoot_Ugly_UnicodePath(t *testing.T) {
|
||||
func TestPaths_CoreRoot_Ugly(t *testing.T) {
|
||||
t.Setenv("CORE_WORKSPACE", "/tmp/core-røot")
|
||||
assert.Equal(t, "/tmp/core-røot", CoreRoot())
|
||||
}
|
||||
|
|
@ -33,18 +33,18 @@ func TestPaths_WorkspaceRoot_Good(t *testing.T) {
|
|||
assert.Equal(t, "/tmp/core-root/workspace", WorkspaceRoot())
|
||||
}
|
||||
|
||||
func TestPaths_WorkspaceRoot_Bad_EmptyEnv(t *testing.T) {
|
||||
func TestPaths_WorkspaceRoot_Bad(t *testing.T) {
|
||||
t.Setenv("CORE_WORKSPACE", "")
|
||||
home := core.Env("DIR_HOME")
|
||||
assert.Equal(t, home+"/Code/.core/workspace", WorkspaceRoot())
|
||||
}
|
||||
|
||||
func TestPaths_WorkspaceRoot_Ugly_NestedCoreRoot(t *testing.T) {
|
||||
func TestPaths_WorkspaceRoot_Ugly(t *testing.T) {
|
||||
t.Setenv("CORE_WORKSPACE", "/srv/core/tenant-a")
|
||||
assert.Equal(t, "/srv/core/tenant-a/workspace", WorkspaceRoot())
|
||||
}
|
||||
|
||||
func TestPaths_ReadStatus_Good_AgenticShape(t *testing.T) {
|
||||
func TestPaths_ReadStatus_Good(t *testing.T) {
|
||||
wsDir := t.TempDir()
|
||||
status := &agentic.WorkspaceStatus{
|
||||
Status: "completed",
|
||||
|
|
@ -71,7 +71,7 @@ func TestPaths_ReadStatus_Good_AgenticShape(t *testing.T) {
|
|||
assert.Equal(t, 2, st.Runs)
|
||||
}
|
||||
|
||||
func TestPaths_ReadStatus_Bad_InvalidJSON(t *testing.T) {
|
||||
func TestPaths_ReadStatus_Bad(t *testing.T) {
|
||||
wsDir := t.TempDir()
|
||||
require.True(t, agentic.LocalFs().WriteAtomic(agentic.WorkspaceStatusPath(wsDir), "{not-json").OK)
|
||||
|
||||
|
|
@ -79,7 +79,45 @@ func TestPaths_ReadStatus_Bad_InvalidJSON(t *testing.T) {
|
|||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestPaths_WriteStatus_Ugly_AtomicOverwrite(t *testing.T) {
|
||||
func TestPaths_ReadStatus_Ugly(t *testing.T) {
|
||||
_, err := ReadStatus(t.TempDir())
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestPaths_WriteStatus_Good(t *testing.T) {
|
||||
wsDir := t.TempDir()
|
||||
|
||||
WriteStatus(wsDir, &WorkspaceStatus{
|
||||
Status: "running",
|
||||
Agent: "codex",
|
||||
Repo: "go-io",
|
||||
Task: "Track workspace",
|
||||
Branch: "agent/ax-cleanup",
|
||||
Runs: 1,
|
||||
})
|
||||
|
||||
st, err := ReadStatus(wsDir)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "running", st.Status)
|
||||
assert.Equal(t, "codex", st.Agent)
|
||||
assert.Equal(t, "go-io", st.Repo)
|
||||
assert.Equal(t, "agent/ax-cleanup", st.Branch)
|
||||
assert.Equal(t, 1, st.Runs)
|
||||
|
||||
agenticStatus, err := agentic.ReadStatus(wsDir)
|
||||
require.NoError(t, err)
|
||||
assert.False(t, agenticStatus.UpdatedAt.IsZero())
|
||||
}
|
||||
|
||||
func TestPaths_WriteStatus_Bad(t *testing.T) {
|
||||
wsDir := t.TempDir()
|
||||
|
||||
WriteStatus(wsDir, nil)
|
||||
|
||||
assert.False(t, agentic.LocalFs().Read(agentic.WorkspaceStatusPath(wsDir)).OK)
|
||||
}
|
||||
|
||||
func TestPaths_WriteStatus_Ugly(t *testing.T) {
|
||||
wsDir := t.TempDir()
|
||||
|
||||
WriteStatus(wsDir, &WorkspaceStatus{
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue