4257307063
Co-authored-by: Kandev Agent <agent@kandev.dev>
125 lines
4.0 KiB
Go
125 lines
4.0 KiB
Go
package plugins
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"github.com/kandev/kandev/internal/common/logger"
|
|
"github.com/kandev/kandev/internal/mcp/plugintools"
|
|
"github.com/kandev/kandev/internal/plugins/manifest"
|
|
"github.com/kandev/kandev/internal/plugins/store"
|
|
"github.com/stretchr/testify/require"
|
|
"go.uber.org/zap"
|
|
"go.uber.org/zap/zaptest/observer"
|
|
)
|
|
|
|
func TestAgentToolCatalogIncludesActiveTools(t *testing.T) {
|
|
svc, _, _ := newTestService(t)
|
|
readOnly := true
|
|
destructive := false
|
|
svc.registry.Add(&store.Record{
|
|
Manifest: manifest.Manifest{
|
|
ID: "task-tags.v1",
|
|
DisplayName: "Task tags",
|
|
AgentTools: []manifest.AgentTool{{
|
|
Name: "add_tag",
|
|
Description: "Add a tag to a task",
|
|
Surfaces: []string{manifest.AgentToolSurfaceKanban},
|
|
InputSchema: map[string]any{
|
|
"type": "object",
|
|
"properties": map[string]any{"tag": map[string]any{"type": "string"}},
|
|
"required": []any{"tag"},
|
|
},
|
|
Annotations: manifest.AgentToolAnnotations{ReadOnlyHint: &readOnly, DestructiveHint: &destructive},
|
|
}},
|
|
},
|
|
Status: store.StatusActive,
|
|
})
|
|
svc.registry.Add(&store.Record{
|
|
Manifest: manifest.Manifest{
|
|
ID: "disabled.v1",
|
|
AgentTools: []manifest.AgentTool{{
|
|
Name: "hidden",
|
|
Description: "Hidden tool",
|
|
Surfaces: []string{manifest.AgentToolSurfaceKanban},
|
|
InputSchema: map[string]any{"type": "object"},
|
|
}},
|
|
},
|
|
Status: store.StatusDisabled,
|
|
})
|
|
|
|
snapshot, err := svc.AgentToolCatalog()
|
|
if err != nil {
|
|
t.Fatalf("AgentToolCatalog() error: %v", err)
|
|
}
|
|
if snapshot.Generation == "" || snapshot.Revision == 0 {
|
|
t.Fatalf("snapshot identity = %#v, want generation and revision", snapshot)
|
|
}
|
|
if len(snapshot.Tools) != 1 {
|
|
t.Fatalf("snapshot tools = %d, want 1", len(snapshot.Tools))
|
|
}
|
|
tool := snapshot.Tools[0]
|
|
if tool.ExposedName != plugintools.ExposedName("task-tags.v1", "add_tag") {
|
|
t.Fatalf("exposed name = %q", tool.ExposedName)
|
|
}
|
|
if !tool.ReadOnlyHint || tool.DestructiveHint {
|
|
t.Fatalf("annotations = %#v, want read-only true and destructive false", tool)
|
|
}
|
|
again, err := svc.AgentToolCatalog()
|
|
if err != nil {
|
|
t.Fatalf("second AgentToolCatalog() error: %v", err)
|
|
}
|
|
if again.Revision != snapshot.Revision {
|
|
t.Fatalf("revision changed without catalog change: %d -> %d", snapshot.Revision, again.Revision)
|
|
}
|
|
}
|
|
|
|
func TestAgentToolCatalogUsesConservativeAnnotationDefaults(t *testing.T) {
|
|
svc, _, _ := newTestService(t)
|
|
svc.registry.Add(&store.Record{
|
|
Manifest: manifest.Manifest{
|
|
ID: "defaults", AgentTools: []manifest.AgentTool{{
|
|
Name: "echo", Description: "Echo input.",
|
|
Surfaces: []string{manifest.AgentToolSurfaceKanban},
|
|
InputSchema: map[string]any{"type": "object"},
|
|
}},
|
|
},
|
|
Status: store.StatusActive,
|
|
})
|
|
|
|
snapshot, err := svc.AgentToolCatalog()
|
|
require.NoError(t, err)
|
|
require.Len(t, snapshot.Tools, 1)
|
|
tool := snapshot.Tools[0]
|
|
require.False(t, tool.ReadOnlyHint)
|
|
require.True(t, tool.DestructiveHint)
|
|
require.False(t, tool.IdempotentHint)
|
|
require.True(t, tool.OpenWorldHint)
|
|
}
|
|
|
|
func TestInvokeAgentToolLogsOnlyBoundMetadata(t *testing.T) {
|
|
core, observed := observer.New(zap.InfoLevel)
|
|
log, err := logger.NewFromZap(zap.New(core))
|
|
require.NoError(t, err)
|
|
svc := NewService(store.NewFSStore(t.TempDir()), NewRegistry(), nil, log)
|
|
|
|
_, err = svc.InvokeAgentTool(context.Background(), "missing", "echo", map[string]any{
|
|
"secret": "do-not-log",
|
|
}, AgentToolInvocationContext{
|
|
InvocationID: "invoke-1", TaskID: "task-1", SessionID: "session-1",
|
|
})
|
|
require.Error(t, err)
|
|
|
|
entries := observed.FilterMessage("plugin agent tool invocation").All()
|
|
require.Len(t, entries, 1)
|
|
fields := entries[0].ContextMap()
|
|
require.Equal(t, "invoke-1", fields["invocation_id"])
|
|
require.Equal(t, "missing", fields["plugin_id"])
|
|
require.Equal(t, "echo", fields["local_name"])
|
|
require.Equal(t, "task-1", fields["task_id"])
|
|
require.Equal(t, "session-1", fields["session_id"])
|
|
require.Equal(t, "error", fields["outcome"])
|
|
require.NotContains(t, fields, "arguments")
|
|
require.NotContains(t, fields, "result")
|
|
}
|