Files
aego/tests/integration/agent_bridge_test.go
sanspie 7b83e82c6e Stage 11b: AI agent in the editor (D341–D343)
MCP over loopback HTTP and an editor session over open documents; proposals with apply, reject, auto-apply and card undo; the Agent panel with Markdown answers, @ mentions, Ask Agent from Hierarchy and Problems, session logs.
2026-10-25 15:31:22 +03:00

74 lines
2.3 KiB
Go

package integration
import (
"net/http"
"os"
"path/filepath"
"slices"
"strings"
"testing"
"aego/app/agentui/backend"
"aego/app/edit/agentbridge"
)
const bridgeScene = "scenes/main.aescene"
// A scripted agent edits the open scene through the editor's MCP server: two Undo steps, unsaved state readable, nothing on disk.
func TestAgentBridgeEditsTheOpenScene(t *testing.T) {
e := bridgeSetup(t, agentbridge.Config{})
d, b := e.doc, e.b
events := e.turn(t, []backend.Step{
{Tool: "scene_add_node", Args: map[string]any{"file": bridgeScene, "name": "Ledge"}},
{Tool: "scene_set", Args: map[string]any{"file": bridgeScene, "node": "Ledge", "path": "transform.position", "value": "[-13, 0]"}},
{Tool: "scene_tree", Args: map[string]any{"file": bridgeScene}},
{Text: "done"},
}, e.approveAll)
var tree string
for _, e := range events {
switch {
case e.Kind == backend.EvError:
t.Fatal(e.Err)
case e.Kind == backend.EvToolResult && e.Failed:
t.Fatalf("%s failed: %s", e.Tool, e.Result)
case e.Kind == backend.EvToolResult && e.Tool == "scene_tree":
tree = e.Result
}
}
if !strings.Contains(tree, "Ledge") {
t.Fatal("scene_tree did not see the unsaved node")
}
steps := d.History.Steps(nil)
if len(steps) != 2 || !slices.ContainsFunc(steps, func(l string) bool { return strings.HasPrefix(l, "Agent: ") }) {
t.Fatalf("history %v, want two agent steps", steps)
}
sc := d.Scene()
h := sc.Find(sc.Root(), "Ledge")
if h.IsZero() || sc.Local(h).Position.X != -13 {
t.Fatal("the node is not in the editor scene")
}
disk, _ := os.ReadFile(filepath.Join(e.root, bridgeScene))
if strings.Contains(string(disk), "Ledge") {
t.Fatal("the agent wrote the open scene to disk")
}
for range 2 {
if err := d.History.Undo(); err != nil {
t.Fatal(err)
}
}
if h := sc.Find(sc.Root(), "Ledge"); !h.IsZero() && sc.IsAlive(h) {
t.Fatal("two Undo steps did not remove the node")
}
resp, err := http.Post(b.Backend().MCPURL, "application/json", strings.NewReader(`{"jsonrpc":"2.0","id":1,"method":"tools/list"}`))
if err != nil {
t.Fatal(err)
}
resp.Body.Close()
if resp.StatusCode != http.StatusUnauthorized {
t.Fatalf("a call without the token got %d", resp.StatusCode)
}
if !strings.HasPrefix(b.Backend().MCPURL, "http://127.0.0.1:") {
t.Fatalf("the server listens on %s", b.Backend().MCPURL)
}
}