Files
dingtalk-workspace-cli/internal/cli/schema_agent_metadata_test.go
T
玉澜 b877172d7d refactor(corecmd): close alias/env validation gaps and prune dead symbols
Honor KindBool aliases in BuildArgs/hasEffectiveValue/constraintProvided;
reject MarkRequired+Aliases combinations at registration; validate
env-sourced values against declared enums; drop dead
ValidationEffective/ProjectDeclaredParameters constants and the unused
AnnotateRuntimeFlag parameter; route confirmationBypass through BoolFlag;
refresh retired-flow comments.
2026-08-04 09:46:55 +08:00

362 lines
14 KiB
Go

// Copyright 2026 Alibaba Group
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cli
import (
"encoding/json"
"strings"
"testing"
"testing/fstest"
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/corecmd/contract"
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/corecmd/contractfinal"
"github.com/spf13/cobra"
)
func TestAgentMetadataFixtureLoadsSplitDomains(t *testing.T) {
// Production no longer embeds or ships schema_agent_metadata/*.json.
// Runtime Agent metadata must stay empty; selection completeness now lives
// in schema_catalog (see TestDeliverySchemaCatalogSelectionCompleteness).
metadata := runtimeAgentMetadata()
if len(metadata.Tools) != 0 || len(metadata.Products) != 0 || len(metadata.Domains) != 0 {
t.Fatalf("retired Agent metadata snapshot must be empty: %#v", metadata)
}
// Temporary MapFS fixture only — exercises the retired split-domain loader
// seam without depending on a committed schema_agent_metadata/ directory.
fixture := fstest.MapFS{
"schema_agent_metadata/index.json": {Data: []byte(`{"domains":["sample"],"coverage":{"tools_with_metadata":1}}`)},
"schema_agent_metadata/sample.json": {Data: []byte(`{"product_id":"sample","tools":{"sample.get":{"agent_summary":"S","use_when":["u"],"avoid_when":["a"],"examples":["dws sample get"],"interface_mode":"local","availability":"available"}}}`)},
}
loaded := loadAgentMetadataFixtureFrom(fixture)
if len(loaded.Tools) != 1 || loaded.Tools["sample.get"].AgentSummary != "S" {
t.Fatalf("fixture loader = %#v", loaded)
}
}
// TestDeliverySchemaCatalogSelectionCompleteness replaces the retired
// schema_agent_metadata/*.json split-domain coverage gate: every delivered
// Catalog tool must carry non-empty selection routing, interface disposition,
// and examples that never bypass confirmation with --yes.
func TestDeliverySchemaCatalogSelectionCompleteness(t *testing.T) {
if !deliverySchemaCatalogAvailable() {
t.Fatalf("delivery schema Catalog is unavailable: %v", deliverySchemaCatalogError())
}
loaded := mustDeliverySchemaCatalogMaps(t)
products := map[string]struct{}{}
for canonical, tool := range loaded.Snapshot.Tools {
product := schemaString(tool["product_id"])
if product == "" {
t.Errorf("tool %s missing product_id", canonical)
continue
}
products[product] = struct{}{}
if len(schemaStringSlice(tool["use_when"])) == 0 ||
len(schemaStringSlice(tool["avoid_when"])) == 0 ||
len(schemaStringSlice(tool["examples"])) == 0 {
t.Errorf("tool %s has incomplete selection metadata: use_when=%v avoid_when=%v examples=%v",
canonical, tool["use_when"], tool["avoid_when"], tool["examples"])
}
if schemaString(tool["interface_mode"]) == "" || schemaString(tool["availability"]) == "" {
t.Errorf("tool %s has incomplete interface disposition: mode=%q availability=%q",
canonical, schemaString(tool["interface_mode"]), schemaString(tool["availability"]))
}
for _, example := range schemaStringSlice(tool["examples"]) {
if strings.Contains(" "+example+" ", " --yes ") {
t.Errorf("tool %s example bypasses confirmation: %q", canonical, example)
}
}
}
if len(products) < 2 {
t.Fatalf("catalog products = %d, want multi-product delivery", len(products))
}
if _, ok := loaded.Snapshot.Tools["calendar.create_calendar_event"]; !ok {
t.Fatalf("calendar.create_calendar_event missing from catalog tools (%d total)", len(loaded.Snapshot.Tools))
}
if got, want := len(loaded.Snapshot.Tools), len(loaded.Index.CanonicalPaths()); got != want {
t.Fatalf("catalog tools = %d, typed index = %d", got, want)
}
}
func TestRuntimeSchemaIncludesAgentMetadata(t *testing.T) {
// Agent selection / safety / interface facts declare on the leaf
// ContractFinal and ProductDecl; the retired agent-metadata inject no
// longer participates in assembly.
root := buildRuntimeSchemaTestRoot()
declareRuntimeSchemaTestRootDoc(t, root, nil)
mcpFixture := embeddedMCPMetadata{Tools: map[string]embeddedMCPToolMetadata{}}
leaf, err := runtimeSchemaPayloadForTestWithMetadata(root, []string{"doc.create_document"}, emptyAgentMetadata(), mcpFixture)
if err != nil {
t.Fatalf("runtimeSchemaPayloadForTest(leaf): %v", err)
}
if leaf["effect"] != "write" || leaf["agent_metadata_source"] != "corecmd.contract" {
t.Fatalf("leaf Agent metadata = %#v", leaf)
}
if leaf["interface_mode"] != "local" || leaf["availability"] != "available" || leaf["interface_reason"] != "test local implementation" {
t.Fatalf("leaf interface disposition = %#v", leaf)
}
if examples, _ := leaf["examples"].([]string); len(examples) != 1 {
t.Fatalf("leaf examples = %#v", leaf["examples"])
}
catalog, err := runtimeSchemaPayloadForTestWithMetadata(root, nil, emptyAgentMetadata(), mcpFixture)
if err != nil {
t.Fatalf("runtimeSchemaPayloadForTest(catalog): %v", err)
}
summary, _ := catalog["agent_metadata"].(map[string]any)
// Catalog-level Agent summary is derived from assembled products/tools
// (ContractFinal / ProductDecl), not from an injected fixture source_hash.
if summary["source"] != ProvenanceEmbeddedSkillMetadata {
t.Fatalf("catalog Agent metadata summary = %#v", summary)
}
if summary["tools_with_metadata"] == nil || summary["products_with_metadata"] == nil {
t.Fatalf("catalog Agent metadata summary missing coverage fields: %#v", summary)
}
products, _ := catalog["products"].([]map[string]any)
doc := findSchemaProduct(products, "doc")
if useWhen, _ := doc["use_when"].([]string); len(useWhen) != 1 {
t.Fatalf("doc product use_when = %#v", doc["use_when"])
}
tools, _ := doc["tools"].([]map[string]any)
if len(tools) != 1 || tools[0]["effect"] != "write" {
t.Fatalf("doc tool summaries = %#v", tools)
}
if _, exists := tools[0]["examples"]; exists {
t.Fatalf("product summary must not include examples: %#v", tools[0])
}
registry, err := schemaRegistryForTestWithMetadata(root, emptyAgentMetadata(), mcpFixture)
if err != nil {
t.Fatalf("schemaRegistryForTest(): %v", err)
}
compact, err := registry.ToOverviewPayload()
if err != nil {
t.Fatalf("ToOverviewPayload(): %v", err)
}
compactProducts, _ := compact["products"].([]map[string]any)
compactDoc := findSchemaProduct(compactProducts, "doc")
if compactDoc["agent_summary"] != "创建、读取和维护钉钉文档" {
t.Fatalf("compact product summary = %#v", compactDoc)
}
if _, exists := compactDoc["agent_source_refs"]; exists {
t.Fatalf("compact product must omit provenance: %#v", compactDoc)
}
if _, exists := compactDoc["use_when"]; exists {
t.Fatalf("compact product with summary must omit routing expansion: %#v", compactDoc)
}
}
func TestRuntimeSchemaAllPayloadContainsFullLeafParameters(t *testing.T) {
// Synthetic fixture declares ContractFinal/ProductDecl so the full export
// exercises the production assembly path.
root := buildRuntimeSchemaTestRoot()
declareRuntimeSchemaTestRootDoc(t, root, nil)
registry, err := schemaRegistryForTestWithMetadata(root, emptyAgentMetadata(), embeddedMCPMetadata{})
if err != nil {
t.Fatal(err)
}
payload, err := registry.ToPayload()
if err != nil {
t.Fatal(err)
}
products := schemaMapSlice(payload["products"])
doc := findSchemaProduct(products, "doc")
tools := schemaMapSlice(doc["tools"])
if len(tools) != 1 {
t.Fatalf("runtime full export tools = %#v", tools)
}
if got := schemaString(tools[0]["canonical_path"]); got != "doc.create_document" {
t.Fatalf("canonical path = %q", got)
}
parameters, ok := tools[0]["parameters"].(map[string]any)
if !ok || parameters["title"] == nil {
t.Fatalf("runtime full export parameters = %#v", tools[0]["parameters"])
}
}
func schemaTestInt(value any) int {
switch typed := value.(type) {
case int:
return typed
case float64:
return int(typed)
case json.Number:
parsed, _ := typed.Int64()
return int(parsed)
case string:
parsed, _ := json.Number(typed).Int64()
return int(parsed)
default:
return 0
}
}
func TestRuntimeSchemaUsesVersionedInterfaceRef(t *testing.T) {
// interface_ref declares on the leaf ContractFinal; the injected MCP
// fixture participates through the gated fixture lookup (remapped via the
// declared Interface.Ref).
root := buildRuntimeSchemaTestRoot()
declareRuntimeSchemaTestRootDoc(t, root, func(payload *contract.ContractFinalPayload) {
payload.Interface = &contract.InterfaceSpec{
Mode: contract.InterfaceModeMCP,
Availability: contract.InterfaceAvailable,
Reason: "reviewed RPC mapping",
Ref: &contract.InterfaceRefSpec{ProductID: "documents", RPCName: "create_doc_v2"},
}
})
mcpFixture := embeddedMCPMetadata{
Tools: map[string]embeddedMCPToolMetadata{
"documents.create_doc_v2": {
Parameters: map[string]embeddedMCPParamMeta{
"title": {Description: "MCP document title"},
},
},
},
}
payload, err := runtimeSchemaPayloadForTestWithMetadata(root, []string{"doc.create_document"}, emptyAgentMetadata(), mcpFixture)
if err != nil {
t.Fatal(err)
}
ref, _ := payload["interface_ref"].(map[string]any)
if ref["product_id"] != "documents" || ref["rpc_name"] != "create_doc_v2" {
t.Fatalf("interface_ref = %#v", payload["interface_ref"])
}
parameters, _ := payload["parameters"].(map[string]any)
title, _ := parameters["title"].(map[string]any)
if title["interface_description"] != "MCP document title" {
t.Fatalf("title metadata = %#v", title)
}
}
func TestMCPRequiredParticipatesInSourcePrecedence(t *testing.T) {
required := true
mcpFixture := embeddedMCPMetadata{
Tools: map[string]embeddedMCPToolMetadata{
"sample.list_items": {
Parameters: map[string]embeddedMCPParamMeta{
"limit": {Required: &required},
},
},
},
}
root := &cobra.Command{Use: "dws"}
list := &cobra.Command{Use: "list", Run: func(*cobra.Command, []string) {}}
list.Flags().Int("limit", 0, "optional page size")
AttachRuntimeSchema(list, "sample", "list_items", "test")
sample := &cobra.Command{Use: "sample"}
sample.AddCommand(list)
root.AddCommand(sample)
declareSampleListItemsLeaf(t, list)
payload, err := runtimeSchemaPayloadForTestWithMetadata(root, []string{"sample.list_items"}, emptyAgentMetadata(), mcpFixture)
if err != nil {
t.Fatal(err)
}
parameters, _ := payload["parameters"].(map[string]any)
limit, _ := parameters["limit"].(map[string]any)
if limit["required"] != true {
t.Fatalf("MCP required candidate did not win over the default: %#v", limit)
}
}
func TestMCPDefaultDoesNotOverrideCLIDefault(t *testing.T) {
mcpFixture := embeddedMCPMetadata{
Tools: map[string]embeddedMCPToolMetadata{
"sample.list_items": {
Parameters: map[string]embeddedMCPParamMeta{
"limit": {Default: "50"},
},
},
},
}
root := &cobra.Command{Use: "dws"}
list := &cobra.Command{Use: "list", Run: func(*cobra.Command, []string) {}}
list.Flags().Int("limit", 10, "optional page size")
AttachRuntimeSchema(list, "sample", "list_items", "test")
sample := &cobra.Command{Use: "sample"}
sample.AddCommand(list)
root.AddCommand(sample)
declareSampleListItemsLeaf(t, list)
payload, err := runtimeSchemaPayloadForTestWithMetadata(root, []string{"sample.list_items"}, emptyAgentMetadata(), mcpFixture)
if err != nil {
t.Fatal(err)
}
parameters, _ := payload["parameters"].(map[string]any)
limit, _ := parameters["limit"].(map[string]any)
if limit["default"] != "10" || limit["interface_default"] != "50" {
t.Fatalf("CLI and interface defaults were not separated: %#v", limit)
}
}
// declareSampleListItemsLeaf registers the ContractFinal / ProductDecl
// declarations for the synthetic sample.list_items leaf so MCP fixture tests
// assemble through the production path.
func declareSampleListItemsLeaf(t *testing.T, list *cobra.Command) {
t.Helper()
contractfinal.RegisterRuntimeContractFinal(list, contract.ContractFinalPayload{
Identity: &contract.ToolIdentitySpec{
ProductID: "sample", Name: "list_items", CanonicalPath: "sample.list_items",
CLIPath: "sample list", PrimaryCLIPath: "sample list",
},
Title: "List items",
Description: "List sample items",
Safety: &contract.SafetySpec{
Effect: "read", Risk: "low", Confirmation: "not_required", Idempotency: "idempotent",
},
Interface: &contract.InterfaceSpec{
Mode: "local", Availability: "available", Reason: "test local leaf",
},
Selection: &contract.SelectionSpec{
AgentSummary: "List sample items",
UseWhen: []string{"list sample items"},
AvoidWhen: []string{"not listing"},
},
})
t.Cleanup(func() { contractfinal.ClearRuntimeContractFinalForTest(list) })
contract.RegisterProductDecl(contract.ProductDecl{
ID: "sample",
Selection: contract.ProductSelectionDecl{
AgentSummary: "Sample product",
UseWhen: []string{"sample routing"},
AvoidWhen: []string{"not sample"},
},
})
t.Cleanup(func() { contract.ClearProductDeclForTest("sample") })
}
func findSchemaProduct(products []map[string]any, id string) map[string]any {
for _, product := range products {
if product["id"] == id {
return product
}
}
return nil
}
func buildRuntimeSchemaTestRoot() *cobra.Command {
root := &cobra.Command{Use: "dws"}
create := &cobra.Command{Use: "create", Short: "Create document", Run: func(*cobra.Command, []string) {}}
create.Flags().String("title", "", "Document title")
AttachRuntimeSchema(create, "doc", "create_document", "runtime:doc")
AnnotateRuntimeFlag(create, "title", "title", "string", true)
doc := &cobra.Command{Use: "doc", Short: "Docs"}
doc.AddCommand(create)
root.AddCommand(doc)
return root
}