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

172 lines
6.3 KiB
Go

package liveprotocol
import (
"encoding/json"
"errors"
"testing"
)
func TestMessageDeltaContractRejectsSchemaDrift(t *testing.T) {
t.Parallel()
tests := map[string]string{
"missing turn identity": `{
"workspaceId":"workspace-1","agentSessionId":"session-1","eventType":"message_delta",
"data":{"workspaceId":"workspace-1","agentSessionId":"session-1","messageId":"message-1",
"role":"assistant","kind":"text","occurredAtUnixMs":1,
"content":{"operation":"set","value":"hello"}}
}`,
"append without text": `{
"workspaceId":"workspace-1","agentSessionId":"session-1","eventType":"message_delta",
"data":{"workspaceId":"workspace-1","agentSessionId":"session-1","messageId":"message-1",
"turnId":"turn-1","role":"assistant","kind":"text","occurredAtUnixMs":1,
"content":{"operation":"append_text"}}
}`,
"scalar semantics": `{
"workspaceId":"workspace-1","agentSessionId":"session-1","eventType":"message_delta",
"data":{"workspaceId":"workspace-1","agentSessionId":"session-1","messageId":"message-1",
"turnId":"turn-1","role":"assistant","kind":"text","occurredAtUnixMs":1,
"semantics":"not-an-object"}
}`,
"duplicate payload unset": `{
"workspaceId":"workspace-1","agentSessionId":"session-1","eventType":"message_delta",
"data":{"workspaceId":"workspace-1","agentSessionId":"session-1","messageId":"message-1",
"turnId":"turn-1","role":"assistant","kind":"text","occurredAtUnixMs":1,
"payloadUnset":["text","text"]}
}`,
"negative lifecycle timestamp": `{
"workspaceId":"workspace-1","agentSessionId":"session-1","eventType":"message_delta",
"data":{"workspaceId":"workspace-1","agentSessionId":"session-1","messageId":"message-1",
"turnId":"turn-1","role":"assistant","kind":"text","occurredAtUnixMs":1,
"completedAtUnixMs":-1}
}`,
}
for name, raw := range tests {
name, raw := name, raw
t.Run(name, func(t *testing.T) {
t.Parallel()
if _, err := DecodeEvent([]byte(raw)); !errors.Is(err, ErrInvalidLiveEvent) {
t.Fatalf("DecodeEvent error = %v, want ErrInvalidLiveEvent", err)
}
})
}
}
func TestRuntimeActivityUpdateContract(t *testing.T) {
t.Parallel()
event, err := NewRuntimeActivityUpdateEvent(RuntimeActivityUpdateData{
WorkspaceID: "workspace-1", AgentSessionID: "session-1",
EventType: EventTypeRuntimeActivityUpdate, State: "running", OccurredAtUnixMS: 10,
})
if err != nil {
t.Fatal(err)
}
raw, err := MarshalEvent(event)
if err != nil {
t.Fatal(err)
}
if _, err := DecodeEvent(raw); err != nil {
t.Fatal(err)
}
event.Data = []byte(`{"workspaceId":"workspace-1","agentSessionId":"session-1","eventType":"runtime_activity_update","state":"busy","occurredAtUnixMs":10}`)
if _, err := MarshalEvent(event); !errors.Is(err, ErrInvalidLiveEvent) {
t.Fatalf("invalid runtime activity error = %v, want ErrInvalidLiveEvent", err)
}
}
func TestTurnUpdateContractClosesTerminalAndActiveStates(t *testing.T) {
t.Parallel()
terminal := mustTerminalTurnUpdate(t)
var terminalData TurnUpdateData
if err := json.Unmarshal(terminal.Data, &terminalData); err != nil {
t.Fatal(err)
}
activeTurnID := terminalData.Turn.TurnID
terminalData.ActiveTurnID = &activeTurnID
if _, err := NewTurnUpdateEvent(terminalData); !errors.Is(err, ErrInvalidLiveEvent) {
t.Fatalf("settled turn with activeTurnId error = %v, want ErrInvalidLiveEvent", err)
}
terminalData.ActiveTurnID = nil
terminalData.Turn.Outcome = nil
if _, err := NewTurnUpdateEvent(terminalData); !errors.Is(err, ErrInvalidLiveEvent) {
t.Fatalf("settled turn without outcome error = %v, want ErrInvalidLiveEvent", err)
}
terminalData.Turn.Phase = "running"
terminalData.Turn.SettledAtUnixMS = nil
terminalData.ActiveTurnID = &activeTurnID
if _, err := NewTurnUpdateEvent(terminalData); err != nil {
t.Fatalf("valid active turn error = %v", err)
}
outcome := "completed"
terminalData.Turn.Outcome = &outcome
if _, err := NewTurnUpdateEvent(terminalData); !errors.Is(err, ErrInvalidLiveEvent) {
t.Fatalf("active turn with outcome error = %v, want ErrInvalidLiveEvent", err)
}
}
func TestCanonicalVariantsRejectNonObjectPayloads(t *testing.T) {
t.Parallel()
auditRaw := []byte(`{
"workspaceId":"workspace-1","agentSessionId":"session-1","eventType":"session_audit",
"data":{"workspaceId":"workspace-1","agentSessionId":"session-1","eventType":"session_audit",
"audit":{"auditId":"audit-1","role":"system","payload":"bad",
"occurredAtUnixMs":1,"version":1}}
}`)
if _, err := DecodeEvent(auditRaw); !errors.Is(err, ErrInvalidLiveEvent) {
t.Fatalf("audit payload error = %v, want ErrInvalidLiveEvent", err)
}
interactionRaw := []byte(`{
"workspaceId":"workspace-1","agentSessionId":"session-1","eventType":"interaction_update",
"data":{"workspaceId":"workspace-1","agentSessionId":"session-1","eventType":"interaction_update",
"occurredAtUnixMs":1,
"interaction":{"requestId":"request-1","agentSessionId":"session-1","turnId":"turn-1",
"kind":"approval","status":"pending","toolName":null,"input":[],
"output":null,"metadata":null,"createdAtUnixMs":1,"updatedAtUnixMs":1}}
}`)
if _, err := DecodeEvent(interactionRaw); !errors.Is(err, ErrInvalidLiveEvent) {
t.Fatalf("interaction input error = %v, want ErrInvalidLiveEvent", err)
}
}
func TestInteractionSnapshotContractTreatsEmptyArrayAsExplicitClear(t *testing.T) {
t.Parallel()
event, err := NewInteractionSnapshotEvent(InteractionSnapshotData{
WorkspaceID: "workspace-1", AgentSessionID: "session-1",
EventType: EventTypeInteractionSnapshot, OccurredAtUnixMS: 0, RootTurnID: "turn-1",
})
if err != nil {
t.Fatal(err)
}
var data InteractionSnapshotData
if err := json.Unmarshal(event.Data, &data); err != nil {
t.Fatal(err)
}
if data.Interactions == nil || len(data.Interactions) != 0 {
t.Fatalf("interactions = %#v, want explicit empty array", data.Interactions)
}
if data.RootTurnID != "turn-1" {
t.Fatalf("root turn = %q, want turn-1", data.RootTurnID)
}
}
func TestInteractionSnapshotContractRequiresRootForEmptyCollection(t *testing.T) {
t.Parallel()
_, err := NewInteractionSnapshotEvent(InteractionSnapshotData{
WorkspaceID: "workspace-1", AgentSessionID: "session-1",
EventType: EventTypeInteractionSnapshot, OccurredAtUnixMS: 0,
})
if !errors.Is(err, ErrInvalidLiveEvent) {
t.Fatalf("missing root error = %v, want ErrInvalidLiveEvent", err)
}
}