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

414 lines
13 KiB
Go

package cli
import (
"context"
"errors"
"testing"
)
func newTestRegistry(t *testing.T, commands ...Command) *Registry {
t.Helper()
registry := newRegistry()
for _, command := range commands {
if err := registry.Register(command); err != nil {
t.Fatalf("Register: %v", err)
}
}
return registry
}
func TestRegistryListsCapabilities(t *testing.T) {
registry := newTestRegistry(t, testCommand("doctor.ping"))
capabilities := registry.Capabilities(context.Background(), InvokeContext{Source: "cli"})
if len(capabilities) != 1 {
t.Fatalf("len(capabilities) = %d, want 1", len(capabilities))
}
if capabilities[0].ID != "doctor.ping" {
t.Fatalf("capability id = %q", capabilities[0].ID)
}
}
func TestRegistryInvokesCommand(t *testing.T) {
registry := newTestRegistry(t, testCommand("doctor.ping"))
output, err := registry.Invoke(context.Background(), InvokeRequest{
CommandID: "doctor.ping",
Context: InvokeContext{Source: "cli"},
})
if err != nil {
t.Fatalf("Invoke: %v", err)
}
if output.Kind != OutputModePlain || output.Text != "ok" {
t.Fatalf("output = %#v", output)
}
}
func TestRegistryReturnsCommandNotFound(t *testing.T) {
registry := newTestRegistry(t, testCommand("doctor.ping"))
_, err := registry.Invoke(context.Background(), InvokeRequest{CommandID: "missing"})
if !errors.Is(err, ErrCommandNotFound) {
t.Fatalf("err = %v, want ErrCommandNotFound", err)
}
}
func TestRegistryRejectsDuplicateCommandID(t *testing.T) {
registry := newRegistry()
_ = registry.Register(testCommand("doctor.ping"))
err := registry.Register(testCommand("doctor.ping"))
if !errors.Is(err, ErrInvalidCommand) {
t.Fatalf("err = %v, want ErrInvalidCommand", err)
}
}
type testProvider struct {
appID string
commands []Command
}
func (p testProvider) AppID() string {
return p.appID
}
func (p testProvider) Commands() []Command {
return p.commands
}
func TestRegistryFromProviders(t *testing.T) {
registry, err := NewRegistryFromProviders(testProvider{
appID: "diagnostics",
commands: []Command{testCommand("diagnostics.doctor.ping")},
})
if err != nil {
t.Fatalf("NewRegistryFromProviders: %v", err)
}
capabilities := registry.Capabilities(context.Background(), InvokeContext{Source: "cli"})
if len(capabilities) != 1 || capabilities[0].ID != "diagnostics.doctor.ping" {
t.Fatalf("capabilities = %#v", capabilities)
}
}
func TestRegistryCapabilitiesKeepRegistrationOrder(t *testing.T) {
registry := newTestRegistry(
t,
testCommandWithPath("diagnostics.second", []string{"second"}),
testCommandWithPath("diagnostics.first", []string{"first"}),
)
capabilities := registry.Capabilities(context.Background(), InvokeContext{Source: "cli"})
if len(capabilities) != 2 {
t.Fatalf("len(capabilities) = %d, want 2", len(capabilities))
}
if capabilities[0].ID != "diagnostics.second" || capabilities[1].ID != "diagnostics.first" {
t.Fatalf("capabilities = %#v", capabilities)
}
}
func TestRegistryProviderCapabilityFilterGovernsDiscoveryAndInvocation(t *testing.T) {
provider := &filteringTestProvider{
testProvider: testProvider{
appID: "diagnostics",
commands: []Command{
testCommandWithPath("diagnostics.hidden", []string{"hidden"}),
testCommandWithPath("diagnostics.second", []string{"second"}),
testCommandWithPath("diagnostics.first", []string{"first"}),
},
},
visibleIDs: map[string]bool{
"diagnostics.second": true,
"diagnostics.first": true,
},
}
registry, err := NewRegistryFromProviders(provider)
if err != nil {
t.Fatalf("NewRegistryFromProviders: %v", err)
}
registry.AppCommands = fakeDynamicCommandRegistry{
capabilities: []Capability{{
ID: "dynamic.app.run",
Path: []string{"app", "run"},
Summary: "Run dynamic app command",
Source: CapabilitySource{Kind: CapabilitySourceApp, AppID: "dynamic-app"},
}},
}
capabilities := registry.Capabilities(context.Background(), InvokeContext{Source: "cli", WorkspaceID: "ws-1"})
if got, want := capabilityIDs(capabilities), []string{"diagnostics.second", "diagnostics.first", "dynamic.app.run"}; !stringSlicesEqual(got, want) {
t.Fatalf("capability ids = %#v, want %#v", got, want)
}
if len(provider.contexts) != 1 || provider.contexts[0].WorkspaceID != "ws-1" {
t.Fatalf("filter contexts = %#v, want workspace ws-1", provider.contexts)
}
_, err = registry.Invoke(context.Background(), InvokeRequest{
CommandID: "diagnostics.hidden",
Context: InvokeContext{
Source: "connector",
WorkspaceID: "ws-1",
SkipCapabilityFilters: true,
},
})
if !errors.Is(err, ErrCommandNotFound) {
t.Fatalf("Invoke hidden command error = %v, want ErrCommandNotFound", err)
}
if got := provider.contexts[len(provider.contexts)-1]; got.SkipCapabilityFilters {
t.Fatalf("invocation filter context = %#v, want skip disabled", got)
}
}
func TestRegistryCapabilitiesCanSkipProviderFilters(t *testing.T) {
provider := &filteringTestProvider{
testProvider: testProvider{
appID: "diagnostics",
commands: []Command{
testCommandWithPath("diagnostics.hidden", []string{"hidden"}),
testCommandWithPath("diagnostics.visible", []string{"visible"}),
},
},
visibleIDs: map[string]bool{
"diagnostics.visible": true,
},
}
registry, err := NewRegistryFromProviders(provider)
if err != nil {
t.Fatalf("NewRegistryFromProviders: %v", err)
}
capabilities := registry.Capabilities(context.Background(), InvokeContext{
Source: "cli",
SkipCapabilityFilters: true,
WorkspaceID: "ws-1",
})
if got, want := capabilityIDs(capabilities), []string{"diagnostics.hidden", "diagnostics.visible"}; !stringSlicesEqual(got, want) {
t.Fatalf("capability ids = %#v, want %#v", got, want)
}
if len(provider.contexts) != 0 {
t.Fatalf("filter contexts = %#v, want none", provider.contexts)
}
}
func TestRegistryHidesIntegrationCapabilitiesFromDefaultList(t *testing.T) {
registry := newTestRegistry(
t,
testCommandWithPath("diagnostics.visible", []string{"visible"}),
Command{
Capability: Capability{
ID: "diagnostics.internal",
Path: []string{"internal"},
Summary: "Run internal command",
Visibility: CapabilityVisibilityIntegration,
Output: CapabilityOutput{
DefaultMode: OutputModePlain,
JSON: true,
},
},
Handler: func(context.Context, InvokeRequest) (CommandOutput, error) {
return CommandOutput{
Kind: OutputModePlain,
Text: "internal ok",
}, nil
},
},
)
capabilities := registry.Capabilities(context.Background(), InvokeContext{Source: "cli"})
if got, want := capabilityIDs(capabilities), []string{"diagnostics.visible"}; !stringSlicesEqual(got, want) {
t.Fatalf("capability ids = %#v, want %#v", got, want)
}
capabilities = registry.Capabilities(context.Background(), InvokeContext{
Source: "cli",
IncludeIntegrationCapabilities: true,
})
if got, want := capabilityIDs(capabilities), []string{"diagnostics.visible", "diagnostics.internal"}; !stringSlicesEqual(got, want) {
t.Fatalf("capability ids with integration = %#v, want %#v", got, want)
}
output, err := registry.Invoke(context.Background(), InvokeRequest{CommandID: "diagnostics.internal"})
if err != nil {
t.Fatalf("Invoke integration command: %v", err)
}
if output.Text != "internal ok" {
t.Fatalf("output = %#v", output)
}
}
func TestRegistryAgentSessionProjectionGovernsDiscoveryAndInvocation(t *testing.T) {
read := testCommandWithPath("issue-manager.issue.get", []string{"issue", "get"})
mutate := testCommandWithPath("issue-manager.issue.update", []string{"issue", "update"})
agentStart := testCommandWithPath("agent-context.agent.start", []string{"agent", "start"})
verdict := testCommandWithPath(
"tutti-goal-review.goal-review.verdict",
[]string{"goal-review", "verdict"},
)
verdict.Capability.Visibility = CapabilityVisibilityIntegration
registry := newTestRegistry(t, read, mutate, agentStart, verdict)
dynamicInvoked := false
registry.AppCommands = fakeDynamicCommandRegistry{
capabilities: []Capability{{
ID: "workspace-app.mutate",
Path: []string{"app", "mutate"},
Summary: "Mutate app state",
Source: CapabilitySource{
Kind: CapabilitySourceApp,
},
}},
invoked: &dynamicInvoked,
}
registry.AgentSessionCapabilities = staticAgentSessionCapabilityResolver{
projection: AgentSessionCapabilityProjection{
AllowedIDs: []string{
"issue-manager.issue.get",
"tutti-goal-review.goal-review.verdict",
},
IncludeIntegrationIDs: []string{"tutti-goal-review.goal-review.verdict"},
ExcludeIDs: []string{"issue-manager.issue.update"},
},
}
invokeContext := InvokeContext{
Source: "cli", WorkspaceID: "workspace-1",
AgentSessionID: "review-session-1",
}
capabilities := registry.Capabilities(context.Background(), invokeContext)
if got, want := capabilityIDs(capabilities), []string{
"issue-manager.issue.get",
"tutti-goal-review.goal-review.verdict",
}; !stringSlicesEqual(got, want) {
t.Fatalf("reviewer capability ids = %#v, want %#v", got, want)
}
if _, err := registry.Invoke(context.Background(), InvokeRequest{
CommandID: "issue-manager.issue.update",
Context: invokeContext,
}); !errors.Is(err, ErrCommandNotFound) {
t.Fatalf("excluded reviewer invocation error = %v, want ErrCommandNotFound", err)
}
if _, err := registry.Invoke(context.Background(), InvokeRequest{
CommandID: "agent-context.agent.start",
Context: invokeContext,
}); !errors.Is(err, ErrCommandNotFound) {
t.Fatalf("non-allowlisted reviewer invocation error = %v, want ErrCommandNotFound", err)
}
if _, err := registry.Invoke(context.Background(), InvokeRequest{
CommandID: "workspace-app.mutate",
Context: invokeContext,
}); !errors.Is(err, ErrCommandNotFound) || dynamicInvoked {
t.Fatalf(
"dynamic reviewer invocation error/invoked = %v/%v, want rejected before handler",
err, dynamicInvoked,
)
}
if _, err := registry.Invoke(context.Background(), InvokeRequest{
CommandID: "tutti-goal-review.goal-review.verdict",
Context: invokeContext,
}); err != nil {
t.Fatalf("included reviewer verdict invocation error = %v", err)
}
}
func TestRegistryAgentSessionProjectionFailsClosedWhenUnavailable(t *testing.T) {
registry := newTestRegistry(t, testCommand("doctor.ping"))
registry.AgentSessionCapabilities = staticAgentSessionCapabilityResolver{
err: errors.New("session projection unavailable"),
}
invokeContext := InvokeContext{
Source: "cli", WorkspaceID: "workspace-1",
AgentSessionID: "review-session-1",
}
if capabilities := registry.Capabilities(context.Background(), invokeContext); len(capabilities) != 0 {
t.Fatalf("capabilities = %#v, want fail-closed empty list", capabilities)
}
if _, err := registry.Invoke(context.Background(), InvokeRequest{
CommandID: "doctor.ping",
Context: invokeContext,
}); !errors.Is(err, ErrServiceUnavailable) {
t.Fatalf("Invoke() error = %v, want ErrServiceUnavailable", err)
}
}
type staticAgentSessionCapabilityResolver struct {
projection AgentSessionCapabilityProjection
err error
}
func (resolver staticAgentSessionCapabilityResolver) ResolveAgentSessionCapabilityProjection(
context.Context,
string,
string,
) (AgentSessionCapabilityProjection, error) {
return resolver.projection, resolver.err
}
type filteringTestProvider struct {
testProvider
visibleIDs map[string]bool
contexts []InvokeContext
}
func (p *filteringTestProvider) FilterCapabilities(_ context.Context, invokeContext InvokeContext, capabilities []Capability) []Capability {
p.contexts = append(p.contexts, invokeContext)
result := make([]Capability, 0, len(capabilities))
for _, capability := range capabilities {
if p.visibleIDs[capability.ID] {
result = append(result, capability)
}
}
return result
}
type fakeDynamicCommandRegistry struct {
capabilities []Capability
invoked *bool
}
func (f fakeDynamicCommandRegistry) Capabilities(context.Context, InvokeContext) []Capability {
return append([]Capability(nil), f.capabilities...)
}
func (f fakeDynamicCommandRegistry) Invoke(context.Context, InvokeRequest) (CommandOutput, error) {
if f.invoked != nil {
*f.invoked = true
}
return CommandOutput{Kind: OutputModePlain, Text: "dynamic invoked"}, nil
}
func capabilityIDs(capabilities []Capability) []string {
ids := make([]string, 0, len(capabilities))
for _, capability := range capabilities {
ids = append(ids, capability.ID)
}
return ids
}
func stringSlicesEqual(left []string, right []string) bool {
if len(left) != len(right) {
return false
}
for index := range left {
if left[index] != right[index] {
return false
}
}
return true
}
func testCommand(id string) Command {
return testCommandWithPath(id, []string{"doctor", "ping"})
}
func testCommandWithPath(id string, path []string) Command {
return Command{
Capability: Capability{
ID: id,
Path: path,
Summary: "Check CLI command routing",
Output: CapabilityOutput{
DefaultMode: OutputModePlain,
JSON: true,
},
},
Handler: func(context.Context, InvokeRequest) (CommandOutput, error) {
return CommandOutput{
Kind: OutputModePlain,
Text: "ok",
}, nil
},
}
}