114 lines
5.8 KiB
Go
114 lines
5.8 KiB
Go
// Copyright 2026 Alibaba Group
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
package agentmetadata
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestValidateInterfaceDispositionsMatrix(t *testing.T) {
|
|
ref := &InterfaceRef{ProductID: "calendar", RPCName: "get_event"}
|
|
for _, test := range []struct {
|
|
name string
|
|
tool ToolMetadata
|
|
want string
|
|
}{
|
|
{name: "mcp available", tool: ToolMetadata{InterfaceMode: "mcp", Availability: "available", InterfaceRef: ref}},
|
|
{name: "local available", tool: ToolMetadata{InterfaceMode: "local", Availability: "available"}},
|
|
{name: "composite available", tool: ToolMetadata{InterfaceMode: "composite", Availability: "available", InterfaceReason: "orchestrates operations"}},
|
|
{name: "mcp unavailable", tool: ToolMetadata{InterfaceMode: "mcp", Availability: "unavailable", InterfaceReason: "RPC retired"}},
|
|
{name: "local unavailable", tool: ToolMetadata{InterfaceMode: "local", Availability: "unavailable", InterfaceReason: "compatibility command frozen"}},
|
|
{name: "composite unavailable", tool: ToolMetadata{InterfaceMode: "composite", Availability: "unavailable", InterfaceReason: "workflow retired"}},
|
|
{name: "unavailable is not mode", tool: ToolMetadata{InterfaceMode: "unavailable", Availability: "unavailable", InterfaceReason: "retired"}, want: "legacy interface_mode=unavailable; migrate"},
|
|
{name: "mcp available without ref", tool: ToolMetadata{InterfaceMode: "mcp", Availability: "available"}, want: "available mcp without interface_ref"},
|
|
{name: "local available with ref", tool: ToolMetadata{InterfaceMode: "local", Availability: "available", InterfaceRef: ref}, want: "available local but declares interface_ref"},
|
|
{name: "composite available with ref", tool: ToolMetadata{InterfaceMode: "composite", Availability: "available", InterfaceReason: "orchestrates operations", InterfaceRef: ref}, want: "available composite but declares a single interface_ref"},
|
|
{name: "composite available without reason", tool: ToolMetadata{InterfaceMode: "composite", Availability: "available"}, want: "available composite without interface_reason"},
|
|
{name: "unavailable with ref", tool: ToolMetadata{InterfaceMode: "mcp", Availability: "unavailable", InterfaceReason: "retired", InterfaceRef: ref}, want: "unavailable but declares interface_ref"},
|
|
{name: "unavailable without reason", tool: ToolMetadata{InterfaceMode: "local", Availability: "unavailable"}, want: "unavailable without interface_reason"},
|
|
{name: "missing availability", tool: ToolMetadata{InterfaceMode: "local"}, want: "unsupported availability"},
|
|
} {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
err := validateInterfaceDispositions(File{Tools: map[string]ToolMetadata{"calendar event get": test.tool}})
|
|
if test.want == "" {
|
|
if err != nil {
|
|
t.Fatalf("validateInterfaceDispositions() error = %v", err)
|
|
}
|
|
return
|
|
}
|
|
if err == nil || !strings.Contains(err.Error(), test.want) {
|
|
t.Fatalf("validateInterfaceDispositions() error = %v, want %q", err, test.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestFinalizeUnavailableDispositionClearsRefAndRecordsNoneWinner(t *testing.T) {
|
|
file := File{Tools: map[string]ToolMetadata{
|
|
"conference camera open": {
|
|
InterfaceMode: "mcp",
|
|
interfaceModeRank: selectionRankReviewedExplicit,
|
|
interfaceModeOrigin: "reviewed.json",
|
|
Availability: "unavailable",
|
|
availabilityRank: selectionRankReviewedExplicit,
|
|
availabilityOrigin: "reviewed.json",
|
|
InterfaceReason: "compatibility command frozen",
|
|
InterfaceRef: &InterfaceRef{ProductID: "conference", RPCName: "open_camera"},
|
|
interfaceRefRank: selectionRankImported,
|
|
interfaceRefOrigin: "pinned-interface.json",
|
|
FieldProvenance: map[string]FieldProvenance{
|
|
"interface_ref": {
|
|
Value: "conference.open_camera",
|
|
Source: "pinned-interface.json",
|
|
Precedence: "imported",
|
|
Resolution: "highest_precedence",
|
|
Candidates: []FieldCandidateProvenance{{
|
|
Value: "conference.open_camera",
|
|
Source: "pinned-interface.json",
|
|
Precedence: "imported",
|
|
Selected: true,
|
|
}},
|
|
},
|
|
},
|
|
},
|
|
}}
|
|
|
|
finalizeInterfaceDispositions(&file)
|
|
tool := file.Tools["conference camera open"]
|
|
if tool.InterfaceRef != nil {
|
|
t.Fatalf("InterfaceRef = %#v, want nil", tool.InterfaceRef)
|
|
}
|
|
provenance, ok := tool.FieldProvenance["interface_ref"]
|
|
if !ok || provenance.Value != nil || provenance.Resolution != "interface_disposition_matrix" {
|
|
t.Fatalf("interface_ref provenance = %#v", provenance)
|
|
}
|
|
if len(provenance.Candidates) != 1 || !provenance.Candidates[0].Selected || provenance.Candidates[0].Value != nil {
|
|
t.Fatalf("interface_ref candidates = %#v", provenance.Candidates)
|
|
}
|
|
if !strings.Contains(provenance.ReviewReason, "forbids Agent invocation") {
|
|
t.Fatalf("interface_ref review reason = %q", provenance.ReviewReason)
|
|
}
|
|
if len(provenance.OverriddenCandidates) != 1 || provenance.OverriddenCandidates[0].Value != "conference.open_camera" {
|
|
t.Fatalf("overridden interface_ref candidates = %#v", provenance.OverriddenCandidates)
|
|
}
|
|
}
|
|
|
|
func TestGenerateRejectsLegacyUnavailableModeWithMigrationError(t *testing.T) {
|
|
root := t.TempDir()
|
|
writePrecedenceFixture(t, root, "calendar event get")
|
|
writeFixture(t, root, "internal/cli/schema_hints/legacy.json", hintFixture("explicit", "legacy-interface", "calendar event get", `{
|
|
"interface_mode": "unavailable",
|
|
"availability": "unavailable",
|
|
"interface_reason": "legacy fixture"
|
|
}`))
|
|
|
|
_, _, err := generateFromSources(precedenceOptions(root, map[string]string{
|
|
"calendar event get": "calendar event get",
|
|
}))
|
|
if err == nil || !strings.Contains(err.Error(), "legacy interface_mode=unavailable; migrate") || !strings.Contains(err.Error(), "availability=unavailable") {
|
|
t.Fatalf("Generate() error = %v, want explicit interface mode migration guidance", err)
|
|
}
|
|
}
|