// 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 app import ( "strings" "testing" "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/cli" "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/pipeline" "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/pkg/cmdutil" "github.com/spf13/cobra" "github.com/spf13/pflag" ) // TestParamAliasFixtureThroughEmbeddedDeliveryPath is the ⑥ regression gate. // It reads the reviewed validation_fixture straight from the embedded concept // dictionary and asserts every reviewed bad case through the REAL delivery // path — not a generator unit test and not a reimplementation of the reduction // logic: // // - the runtime PreParse engine built by newPipelineEngine() (the exact // handler chain root.go installs, whose SemanticAliasHandler is wired to // cli.LookupParamAlias over the embedded generated table), // - one distribution-owned Cobra tree, reused because PreParse reads command // and flag metadata but does not parse or mutate individual flag values, // and // - the embedded cli.LookupParamAlias query used to prove that a // did-you-mean case is an intentional block/ambiguous guard rather than a // name that merely happens to be absent from the table. // // Fixture expect semantics (see spec §⑥): // - expect= : emitted must reduce to that canonical flag. // - expect=did-you-mean:blocked : block guard hit; never auto-rewritten. // - expect=did-you-mean:ambiguous: co-occurrence guard hit; never rewritten. func TestCrossPlatformCoverageParamAliasFixtureThroughEmbeddedDeliveryPath(t *testing.T) { concepts, err := cli.LoadParamConcepts() if err != nil { t.Fatalf("LoadParamConcepts() error = %v", err) } if len(concepts.Fixture) == 0 { t.Fatal("validation_fixture declares no cases; ⑥ gate would be vacuous") } // The exact runtime handler chain (alias → semantic → sticky → // paramname), with the semantic table sourced from the embedded generated // snapshot. Build the distribution-owned tree once: constructing the full // 800+ command tree for every fixture made the macOS race package exceed its // 10-minute budget, while RunPreParseArgs itself only reads this tree. engine := newPipelineEngine() root := NewSchemaSourceRootCommand() for _, c := range concepts.Fixture { t.Run(c.Command+"/"+c.Emitted, func(t *testing.T) { leaf := resolveParamLeaf(root, c.Command) if leaf == nil { t.Fatalf("fixture command %q is not a live Cobra command", c.Command) } // Fixture command paths carry no "dws" prefix; LookupParamAlias // normalizes to the same key the generator used, so the runtime // lookup is byte-identical to the build-time key. entry, hasEntry := cli.LookupParamAlias(c.Command) fixtureValue := paramFixtureValue(leaf, c.Emitted, c.Expect) rawArgs := append(strings.Fields(c.Command), "--"+c.Emitted, fixtureValue) root.SetArgs(rawArgs) ctx, err := pipeline.RunPreParseArgs(root, engine, rawArgs) if err != nil { t.Fatalf("RunPreParseArgs error = %v", err) } if ctx == nil { t.Fatal("RunPreParseArgs skipped a fixture command with real flags") } morphed := cmdutil.Morph(c.Emitted) switch c.Expect { case "did-you-mean:ambiguous": if !hasEntry || !entry.IsAmbiguous(morphed) { t.Fatalf("%q on %q: expected co-occurrence guard (ambiguous) but embedded entry does not classify it; ambiguous=%v", c.Emitted, c.Command, entry.Ambiguous) } if commandHasRealFlagByMorph(leaf, morphed) { t.Fatalf("guarded --%s on %q is a real Cobra flag and would bypass the unknown-flag recovery path", c.Emitted, c.Command) } assertLeftUnchanged(t, ctx, c.Emitted, fixtureValue) case "did-you-mean:blocked": if !hasEntry || !entry.IsBlocked(morphed) { t.Fatalf("%q on %q: expected block guard but embedded entry does not classify it; blocked=%v", c.Emitted, c.Command, entry.Blocked) } if commandHasRealFlagByMorph(leaf, morphed) { t.Fatalf("guarded --%s on %q is a real Cobra flag and would bypass the unknown-flag recovery path", c.Emitted, c.Command) } assertLeftUnchanged(t, ctx, c.Emitted, fixtureValue) default: // Real-flag expect: the reviewed canonical outcome is delivered // one of two equally valid ways, and the gate accepts either // (failing only on a genuine unknown-flag hallucination): // 1. semantic rewrite — the emitted synonym is not a real flag, // so the embedded table rewrites it to the canonical flag; or // 2. native acceptance — the emitted synonym is still a genuine // (usually hidden) real flag the command accepts directly and // maps to the same entity via its fallback wiring. Native // compatibility flags intentionally remain command-owned. if !commandHasRealFlagByMorph(leaf, cmdutil.Morph(c.Expect)) { t.Fatalf("reviewed canonical --%s on %q is not a real Cobra flag", c.Expect, c.Command) } flagArgs := ctx.Args[len(strings.Fields(c.Command)):] if len(flagArgs) == 0 { t.Fatalf("%q on %q lost its flag and value: args=%v", c.Emitted, c.Command, ctx.Args) } got := flagArgs[0] gotBare, inlineValue, hasInlineValue := strings.Cut(strings.TrimPrefix(got, "--"), "=") switch { case hasInlineValue && inlineValue != fixtureValue: t.Fatalf("%q on %q changed its inline value: got %q, want %q (args=%v)", c.Emitted, c.Command, inlineValue, fixtureValue, ctx.Args) case !hasInlineValue && (len(flagArgs) < 2 || flagArgs[1] != fixtureValue): t.Fatalf("%q on %q lost its value: args=%v", c.Emitted, c.Command, ctx.Args) } switch { case gotBare == c.Expect: // (1) rewritten; the embedded table must agree. if !hasEntry { t.Fatalf("%q on %q was rewritten without an embedded alias entry", c.Emitted, c.Command) } if canon, hit := entry.ResolveAlias(morphed); !hit || canon != c.Expect { t.Fatalf("embedded table ResolveAlias(%q) on %q = %q (hit=%v), want %q", morphed, c.Command, canon, hit, c.Expect) } case cmdutil.Morph(gotBare) == morphed && commandHasRealFlagByMorph(leaf, morphed): // (2) not rewritten — only valid if the command natively // accepts the emitted synonym as a real flag. default: t.Fatalf("%q on %q reduced to unexpected %q, want --%s or native --%s (args=%v)", c.Emitted, c.Command, got, c.Expect, c.Emitted, ctx.Args) } } }) } } // assertLeftUnchanged verifies a guarded (blocked/ambiguous) synonym is never // silently rewritten: the flag token and its value survive verbatim so the // unknown-flag did-you-mean path can surface the reviewed candidates. func assertLeftUnchanged(t *testing.T, ctx *pipeline.Context, emitted, value string) { t.Helper() flagIndex := -1 for i, arg := range ctx.Args { if arg == "--"+emitted || strings.HasPrefix(arg, "--"+emitted+"=") { flagIndex = i break } } if flagIndex < 0 { t.Fatalf("guarded synonym --%s disappeared: args=%v", emitted, ctx.Args) } if got := ctx.Args[flagIndex]; got != "--"+emitted { t.Fatalf("guarded synonym --%s was rewritten to %q (must be left for did-you-mean): args=%v", emitted, got, ctx.Args) } if len(ctx.Args) <= flagIndex+1 || ctx.Args[flagIndex+1] != value { t.Fatalf("guarded synonym --%s lost its value: args=%v", emitted, ctx.Args) } for _, corr := range ctx.Corrections { if corr.Handler == "semantic-alias" && corr.Original == "--"+emitted { t.Fatalf("guarded synonym --%s was corrected by %s (must not be): %+v", emitted, corr.Handler, corr) } } } func paramFixtureValue(cmd *cobra.Command, emitted, expect string) string { if cmd == nil { return "FIXTURE_VALUE" } wanted := []string{emitted} if !strings.HasPrefix(expect, "did-you-mean:") { wanted = append(wanted, expect) } for _, name := range wanted { var found *pflag.Flag cmd.Flags().VisitAll(func(flag *pflag.Flag) { if found == nil && cmdutil.Morph(flag.Name) == cmdutil.Morph(name) { found = flag } }) if found == nil { continue } switch found.Value.Type() { case "bool": return "true" case "int", "int8", "int16", "int32", "int64", "uint", "uint8", "uint16", "uint32", "uint64", "float32", "float64": return "1" } } return "FIXTURE_VALUE" } // resolveParamLeaf resolves a fixture command path (no "dws" prefix, e.g. // "chat message search-advanced") to its live Cobra command, or nil. func resolveParamLeaf(root *cobra.Command, path string) *cobra.Command { cmd, _, err := root.Find(strings.Fields(path)) if err != nil || cmd == nil || cmd == root { return nil } return cmd } // commandHasRealFlagByMorph reports whether the command has any real flag // (local or inherited, including hidden) whose Morph matches morphed — the same // notion of "real flag" the build-time reducer uses to absorb legacy synonyms. func commandHasRealFlagByMorph(cmd *cobra.Command, morphed string) bool { found := false check := func(f *pflag.Flag) { if f.Name != "help" && cmdutil.Morph(f.Name) == morphed { found = true } } cmd.Flags().VisitAll(check) cmd.InheritedFlags().VisitAll(check) return found }