Files
tutti/services/tuttid/service/agent/activity_projection_reference_integrity_test.go
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

352 lines
13 KiB
Go

package agent
import (
"context"
"strings"
"testing"
agentsessionstore "github.com/tutti-os/tutti/packages/agent/daemon/activity"
"github.com/tutti-os/tutti/packages/agent/store-sqlite/canonical"
agenttargetbiz "github.com/tutti-os/tutti/services/tuttid/biz/agenttarget"
workspacebiz "github.com/tutti-os/tutti/services/tuttid/biz/workspace"
workspacedata "github.com/tutti-os/tutti/services/tuttid/data/workspace"
)
// fakeAgentTargetRegistry models the local agent target registry including
// the alias table a shared agent's registration record will carry (the
// owner-domain agentTargetId claimed as an alias of the local registration).
type fakeAgentTargetRegistry struct {
targets map[string]agenttargetbiz.Target
aliases map[string]string
}
func (f fakeAgentTargetRegistry) GetAgentTarget(_ context.Context, id string) (agenttargetbiz.Target, error) {
target, ok := f.targets[strings.TrimSpace(id)]
if !ok {
return agenttargetbiz.Target{}, workspacedata.ErrAgentTargetNotFound
}
return target, nil
}
func (f fakeAgentTargetRegistry) ResolveAgentTargetAlias(_ context.Context, id string) (string, bool) {
canonicalID, ok := f.aliases[strings.TrimSpace(id)]
return canonicalID, ok
}
func testSharedAgentTargetRegistry() fakeAgentTargetRegistry {
return fakeAgentTargetRegistry{
targets: map[string]agenttargetbiz.Target{
agenttargetbiz.IDLocalCodex: {
ID: agenttargetbiz.IDLocalCodex,
Provider: "codex",
LaunchRefJSON: agenttargetbiz.MustLocalCLILaunchRefJSON("codex"),
Name: "Codex",
Enabled: true,
Source: agenttargetbiz.SourceSystem,
},
"shared-agent:codex-1": {
ID: "shared-agent:codex-1",
Provider: "codex",
LaunchRefJSON: agenttargetbiz.MustLocalCLILaunchRefJSON("codex"),
Name: "Shared Codex",
Enabled: true,
Source: agenttargetbiz.SourceUser,
},
},
aliases: map[string]string{
// The shared-agent registration claims its owner-domain id.
"02fc7056aaaa4bbb8cccdddd0000eeee": "shared-agent:codex-1",
},
}
}
// TestActivityProjectionReportSessionStateEnforcesTargetReferenceIntegrity
// pins the WS0 invariant: every persisted session carries an agentTargetId
// that resolves in the local target registry, or none at all. Owner-domain
// ids that leak in from shared sessions are rewritten when a registered
// target claims them as an alias, or dropped, with the original value
// preserved only for diagnostics. Identity is decided exclusively against
// the registry — never inferred from session-carried data.
func TestActivityProjectionReportSessionStateEnforcesTargetReferenceIntegrity(t *testing.T) {
ctx := context.Background()
store := openAgentServiceSQLiteStore(t)
if err := store.Create(ctx, workspacebiz.Summary{ID: "ws-1", Name: "Workspace One"}); err != nil {
t.Fatalf("Create workspace error = %v", err)
}
projection := NewActivityProjection(store)
projection.SetAgentTargetResolver(testSharedAgentTargetRegistry())
for _, tc := range []struct {
name string
sessionID string
agentTargetID string
wantTargetID string
wantStashKey string
}{
{
name: "registered id passes through verbatim",
sessionID: "session-local",
agentTargetID: agenttargetbiz.IDLocalCodex,
wantTargetID: agenttargetbiz.IDLocalCodex,
},
{
name: "owner-domain id rewritten via registry alias",
sessionID: "session-shared",
agentTargetID: "02fc7056aaaa4bbb8cccdddd0000eeee",
wantTargetID: "shared-agent:codex-1",
wantStashKey: runtimeContextAliasedAgentTargetIDKey,
},
{
name: "unclaimed id dropped and stashed",
sessionID: "session-orphan",
agentTargetID: "02fc7056aaaa4bbb8cccdddd0000ffff",
wantTargetID: "",
wantStashKey: runtimeContextUnresolvedAgentTargetIDKey,
},
} {
t.Run(tc.name, func(t *testing.T) {
if _, err := projection.ReportSessionState(ctx, canonical.ReportSessionStateInput{
WorkspaceID: "ws-1",
AgentSessionID: tc.sessionID,
SessionOrigin: agentsessionstore.WorkspaceAgentSessionOriginRuntime,
State: canonical.WorkspaceAgentSessionStateUpdate{
AgentTargetID: tc.agentTargetID,
Provider: "codex",
CurrentPhase: "idle",
OccurredAtUnixMS: 1000,
},
}); err != nil {
t.Fatalf("ReportSessionState error = %v", err)
}
session, ok := projection.GetSession("ws-1", tc.sessionID)
if !ok {
t.Fatalf("GetSession(%q) not found", tc.sessionID)
}
if session.AgentTargetID != tc.wantTargetID {
t.Fatalf("persisted agentTargetId = %q, want %q", session.AgentTargetID, tc.wantTargetID)
}
if tc.wantStashKey == "" {
for _, key := range []string{runtimeContextAliasedAgentTargetIDKey, runtimeContextUnresolvedAgentTargetIDKey} {
if _, present := session.InternalRuntimeContext[key]; present {
t.Fatalf("unexpected diagnostic stash %q: %#v", key, session.InternalRuntimeContext)
}
}
return
}
stashed, _ := session.InternalRuntimeContext[tc.wantStashKey].(string)
if stashed != tc.agentTargetID {
t.Fatalf("runtimeContext[%q] = %q, want %q", tc.wantStashKey, stashed, tc.agentTargetID)
}
})
}
}
func TestActivityProjectionTargetDiagnosticsPreserveRuntimeContextPatchMode(t *testing.T) {
ctx := context.Background()
store := openAgentServiceSQLiteStore(t)
if err := store.Create(ctx, workspacebiz.Summary{ID: "ws-1", Name: "Workspace One"}); err != nil {
t.Fatalf("Create workspace error = %v", err)
}
projection := NewActivityProjection(store)
projection.SetAgentTargetResolver(testSharedAgentTargetRegistry())
for _, tc := range []struct {
sessionID string
agentTargetID string
wantTargetID string
wantStashKey string
}{
{"session-patch-alias", "02fc7056aaaa4bbb8cccdddd0000eeee", "shared-agent:codex-1", runtimeContextAliasedAgentTargetIDKey},
{"session-patch-unresolved", "02fc7056aaaa4bbb8cccdddd0000ffff", "", runtimeContextUnresolvedAgentTargetIDKey},
} {
if _, err := projection.ReportSessionState(ctx, canonical.ReportSessionStateInput{
WorkspaceID: "ws-1",
AgentSessionID: tc.sessionID,
SessionOrigin: agentsessionstore.WorkspaceAgentSessionOriginRuntime,
State: canonical.WorkspaceAgentSessionStateUpdate{
AgentTargetID: tc.agentTargetID,
Provider: "codex",
RuntimeContextPatch: &canonical.RuntimeContextPatch{
Set: map[string]any{"providerState": "ready"},
},
},
}); err != nil {
t.Fatalf("ReportSessionState(%s) error = %v", tc.sessionID, err)
}
session, ok := projection.GetSession("ws-1", tc.sessionID)
if !ok {
t.Fatalf("GetSession(%s) not found", tc.sessionID)
}
if session.AgentTargetID != tc.wantTargetID {
t.Fatalf("agentTargetId = %q, want %q", session.AgentTargetID, tc.wantTargetID)
}
if session.InternalRuntimeContext["providerState"] != "ready" || session.InternalRuntimeContext[tc.wantStashKey] != tc.agentTargetID {
t.Fatalf("runtime context = %#v", session.InternalRuntimeContext)
}
}
}
func TestActivityProjectionTargetOnlyDiagnosticPreservesExistingRuntimeContext(t *testing.T) {
ctx := context.Background()
store := openAgentServiceSQLiteStore(t)
if err := store.Create(ctx, workspacebiz.Summary{ID: "ws-1", Name: "Workspace One"}); err != nil {
t.Fatalf("Create workspace error = %v", err)
}
projection := NewActivityProjection(store)
projection.SetAgentTargetResolver(testSharedAgentTargetRegistry())
for _, tc := range []struct {
sessionID string
agentTargetID string
wantTargetID string
wantStashKey string
}{
{"session-target-only-alias", "02fc7056aaaa4bbb8cccdddd0000eeee", "shared-agent:codex-1", runtimeContextAliasedAgentTargetIDKey},
{"session-target-only-unresolved", "02fc7056aaaa4bbb8cccdddd0000ffff", agenttargetbiz.IDLocalCodex, runtimeContextUnresolvedAgentTargetIDKey},
} {
if _, err := projection.ReportSessionState(ctx, canonical.ReportSessionStateInput{
WorkspaceID: "ws-1",
AgentSessionID: tc.sessionID,
SessionOrigin: agentsessionstore.WorkspaceAgentSessionOriginRuntime,
State: canonical.WorkspaceAgentSessionStateUpdate{
AgentTargetID: agenttargetbiz.IDLocalCodex,
Provider: "codex",
RuntimeContext: map[string]any{"providerState": "ready"},
OccurredAtUnixMS: 1,
},
}); err != nil {
t.Fatalf("seed ReportSessionState(%s) error = %v", tc.sessionID, err)
}
if _, err := projection.ReportSessionState(ctx, canonical.ReportSessionStateInput{
WorkspaceID: "ws-1",
AgentSessionID: tc.sessionID,
SessionOrigin: agentsessionstore.WorkspaceAgentSessionOriginRuntime,
State: canonical.WorkspaceAgentSessionStateUpdate{
AgentTargetID: tc.agentTargetID,
OccurredAtUnixMS: 2,
},
}); err != nil {
t.Fatalf("target-only ReportSessionState(%s) error = %v", tc.sessionID, err)
}
session, ok := projection.GetSession("ws-1", tc.sessionID)
if !ok {
t.Fatalf("GetSession(%s) not found", tc.sessionID)
}
if session.AgentTargetID != tc.wantTargetID {
t.Fatalf("agentTargetId = %q, want %q", session.AgentTargetID, tc.wantTargetID)
}
if session.InternalRuntimeContext["providerState"] != "ready" || session.InternalRuntimeContext[tc.wantStashKey] != tc.agentTargetID {
t.Fatalf("runtime context = %#v", session.InternalRuntimeContext)
}
}
}
// TestActivityProjectionProjectsLegacyOwnerDomainTargetIDAtReadTime covers the
// existing-data strategy: rows persisted before the ingestion boundary was
// hardened are re-canonicalized at read time (non-destructively), so a legacy
// owner-domain agentTargetId never reaches the projection surface.
func TestActivityProjectionProjectsLegacyOwnerDomainTargetIDAtReadTime(t *testing.T) {
ctx := context.Background()
store := openAgentServiceSQLiteStore(t)
if err := store.Create(ctx, workspacebiz.Summary{ID: "ws-1", Name: "Workspace One"}); err != nil {
t.Fatalf("Create workspace error = %v", err)
}
// Seed legacy rows through a resolver-less projection: the owner-domain
// ids land verbatim, exactly as they would have before this change.
legacy := NewActivityProjection(store)
const aliasedOwnerDomainID = "02fc7056aaaa4bbb8cccdddd0000eeee"
const orphanOwnerDomainID = "02fc7056aaaa4bbb8cccdddd0000ffff"
for sessionID, ownerDomainID := range map[string]string{
"session-legacy-aliased": aliasedOwnerDomainID,
"session-legacy-orphan": orphanOwnerDomainID,
} {
if _, err := legacy.ReportSessionState(ctx, canonical.ReportSessionStateInput{
WorkspaceID: "ws-1",
AgentSessionID: sessionID,
SessionOrigin: agentsessionstore.WorkspaceAgentSessionOriginRuntime,
State: canonical.WorkspaceAgentSessionStateUpdate{
AgentTargetID: ownerDomainID,
Provider: "codex",
CurrentPhase: "idle",
OccurredAtUnixMS: 1000,
},
}); err != nil {
t.Fatalf("legacy ReportSessionState(%s) error = %v", sessionID, err)
}
}
reader := NewActivityProjection(store)
reader.SetAgentTargetResolver(testSharedAgentTargetRegistry())
aliased, ok := reader.GetSession("ws-1", "session-legacy-aliased")
if !ok {
t.Fatalf("GetSession(aliased) not found")
}
if aliased.AgentTargetID != "shared-agent:codex-1" {
t.Fatalf("projected agentTargetId = %q, want shared-agent:codex-1", aliased.AgentTargetID)
}
orphan, ok := reader.GetSession("ws-1", "session-legacy-orphan")
if !ok {
t.Fatalf("GetSession(orphan) not found")
}
if orphan.AgentTargetID != "" {
t.Fatalf("projected agentTargetId = %q, want empty for unclaimed legacy id", orphan.AgentTargetID)
}
sessions, ok := reader.ListSessions("ws-1")
if !ok {
t.Fatalf("ListSessions not found")
}
for _, session := range sessions {
if session.AgentTargetID == aliasedOwnerDomainID || session.AgentTargetID == orphanOwnerDomainID {
t.Fatalf("ListSessions projected leaked owner-domain agentTargetId for %q", session.ID)
}
}
}
// TestActivityProjectionReportSessionStatePreservesIDsWithoutResolver keeps the
// projection backward compatible: when no target registry is wired it cannot
// validate, so ids are persisted untouched.
func TestActivityProjectionReportSessionStatePreservesIDsWithoutResolver(t *testing.T) {
ctx := context.Background()
store := openAgentServiceSQLiteStore(t)
if err := store.Create(ctx, workspacebiz.Summary{ID: "ws-1", Name: "Workspace One"}); err != nil {
t.Fatalf("Create workspace error = %v", err)
}
projection := NewActivityProjection(store)
const rawID = "02fc7056aaaa4bbb8cccdddd0000eeee"
if _, err := projection.ReportSessionState(ctx, canonical.ReportSessionStateInput{
WorkspaceID: "ws-1",
AgentSessionID: "session-no-resolver",
SessionOrigin: agentsessionstore.WorkspaceAgentSessionOriginRuntime,
State: canonical.WorkspaceAgentSessionStateUpdate{
AgentTargetID: rawID,
Provider: "codex",
CurrentPhase: "idle",
OccurredAtUnixMS: 1000,
},
}); err != nil {
t.Fatalf("ReportSessionState error = %v", err)
}
session, ok := projection.GetSession("ws-1", "session-no-resolver")
if !ok {
t.Fatalf("GetSession not found")
}
if session.AgentTargetID != rawID {
t.Fatalf("persisted agentTargetId = %q, want %q (unchanged)", session.AgentTargetID, rawID)
}
for _, key := range []string{runtimeContextAliasedAgentTargetIDKey, runtimeContextUnresolvedAgentTargetIDKey} {
if _, present := session.InternalRuntimeContext[key]; present {
t.Fatalf("unexpected diagnostic stash %q without a resolver: %#v", key, session.InternalRuntimeContext)
}
}
}