Drop the legacy-metadata overlay path (runtimeToolSpecAllowingLegacy, assembleSchemaRegistryFromBoundAllowingLegacy, metadata-based agent selection, dry-run seams) so assembly flows exclusively through ContractFinal; gate MCP fixture machinery behind a build flag so it can never enter production assembly. Wire output verified identical before and after; policy tripwire reports 26 products, 847 tools.
165 lines
5.6 KiB
Go
165 lines
5.6 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 (
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"runtime"
|
|
"strings"
|
|
"sync"
|
|
"testing"
|
|
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
// packageCLIAssembledDelivery holds the ResolveSchemaBuild dump installed by
|
|
// TestMain for package-cli tests (cannot import app).
|
|
var packageCLIAssembledDelivery *loadedSchemaCatalog
|
|
|
|
// TestMain assembles a fresh Catalog through cmd_schema_catalog (ResolveSchemaBuild)
|
|
// and installs it as the package-cli delivery source. Package cli cannot import
|
|
// internal/app (cycle); the generator subprocess owns the app root factory.
|
|
// Production binaries never use this path — only RegisterSchemaSourceRoot from app.
|
|
func TestMain(m *testing.M) {
|
|
cleanup, err := installAssembledSchemaDeliveryForPackageCLITests()
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "schema package-cli TestMain: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
code := m.Run()
|
|
cleanup()
|
|
os.Exit(code)
|
|
}
|
|
|
|
func installAssembledSchemaDeliveryForPackageCLITests() (func(), error) {
|
|
noop := func() {}
|
|
repoRoot, err := repoRootFromCLIPackage()
|
|
if err != nil {
|
|
return noop, err
|
|
}
|
|
tmp, err := os.MkdirTemp("", "dws-cli-schema-test-*")
|
|
if err != nil {
|
|
return noop, err
|
|
}
|
|
cleanup := func() { _ = os.RemoveAll(tmp) }
|
|
outDir := filepath.Join(tmp, "schema_catalog")
|
|
// On Windows, go build -o without .exe writes foo.exe; exec.Command must
|
|
// use the same path or TestMain fails during go test -list / coverage gate.
|
|
generator := filepath.Join(tmp, "cmd_schema_catalog")
|
|
if runtime.GOOS == "windows" {
|
|
generator += ".exe"
|
|
}
|
|
build := exec.Command("go", "build", "-o", generator, "./internal/generator/cmd_schema_catalog")
|
|
build.Dir = repoRoot
|
|
if out, err := build.CombinedOutput(); err != nil {
|
|
cleanup()
|
|
return noop, fmt.Errorf("build cmd_schema_catalog: %w\n%s", err, out)
|
|
}
|
|
run := exec.Command(generator, "-root", repoRoot, "-output", outDir, "-meta-index", filepath.Join(tmp, "schema_meta_index.gob"))
|
|
run.Dir = repoRoot
|
|
if out, err := run.CombinedOutput(); err != nil {
|
|
cleanup()
|
|
return noop, fmt.Errorf("run cmd_schema_catalog: %w\n%s", err, out)
|
|
}
|
|
envelope, err := os.ReadFile(filepath.Join(outDir, "catalog.json"))
|
|
if err != nil {
|
|
cleanup()
|
|
return noop, err
|
|
}
|
|
snapshot, err := mergeSchemaCatalogDump(envelope, filepath.Join(outDir, "tools"))
|
|
if err != nil {
|
|
cleanup()
|
|
return noop, fmt.Errorf("merge schema catalog dump: %w", err)
|
|
}
|
|
loaded, err := loadSchemaCatalogSnapshot(snapshot)
|
|
if err != nil {
|
|
cleanup()
|
|
return noop, fmt.Errorf("load schema catalog dump: %w", err)
|
|
}
|
|
packageCLIAssembledDelivery = &loaded
|
|
restorePackageCLISchemaDeliveryHook = restorePackageCLISchemaDeliveryForTest
|
|
restorePackageCLISchemaDeliveryForTest()
|
|
return cleanup, nil
|
|
}
|
|
|
|
// mergeSchemaCatalogDump re-merges a cmd_schema_catalog dump (catalog.json
|
|
// envelope plus per-product tools shards) into the single-document snapshot
|
|
// shape. Test-only: production delivery never reads a dump, and the generator
|
|
// owns the shard writer types.
|
|
func mergeSchemaCatalogDump(envelopeJSON []byte, toolsDir string) (SchemaCatalogSnapshot, error) {
|
|
var snapshot SchemaCatalogSnapshot
|
|
if err := decodeStrictSchemaJSON(envelopeJSON, &snapshot); err != nil {
|
|
return SchemaCatalogSnapshot{}, fmt.Errorf("decode schema catalog.json: %w", err)
|
|
}
|
|
entries, err := os.ReadDir(toolsDir)
|
|
if err != nil {
|
|
return SchemaCatalogSnapshot{}, fmt.Errorf("read schema catalog tools directory: %w", err)
|
|
}
|
|
tools := make(map[string]map[string]any, len(entries)*8)
|
|
for _, entry := range entries {
|
|
if entry.IsDir() || !strings.HasSuffix(entry.Name(), ".json") {
|
|
continue
|
|
}
|
|
data, readErr := os.ReadFile(filepath.Join(toolsDir, entry.Name()))
|
|
if readErr != nil {
|
|
return SchemaCatalogSnapshot{}, fmt.Errorf("read schema catalog shard %s: %w", entry.Name(), readErr)
|
|
}
|
|
var shard struct {
|
|
Product string `json:"product"`
|
|
Tools map[string]map[string]any `json:"tools"`
|
|
}
|
|
if err := decodeStrictSchemaJSON(data, &shard); err != nil {
|
|
return SchemaCatalogSnapshot{}, fmt.Errorf("decode schema catalog shard %s: %w", entry.Name(), err)
|
|
}
|
|
for canonical, spec := range shard.Tools {
|
|
tools[canonical] = spec
|
|
}
|
|
}
|
|
snapshot.Tools = tools
|
|
return snapshot, nil
|
|
}
|
|
|
|
func restorePackageCLISchemaDeliveryForTest() {
|
|
if packageCLIAssembledDelivery == nil {
|
|
return
|
|
}
|
|
loaded := *packageCLIAssembledDelivery
|
|
storeSchemaSourceRootFn(func() *cobra.Command {
|
|
return &cobra.Command{Use: "dws"}
|
|
})
|
|
assembleDeliverySchemaCatalogFn = func(*cobra.Command) (loadedSchemaCatalog, error) {
|
|
return loaded, nil
|
|
}
|
|
resetDeliverySchemaCatalogStateForTest()
|
|
metaByCLIPathOnce = sync.Once{}
|
|
metaByCLIPath = nil
|
|
runtimeDeliverySchemaMetaIndexErr = nil
|
|
runtimeDeliverySchemaMetaIndexLazyCount.Store(0)
|
|
}
|
|
|
|
func repoRootFromCLIPackage() (string, error) {
|
|
_, file, _, ok := runtime.Caller(0)
|
|
if !ok {
|
|
return "", fmt.Errorf("runtime.Caller failed")
|
|
}
|
|
root := filepath.Clean(filepath.Join(filepath.Dir(file), "..", ".."))
|
|
if _, err := os.Stat(filepath.Join(root, "go.mod")); err != nil {
|
|
return "", fmt.Errorf("repo root %q: %w", root, err)
|
|
}
|
|
return root, nil
|
|
}
|