139 lines
4.9 KiB
Go
139 lines
4.9 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 helpers
|
||
|
||
import (
|
||
"encoding/json"
|
||
"strings"
|
||
"testing"
|
||
|
||
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/cli"
|
||
)
|
||
|
||
// devSchemaAllDump 是 dev 子树的 schema --all 装配 dump 形态(只读投影)。
|
||
// 由 AssembleSchemaRegistryFromBound(bind 后装配)→ ToPayload(--all dump)
|
||
// 生成,与生产 registerSchemaSourceRoot → ResolveSchemaBuild →
|
||
// deliverySchemaCatalog 同一条装配语义,仅命令表面收敛为 dev 子树。
|
||
type devSchemaAllDump struct {
|
||
Products []struct {
|
||
ID string `json:"id"`
|
||
Tools []struct {
|
||
CanonicalPath string `json:"canonical_path"`
|
||
Name string `json:"name"`
|
||
CLIPath string `json:"cli_path"`
|
||
} `json:"tools"`
|
||
} `json:"products"`
|
||
}
|
||
|
||
// assembleDevSchemaAllDump 在 dev 测试 root 上走 identity 收集 → bind → 装配 →
|
||
// --all dump 的旁路,返回轻量解析结构。只读,不改装配。
|
||
func assembleDevSchemaAllDump(t *testing.T) devSchemaAllDump {
|
||
t.Helper()
|
||
root := newDevAppTestRoot(&captureRunner{})
|
||
effective, err := cli.BuildEffectiveCommandRegistry(root)
|
||
if err != nil {
|
||
t.Fatalf("BuildEffectiveCommandRegistry(dev): %v", err)
|
||
}
|
||
bound, err := cli.BindEffectiveCommandRegistry(root, effective)
|
||
if err != nil {
|
||
t.Fatalf("BindEffectiveCommandRegistry(dev): %v", err)
|
||
}
|
||
registry, err := cli.AssembleSchemaRegistryFromBound(bound)
|
||
if err != nil {
|
||
t.Fatalf("AssembleSchemaRegistryFromBound(dev): %v", err)
|
||
}
|
||
payload, err := registry.ToPayload()
|
||
if err != nil {
|
||
t.Fatalf("SchemaRegistry.ToPayload(dev): %v", err)
|
||
}
|
||
raw, err := json.Marshal(payload)
|
||
if err != nil {
|
||
t.Fatalf("marshal dev schema --all dump: %v", err)
|
||
}
|
||
var dump devSchemaAllDump
|
||
if err := json.Unmarshal(raw, &dump); err != nil {
|
||
t.Fatalf("dev schema --all dump is not parseable: %v\n%s", err, raw)
|
||
}
|
||
return dump
|
||
}
|
||
|
||
// TestDevSchemaAllDumpToolsParseable 是队列 B192 的 dev tool 可解析旁路断言:
|
||
// dev 子树 schema --all 装配 dump 中每个 tool 必须可解析(canonical_path 与
|
||
// name 非空、cli_path 非空),且代表 dev 域核心能力的 tool 必须存在。任何
|
||
// 装配后在 dump 中泄漏「空 canonical / 空 name」的 dev tool 都会在此暴露。
|
||
func TestDevSchemaAllDumpToolsParseable(t *testing.T) {
|
||
dump := assembleDevSchemaAllDump(t)
|
||
|
||
var devProduct *struct {
|
||
ID string `json:"id"`
|
||
Tools []struct {
|
||
CanonicalPath string `json:"canonical_path"`
|
||
Name string `json:"name"`
|
||
CLIPath string `json:"cli_path"`
|
||
} `json:"tools"`
|
||
}
|
||
for i := range dump.Products {
|
||
if dump.Products[i].ID == "dev" {
|
||
devProduct = &dump.Products[i]
|
||
break
|
||
}
|
||
}
|
||
if devProduct == nil {
|
||
t.Fatalf("dev schema --all dump missing 'dev' product: got products %#v", dump.Products)
|
||
}
|
||
if len(devProduct.Tools) == 0 {
|
||
t.Fatal("dev schema --all dump has 0 tools")
|
||
}
|
||
|
||
canonicals := make(map[string]bool)
|
||
for _, tool := range devProduct.Tools {
|
||
toolName := tool.Name
|
||
t.Run(toolName, func(t *testing.T) {
|
||
if strings.TrimSpace(tool.CanonicalPath) == "" {
|
||
t.Fatalf("dev tool %q has empty canonical_path (not parseable)", toolName)
|
||
}
|
||
if strings.TrimSpace(tool.Name) == "" {
|
||
t.Fatalf("dev tool has empty name (canonical_path=%q)", tool.CanonicalPath)
|
||
}
|
||
if strings.TrimSpace(tool.CLIPath) == "" {
|
||
t.Fatalf("dev tool %q has empty cli_path", toolName)
|
||
}
|
||
canonicals[tool.CanonicalPath] = true
|
||
})
|
||
}
|
||
|
||
// dev 域核心能力必须出现在装配 dump(可解析旁路的目标集合)。
|
||
for _, want := range []string{"dev.get_dev_app", "dev.list_dev_app", "dev.list_dev_app_versions", "dev.list_dev_app_events", "dev.list_dev_app_permissions", "dev.list_dev_app_members"} {
|
||
if !canonicals[want] {
|
||
t.Errorf("dev schema --all dump missing canonical tool %q", want)
|
||
}
|
||
}
|
||
}
|
||
|
||
// TestDevSchemaAllDumpCountStable 是队列 B192 的补充观察断言:dev 子树装配 dump
|
||
// 的 tool 数量稳定(当前 37;统一框架已收编三条原 exclusion),作为回归基线快照。数量变化提示 dev 命令表面
|
||
// 变更,需复核装配。
|
||
func TestDevSchemaAllDumpCountStable(t *testing.T) {
|
||
dump := assembleDevSchemaAllDump(t)
|
||
for _, p := range dump.Products {
|
||
if p.ID == "dev" {
|
||
if len(p.Tools) != 37 {
|
||
t.Fatalf("dev schema --all tool count = %d, want 37 (baseline)", len(p.Tools))
|
||
}
|
||
return
|
||
}
|
||
}
|
||
t.Fatal("dev product not found in schema --all dump")
|
||
}
|