Files
dingtalk-workspace-cli/internal/cli/schema_registry_test_helpers_test.go
玉澜andCursor 98f45cfe23 Retire dead MCP pin candidates from Schema parameter assembly.
Production already ships an empty pin; remove the leftover mcp_metadata
candidate path so parameter resolution only uses declare/Cobra sources.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-05 22:45:33 +08:00

206 lines
7.4 KiB
Go

// Copyright 2026 Alibaba Group
// Licensed under the Apache License, Version 2.0 (the "License");
package cli
import (
"fmt"
"sort"
"strings"
"testing"
"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"
)
// boundTestCommandRegistry adapts the small synthetic Cobra trees used by unit
// tests to the same explicit registry input as production. Annotation discovery
// lives only in this _test.go helper; production never falls back to it.
func boundTestCommandRegistry(root *cobra.Command) (BoundCommandRegistry, error) {
grouped := map[string][]CommandSpec{}
walkLeafCommands(root, func(leaf *cobra.Command) {
if runtimeSchemaExcluded(leaf) {
return
}
path := normalizeSchemaCLIPath(strings.Join(commandPathParts(leaf), " "))
productID, toolName, source := runtimeSchemaAnnotations(leaf)
if productID == "" || toolName == "" {
return
}
canonical := productID + "." + toolName
grouped[canonical] = append(grouped[canonical], CommandSpec{
CanonicalPath: canonical,
SourceProductID: productID,
PrimaryCLIPath: path,
Source: defaultString(strings.TrimSpace(source), "test_registry"),
ReviewReason: "test fixture native identity",
})
})
canonicals := make([]string, 0, len(grouped))
for canonical := range grouped {
canonicals = append(canonicals, canonical)
}
sort.Strings(canonicals)
specs := make([]CommandSpec, 0, len(canonicals))
for _, canonical := range canonicals {
paths := grouped[canonical]
sort.Slice(paths, func(i, j int) bool { return paths[i].PrimaryCLIPath < paths[j].PrimaryCLIPath })
primary := paths[0]
for index := range paths {
if index != 0 {
primary.Aliases = append(primary.Aliases, paths[index].PrimaryCLIPath)
}
}
specs = append(specs, primary)
}
effective, err := newEffectiveCommandRegistry(specs)
if err != nil {
return BoundCommandRegistry{}, err
}
return BindEffectiveCommandRegistry(root, effective)
}
func schemaRegistryForTest(root *cobra.Command) (SchemaRegistry, error) {
bound, err := boundTestCommandRegistry(root)
if err != nil {
return SchemaRegistry{}, err
}
return AssembleSchemaRegistryFromBound(bound)
}
// declareRuntimeSchemaTestRootDoc registers the ContractFinal / ProductDecl
// declarations for the synthetic doc.create_document tree built by
// buildRuntimeSchemaTestRoot, so production-shaped assembly can resolve it.
// Declarations are removed again through t.Cleanup.
func declareRuntimeSchemaTestRootDoc(t *testing.T, root *cobra.Command, mutate func(*contract.ContractFinalPayload)) {
t.Helper()
create, _, err := root.Find([]string{"doc", "create"})
if err != nil {
t.Fatalf("locate doc create leaf: %v", err)
}
payload := contract.ContractFinalPayload{
Identity: &contract.ToolIdentitySpec{
ProductID: "doc", Name: "create_document", CanonicalPath: "doc.create_document",
CLIPath: "doc create", PrimaryCLIPath: "doc create",
},
Title: "Create document",
Description: "Create a DingTalk document",
Safety: &contract.SafetySpec{
Effect: "write", EffectSource: "command-verb", Risk: "medium",
Confirmation: "not_required", Idempotency: "non_idempotent",
},
Interface: &contract.InterfaceSpec{
Mode: "local", Availability: "available", Reason: "test local implementation",
},
Selection: &contract.SelectionSpec{
AgentSummary: "新建钉钉文档",
UseWhen: []string{"新建文档"},
AvoidWhen: []string{"只需读取文档时"},
Examples: []string{"dws doc create --title test"},
},
}
if mutate != nil {
mutate(&payload)
}
contractfinal.RegisterRuntimeContractFinal(create, payload)
t.Cleanup(func() { contractfinal.ClearRuntimeContractFinalForTest(create) })
contract.RegisterProductDecl(contract.ProductDecl{
ID: "doc",
Selection: contract.ProductSelectionDecl{
AgentSummary: "创建、读取和维护钉钉文档",
UseWhen: []string{"需要创建或读取文档"},
AvoidWhen: []string{"管理表格"},
},
})
t.Cleanup(func() { contract.ClearProductDeclForTest("doc") })
}
// loadedSchemaCatalogForTestRegistry wraps an assembled test registry in the
// production loadedSchemaCatalog shape so payload helpers exercise the same
// query path as delivery (schemaPayloadFromLoadedCatalog).
func loadedSchemaCatalogForTestRegistry(registry SchemaRegistry) (loadedSchemaCatalog, error) {
index, err := registry.Index()
if err != nil {
return loadedSchemaCatalog{}, err
}
return loadedSchemaCatalog{
Snapshot: SchemaCatalogSnapshot{Version: SchemaCatalogSnapshotVersion},
Registry: registry,
Index: index,
}, nil
}
func runtimeSchemaPayloadForTest(root *cobra.Command, args []string) (map[string]any, error) {
registry, err := schemaRegistryForTest(root)
if err != nil {
return nil, err
}
loaded, err := loadedSchemaCatalogForTestRegistry(registry)
if err != nil {
return nil, err
}
return schemaPayloadFromLoadedCatalog(loaded, args)
}
func runtimeSchemaAllPayloadForTest(root *cobra.Command) (map[string]any, error) {
registry, err := schemaRegistryForTest(root)
if err != nil {
return nil, err
}
return registry.ToPayload()
}
func runtimeSchemaCompletenessForTest(root *cobra.Command, exclusions []RuntimeSchemaExclusion) RuntimeSchemaCompletenessReport {
bound, err := boundTestCommandRegistry(root)
if err != nil {
return RuntimeSchemaCompletenessReport{DeliveryErrors: []string{err.Error()}}
}
covered := map[string]bool{}
for _, command := range bound.Commands {
addSchemaCoveredPath(covered, command.PrimaryCLIPath)
for _, alias := range command.Aliases {
addSchemaCoveredPath(covered, alias)
}
}
return runtimeSchemaCompletenessAgainstPaths(root, exclusions, covered)
}
func testRegistryIdentityByCLIPath(root *cobra.Command) (map[string]runtimeSchemaResolvedIdentity, []string) {
bound, err := boundTestCommandRegistry(root)
if err != nil {
return map[string]runtimeSchemaResolvedIdentity{}, []string{err.Error()}
}
result := map[string]runtimeSchemaResolvedIdentity{}
for _, command := range bound.Commands {
for _, path := range append([]string{command.PrimaryCLIPath}, command.Aliases...) {
result[normalizeSchemaCLIPath(path)] = runtimeSchemaResolvedIdentity{
CanonicalPath: command.CanonicalPath,
Source: command.Source,
}
}
}
return result, nil
}
func schemaCatalogDeliveryCompletenessForTest(root *cobra.Command, snapshot SchemaCatalogSnapshot, exclusions []RuntimeSchemaExclusion) RuntimeSchemaCompletenessReport {
loaded, err := loadSchemaCatalogSnapshot(snapshot)
if err != nil {
return RuntimeSchemaCompletenessReport{DeliveryErrors: []string{err.Error()}}
}
expected, mappingErrors := testRegistryIdentityByCLIPath(root)
return schemaCatalogDeliveryCompletenessAgainstLoadedAndIdentity(root, loaded, exclusions, expected, mappingErrors)
}
func validateSchemaCatalogDeliveryCompletenessForTest(root *cobra.Command, snapshot SchemaCatalogSnapshot, exclusions []RuntimeSchemaExclusion) error {
report := schemaCatalogDeliveryCompletenessForTest(root, snapshot, exclusions)
if len(report.DeliveryErrors) > 0 {
return fmt.Errorf("invalid final Schema Catalog delivery: %s", strings.Join(report.DeliveryErrors, "; "))
}
if len(report.Missing) > 0 {
return fmt.Errorf("public Cobra leaves missing from final Schema Catalog or reviewed exclusions: %s", strings.Join(report.Missing, ", "))
}
return nil
}