Files
agent-radar 2ed3415fb7
Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
2026-09-01 20:17:27 +08:00

144 lines
5.7 KiB
Go

package agentsessionstore_test
import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
agentsessionstore "github.com/tutti-os/tutti/packages/agent/daemon/activity"
canonical "github.com/tutti-os/tutti/packages/agent/store-sqlite/canonical"
)
func TestWorkspaceAgentMessageFunctionalFlow(t *testing.T) {
t.Parallel()
var acceptedActivity int
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
switch {
case r.Method == http.MethodPost && r.URL.Path == "/v1/rooms/ws-1/agents/sessions/codex-1/state":
var body struct {
State map[string]any `json:"state"`
}
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
t.Fatalf("decode state body: %v", err)
}
if len(body.State) == 0 {
t.Fatal("state body is empty")
}
acceptedActivity++
_ = json.NewEncoder(w).Encode(map[string]bool{"accepted": true})
case r.Method == http.MethodPost && r.URL.Path == "/v1/rooms/ws-1/agents/sessions/codex-1/messages":
var body struct {
Updates []map[string]any `json:"updates"`
}
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
t.Fatalf("decode messages body: %v", err)
}
acceptedActivity += len(body.Updates)
_ = json.NewEncoder(w).Encode(map[string]int{"acceptedCount": len(body.Updates)})
case r.Method == http.MethodGet && r.URL.Path == "/v1/rooms/ws-1/agents/list":
_, _ = w.Write([]byte(`{
"presences":[{"id":1,"roomId":"ws-1","userId":"user-1","provider":"codex","status":"working"}],
"sessions":[{"id":2,"agentSessionId":"codex-1","presenceId":1,"providerSessionId":"codex-1","cwd":"/workspace/ws-1","effectiveStatus":"working","sessionOrigin":"WORKSPACE_AGENT_SESSION_ORIGIN_RUNTIME"}]
}`))
case r.Method == http.MethodGet && r.URL.Path == "/v1/rooms/ws-1/agents/sessions/codex-1/messages":
if r.URL.Query().Get("after_version") != "0" && r.URL.Query().Get("after_version") != "" {
t.Fatalf("unexpected after_version = %q", r.URL.Query().Get("after_version"))
}
if r.URL.Query().Get("session_origin") != agentsessionstore.WorkspaceAgentSessionOriginRuntime {
t.Fatalf("session_origin = %q", r.URL.Query().Get("session_origin"))
}
_, _ = w.Write([]byte(`{
"messages":[
{"id":7,"agentSessionId":"agent-session-1","messageId":"msg-1","role":"assistant","kind":"text","payload":{"content":"Done."},"occurredAtUnixMs":1776934803000,"createdAtUnixMs":1776934803100,"version":7},
{"id":8,"agentSessionId":"agent-session-1","messageId":"act-1","role":"tool","kind":"tool_call","status":"running","payload":{"callId":"call-1","name":"exec_command"},"occurredAtUnixMs":1776934804000,"createdAtUnixMs":1776934804100,"version":8}
],
"latestVersion":8
}`))
default:
http.NotFound(w, r)
}
}))
defer server.Close()
client := agentsessionstore.NewClient(agentsessionstore.Config{BaseURL: server.URL, UserID: "user-1"})
reply, err := client.ReportActivity(context.Background(), agentsessionstore.ReportActivityInput{
WorkspaceID: "ws-1",
StatePatches: []agentsessionstore.WorkspaceAgentStatePatch{{
AgentSessionID: "codex-1",
Provider: "codex",
ProviderSessionID: "codex-1",
CWD: "/workspace/ws-1",
CurrentPhase: "working",
LifecycleStatus: "active",
OccurredAtUnixMS: 1776934800000,
}},
MessageUpdates: []agentsessionstore.WorkspaceAgentMessageUpdate{
{
AgentSessionID: "codex-1",
MessageID: "msg-user-1",
TurnID: "turn-1",
Role: "user",
Kind: "text",
Payload: map[string]any{"content": "Inspect README."},
OccurredAtUnixMS: 1776934801000,
},
{
AgentSessionID: "codex-1",
MessageID: "msg-assistant-1",
TurnID: "turn-1",
Role: "assistant",
Kind: "text",
Payload: map[string]any{"content": "Done."},
OccurredAtUnixMS: 1776934803000,
},
},
Source: canonical.EventSource{
SessionOrigin: agentsessionstore.WorkspaceAgentSessionOriginRuntime,
Provider: "codex",
},
})
if err != nil {
t.Fatalf("ReportActivity() error = %v", err)
}
acceptedReply := reply.AcceptedStatePatchCount + reply.AcceptedMessageUpdateCount
if acceptedReply != acceptedActivity || acceptedActivity == 0 {
t.Fatalf("accepted = reply:%d server:%d want nonzero matching count", acceptedReply, acceptedActivity)
}
poller := agentsessionstore.NewPoller(client, agentsessionstore.PollerOptions{Limit: 20})
result, err := poller.Poll(context.Background(), "ws-1", nil)
if err != nil {
t.Fatalf("Poll() error = %v", err)
}
if len(result.Snapshot.Presences) != 1 || result.Snapshot.Presences[0].Provider != "codex" {
t.Fatalf("snapshot presences = %#v", result.Snapshot.Presences)
}
messages := result.Messages["codex-1"]
if len(messages.Messages) != 2 {
t.Fatalf("messages = %#v", messages.Messages)
}
if messages.Messages[0].Kind != "text" ||
messages.Messages[0].Role != "assistant" ||
messages.Messages[0].Payload["content"] != "Done." ||
messages.Messages[0].OccurredAtUnixMS != 1776934803000 ||
messages.Messages[0].CreatedAtUnixMS != 1776934803100 {
t.Fatalf("message entry = %#v", messages.Messages[0])
}
if messages.Messages[1].Kind != "tool_call" ||
messages.Messages[1].Role != "tool" ||
messages.Messages[1].Payload["callId"] != "call-1" ||
messages.Messages[1].Payload["name"] != "exec_command" ||
messages.Messages[1].OccurredAtUnixMS != 1776934804000 ||
messages.Messages[1].CreatedAtUnixMS != 1776934804100 {
t.Fatalf("tool message entry = %#v", messages.Messages[1])
}
if result.Cursors["codex-1"].AfterVersion != 8 {
t.Fatalf("cursor = %#v", result.Cursors["codex-1"])
}
}