Rename misleading public Embedded* loaders to Reviewed/Load APIs, keep fail-closed HintsDir/-hints valves, and centralize wire provenance string literals behind named consts without changing Catalog values. Co-authored-by: Cursor <cursoragent@cursor.com>
277 lines
9.6 KiB
Go
277 lines
9.6 KiB
Go
// Copyright 2026 Alibaba Group
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
package cli
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"os"
|
|
"os/exec"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/corecmd/contract"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
const schemaParameterBindingInvalidBuildChildEnv = "DWS_SCHEMA_PARAMETER_BINDING_INVALID_BUILD_CHILD"
|
|
|
|
func TestSchemaParameterBindingsMatchReviewedBaselineAndDeliveryCatalog(t *testing.T) {
|
|
if err := ValidateSchemaParameterBindings(); err != nil {
|
|
t.Fatalf("ValidateSchemaParameterBindings() error = %v", err)
|
|
}
|
|
snapshot, err := runtimeSchemaParameterBindingData()
|
|
if err != nil {
|
|
t.Fatalf("runtimeSchemaParameterBindingData() error = %v", err)
|
|
}
|
|
manifestHash, err := schemaParameterBindingManifestHash(snapshot.Bindings)
|
|
if err != nil {
|
|
t.Fatalf("schemaParameterBindingManifestHash() error = %v", err)
|
|
}
|
|
if manifestHash != snapshot.Baseline.SHA256 {
|
|
t.Fatalf("active binding manifest hash = %q, reviewed baseline = %q", manifestHash, snapshot.Baseline.SHA256)
|
|
}
|
|
|
|
loaded := mustDeliverySchemaCatalogMaps(t)
|
|
for canonical, bindings := range snapshot.Bindings {
|
|
detail, ok := loaded.Snapshot.Tools[canonical]
|
|
if !ok {
|
|
t.Errorf("binding references unknown canonical path %q", canonical)
|
|
continue
|
|
}
|
|
parameters, _ := detail["parameters"].(map[string]any)
|
|
for flagName, propertyName := range bindings {
|
|
parameter, _ := parameters[flagName].(map[string]any)
|
|
if parameter == nil {
|
|
t.Errorf("binding %s --%s references an unknown flag", canonical, flagName)
|
|
continue
|
|
}
|
|
if got := schemaString(parameter["property"]); got != propertyName {
|
|
t.Errorf("binding %s --%s property = %q, want %q", canonical, flagName, got, propertyName)
|
|
}
|
|
}
|
|
}
|
|
|
|
for key, correction := range snapshot.Corrections {
|
|
canonical, flagName, _ := strings.Cut(key, " --")
|
|
if got := snapshot.Bindings[canonical][flagName]; got != correction.NewProperty {
|
|
t.Errorf("reviewed correction %q delivers %q, want %q", key, got, correction.NewProperty)
|
|
}
|
|
}
|
|
for key, removal := range snapshot.Removals {
|
|
canonical, flagName, _ := strings.Cut(key, " --")
|
|
if got := snapshot.Bindings[canonical][flagName]; got != "" {
|
|
t.Errorf("reviewed removal %q remains active as %q", key, got)
|
|
}
|
|
if removal.ReplacedBy != "" {
|
|
replacementCanonical, replacementFlag, _ := strings.Cut(removal.ReplacedBy, " --")
|
|
if got := snapshot.Bindings[replacementCanonical][replacementFlag]; got == "" {
|
|
t.Errorf("reviewed removal %q replacement %q is not active", key, removal.ReplacedBy)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Track 1 Phase 2: active bindings manifest is empty; property delivery is
|
|
// owned by leaf ParamDecl.Property (native annotations), not this JSON.
|
|
if len(snapshot.Bindings) != 0 {
|
|
t.Fatalf("active bindings = %d groups, want empty after Phase 2 retirement", len(snapshot.Bindings))
|
|
}
|
|
}
|
|
|
|
func TestDecodeSchemaParameterBindingsFailsClosed(t *testing.T) {
|
|
valid := append([]byte(nil), embeddedSchemaParameterBindingsJSON...)
|
|
replaceOnce := func(old, replacement string) []byte {
|
|
t.Helper()
|
|
updated := bytes.Replace(valid, []byte(old), []byte(replacement), 1)
|
|
if bytes.Equal(updated, valid) {
|
|
t.Fatalf("fixture does not contain %q", old)
|
|
}
|
|
return updated
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
data []byte
|
|
want string
|
|
}{
|
|
{
|
|
name: "unknown top-level field",
|
|
data: replaceOnce(`"version": 3,`, `"version": 3, "unexpected": true,`),
|
|
want: "unknown field",
|
|
},
|
|
{
|
|
name: "unknown baseline field",
|
|
data: replaceOnce(`"manifest": "schema-parameter-bindings-v3",`, `"manifest": "schema-parameter-bindings-v3", "count": 653,`),
|
|
want: "unknown field",
|
|
},
|
|
{
|
|
name: "multiple JSON values",
|
|
data: append(append([]byte(nil), valid...), []byte("\n{}")...),
|
|
want: "multiple JSON values",
|
|
},
|
|
{
|
|
name: "unsupported version",
|
|
data: replaceOnce(`"version": 3`, `"version": 999`),
|
|
want: "unsupported schema parameter bindings version",
|
|
},
|
|
{
|
|
name: "unreviewed baseline",
|
|
data: replaceOnce(`"reviewed": true`, `"reviewed": false`),
|
|
want: "baseline must be reviewed",
|
|
},
|
|
{
|
|
name: "active binding drift",
|
|
data: replaceOnce(
|
|
`"sha256": "sha256:4f53cda18c2baa0c0354bb5f9a3ecbe5ed12ab4d8e11ba873c2f11161202b945"`,
|
|
`"sha256": "sha256:0000000000000000000000000000000000000000000000000000000000000000"`,
|
|
),
|
|
want: "want exact active manifest",
|
|
},
|
|
{
|
|
name: "legacy count ledger",
|
|
data: replaceOnce(`"version": 3,`, `"version": 3, "historical_binding_count": 653,`),
|
|
want: "unknown field",
|
|
},
|
|
}
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
snapshot, err := decodeSchemaParameterBindings(test.data)
|
|
if err == nil || !strings.Contains(err.Error(), test.want) {
|
|
t.Fatalf("decodeSchemaParameterBindings() snapshot=%#v error=%v, want %q", snapshot, err, test.want)
|
|
}
|
|
if len(snapshot.Bindings) != 0 {
|
|
t.Fatalf("failed decode returned %d active binding groups; want no inference-capable fallback", len(snapshot.Bindings))
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSchemaParameterBindingManifestHashIsExactContentNotCount(t *testing.T) {
|
|
left := map[string]map[string]string{
|
|
"sample.read": {"item-id": "itemId", "limit": "pageSize"},
|
|
}
|
|
right := map[string]map[string]string{
|
|
"sample.read": {"limit": "pageSize", "item-id": "itemId"},
|
|
}
|
|
changed := map[string]map[string]string{
|
|
"sample.read": {"item-id": "id", "limit": "pageSize"},
|
|
}
|
|
leftHash, err := schemaParameterBindingManifestHash(left)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
rightHash, err := schemaParameterBindingManifestHash(right)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
changedHash, err := schemaParameterBindingManifestHash(changed)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if leftHash != rightHash {
|
|
t.Fatalf("manifest hash depends on map iteration order: %q != %q", leftHash, rightHash)
|
|
}
|
|
if leftHash == changedHash {
|
|
t.Fatalf("same-size manifest property change did not change hash: %q", leftHash)
|
|
}
|
|
}
|
|
|
|
func TestBindEffectiveCommandRegistryFailsClosedOnInvalidParameterBindingSource(t *testing.T) {
|
|
if os.Getenv(schemaParameterBindingInvalidBuildChildEnv) == "1" {
|
|
if got := runtimeSchemaParameterBindingsLazyLoadCount.Load(); got != 0 {
|
|
t.Fatalf("parameter bindings loaded before child test: %d", got)
|
|
}
|
|
embeddedSchemaParameterBindingsJSON = []byte(`{"version":3,"unexpected":true}`)
|
|
effective, err := BuildEffectiveCommandRegistry(&cobra.Command{Use: "dws"})
|
|
if err != nil {
|
|
t.Fatalf("BuildEffectiveCommandRegistry() error = %v, want identity build without binding audit", err)
|
|
}
|
|
_, err = BindEffectiveCommandRegistry(&cobra.Command{Use: "dws"}, effective)
|
|
if err == nil || !strings.Contains(err.Error(), "validate reviewed Schema parameter bindings") || !strings.Contains(err.Error(), "unknown field") {
|
|
t.Fatalf("BindEffectiveCommandRegistry() error = %v, want strict binding/exclusion validation", err)
|
|
}
|
|
return
|
|
}
|
|
|
|
command := exec.Command(os.Args[0], "-test.run=^TestBindEffectiveCommandRegistryFailsClosedOnInvalidParameterBindingSource$", "-test.count=1")
|
|
command.Env = append(os.Environ(), schemaParameterBindingInvalidBuildChildEnv+"=1")
|
|
output, err := command.CombinedOutput()
|
|
if err != nil {
|
|
t.Fatalf("invalid binding Bind child failed: %v\n%s", err, strings.TrimSpace(string(output)))
|
|
}
|
|
}
|
|
|
|
func TestValidateSchemaParameterBindingDeliveryRejectsStaleReviewedKeys(t *testing.T) {
|
|
command := &cobra.Command{Use: "read"}
|
|
command.Flags().String("item-id", "", "item id")
|
|
boundSpec := BoundCommandSpec{
|
|
CommandSpec: CommandSpec{
|
|
CanonicalPath: "sample.read",
|
|
PrimaryCLIPath: "sample read",
|
|
Visibility: SchemaVisibilityPublic,
|
|
},
|
|
PrimaryCommand: command,
|
|
}
|
|
bound := BoundCommandRegistry{
|
|
Commands: []BoundCommandSpec{boundSpec},
|
|
ByCanonical: map[string]BoundCommandSpec{"sample.read": boundSpec},
|
|
}
|
|
parameter := ParameterSpec{
|
|
Name: "item-id",
|
|
Property: "itemId",
|
|
FieldProvenance: map[string]contract.FieldProvenance{
|
|
"property": {
|
|
Value: json.RawMessage(`"itemId"`),
|
|
Source: "versioned_parameter_binding",
|
|
Resolution: "highest_precedence",
|
|
},
|
|
},
|
|
}
|
|
registry := SchemaRegistry{Products: []ProductSpec{{ID: "sample", Tools: []ToolSpec{{
|
|
Identity: contract.ToolIdentitySpec{CanonicalPath: "sample.read"},
|
|
Parameters: []ParameterSpec{parameter},
|
|
}}}}}
|
|
valid := schemaParameterBindingSnapshot{Bindings: map[string]map[string]string{
|
|
"sample.read": {"item-id": "itemId"},
|
|
}}
|
|
if err := validateSchemaParameterBindingDelivery(valid, bound, registry); err != nil {
|
|
t.Fatalf("valid delivery rejected: %v", err)
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
snapshot schemaParameterBindingSnapshot
|
|
want string
|
|
}{
|
|
{
|
|
name: "unknown active canonical",
|
|
snapshot: schemaParameterBindingSnapshot{Bindings: map[string]map[string]string{
|
|
"sample.missing": {"item-id": "itemId"},
|
|
}},
|
|
want: "does not reference a public bound command",
|
|
},
|
|
{
|
|
name: "unknown mapping exclusion flag",
|
|
snapshot: schemaParameterBindingSnapshot{MappingExclusions: map[string]string{
|
|
"sample.read --missing": "reviewed local selector",
|
|
}},
|
|
want: "does not reference an exact final public Schema parameter",
|
|
},
|
|
{
|
|
name: "unknown removal canonical",
|
|
snapshot: schemaParameterBindingSnapshot{Removals: map[string]schemaParameterBindingRemoval{
|
|
"sample.missing --item-id": {Reason: "reviewed removal", Reviewed: true},
|
|
}},
|
|
want: "has a stale canonical path",
|
|
},
|
|
}
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
err := validateSchemaParameterBindingDelivery(test.snapshot, bound, registry)
|
|
if err == nil || !strings.Contains(err.Error(), test.want) {
|
|
t.Fatalf("validateSchemaParameterBindingDelivery() error = %v, want %q", err, test.want)
|
|
}
|
|
})
|
|
}
|
|
}
|