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
363 lines
12 KiB
Go
363 lines
12 KiB
Go
package agentsessionstore
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"io"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strconv"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestReportSessionStateIncludesOptionalAgentTargetAndDeviceMetadata(t *testing.T) {
|
|
var got map[string]any
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if err := json.NewDecoder(r.Body).Decode(&got); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
_, _ = w.Write([]byte(`{"accepted":true}`))
|
|
}))
|
|
defer server.Close()
|
|
|
|
client := NewClient(Config{BaseURL: server.URL, HTTPClient: server.Client()})
|
|
_, err := client.ReportSessionState(context.Background(), ReportSessionStateInput{
|
|
WorkspaceID: "ws-1",
|
|
AgentSessionID: "session-1",
|
|
AgentTargetID: "target-1",
|
|
DeviceID: "device-1",
|
|
SessionOrigin: WorkspaceAgentSessionOriginRuntime,
|
|
Source: EventSource{
|
|
Provider: "codex",
|
|
AgentID: "session-1",
|
|
SessionOrigin: WorkspaceAgentSessionOriginRuntime,
|
|
},
|
|
State: WorkspaceAgentSessionStateUpdate{LifecycleStatus: "active"},
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got["agentTargetId"] != "target-1" || got["deviceId"] != "device-1" {
|
|
t.Fatalf("request metadata = %#v, want top-level agentTargetId/deviceId", got)
|
|
}
|
|
source, ok := got["source"].(map[string]any)
|
|
if !ok || source["agentTargetId"] != "target-1" || source["deviceId"] != "device-1" {
|
|
t.Fatalf("source = %#v, want propagated metadata", got["source"])
|
|
}
|
|
state, ok := got["state"].(map[string]any)
|
|
if !ok || state["agentTargetId"] != "target-1" || state["deviceId"] != "device-1" {
|
|
t.Fatalf("state = %#v, want propagated metadata", got["state"])
|
|
}
|
|
}
|
|
|
|
func TestReportSessionStateOmitsMetadataKeysWhenUnset(t *testing.T) {
|
|
var raw map[string]json.RawMessage
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if err := json.NewDecoder(r.Body).Decode(&raw); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
_, _ = w.Write([]byte(`{"accepted":true}`))
|
|
}))
|
|
defer server.Close()
|
|
|
|
client := NewClient(Config{BaseURL: server.URL, HTTPClient: server.Client()})
|
|
_, err := client.ReportSessionState(context.Background(), ReportSessionStateInput{
|
|
WorkspaceID: "ws-1",
|
|
AgentSessionID: "session-1",
|
|
SessionOrigin: WorkspaceAgentSessionOriginRuntime,
|
|
Source: EventSource{
|
|
Provider: "codex",
|
|
AgentID: "session-1",
|
|
SessionOrigin: WorkspaceAgentSessionOriginRuntime,
|
|
},
|
|
State: WorkspaceAgentSessionStateUpdate{LifecycleStatus: "active"},
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, key := range []string{"agentTargetId", "deviceId"} {
|
|
if _, present := raw[key]; present {
|
|
t.Fatalf("request unexpectedly contains %q: %s", key, raw[key])
|
|
}
|
|
}
|
|
var source map[string]json.RawMessage
|
|
if err := json.Unmarshal(raw["source"], &source); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, present := source["deviceId"]; present {
|
|
t.Fatalf("source unexpectedly contains deviceId: %s", raw["source"])
|
|
}
|
|
}
|
|
|
|
func TestReportSessionStateDerivesTopLevelMetadataFromSourceAndState(t *testing.T) {
|
|
var got map[string]any
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if err := json.NewDecoder(r.Body).Decode(&got); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
_, _ = w.Write([]byte(`{"accepted":true}`))
|
|
}))
|
|
defer server.Close()
|
|
|
|
client := NewClient(Config{BaseURL: server.URL, HTTPClient: server.Client()})
|
|
// The adapter path carries metadata only inside Source/State; the
|
|
// explicit input fields stay empty. Top-level request metadata must still
|
|
// be populated so controlplanes keying off it get scoped activity.
|
|
_, err := client.ReportSessionState(context.Background(), ReportSessionStateInput{
|
|
WorkspaceID: "ws-1",
|
|
AgentSessionID: "session-1",
|
|
SessionOrigin: WorkspaceAgentSessionOriginRuntime,
|
|
Source: EventSource{
|
|
Provider: "codex",
|
|
AgentTargetID: "target-1",
|
|
DeviceID: "device-1",
|
|
SessionOrigin: WorkspaceAgentSessionOriginRuntime,
|
|
},
|
|
State: WorkspaceAgentSessionStateUpdate{LifecycleStatus: "active"},
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got["agentTargetId"] != "target-1" || got["deviceId"] != "device-1" {
|
|
t.Fatalf("request metadata = %#v, want top-level metadata derived from source", got)
|
|
}
|
|
state, ok := got["state"].(map[string]any)
|
|
if !ok || state["agentTargetId"] != "target-1" || state["deviceId"] != "device-1" {
|
|
t.Fatalf("state = %#v, want metadata filled from source", got["state"])
|
|
}
|
|
}
|
|
|
|
func TestReportSessionMessagesDerivesTopLevelMetadataFromSource(t *testing.T) {
|
|
var got map[string]any
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if err := json.NewDecoder(r.Body).Decode(&got); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
_, _ = w.Write([]byte(`{"acceptedCount":1}`))
|
|
}))
|
|
defer server.Close()
|
|
|
|
client := NewClient(Config{BaseURL: server.URL, HTTPClient: server.Client()})
|
|
_, err := client.ReportSessionMessages(context.Background(), ReportSessionMessagesInput{
|
|
WorkspaceID: "ws-1",
|
|
AgentSessionID: "session-1",
|
|
SessionOrigin: WorkspaceAgentSessionOriginRuntime,
|
|
Source: EventSource{
|
|
Provider: "codex",
|
|
AgentTargetID: "target-1",
|
|
DeviceID: "device-1",
|
|
SessionOrigin: WorkspaceAgentSessionOriginRuntime,
|
|
},
|
|
Updates: []WorkspaceAgentSessionMessageUpdate{{
|
|
MessageID: "message-1",
|
|
TurnID: "turn-1",
|
|
Role: "assistant",
|
|
Kind: "text",
|
|
}},
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got["agentTargetId"] != "target-1" || got["deviceId"] != "device-1" {
|
|
t.Fatalf("request metadata = %#v, want top-level metadata derived from source", got)
|
|
}
|
|
}
|
|
|
|
func TestReportSessionStateRequestBodyIsByteIdenticalWhenMetadataUnset(t *testing.T) {
|
|
var raw []byte
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
body, err := io.ReadAll(r.Body)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
raw = body
|
|
_, _ = w.Write([]byte(`{"accepted":true}`))
|
|
}))
|
|
defer server.Close()
|
|
|
|
client := NewClient(Config{BaseURL: server.URL, HTTPClient: server.Client()})
|
|
_, err := client.ReportSessionState(context.Background(), ReportSessionStateInput{
|
|
WorkspaceID: "ws-1",
|
|
AgentSessionID: "session-1",
|
|
SessionOrigin: WorkspaceAgentSessionOriginRuntime,
|
|
Source: EventSource{Provider: "codex"},
|
|
State: WorkspaceAgentSessionStateUpdate{LifecycleStatus: "active"},
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// Golden body: exactly what this request serialized to before the
|
|
// optional AgentTargetID/DeviceID inputs existed. Guards the hard
|
|
// constraint that unset metadata leaves the wire byte-for-byte unchanged.
|
|
want := `{"roomId":"ws-1","agentSessionId":"session-1",` +
|
|
`"sessionOrigin":"WORKSPACE_AGENT_SESSION_ORIGIN_RUNTIME",` +
|
|
`"source":{"provider":"codex"},"state":{"lifecycleStatus":"active"}}`
|
|
if string(raw) != want {
|
|
t.Fatalf("request body = %s, want %s", raw, want)
|
|
}
|
|
}
|
|
|
|
func TestReportSessionMessagesSplitBatchesPreserveMetadata(t *testing.T) {
|
|
rawText := strings.Repeat("x", maxUpstreamToolPayloadStringBytes+512)
|
|
var requestCount int
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
var got reportSessionMessagesRequest
|
|
if err := json.NewDecoder(r.Body).Decode(&got); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
requestCount++
|
|
if got.AgentTargetID != "target-1" || got.DeviceID != "device-1" {
|
|
t.Fatalf("batch %d metadata = %q/%q, want target-1/device-1", requestCount, got.AgentTargetID, got.DeviceID)
|
|
}
|
|
_, _ = w.Write([]byte(`{"acceptedCount":` + strconv.Itoa(len(got.Updates)) + `}`))
|
|
}))
|
|
defer server.Close()
|
|
|
|
updates := make([]WorkspaceAgentSessionMessageUpdate, 0, 80)
|
|
for i := 0; i < 80; i++ {
|
|
updates = append(updates, WorkspaceAgentSessionMessageUpdate{
|
|
MessageID: "message-" + strconv.Itoa(i),
|
|
TurnID: "turn-1",
|
|
Role: "assistant",
|
|
Kind: "tool_call",
|
|
Payload: map[string]any{
|
|
"input": map[string]any{"stdout": rawText},
|
|
"output": map[string]any{"stdout": rawText},
|
|
},
|
|
})
|
|
}
|
|
|
|
client := NewClient(Config{BaseURL: server.URL, HTTPClient: server.Client()})
|
|
if _, err := client.ReportSessionMessages(context.Background(), ReportSessionMessagesInput{
|
|
WorkspaceID: "ws-1",
|
|
AgentSessionID: "session-1",
|
|
AgentTargetID: "target-1",
|
|
DeviceID: "device-1",
|
|
Updates: updates,
|
|
}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if requestCount < 2 {
|
|
t.Fatalf("requestCount = %d, want oversized request split into multiple uploads", requestCount)
|
|
}
|
|
}
|
|
|
|
func TestReportSessionMessagesIncludesOptionalMetadata(t *testing.T) {
|
|
var got map[string]any
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if err := json.NewDecoder(r.Body).Decode(&got); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
_, _ = w.Write([]byte(`{"acceptedCount":1}`))
|
|
}))
|
|
defer server.Close()
|
|
|
|
client := NewClient(Config{BaseURL: server.URL, HTTPClient: server.Client()})
|
|
_, err := client.ReportSessionMessages(context.Background(), ReportSessionMessagesInput{
|
|
WorkspaceID: "ws-1",
|
|
AgentSessionID: "session-1",
|
|
AgentTargetID: "target-1",
|
|
DeviceID: "device-1",
|
|
SessionOrigin: WorkspaceAgentSessionOriginRuntime,
|
|
Source: EventSource{
|
|
Provider: "codex",
|
|
AgentID: "session-1",
|
|
SessionOrigin: WorkspaceAgentSessionOriginRuntime,
|
|
},
|
|
Updates: []WorkspaceAgentSessionMessageUpdate{{
|
|
MessageID: "message-1",
|
|
TurnID: "turn-1",
|
|
Role: "assistant",
|
|
Kind: "text",
|
|
}},
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got["agentTargetId"] != "target-1" || got["deviceId"] != "device-1" {
|
|
t.Fatalf("request metadata = %#v, want top-level agentTargetId/deviceId", got)
|
|
}
|
|
source, ok := got["source"].(map[string]any)
|
|
if !ok || source["deviceId"] != "device-1" {
|
|
t.Fatalf("source = %#v, want propagated deviceId", got["source"])
|
|
}
|
|
}
|
|
|
|
func TestListSessionMessagesSendsDeviceIDQueryOnlyWhenSet(t *testing.T) {
|
|
var queries []string
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
queries = append(queries, r.URL.Query().Get("device_id"))
|
|
_, _ = w.Write([]byte(`{"messages":[]}`))
|
|
}))
|
|
defer server.Close()
|
|
|
|
client := NewClient(Config{BaseURL: server.URL, HTTPClient: server.Client()})
|
|
if _, err := client.ListSessionMessages(context.Background(), ListSessionMessagesInput{
|
|
WorkspaceID: "ws-1",
|
|
AgentSessionID: "session-1",
|
|
DeviceID: "device-1",
|
|
}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := client.ListSessionMessages(context.Background(), ListSessionMessagesInput{
|
|
WorkspaceID: "ws-1",
|
|
AgentSessionID: "session-1",
|
|
}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(queries) != 2 || queries[0] != "device-1" || queries[1] != "" {
|
|
t.Fatalf("device_id queries = %#v, want [device-1 \"\"]", queries)
|
|
}
|
|
}
|
|
|
|
func TestListAgentsWithFilterSendsDeviceIDQuery(t *testing.T) {
|
|
var deviceID string
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
deviceID = r.URL.Query().Get("device_id")
|
|
_, _ = w.Write([]byte(`{"presences":[],"sessions":[]}`))
|
|
}))
|
|
defer server.Close()
|
|
|
|
client := NewClient(Config{BaseURL: server.URL, HTTPClient: server.Client()})
|
|
if _, err := client.ListAgentsWithFilter(context.Background(), ListAgentsInput{
|
|
WorkspaceID: "ws-1",
|
|
DeviceID: "device-1",
|
|
}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if deviceID != "device-1" {
|
|
t.Fatalf("device_id query = %q, want device-1", deviceID)
|
|
}
|
|
}
|
|
|
|
func TestWorkspaceAgentSessionUnmarshalsDeviceIDLeniently(t *testing.T) {
|
|
var camel ProviderActivitySessionProjection
|
|
if err := json.Unmarshal([]byte(`{"agentSessionId":"session-1","deviceId":"device-1"}`), &camel); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if camel.DeviceID != "device-1" {
|
|
t.Fatalf("camelCase deviceId = %q, want device-1", camel.DeviceID)
|
|
}
|
|
|
|
var snake ProviderActivitySessionProjection
|
|
if err := json.Unmarshal([]byte(`{"agent_session_id":"session-1","device_id":"device-2"}`), &snake); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if snake.DeviceID != "device-2" {
|
|
t.Fatalf("snake_case device_id = %q, want device-2", snake.DeviceID)
|
|
}
|
|
|
|
roundtrip, err := json.Marshal(ProviderActivitySessionProjection{AgentSessionID: "session-1", DeviceID: "device-1"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var decoded map[string]any
|
|
if err := json.Unmarshal(roundtrip, &decoded); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if decoded["deviceId"] != "device-1" {
|
|
t.Fatalf("marshal output = %s, want deviceId", roundtrip)
|
|
}
|
|
}
|