111 lines
3.3 KiB
Go
111 lines
3.3 KiB
Go
package bnp
|
|
|
|
import (
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
abiv1 "git.dev.alexdunmow.com/block/pluginsdk/abi/v1"
|
|
"google.golang.org/protobuf/proto"
|
|
)
|
|
|
|
func wikiMCPManifest(hasHTTPHandler bool) *abiv1.PluginManifest {
|
|
procedure := "/wiki.v1.WikiService/SaveArticle"
|
|
return &abiv1.PluginManifest{
|
|
AbiVersion: 1,
|
|
Name: "wiki",
|
|
Version: "0.1.12",
|
|
HasHttpHandler: hasHTTPHandler,
|
|
RbacMethodRoles: map[string]string{procedure: "admin"},
|
|
McpTools: []*abiv1.MCPToolDescriptor{{
|
|
Procedure: procedure,
|
|
ServiceFullName: "wiki.v1.WikiService",
|
|
MethodName: "SaveArticle",
|
|
InputSchemaJson: []byte(`{"type":"object","properties":{"article":{"type":"object"}}}`),
|
|
}},
|
|
}
|
|
}
|
|
|
|
func TestValidateMCPManifestAcceptsCallableWikiDescriptor(t *testing.T) {
|
|
manifest := wikiMCPManifest(true)
|
|
if err := ValidateMCPManifest(manifest, "@ninja", "wiki"); err != nil {
|
|
t.Fatalf("ValidateMCPManifest() error = %v", err)
|
|
}
|
|
name, err := mcpToolName("@ninja", "wiki", "wiki.v1.WikiService", "SaveArticle")
|
|
if err != nil {
|
|
t.Fatalf("mcpToolName() error = %v", err)
|
|
}
|
|
if name != "plugin_ninja_wiki_v1_save_article" {
|
|
t.Fatalf("tool name = %q", name)
|
|
}
|
|
}
|
|
|
|
func TestValidateMCPManifestRejectsMissingRuntimeHandler(t *testing.T) {
|
|
err := ValidateMCPManifest(wikiMCPManifest(false), "@ninja", "wiki")
|
|
if err == nil || !strings.Contains(err.Error(), "has_http_handler") {
|
|
t.Fatalf("error = %v, want has_http_handler rejection", err)
|
|
}
|
|
}
|
|
|
|
func TestValidateMCPManifestRejectsMalformedDescriptors(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
mutate func(*abiv1.PluginManifest)
|
|
want string
|
|
}{
|
|
{
|
|
name: "unmapped procedure",
|
|
mutate: func(manifest *abiv1.PluginManifest) {
|
|
manifest.RbacMethodRoles = nil
|
|
},
|
|
want: "RBAC role",
|
|
},
|
|
{
|
|
name: "invalid schema",
|
|
mutate: func(manifest *abiv1.PluginManifest) {
|
|
manifest.McpTools[0].InputSchemaJson = []byte(`{"type":"string"}`)
|
|
},
|
|
want: "input schema",
|
|
},
|
|
{
|
|
name: "missing API major",
|
|
mutate: func(manifest *abiv1.PluginManifest) {
|
|
descriptor := manifest.McpTools[0]
|
|
descriptor.Procedure = "/wiki.WikiService/SaveArticle"
|
|
descriptor.ServiceFullName = "wiki.WikiService"
|
|
manifest.RbacMethodRoles = map[string]string{descriptor.Procedure: "admin"}
|
|
},
|
|
want: "API major",
|
|
},
|
|
}
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
manifest := wikiMCPManifest(true)
|
|
test.mutate(manifest)
|
|
err := ValidateMCPManifest(manifest, "@ninja", "wiki")
|
|
if err == nil || !strings.Contains(err.Error(), test.want) {
|
|
t.Fatalf("error = %v, want %q", err, test.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestVerifyRejectsMCPDescriptorsWithoutRuntimeHandler(t *testing.T) {
|
|
manifestBytes, err := proto.Marshal(wikiMCPManifest(false))
|
|
if err != nil {
|
|
t.Fatalf("marshal manifest: %v", err)
|
|
}
|
|
out := filepath.Join(t.TempDir(), "wiki.bnp")
|
|
_, err = packArtifact(out, []packEntry{
|
|
{ArtifactPath: fileWasm, Data: []byte("wasm")},
|
|
{ArtifactPath: fileMod, Data: []byte("[plugin]\nname = \"wiki\"\nscope = \"@ninja\"\nversion = \"0.1.12\"\n")},
|
|
{ArtifactPath: fileManifest, Data: manifestBytes},
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("pack artifact: %v", err)
|
|
}
|
|
if _, err := Verify(out); err == nil || !strings.Contains(err.Error(), "has_http_handler") {
|
|
t.Fatalf("Verify() error = %v, want has_http_handler rejection", err)
|
|
}
|
|
}
|