Files
dingtalk-workspace-cli/internal/cli/catalogcodegen/fidelity_probe_test.go
T
玉澜 e705012011 feat(schema): migrate parameter overlays into code declarations
Move 121/210 parameter-level Schema field overlays from
schema_hints/metadata JSON into in-code ParamDecl declarations on
DeclareLeafMetadata commands. The declared values are emitted as
dws.schema.* annotations at assembly time via ApplyParamDecls,
outranking tool_schema_hint (rank 620 > 500) so the hint overlay
becomes redundant once the declaration is in place.

Key mechanism changes:
- Add SchemaDecl.Parameters []ParamDecl with property/required/
  interface_type/description/required_when/enum fields
- Add cli.ParamDecl type carried inside ContractFinalPayload
- ApplyParamDecls emits dws.schema.* annotations from the payload
  at assembly time (no sync.Map, no tree-rebuild key issue)
- Add cli.AnnotateRuntimeFlagInterfaceType (41 overlays needed it)
- Add cli.AnnotateRuntimeFlagRequiredValue for explicit true/false
- Compatibility alias check tolerates dws.schema.* annotation
  differences between primary and alias commands
- Remove runtime source_hash recomputation (87ms/997k allocs saved;
  enforced by check-generated-drift.sh at build time instead)
- Add shortcut.Flag.RequiredWhen and wire through FromShortcut
- Add boolFlag OR semantics to fix confirmationBypass disagreement
- Add bindKey default kebab-to-camel for forgotten Bind
- Add schema consumption benchmarks (catalog decode 1.5s/817MB,
  shortcut load 1.9ms/3MB — three orders of magnitude apart)
- Add catalog codegen feasibility probe (34 tools: 0.06s compile,
  31ns access, 87KB linked — extrapolates to 2.1MB for 845 tools)

Migrated products (29 tools, 121 fields):
  aisearch(1), chat(12), contact(4), doc(3), drive(1),
  hrbrain(10), mail(2), report(1), sheet(3), todo(6)

Remaining 89 fields across 11 products blocked by:
  - RPCName not found as string literal (19 tools, shortcut/variable)
  - No matching DeclareLeafMetadata near callMCPTool (11 tools)
  - No single RPCName for multi-step commands (drive.upload etc.)

All tests green: corecmd, cli, helpers. Generation and drift clean.
2026-08-01 09:25:40 +08:00

64 lines
2.0 KiB
Go

// Copyright 2026 Alibaba Group
// Licensed under the Apache License, Version 2.0 (the "License");
package catalogcodegen_test
import (
"reflect"
"strings"
"testing"
"unicode"
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/cli"
)
// TestToolSpecIsExpressibleAsLiterals decides whether the catalog can be stored
// as compiled-in Go literals at all.
//
// Byte-identical `dws schema` output requires that a ToolSpec built by generated
// code in another package can hold exactly the state the JSON path produces. That
// is possible if and only if every field reachable from ToolSpec is exported —
// an unexported field cannot be set from outside internal/cli, so its value could
// never be reproduced. This walks the type graph and reports any that block it.
func TestToolSpecIsExpressibleAsLiterals(t *testing.T) {
var blockers []string
seen := map[reflect.Type]bool{}
var walk func(t reflect.Type, path string)
walk = func(rt reflect.Type, path string) {
for rt.Kind() == reflect.Ptr || rt.Kind() == reflect.Slice || rt.Kind() == reflect.Array {
rt = rt.Elem()
}
if rt.Kind() == reflect.Map {
walk(rt.Key(), path+"[key]")
rt = rt.Elem()
for rt.Kind() == reflect.Ptr || rt.Kind() == reflect.Slice {
rt = rt.Elem()
}
}
if rt.Kind() != reflect.Struct || seen[rt] {
return
}
seen[rt] = true
for i := 0; i < rt.NumField(); i++ {
field := rt.Field(i)
name := path + "." + field.Name
if !unicode.IsUpper(rune(field.Name[0])) {
// json.RawMessage and time internals are stdlib types a literal
// can still produce by value; only our own types matter.
if strings.HasPrefix(rt.PkgPath(), "github.com/DingTalk-Real-AI") {
blockers = append(blockers, name+" ("+rt.String()+")")
}
continue
}
walk(field.Type, name)
}
}
walk(reflect.TypeOf(cli.ToolSpec{}), "ToolSpec")
if len(blockers) != 0 {
t.Fatalf("unexported fields block literal construction: %v", blockers)
}
t.Logf("ToolSpec graph is fully exported across %d struct types", len(seen))
}