From ca88aae4cd8bf28afceea3015aa04923f0f6b1a2 Mon Sep 17 00:00:00 2001 From: Liu Cong Date: Thu, 19 Feb 2026 05:23:59 +0800 Subject: [PATCH 1/3] args auto map: implemented #222 Signed-off-by: Liu Cong --- pkg/core/model/args_auto_map.go | 51 ++- pkg/core/model/args_auto_map_conflict_test.go | 326 +++++++++++++++++ .../model/args_auto_map_integration_test.go | 118 +++++++ pkg/core/model/args_auto_map_nested_test.go | 329 ++++++++++++++++++ pkg/core/model/args_auto_map_smart_test.go | 134 +++++++ 5 files changed, 956 insertions(+), 2 deletions(-) create mode 100644 pkg/core/model/args_auto_map_conflict_test.go create mode 100644 pkg/core/model/args_auto_map_integration_test.go create mode 100644 pkg/core/model/args_auto_map_nested_test.go create mode 100644 pkg/core/model/args_auto_map_smart_test.go diff --git a/pkg/core/model/args_auto_map.go b/pkg/core/model/args_auto_map.go index 8d9f6000..69f7821b 100644 --- a/pkg/core/model/args_auto_map.go +++ b/pkg/core/model/args_auto_map.go @@ -30,7 +30,7 @@ func autoMapArg2EnvForCmd( targetCmd *Cmd, stackDepth int) (done bool) { - targetCmd.GetArgsAutoMapStatus().MarkMet(srcCmd) + targetCmd.GetArgsAutoMapStatus().MarkMetWithArgv(srcCmd, argv) if !srcCmd.HasSubFlow(true) { return false } @@ -50,6 +50,10 @@ func autoMapArg2EnvForCmd( flowEnv := env.NewLayer(EnvLayerSubFlow) parsedFlow.GlobalEnv.WriteNotArgTo(flowEnv, cc.Cmds.Strs.EnvValDelAllMark) + for key := range parsedFlow.GlobalEnv { + targetCmd.GetArgsAutoMapStatus().RecordGlobalEnvKey(key) + } + return autoMapArg2EnvForCmdsInFlow(cc, flowEnv, parsedFlow, 0, envOpCmds, targetCmd, stackDepth) } @@ -83,9 +87,15 @@ func autoMapArg2EnvForCmdsInFlow( return true } - TryExeEnvOpCmds(argv, cc, cmdEnv, flow, i, envOpCmds, nil, + var checker EnvOpsChecker + checker = EnvOpsChecker{} + TryExeEnvOpCmds(argv, cc, cmdEnv, flow, i, envOpCmds, &checker, "failed to execute env op-cmd in depends collecting") + for key := range checker { + targetCmd.GetArgsAutoMapStatus().RecordGlobalEnvKey(key) + } + //println(cic.Owner().DisplayPath(), " (arg2env) =>", targetCmd.Owner().DisplayPath()) targetCmd.AddArg2EnvFromAnotherCmd(cic) if targetCmd.GetArgsAutoMapStatus().FullyMapped() { @@ -216,6 +226,14 @@ func (self *ArgsAutoMapStatus) MarkMet(srcCmd *Cmd) { self.recordProvidedKeys(srcCmd) } +func (self *ArgsAutoMapStatus) MarkMetWithArgv(srcCmd *Cmd, argv ArgVals) { + if _, ok := self.metCmds[srcCmd]; ok { + return + } + self.metCmds[srcCmd] = true + self.recordProvidedKeysWithArgv(srcCmd, argv) +} + func (self *ArgsAutoMapStatus) FullyMappedOrMapAll() bool { if self.mapAll || self.mapNoProvider { return true @@ -373,4 +391,33 @@ func (self *ArgsAutoMapStatus) recordProvidedKeys(srcCmd *Cmd) { for _, key := range envOps.AllWriteKeys() { self.providedKeys[key] = true } + + val2env := srcCmd.GetVal2Env() + for _, key := range val2env.EnvKeys() { + self.providedKeys[key] = true + } +} + +func (self *ArgsAutoMapStatus) recordProvidedKeysWithArgv(srcCmd *Cmd, argv ArgVals) { + envOps := srcCmd.EnvOps() + for _, key := range envOps.AllWriteKeys() { + self.providedKeys[key] = true + } + + val2env := srcCmd.GetVal2Env() + for _, key := range val2env.EnvKeys() { + self.providedKeys[key] = true + } + + arg2env := srcCmd.GetArg2Env() + for _, envKey := range arg2env.EnvKeys() { + argName := arg2env.GetArgName(srcCmd, envKey, true) + if val, ok := argv[argName]; ok && val.Provided { + self.providedKeys[envKey] = true + } + } +} + +func (self *ArgsAutoMapStatus) RecordGlobalEnvKey(key string) { + self.providedKeys[key] = true } diff --git a/pkg/core/model/args_auto_map_conflict_test.go b/pkg/core/model/args_auto_map_conflict_test.go new file mode 100644 index 00000000..fc746e0b --- /dev/null +++ b/pkg/core/model/args_auto_map_conflict_test.go @@ -0,0 +1,326 @@ +package model + +import ( + "testing" +) + +func TestArgsAutoMapSameKeyFromDifferentSources(t *testing.T) { + strs := CmdTreeStrsForTest() + tree := NewCmdTree(strs) + + // Create source 1 with key1 + src1Cmd := tree.AddSub("src1") + src1 := src1Cmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { + return currCmdIdx, nil + }, "source 1") + src1.AddArg("arg1", "default1") + src1.AddArg2Env("conflict.key", "arg1") + + // Create source 2 with same key + src2Cmd := tree.AddSub("src2") + src2 := src2Cmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { + return currCmdIdx, nil + }, "source 2") + src2.AddArg("arg2", "default2") + src2.AddArg2Env("conflict.key", "arg2") + + // Create target with auto map + targetCmd := tree.AddSub("conflicttarget1") + target := targetCmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { + return currCmdIdx, nil + }, "conflict target 1") + + _, err := target.SetArg2EnvAutoMap([]string{"*"}) + if err != nil { + t.Fatalf("failed to set auto map: %v", err) + } + + status := target.GetArgsAutoMapStatus() + + // Mark both sources + argv1 := ArgVals{"arg1": ArgVal{Raw: "val1", Provided: true}} + argv2 := ArgVals{"arg2": ArgVal{Raw: "val2", Provided: true}} + + status.MarkMetWithArgv(src1, argv1) + status.MarkMetWithArgv(src2, argv2) + + // The key should be recorded once (from whichever source marked it first) + if !status.providedKeys["conflict.key"] { + t.Error("conflict key should be recorded as provided") + } +} + +func TestArgsAutoMapArgNameConflictFromDifferentSources(t *testing.T) { + strs := CmdTreeStrsForTest() + tree := NewCmdTree(strs) + + // Create source 1 with arg "data" + src1Cmd := tree.AddSub("srcarg1") + src1 := src1Cmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { + return currCmdIdx, nil + }, "source arg 1") + src1.AddArg("data", "default1") + src1.AddArg2Env("key1", "data") + + // Create source 2 with same arg name "data" + src2Cmd := tree.AddSub("srcarg2") + src2 := src2Cmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { + return currCmdIdx, nil + }, "source arg 2") + src2.AddArg("data", "default2") + src2.AddArg2Env("key2", "data") + + // Create target with auto map + targetCmd := tree.AddSub("argconflicttarget") + target := targetCmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { + return currCmdIdx, nil + }, "arg conflict target") + + _, err := target.SetArg2EnvAutoMap([]string{"*"}) + if err != nil { + t.Fatalf("failed to set auto map: %v", err) + } + + status := target.GetArgsAutoMapStatus() + + // Mark both sources with provided args + argv1 := ArgVals{"data": ArgVal{Raw: "val1", Provided: true}} + argv2 := ArgVals{"data": ArgVal{Raw: "val2", Provided: true}} + + status.MarkMetWithArgv(src1, argv1) + status.MarkMetWithArgv(src2, argv2) + + // Both keys should be recorded + if !status.providedKeys["key1"] { + t.Error("key1 should be recorded as provided") + } + if !status.providedKeys["key2"] { + t.Error("key2 should be recorded as provided") + } +} + +func TestArgsAutoMapNestedFlowKeyConflict(t *testing.T) { + strs := CmdTreeStrsForTest() + tree := NewCmdTree(strs) + + // Create inner command with key + innerCmd := tree.AddSub("innerconflict") + inner := innerCmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { + return currCmdIdx, nil + }, "inner conflict") + inner.AddArg("arg", "default") + inner.AddArg2Env("nested.conflict.key", "arg") + + // Create outer command with same key + outerCmd := tree.AddSub("outerconflict") + outer := outerCmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { + return currCmdIdx, nil + }, "outer conflict") + outer.AddArg("arg", "default") + outer.AddArg2Env("nested.conflict.key", "arg") + + // Create target with auto map + targetCmd := tree.AddSub("nestedconflicttarget") + target := targetCmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { + return currCmdIdx, nil + }, "nested conflict target") + + _, err := target.SetArg2EnvAutoMap([]string{"*"}) + if err != nil { + t.Fatalf("failed to set auto map: %v", err) + } + + status := target.GetArgsAutoMapStatus() + + // Mark both with provided args + argvInner := ArgVals{"arg": ArgVal{Raw: "inner-val", Provided: true}} + argvOuter := ArgVals{"arg": ArgVal{Raw: "outer-val", Provided: true}} + + status.MarkMetWithArgv(inner, argvInner) + status.MarkMetWithArgv(outer, argvOuter) + + // The key should be recorded (once) + if !status.providedKeys["nested.conflict.key"] { + t.Error("nested conflict key should be recorded as provided") + } +} + +func TestArgsAutoMapMultipleAbbrConflict(t *testing.T) { + strs := CmdTreeStrsForTest() + tree := NewCmdTree(strs) + + // Create source with arg that has abbr + srcCmd := tree.AddSub("abbrconflictsrc") + src := srcCmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { + return currCmdIdx, nil + }, "abbr conflict source") + src.AddArg("longargname", "default", "lan", "ln") + src.AddArg2Env("abbr.key", "longargname") + + // Create target with auto map + targetCmd := tree.AddSub("abbrconflicttarget") + target := targetCmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { + return currCmdIdx, nil + }, "abbr conflict target") + + // Target already has an arg with conflicting abbr + target.AddArg("existing", "default", "lan") + + _, err := target.SetArg2EnvAutoMap([]string{"*"}) + if err != nil { + t.Fatalf("failed to set auto map: %v", err) + } + + status := target.GetArgsAutoMapStatus() + + // Mark source with provided arg + argv := ArgVals{"longargname": ArgVal{Raw: "val", Provided: true}} + status.MarkMetWithArgv(src, argv) + + // The key should be recorded + if !status.providedKeys["abbr.key"] { + t.Error("abbr key should be recorded as provided") + } +} + +func TestArgsAutoMapKeyConflictWithVal2EnvAndArg2Env(t *testing.T) { + strs := CmdTreeStrsForTest() + tree := NewCmdTree(strs) + + // Create source with both val2env and arg2env to same key + srcCmd := tree.AddSub("valargconflictsrc") + src := srcCmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { + return currCmdIdx, nil + }, "val arg conflict source") + + // Add val2env first + src.AddVal2Env("conflict.key", "val-value") + + // Try to add arg2env to same key - this should be prevented by AddArg2Env + // But we can test the provided keys tracking + src.AddArg("arg", "default") + + // Create target + targetCmd := tree.AddSub("valargconflicttarget") + target := targetCmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { + return currCmdIdx, nil + }, "val arg conflict target") + + _, err := target.SetArg2EnvAutoMap([]string{"*"}) + if err != nil { + t.Fatalf("failed to set auto map: %v", err) + } + + status := target.GetArgsAutoMapStatus() + + // Mark source + argv := ArgVals{"arg": ArgVal{Raw: "arg-val", Provided: true}} + status.MarkMetWithArgv(src, argv) + + // val2env key should be recorded + if !status.providedKeys["conflict.key"] { + t.Error("val2env key should be recorded as provided") + } +} + +func TestArgsAutoMapDeepNestedConflictResolution(t *testing.T) { + strs := CmdTreeStrsForTest() + tree := NewCmdTree(strs) + + // Create 3 levels, each trying to provide the same key + level1Cmd := tree.AddSub("deepconflict1") + level1 := level1Cmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { + return currCmdIdx, nil + }, "deep conflict level 1") + level1.AddArg("arg1", "default1") + level1.AddArg2Env("deep.conflict", "arg1") + + level2Cmd := tree.AddSub("deepconflict2") + level2 := level2Cmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { + return currCmdIdx, nil + }, "deep conflict level 2") + level2.AddArg("arg2", "default2") + level2.AddArg2Env("deep.conflict", "arg2") + + level3Cmd := tree.AddSub("deepconflict3") + level3 := level3Cmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { + return currCmdIdx, nil + }, "deep conflict level 3") + level3.AddArg("arg3", "default3") + level3.AddArg2Env("deep.conflict", "arg3") + + // Create target + targetCmd := tree.AddSub("deepconflicttarget") + target := targetCmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { + return currCmdIdx, nil + }, "deep conflict target") + + _, err := target.SetArg2EnvAutoMap([]string{"*"}) + if err != nil { + t.Fatalf("failed to set auto map: %v", err) + } + + status := target.GetArgsAutoMapStatus() + + // Mark all levels with provided args + argv1 := ArgVals{"arg1": ArgVal{Raw: "val1", Provided: true}} + argv2 := ArgVals{"arg2": ArgVal{Raw: "val2", Provided: true}} + argv3 := ArgVals{"arg3": ArgVal{Raw: "val3", Provided: true}} + + status.MarkMetWithArgv(level1, argv1) + status.MarkMetWithArgv(level2, argv2) + status.MarkMetWithArgv(level3, argv3) + + // The key should be recorded once (first one wins) + if !status.providedKeys["deep.conflict"] { + t.Error("deep conflict key should be recorded as provided") + } + + // Count should be 1, not 3 + count := 0 + for key := range status.providedKeys { + if key == "deep.conflict" { + count++ + } + } + if count != 1 { + t.Errorf("deep.conflict key should be recorded once, got %d times", count) + } +} + +func TestArgsAutoMapConflictWithExistingTargetArg(t *testing.T) { + strs := CmdTreeStrsForTest() + tree := NewCmdTree(strs) + + // Create source with arg + srcCmd := tree.AddSub("existingargsrc") + src := srcCmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { + return currCmdIdx, nil + }, "existing arg source") + src.AddArg("srcarg", "default") + src.AddArg2Env("existing.key", "srcarg") + + // Create target with existing arg that maps to different key + targetCmd := tree.AddSub("existingargtarget") + target := targetCmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { + return currCmdIdx, nil + }, "existing arg target") + target.AddArg("targetarg", "default") + target.AddArg2Env("existing.key", "targetarg") + + _, err := target.SetArg2EnvAutoMap([]string{"*"}) + if err != nil { + t.Fatalf("failed to set auto map: %v", err) + } + + status := target.GetArgsAutoMapStatus() + + // Mark source + argv := ArgVals{"srcarg": ArgVal{Raw: "val", Provided: true}} + status.MarkMetWithArgv(src, argv) + + // The key should be recorded + if !status.providedKeys["existing.key"] { + t.Error("existing key should be recorded as provided") + } +} diff --git a/pkg/core/model/args_auto_map_integration_test.go b/pkg/core/model/args_auto_map_integration_test.go new file mode 100644 index 00000000..2e112c30 --- /dev/null +++ b/pkg/core/model/args_auto_map_integration_test.go @@ -0,0 +1,118 @@ +package model + +import ( + "testing" +) + +func TestArgsAutoMapSmartMappingIntegration(t *testing.T) { + strs := CmdTreeStrsForTest() + tree := NewCmdTree(strs) + + // Create target command with smart mapping + targetCmd := tree.AddSub("inttarget") + target := targetCmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { + return currCmdIdx, nil + }, "integration target command") + + _, err := target.SetArg2EnvAutoMap([]string{"*"}) + if err != nil { + t.Fatalf("failed to set auto map: %v", err) + } + + // Create source command with multiple env operations + srcCmd := tree.AddSub("intsource") + src := srcCmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { + return currCmdIdx, nil + }, "integration source command") + + // Add val2env + src.AddVal2Env("val.key", "val-value") + + // Add arg2env + src.AddArg("arg1", "default1") + src.AddArg2Env("arg.key", "arg1") + + // Add env op + src.AddEnvOp("env.op.key", EnvOpTypeWrite) + + // Create argv with provided argument + argv := ArgVals{ + "arg1": ArgVal{Raw: "provided-value", Provided: true}, + } + + // Mark the source command with argv + status := target.GetArgsAutoMapStatus() + status.MarkMetWithArgv(src, argv) + + // Verify all provided keys are recorded + if !status.providedKeys["val.key"] { + t.Error("val2env key should be recorded as provided") + } + if !status.providedKeys["arg.key"] { + t.Error("arg2env key with provided arg should be recorded as provided") + } + if !status.providedKeys["env.op.key"] { + t.Error("env op key should be recorded as provided") + } + + // Test that non-provided arg2env is not recorded + src2Cmd := tree.AddSub("intsource2") + src2 := src2Cmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { + return currCmdIdx, nil + }, "integration source command 2") + + src2.AddArg("arg2", "default2") + src2.AddArg2Env("not.provided.arg.key", "arg2") + + argv2 := ArgVals{ + "arg2": ArgVal{Raw: "default2", Provided: false}, + } + + status.MarkMetWithArgv(src2, argv2) + + if status.providedKeys["not.provided.arg.key"] { + t.Error("arg2env key with non-provided arg should NOT be recorded as provided") + } +} + +func TestArgsAutoMapFlushCacheWithSmartMapping(t *testing.T) { + strs := CmdTreeStrsForTest() + tree := NewCmdTree(strs) + + // Create target command with smart mapping + targetCmd := tree.AddSub("flushtarget") + target := targetCmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { + return currCmdIdx, nil + }, "flush target command") + + _, err := target.SetArg2EnvAutoMap([]string{"*"}) + if err != nil { + t.Fatalf("failed to set auto map: %v", err) + } + + // Create source command + srcCmd := tree.AddSub("flushsource") + src := srcCmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { + return currCmdIdx, nil + }, "flush source command") + + src.AddArg("arg1", "default1") + + // Mark source as met and cache a mapping + status := target.GetArgsAutoMapStatus() + status.MarkAndCacheMapping(src, "test.key", "test.arg", "default-val", nil, true) + + // Record the key as provided + status.RecordGlobalEnvKey("test.key") + + // Flush cache + err = status.FlushCache(target) + if err != nil { + t.Fatalf("failed to flush cache: %v", err) + } + + // Verify the provided key was NOT mapped (smart mapping) + if target.GetArg2Env().Has("test.key") { + t.Error("provided key should not be mapped in smart mode") + } +} diff --git a/pkg/core/model/args_auto_map_nested_test.go b/pkg/core/model/args_auto_map_nested_test.go new file mode 100644 index 00000000..77330afb --- /dev/null +++ b/pkg/core/model/args_auto_map_nested_test.go @@ -0,0 +1,329 @@ +package model + +import ( + "testing" +) + +func TestArgsAutoMapNestedFlowWithProvidedKeysAtDifferentLevels(t *testing.T) { + strs := CmdTreeStrsForTest() + tree := NewCmdTree(strs) + + // Create level 3 command with val2env + level3Cmd := tree.AddSub("deeplevel") + level3 := level3Cmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { + return currCmdIdx, nil + }, "deep level command") + level3.AddVal2Env("deep.provided.key", "deep-value") + level3.AddArg("deeparg", "deep-default") + level3.AddArg2Env("deep.arg.key", "deeparg") + + // Create level 2 command with arg2env + level2Cmd := tree.AddSub("midlevel") + level2 := level2Cmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { + return currCmdIdx, nil + }, "mid level command") + level2.AddArg("midarg", "mid-default") + level2.AddArg2Env("mid.arg.key", "midarg") + + // Create level 1 command + level1Cmd := tree.AddSub("toplevel") + level1 := level1Cmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { + return currCmdIdx, nil + }, "top level command") + level1.AddArg("toparg", "top-default") + level1.AddArg2Env("top.arg.key", "toparg") + + // Create target command with smart mapping + targetCmd := tree.AddSub("nestedtarget") + target := targetCmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { + return currCmdIdx, nil + }, "nested target command") + + _, err := target.SetArg2EnvAutoMap([]string{"*"}) + if err != nil { + t.Fatalf("failed to set auto map: %v", err) + } + + status := target.GetArgsAutoMapStatus() + + // Mark all levels with different argv states + argvDeep := ArgVals{ + "deeparg": ArgVal{Raw: "provided-deep", Provided: true}, + } + argvMid := ArgVals{ + "midarg": ArgVal{Raw: "mid-default", Provided: false}, + } + argvTop := ArgVals{ + "toparg": ArgVal{Raw: "provided-top", Provided: true}, + } + + status.MarkMetWithArgv(level3, argvDeep) + status.MarkMetWithArgv(level2, argvMid) + status.MarkMetWithArgv(level1, argvTop) + + // Verify all provided keys are recorded + if !status.providedKeys["deep.provided.key"] { + t.Error("deep level val2env key should be recorded") + } + if !status.providedKeys["deep.arg.key"] { + t.Error("deep level arg2env key with provided arg should be recorded") + } + if !status.providedKeys["top.arg.key"] { + t.Error("top level arg2env key with provided arg should be recorded") + } + if status.providedKeys["mid.arg.key"] { + t.Error("mid level arg2env key with non-provided arg should NOT be recorded") + } +} + +func TestArgsAutoMapFiveLevelNestedFlow(t *testing.T) { + strs := CmdTreeStrsForTest() + tree := NewCmdTree(strs) + + // Create 5 levels of nested commands + for i := 5; i >= 1; i-- { + cmdName := "nested5l" + string(rune('0'+i)) + cmdTree := tree.AddSub(cmdName) + + if i == 5 { + // Deepest level - power cmd with arg + cmd := cmdTree.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { + return currCmdIdx, nil + }, cmdName+" command") + cmd.AddArg("deeparg", "deepval") + cmd.AddArg2Env("nested.deep.key", "deeparg") + } else { + // Upper levels - flow cmds + prevPath := "nested5l" + string(rune('0'+i+1)) + cmdTree.RegFlowCmd([]string{prevPath}, cmdName+" command", "") + } + } + + // Create target command + targetCmd := tree.AddSub("nested5target") + target := targetCmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { + return currCmdIdx, nil + }, "5 level nested target") + + _, err := target.SetArg2EnvAutoMap([]string{"*"}) + if err != nil { + t.Fatalf("failed to set auto map: %v", err) + } + + // Verify the structure is correct + status := target.GetArgsAutoMapStatus() + if status.mapNoProvider != true { + t.Error("smart mapping should be enabled") + } +} + +func TestArgsAutoMapNestedFlowWithGlobalEnvAtDifferentLevels(t *testing.T) { + strs := CmdTreeStrsForTest() + tree := NewCmdTree(strs) + + // Create inner command + innerCmd := tree.AddSub("innercmd") + inner := innerCmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { + return currCmdIdx, nil + }, "inner command") + inner.AddArg("innerarg", "inner-default") + inner.AddArg2Env("inner.key", "innerarg") + + // Create outer command + outerCmd := tree.AddSub("outercmd") + outer := outerCmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { + return currCmdIdx, nil + }, "outer command") + outer.AddArg("outerarg", "outer-default") + outer.AddArg2Env("outer.key", "outerarg") + + // Create target command + targetCmd := tree.AddSub("nestedglobaltarget") + target := targetCmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { + return currCmdIdx, nil + }, "nested global env target") + + _, err := target.SetArg2EnvAutoMap([]string{"*"}) + if err != nil { + t.Fatalf("failed to set auto map: %v", err) + } + + status := target.GetArgsAutoMapStatus() + + // Record global env keys at different levels + status.RecordGlobalEnvKey("global.level1.key") + status.RecordGlobalEnvKey("global.level2.key") + + // Mark inner and outer with provided args + innerArgv := ArgVals{ + "innerarg": ArgVal{Raw: "inner-provided", Provided: true}, + } + outerArgv := ArgVals{ + "outerarg": ArgVal{Raw: "outer-provided", Provided: true}, + } + + status.MarkMetWithArgv(inner, innerArgv) + status.MarkMetWithArgv(outer, outerArgv) + + // Verify all keys are recorded + expectedKeys := []string{ + "global.level1.key", + "global.level2.key", + "inner.key", + "outer.key", + } + + for _, key := range expectedKeys { + if !status.providedKeys[key] { + t.Errorf("key %s should be recorded as provided", key) + } + } +} + +func TestArgsAutoMapNestedFlowWithEnvOpsAtDifferentLevels(t *testing.T) { + strs := CmdTreeStrsForTest() + tree := NewCmdTree(strs) + + // Create commands at different levels with env ops + level1Cmd := tree.AddSub("envopsl1") + level1 := level1Cmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { + return currCmdIdx, nil + }, "env ops level 1") + level1.AddEnvOp("envop.l1.key1", EnvOpTypeWrite) + level1.AddEnvOp("envop.l1.key2", EnvOpTypeWrite) + + level2Cmd := tree.AddSub("envopsl2") + level2 := level2Cmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { + return currCmdIdx, nil + }, "env ops level 2") + level2.AddEnvOp("envop.l2.key1", EnvOpTypeWrite) + + level3Cmd := tree.AddSub("envopsl3") + level3 := level3Cmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { + return currCmdIdx, nil + }, "env ops level 3") + level3.AddEnvOp("envop.l3.key1", EnvOpTypeWrite) + + // Create target command + targetCmd := tree.AddSub("envopstarget") + target := targetCmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { + return currCmdIdx, nil + }, "env ops target") + + _, err := target.SetArg2EnvAutoMap([]string{"*"}) + if err != nil { + t.Fatalf("failed to set auto map: %v", err) + } + + status := target.GetArgsAutoMapStatus() + + // Mark all levels + status.MarkMet(level1) + status.MarkMet(level2) + status.MarkMet(level3) + + // Verify all env op keys are recorded + expectedKeys := []string{ + "envop.l1.key1", + "envop.l1.key2", + "envop.l2.key1", + "envop.l3.key1", + } + + for _, key := range expectedKeys { + if !status.providedKeys[key] { + t.Errorf("env op key %s should be recorded as provided", key) + } + } +} + +func TestArgsAutoMapDeeplyNestedWithMixedProvidedKeys(t *testing.T) { + strs := CmdTreeStrsForTest() + tree := NewCmdTree(strs) + + // Create 4 levels with mixed provided key types + // Level 4: val2env + arg2env (provided) + level4Cmd := tree.AddSub("mixl4") + level4 := level4Cmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { + return currCmdIdx, nil + }, "mixed level 4") + level4.AddVal2Env("mix.val.key", "val") + level4.AddArg("arg4", "default4") + level4.AddArg2Env("mix.arg4.key", "arg4") + + // Level 3: env op + arg2env (not provided) + level3Cmd := tree.AddSub("mixl3") + level3 := level3Cmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { + return currCmdIdx, nil + }, "mixed level 3") + level3.AddEnvOp("mix.envop.key", EnvOpTypeWrite) + level3.AddArg("arg3", "default3") + level3.AddArg2Env("mix.arg3.key", "arg3") + + // Level 2: only arg2env (provided) + level2Cmd := tree.AddSub("mixl2") + level2 := level2Cmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { + return currCmdIdx, nil + }, "mixed level 2") + level2.AddArg("arg2", "default2") + level2.AddArg2Env("mix.arg2.key", "arg2") + + // Level 1: arg2env (not provided) + level1Cmd := tree.AddSub("mixl1") + level1 := level1Cmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { + return currCmdIdx, nil + }, "mixed level 1") + level1.AddArg("arg1", "default1") + level1.AddArg2Env("mix.arg1.key", "arg1") + + // Create target + targetCmd := tree.AddSub("mixedtarget") + target := targetCmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { + return currCmdIdx, nil + }, "mixed target") + + _, err := target.SetArg2EnvAutoMap([]string{"*"}) + if err != nil { + t.Fatalf("failed to set auto map: %v", err) + } + + status := target.GetArgsAutoMapStatus() + + // Mark levels with different argv states + argv4 := ArgVals{"arg4": ArgVal{Raw: "provided4", Provided: true}} + argv3 := ArgVals{"arg3": ArgVal{Raw: "default3", Provided: false}} + argv2 := ArgVals{"arg2": ArgVal{Raw: "provided2", Provided: true}} + argv1 := ArgVals{"arg1": ArgVal{Raw: "default1", Provided: false}} + + status.MarkMetWithArgv(level4, argv4) + status.MarkMetWithArgv(level3, argv3) + status.MarkMetWithArgv(level2, argv2) + status.MarkMetWithArgv(level1, argv1) + status.RecordGlobalEnvKey("mix.global.key") + + // Verify expected provided keys + shouldExist := []string{ + "mix.val.key", // val2env always provided + "mix.arg4.key", // arg2env with provided arg + "mix.envop.key", // env op always provided + "mix.arg2.key", // arg2env with provided arg + "mix.global.key", // global env + } + + shouldNotExist := []string{ + "mix.arg3.key", // arg2env with non-provided arg + "mix.arg1.key", // arg2env with non-provided arg + } + + for _, key := range shouldExist { + if !status.providedKeys[key] { + t.Errorf("key %s should be recorded as provided", key) + } + } + + for _, key := range shouldNotExist { + if status.providedKeys[key] { + t.Errorf("key %s should NOT be recorded as provided", key) + } + } +} diff --git a/pkg/core/model/args_auto_map_smart_test.go b/pkg/core/model/args_auto_map_smart_test.go new file mode 100644 index 00000000..217649a6 --- /dev/null +++ b/pkg/core/model/args_auto_map_smart_test.go @@ -0,0 +1,134 @@ +package model + +import ( + "testing" +) + +func TestArgsAutoMapSmartMappingWithVal2Env(t *testing.T) { + strs := CmdTreeStrsForTest() + tree := NewCmdTree(strs) + + // Create target command with smart mapping + targetCmd := tree.AddSub("target") + target := targetCmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { + return currCmdIdx, nil + }, "target command") + + _, err := target.SetArg2EnvAutoMap([]string{"*"}) + if err != nil { + t.Fatalf("failed to set auto map: %v", err) + } + + // Create source command with val2env + srcCmd := tree.AddSub("source") + src := srcCmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { + return currCmdIdx, nil + }, "source command") + src.AddVal2Env("provided.key", "value") + + // Verify that the provided key is recorded + status := target.GetArgsAutoMapStatus() + status.MarkMet(src) + + if !status.providedKeys["provided.key"] { + t.Error("val2env key should be recorded as provided") + } +} + +func TestArgsAutoMapSmartMappingWithArg2EnvProvided(t *testing.T) { + strs := CmdTreeStrsForTest() + tree := NewCmdTree(strs) + + // Create target command with smart mapping + targetCmd := tree.AddSub("target2") + target := targetCmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { + return currCmdIdx, nil + }, "target command") + + _, err := target.SetArg2EnvAutoMap([]string{"*"}) + if err != nil { + t.Fatalf("failed to set auto map: %v", err) + } + + // Create source command with arg2env + srcCmd := tree.AddSub("source2") + src := srcCmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { + return currCmdIdx, nil + }, "source command") + src.AddArg("myarg", "default-value") + src.AddArg2Env("provided.arg.key", "myarg") + + // Create argv with provided argument + argv := ArgVals{ + "myarg": ArgVal{Raw: "provided-value", Provided: true}, + } + + // Verify that the provided key is recorded when arg is provided + status := target.GetArgsAutoMapStatus() + status.MarkMetWithArgv(src, argv) + + if !status.providedKeys["provided.arg.key"] { + t.Error("arg2env key should be recorded as provided when arg is provided") + } +} + +func TestArgsAutoMapSmartMappingWithArg2EnvNotProvided(t *testing.T) { + strs := CmdTreeStrsForTest() + tree := NewCmdTree(strs) + + // Create target command with smart mapping + targetCmd := tree.AddSub("target3") + target := targetCmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { + return currCmdIdx, nil + }, "target command") + + _, err := target.SetArg2EnvAutoMap([]string{"*"}) + if err != nil { + t.Fatalf("failed to set auto map: %v", err) + } + + // Create source command with arg2env + srcCmd := tree.AddSub("source3") + src := srcCmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { + return currCmdIdx, nil + }, "source command") + src.AddArg("myarg", "default-value") + src.AddArg2Env("not.provided.key", "myarg") + + // Create argv without provided argument + argv := ArgVals{ + "myarg": ArgVal{Raw: "default-value", Provided: false}, + } + + // Verify that the key is NOT recorded when arg is not provided + status := target.GetArgsAutoMapStatus() + status.MarkMetWithArgv(src, argv) + + if status.providedKeys["not.provided.key"] { + t.Error("arg2env key should NOT be recorded as provided when arg is not provided") + } +} + +func TestArgsAutoMapSmartMappingWithGlobalEnv(t *testing.T) { + strs := CmdTreeStrsForTest() + tree := NewCmdTree(strs) + + // Create target command with smart mapping + targetCmd := tree.AddSub("target4") + target := targetCmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { + return currCmdIdx, nil + }, "target command") + + _, err := target.SetArg2EnvAutoMap([]string{"*"}) + if err != nil { + t.Fatalf("failed to set auto map: %v", err) + } + + // Verify that global env keys are recorded + status := target.GetArgsAutoMapStatus() + status.RecordGlobalEnvKey("global.env.key") + + if !status.providedKeys["global.env.key"] { + t.Error("global env key should be recorded as provided") + } +} From c536d36e385f7efa8424df57db3165f3d75c659a Mon Sep 17 00:00:00 2001 From: Liu Cong Date: Thu, 19 Feb 2026 05:31:42 +0800 Subject: [PATCH 2/3] lint Signed-off-by: Liu Cong --- pkg/core/model/args_auto_map_conflict_test.go | 110 +++++++++--------- .../model/args_auto_map_integration_test.go | 44 +++---- pkg/core/model/args_auto_map_nested_test.go | 106 ++++++++--------- pkg/core/model/args_auto_map_smart_test.go | 42 +++---- 4 files changed, 151 insertions(+), 151 deletions(-) diff --git a/pkg/core/model/args_auto_map_conflict_test.go b/pkg/core/model/args_auto_map_conflict_test.go index fc746e0b..7080e0f7 100644 --- a/pkg/core/model/args_auto_map_conflict_test.go +++ b/pkg/core/model/args_auto_map_conflict_test.go @@ -7,7 +7,7 @@ import ( func TestArgsAutoMapSameKeyFromDifferentSources(t *testing.T) { strs := CmdTreeStrsForTest() tree := NewCmdTree(strs) - + // Create source 1 with key1 src1Cmd := tree.AddSub("src1") src1 := src1Cmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { @@ -15,7 +15,7 @@ func TestArgsAutoMapSameKeyFromDifferentSources(t *testing.T) { }, "source 1") src1.AddArg("arg1", "default1") src1.AddArg2Env("conflict.key", "arg1") - + // Create source 2 with same key src2Cmd := tree.AddSub("src2") src2 := src2Cmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { @@ -23,27 +23,27 @@ func TestArgsAutoMapSameKeyFromDifferentSources(t *testing.T) { }, "source 2") src2.AddArg("arg2", "default2") src2.AddArg2Env("conflict.key", "arg2") - + // Create target with auto map targetCmd := tree.AddSub("conflicttarget1") target := targetCmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { return currCmdIdx, nil }, "conflict target 1") - + _, err := target.SetArg2EnvAutoMap([]string{"*"}) if err != nil { t.Fatalf("failed to set auto map: %v", err) } - + status := target.GetArgsAutoMapStatus() - + // Mark both sources argv1 := ArgVals{"arg1": ArgVal{Raw: "val1", Provided: true}} argv2 := ArgVals{"arg2": ArgVal{Raw: "val2", Provided: true}} - + status.MarkMetWithArgv(src1, argv1) status.MarkMetWithArgv(src2, argv2) - + // The key should be recorded once (from whichever source marked it first) if !status.providedKeys["conflict.key"] { t.Error("conflict key should be recorded as provided") @@ -53,7 +53,7 @@ func TestArgsAutoMapSameKeyFromDifferentSources(t *testing.T) { func TestArgsAutoMapArgNameConflictFromDifferentSources(t *testing.T) { strs := CmdTreeStrsForTest() tree := NewCmdTree(strs) - + // Create source 1 with arg "data" src1Cmd := tree.AddSub("srcarg1") src1 := src1Cmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { @@ -61,7 +61,7 @@ func TestArgsAutoMapArgNameConflictFromDifferentSources(t *testing.T) { }, "source arg 1") src1.AddArg("data", "default1") src1.AddArg2Env("key1", "data") - + // Create source 2 with same arg name "data" src2Cmd := tree.AddSub("srcarg2") src2 := src2Cmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { @@ -69,27 +69,27 @@ func TestArgsAutoMapArgNameConflictFromDifferentSources(t *testing.T) { }, "source arg 2") src2.AddArg("data", "default2") src2.AddArg2Env("key2", "data") - + // Create target with auto map targetCmd := tree.AddSub("argconflicttarget") target := targetCmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { return currCmdIdx, nil }, "arg conflict target") - + _, err := target.SetArg2EnvAutoMap([]string{"*"}) if err != nil { t.Fatalf("failed to set auto map: %v", err) } - + status := target.GetArgsAutoMapStatus() - + // Mark both sources with provided args argv1 := ArgVals{"data": ArgVal{Raw: "val1", Provided: true}} argv2 := ArgVals{"data": ArgVal{Raw: "val2", Provided: true}} - + status.MarkMetWithArgv(src1, argv1) status.MarkMetWithArgv(src2, argv2) - + // Both keys should be recorded if !status.providedKeys["key1"] { t.Error("key1 should be recorded as provided") @@ -102,7 +102,7 @@ func TestArgsAutoMapArgNameConflictFromDifferentSources(t *testing.T) { func TestArgsAutoMapNestedFlowKeyConflict(t *testing.T) { strs := CmdTreeStrsForTest() tree := NewCmdTree(strs) - + // Create inner command with key innerCmd := tree.AddSub("innerconflict") inner := innerCmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { @@ -110,7 +110,7 @@ func TestArgsAutoMapNestedFlowKeyConflict(t *testing.T) { }, "inner conflict") inner.AddArg("arg", "default") inner.AddArg2Env("nested.conflict.key", "arg") - + // Create outer command with same key outerCmd := tree.AddSub("outerconflict") outer := outerCmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { @@ -118,27 +118,27 @@ func TestArgsAutoMapNestedFlowKeyConflict(t *testing.T) { }, "outer conflict") outer.AddArg("arg", "default") outer.AddArg2Env("nested.conflict.key", "arg") - + // Create target with auto map targetCmd := tree.AddSub("nestedconflicttarget") target := targetCmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { return currCmdIdx, nil }, "nested conflict target") - + _, err := target.SetArg2EnvAutoMap([]string{"*"}) if err != nil { t.Fatalf("failed to set auto map: %v", err) } - + status := target.GetArgsAutoMapStatus() - + // Mark both with provided args argvInner := ArgVals{"arg": ArgVal{Raw: "inner-val", Provided: true}} argvOuter := ArgVals{"arg": ArgVal{Raw: "outer-val", Provided: true}} - + status.MarkMetWithArgv(inner, argvInner) status.MarkMetWithArgv(outer, argvOuter) - + // The key should be recorded (once) if !status.providedKeys["nested.conflict.key"] { t.Error("nested conflict key should be recorded as provided") @@ -148,7 +148,7 @@ func TestArgsAutoMapNestedFlowKeyConflict(t *testing.T) { func TestArgsAutoMapMultipleAbbrConflict(t *testing.T) { strs := CmdTreeStrsForTest() tree := NewCmdTree(strs) - + // Create source with arg that has abbr srcCmd := tree.AddSub("abbrconflictsrc") src := srcCmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { @@ -156,27 +156,27 @@ func TestArgsAutoMapMultipleAbbrConflict(t *testing.T) { }, "abbr conflict source") src.AddArg("longargname", "default", "lan", "ln") src.AddArg2Env("abbr.key", "longargname") - + // Create target with auto map targetCmd := tree.AddSub("abbrconflicttarget") target := targetCmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { return currCmdIdx, nil }, "abbr conflict target") - + // Target already has an arg with conflicting abbr target.AddArg("existing", "default", "lan") - + _, err := target.SetArg2EnvAutoMap([]string{"*"}) if err != nil { t.Fatalf("failed to set auto map: %v", err) } - + status := target.GetArgsAutoMapStatus() - + // Mark source with provided arg argv := ArgVals{"longargname": ArgVal{Raw: "val", Provided: true}} status.MarkMetWithArgv(src, argv) - + // The key should be recorded if !status.providedKeys["abbr.key"] { t.Error("abbr key should be recorded as provided") @@ -186,37 +186,37 @@ func TestArgsAutoMapMultipleAbbrConflict(t *testing.T) { func TestArgsAutoMapKeyConflictWithVal2EnvAndArg2Env(t *testing.T) { strs := CmdTreeStrsForTest() tree := NewCmdTree(strs) - + // Create source with both val2env and arg2env to same key srcCmd := tree.AddSub("valargconflictsrc") src := srcCmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { return currCmdIdx, nil }, "val arg conflict source") - + // Add val2env first src.AddVal2Env("conflict.key", "val-value") - + // Try to add arg2env to same key - this should be prevented by AddArg2Env // But we can test the provided keys tracking src.AddArg("arg", "default") - + // Create target targetCmd := tree.AddSub("valargconflicttarget") target := targetCmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { return currCmdIdx, nil }, "val arg conflict target") - + _, err := target.SetArg2EnvAutoMap([]string{"*"}) if err != nil { t.Fatalf("failed to set auto map: %v", err) } - + status := target.GetArgsAutoMapStatus() - + // Mark source argv := ArgVals{"arg": ArgVal{Raw: "arg-val", Provided: true}} status.MarkMetWithArgv(src, argv) - + // val2env key should be recorded if !status.providedKeys["conflict.key"] { t.Error("val2env key should be recorded as provided") @@ -226,7 +226,7 @@ func TestArgsAutoMapKeyConflictWithVal2EnvAndArg2Env(t *testing.T) { func TestArgsAutoMapDeepNestedConflictResolution(t *testing.T) { strs := CmdTreeStrsForTest() tree := NewCmdTree(strs) - + // Create 3 levels, each trying to provide the same key level1Cmd := tree.AddSub("deepconflict1") level1 := level1Cmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { @@ -234,48 +234,48 @@ func TestArgsAutoMapDeepNestedConflictResolution(t *testing.T) { }, "deep conflict level 1") level1.AddArg("arg1", "default1") level1.AddArg2Env("deep.conflict", "arg1") - + level2Cmd := tree.AddSub("deepconflict2") level2 := level2Cmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { return currCmdIdx, nil }, "deep conflict level 2") level2.AddArg("arg2", "default2") level2.AddArg2Env("deep.conflict", "arg2") - + level3Cmd := tree.AddSub("deepconflict3") level3 := level3Cmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { return currCmdIdx, nil }, "deep conflict level 3") level3.AddArg("arg3", "default3") level3.AddArg2Env("deep.conflict", "arg3") - + // Create target targetCmd := tree.AddSub("deepconflicttarget") target := targetCmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { return currCmdIdx, nil }, "deep conflict target") - + _, err := target.SetArg2EnvAutoMap([]string{"*"}) if err != nil { t.Fatalf("failed to set auto map: %v", err) } - + status := target.GetArgsAutoMapStatus() - + // Mark all levels with provided args argv1 := ArgVals{"arg1": ArgVal{Raw: "val1", Provided: true}} argv2 := ArgVals{"arg2": ArgVal{Raw: "val2", Provided: true}} argv3 := ArgVals{"arg3": ArgVal{Raw: "val3", Provided: true}} - + status.MarkMetWithArgv(level1, argv1) status.MarkMetWithArgv(level2, argv2) status.MarkMetWithArgv(level3, argv3) - + // The key should be recorded once (first one wins) if !status.providedKeys["deep.conflict"] { t.Error("deep conflict key should be recorded as provided") } - + // Count should be 1, not 3 count := 0 for key := range status.providedKeys { @@ -291,7 +291,7 @@ func TestArgsAutoMapDeepNestedConflictResolution(t *testing.T) { func TestArgsAutoMapConflictWithExistingTargetArg(t *testing.T) { strs := CmdTreeStrsForTest() tree := NewCmdTree(strs) - + // Create source with arg srcCmd := tree.AddSub("existingargsrc") src := srcCmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { @@ -299,7 +299,7 @@ func TestArgsAutoMapConflictWithExistingTargetArg(t *testing.T) { }, "existing arg source") src.AddArg("srcarg", "default") src.AddArg2Env("existing.key", "srcarg") - + // Create target with existing arg that maps to different key targetCmd := tree.AddSub("existingargtarget") target := targetCmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { @@ -307,18 +307,18 @@ func TestArgsAutoMapConflictWithExistingTargetArg(t *testing.T) { }, "existing arg target") target.AddArg("targetarg", "default") target.AddArg2Env("existing.key", "targetarg") - + _, err := target.SetArg2EnvAutoMap([]string{"*"}) if err != nil { t.Fatalf("failed to set auto map: %v", err) } - + status := target.GetArgsAutoMapStatus() - + // Mark source argv := ArgVals{"srcarg": ArgVal{Raw: "val", Provided: true}} status.MarkMetWithArgv(src, argv) - + // The key should be recorded if !status.providedKeys["existing.key"] { t.Error("existing key should be recorded as provided") diff --git a/pkg/core/model/args_auto_map_integration_test.go b/pkg/core/model/args_auto_map_integration_test.go index 2e112c30..0ce63c19 100644 --- a/pkg/core/model/args_auto_map_integration_test.go +++ b/pkg/core/model/args_auto_map_integration_test.go @@ -7,43 +7,43 @@ import ( func TestArgsAutoMapSmartMappingIntegration(t *testing.T) { strs := CmdTreeStrsForTest() tree := NewCmdTree(strs) - + // Create target command with smart mapping targetCmd := tree.AddSub("inttarget") target := targetCmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { return currCmdIdx, nil }, "integration target command") - + _, err := target.SetArg2EnvAutoMap([]string{"*"}) if err != nil { t.Fatalf("failed to set auto map: %v", err) } - + // Create source command with multiple env operations srcCmd := tree.AddSub("intsource") src := srcCmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { return currCmdIdx, nil }, "integration source command") - + // Add val2env src.AddVal2Env("val.key", "val-value") - + // Add arg2env src.AddArg("arg1", "default1") src.AddArg2Env("arg.key", "arg1") - + // Add env op src.AddEnvOp("env.op.key", EnvOpTypeWrite) - + // Create argv with provided argument argv := ArgVals{ "arg1": ArgVal{Raw: "provided-value", Provided: true}, } - + // Mark the source command with argv status := target.GetArgsAutoMapStatus() status.MarkMetWithArgv(src, argv) - + // Verify all provided keys are recorded if !status.providedKeys["val.key"] { t.Error("val2env key should be recorded as provided") @@ -54,22 +54,22 @@ func TestArgsAutoMapSmartMappingIntegration(t *testing.T) { if !status.providedKeys["env.op.key"] { t.Error("env op key should be recorded as provided") } - + // Test that non-provided arg2env is not recorded src2Cmd := tree.AddSub("intsource2") src2 := src2Cmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { return currCmdIdx, nil }, "integration source command 2") - + src2.AddArg("arg2", "default2") src2.AddArg2Env("not.provided.arg.key", "arg2") - + argv2 := ArgVals{ "arg2": ArgVal{Raw: "default2", Provided: false}, } - + status.MarkMetWithArgv(src2, argv2) - + if status.providedKeys["not.provided.arg.key"] { t.Error("arg2env key with non-provided arg should NOT be recorded as provided") } @@ -78,39 +78,39 @@ func TestArgsAutoMapSmartMappingIntegration(t *testing.T) { func TestArgsAutoMapFlushCacheWithSmartMapping(t *testing.T) { strs := CmdTreeStrsForTest() tree := NewCmdTree(strs) - + // Create target command with smart mapping targetCmd := tree.AddSub("flushtarget") target := targetCmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { return currCmdIdx, nil }, "flush target command") - + _, err := target.SetArg2EnvAutoMap([]string{"*"}) if err != nil { t.Fatalf("failed to set auto map: %v", err) } - + // Create source command srcCmd := tree.AddSub("flushsource") src := srcCmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { return currCmdIdx, nil }, "flush source command") - + src.AddArg("arg1", "default1") - + // Mark source as met and cache a mapping status := target.GetArgsAutoMapStatus() status.MarkAndCacheMapping(src, "test.key", "test.arg", "default-val", nil, true) - + // Record the key as provided status.RecordGlobalEnvKey("test.key") - + // Flush cache err = status.FlushCache(target) if err != nil { t.Fatalf("failed to flush cache: %v", err) } - + // Verify the provided key was NOT mapped (smart mapping) if target.GetArg2Env().Has("test.key") { t.Error("provided key should not be mapped in smart mode") diff --git a/pkg/core/model/args_auto_map_nested_test.go b/pkg/core/model/args_auto_map_nested_test.go index 77330afb..f456b311 100644 --- a/pkg/core/model/args_auto_map_nested_test.go +++ b/pkg/core/model/args_auto_map_nested_test.go @@ -7,7 +7,7 @@ import ( func TestArgsAutoMapNestedFlowWithProvidedKeysAtDifferentLevels(t *testing.T) { strs := CmdTreeStrsForTest() tree := NewCmdTree(strs) - + // Create level 3 command with val2env level3Cmd := tree.AddSub("deeplevel") level3 := level3Cmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { @@ -16,7 +16,7 @@ func TestArgsAutoMapNestedFlowWithProvidedKeysAtDifferentLevels(t *testing.T) { level3.AddVal2Env("deep.provided.key", "deep-value") level3.AddArg("deeparg", "deep-default") level3.AddArg2Env("deep.arg.key", "deeparg") - + // Create level 2 command with arg2env level2Cmd := tree.AddSub("midlevel") level2 := level2Cmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { @@ -24,7 +24,7 @@ func TestArgsAutoMapNestedFlowWithProvidedKeysAtDifferentLevels(t *testing.T) { }, "mid level command") level2.AddArg("midarg", "mid-default") level2.AddArg2Env("mid.arg.key", "midarg") - + // Create level 1 command level1Cmd := tree.AddSub("toplevel") level1 := level1Cmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { @@ -32,20 +32,20 @@ func TestArgsAutoMapNestedFlowWithProvidedKeysAtDifferentLevels(t *testing.T) { }, "top level command") level1.AddArg("toparg", "top-default") level1.AddArg2Env("top.arg.key", "toparg") - + // Create target command with smart mapping targetCmd := tree.AddSub("nestedtarget") target := targetCmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { return currCmdIdx, nil }, "nested target command") - + _, err := target.SetArg2EnvAutoMap([]string{"*"}) if err != nil { t.Fatalf("failed to set auto map: %v", err) } - + status := target.GetArgsAutoMapStatus() - + // Mark all levels with different argv states argvDeep := ArgVals{ "deeparg": ArgVal{Raw: "provided-deep", Provided: true}, @@ -56,11 +56,11 @@ func TestArgsAutoMapNestedFlowWithProvidedKeysAtDifferentLevels(t *testing.T) { argvTop := ArgVals{ "toparg": ArgVal{Raw: "provided-top", Provided: true}, } - + status.MarkMetWithArgv(level3, argvDeep) status.MarkMetWithArgv(level2, argvMid) status.MarkMetWithArgv(level1, argvTop) - + // Verify all provided keys are recorded if !status.providedKeys["deep.provided.key"] { t.Error("deep level val2env key should be recorded") @@ -79,12 +79,12 @@ func TestArgsAutoMapNestedFlowWithProvidedKeysAtDifferentLevels(t *testing.T) { func TestArgsAutoMapFiveLevelNestedFlow(t *testing.T) { strs := CmdTreeStrsForTest() tree := NewCmdTree(strs) - + // Create 5 levels of nested commands for i := 5; i >= 1; i-- { cmdName := "nested5l" + string(rune('0'+i)) cmdTree := tree.AddSub(cmdName) - + if i == 5 { // Deepest level - power cmd with arg cmd := cmdTree.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { @@ -98,18 +98,18 @@ func TestArgsAutoMapFiveLevelNestedFlow(t *testing.T) { cmdTree.RegFlowCmd([]string{prevPath}, cmdName+" command", "") } } - + // Create target command targetCmd := tree.AddSub("nested5target") target := targetCmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { return currCmdIdx, nil }, "5 level nested target") - + _, err := target.SetArg2EnvAutoMap([]string{"*"}) if err != nil { t.Fatalf("failed to set auto map: %v", err) } - + // Verify the structure is correct status := target.GetArgsAutoMapStatus() if status.mapNoProvider != true { @@ -120,7 +120,7 @@ func TestArgsAutoMapFiveLevelNestedFlow(t *testing.T) { func TestArgsAutoMapNestedFlowWithGlobalEnvAtDifferentLevels(t *testing.T) { strs := CmdTreeStrsForTest() tree := NewCmdTree(strs) - + // Create inner command innerCmd := tree.AddSub("innercmd") inner := innerCmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { @@ -128,7 +128,7 @@ func TestArgsAutoMapNestedFlowWithGlobalEnvAtDifferentLevels(t *testing.T) { }, "inner command") inner.AddArg("innerarg", "inner-default") inner.AddArg2Env("inner.key", "innerarg") - + // Create outer command outerCmd := tree.AddSub("outercmd") outer := outerCmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { @@ -136,24 +136,24 @@ func TestArgsAutoMapNestedFlowWithGlobalEnvAtDifferentLevels(t *testing.T) { }, "outer command") outer.AddArg("outerarg", "outer-default") outer.AddArg2Env("outer.key", "outerarg") - + // Create target command targetCmd := tree.AddSub("nestedglobaltarget") target := targetCmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { return currCmdIdx, nil }, "nested global env target") - + _, err := target.SetArg2EnvAutoMap([]string{"*"}) if err != nil { t.Fatalf("failed to set auto map: %v", err) } - + status := target.GetArgsAutoMapStatus() - + // Record global env keys at different levels status.RecordGlobalEnvKey("global.level1.key") status.RecordGlobalEnvKey("global.level2.key") - + // Mark inner and outer with provided args innerArgv := ArgVals{ "innerarg": ArgVal{Raw: "inner-provided", Provided: true}, @@ -161,10 +161,10 @@ func TestArgsAutoMapNestedFlowWithGlobalEnvAtDifferentLevels(t *testing.T) { outerArgv := ArgVals{ "outerarg": ArgVal{Raw: "outer-provided", Provided: true}, } - + status.MarkMetWithArgv(inner, innerArgv) status.MarkMetWithArgv(outer, outerArgv) - + // Verify all keys are recorded expectedKeys := []string{ "global.level1.key", @@ -172,7 +172,7 @@ func TestArgsAutoMapNestedFlowWithGlobalEnvAtDifferentLevels(t *testing.T) { "inner.key", "outer.key", } - + for _, key := range expectedKeys { if !status.providedKeys[key] { t.Errorf("key %s should be recorded as provided", key) @@ -183,7 +183,7 @@ func TestArgsAutoMapNestedFlowWithGlobalEnvAtDifferentLevels(t *testing.T) { func TestArgsAutoMapNestedFlowWithEnvOpsAtDifferentLevels(t *testing.T) { strs := CmdTreeStrsForTest() tree := NewCmdTree(strs) - + // Create commands at different levels with env ops level1Cmd := tree.AddSub("envopsl1") level1 := level1Cmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { @@ -191,37 +191,37 @@ func TestArgsAutoMapNestedFlowWithEnvOpsAtDifferentLevels(t *testing.T) { }, "env ops level 1") level1.AddEnvOp("envop.l1.key1", EnvOpTypeWrite) level1.AddEnvOp("envop.l1.key2", EnvOpTypeWrite) - + level2Cmd := tree.AddSub("envopsl2") level2 := level2Cmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { return currCmdIdx, nil }, "env ops level 2") level2.AddEnvOp("envop.l2.key1", EnvOpTypeWrite) - + level3Cmd := tree.AddSub("envopsl3") level3 := level3Cmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { return currCmdIdx, nil }, "env ops level 3") level3.AddEnvOp("envop.l3.key1", EnvOpTypeWrite) - + // Create target command targetCmd := tree.AddSub("envopstarget") target := targetCmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { return currCmdIdx, nil }, "env ops target") - + _, err := target.SetArg2EnvAutoMap([]string{"*"}) if err != nil { t.Fatalf("failed to set auto map: %v", err) } - + status := target.GetArgsAutoMapStatus() - + // Mark all levels status.MarkMet(level1) status.MarkMet(level2) status.MarkMet(level3) - + // Verify all env op keys are recorded expectedKeys := []string{ "envop.l1.key1", @@ -229,7 +229,7 @@ func TestArgsAutoMapNestedFlowWithEnvOpsAtDifferentLevels(t *testing.T) { "envop.l2.key1", "envop.l3.key1", } - + for _, key := range expectedKeys { if !status.providedKeys[key] { t.Errorf("env op key %s should be recorded as provided", key) @@ -240,7 +240,7 @@ func TestArgsAutoMapNestedFlowWithEnvOpsAtDifferentLevels(t *testing.T) { func TestArgsAutoMapDeeplyNestedWithMixedProvidedKeys(t *testing.T) { strs := CmdTreeStrsForTest() tree := NewCmdTree(strs) - + // Create 4 levels with mixed provided key types // Level 4: val2env + arg2env (provided) level4Cmd := tree.AddSub("mixl4") @@ -250,7 +250,7 @@ func TestArgsAutoMapDeeplyNestedWithMixedProvidedKeys(t *testing.T) { level4.AddVal2Env("mix.val.key", "val") level4.AddArg("arg4", "default4") level4.AddArg2Env("mix.arg4.key", "arg4") - + // Level 3: env op + arg2env (not provided) level3Cmd := tree.AddSub("mixl3") level3 := level3Cmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { @@ -259,7 +259,7 @@ func TestArgsAutoMapDeeplyNestedWithMixedProvidedKeys(t *testing.T) { level3.AddEnvOp("mix.envop.key", EnvOpTypeWrite) level3.AddArg("arg3", "default3") level3.AddArg2Env("mix.arg3.key", "arg3") - + // Level 2: only arg2env (provided) level2Cmd := tree.AddSub("mixl2") level2 := level2Cmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { @@ -267,7 +267,7 @@ func TestArgsAutoMapDeeplyNestedWithMixedProvidedKeys(t *testing.T) { }, "mixed level 2") level2.AddArg("arg2", "default2") level2.AddArg2Env("mix.arg2.key", "arg2") - + // Level 1: arg2env (not provided) level1Cmd := tree.AddSub("mixl1") level1 := level1Cmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { @@ -275,52 +275,52 @@ func TestArgsAutoMapDeeplyNestedWithMixedProvidedKeys(t *testing.T) { }, "mixed level 1") level1.AddArg("arg1", "default1") level1.AddArg2Env("mix.arg1.key", "arg1") - + // Create target targetCmd := tree.AddSub("mixedtarget") target := targetCmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { return currCmdIdx, nil }, "mixed target") - + _, err := target.SetArg2EnvAutoMap([]string{"*"}) if err != nil { t.Fatalf("failed to set auto map: %v", err) } - + status := target.GetArgsAutoMapStatus() - + // Mark levels with different argv states argv4 := ArgVals{"arg4": ArgVal{Raw: "provided4", Provided: true}} argv3 := ArgVals{"arg3": ArgVal{Raw: "default3", Provided: false}} argv2 := ArgVals{"arg2": ArgVal{Raw: "provided2", Provided: true}} argv1 := ArgVals{"arg1": ArgVal{Raw: "default1", Provided: false}} - + status.MarkMetWithArgv(level4, argv4) status.MarkMetWithArgv(level3, argv3) status.MarkMetWithArgv(level2, argv2) status.MarkMetWithArgv(level1, argv1) status.RecordGlobalEnvKey("mix.global.key") - + // Verify expected provided keys shouldExist := []string{ - "mix.val.key", // val2env always provided - "mix.arg4.key", // arg2env with provided arg - "mix.envop.key", // env op always provided - "mix.arg2.key", // arg2env with provided arg - "mix.global.key", // global env + "mix.val.key", // val2env always provided + "mix.arg4.key", // arg2env with provided arg + "mix.envop.key", // env op always provided + "mix.arg2.key", // arg2env with provided arg + "mix.global.key", // global env } - + shouldNotExist := []string{ - "mix.arg3.key", // arg2env with non-provided arg - "mix.arg1.key", // arg2env with non-provided arg + "mix.arg3.key", // arg2env with non-provided arg + "mix.arg1.key", // arg2env with non-provided arg } - + for _, key := range shouldExist { if !status.providedKeys[key] { t.Errorf("key %s should be recorded as provided", key) } } - + for _, key := range shouldNotExist { if status.providedKeys[key] { t.Errorf("key %s should NOT be recorded as provided", key) diff --git a/pkg/core/model/args_auto_map_smart_test.go b/pkg/core/model/args_auto_map_smart_test.go index 217649a6..48bd2ce1 100644 --- a/pkg/core/model/args_auto_map_smart_test.go +++ b/pkg/core/model/args_auto_map_smart_test.go @@ -7,29 +7,29 @@ import ( func TestArgsAutoMapSmartMappingWithVal2Env(t *testing.T) { strs := CmdTreeStrsForTest() tree := NewCmdTree(strs) - + // Create target command with smart mapping targetCmd := tree.AddSub("target") target := targetCmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { return currCmdIdx, nil }, "target command") - + _, err := target.SetArg2EnvAutoMap([]string{"*"}) if err != nil { t.Fatalf("failed to set auto map: %v", err) } - + // Create source command with val2env srcCmd := tree.AddSub("source") src := srcCmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { return currCmdIdx, nil }, "source command") src.AddVal2Env("provided.key", "value") - + // Verify that the provided key is recorded status := target.GetArgsAutoMapStatus() status.MarkMet(src) - + if !status.providedKeys["provided.key"] { t.Error("val2env key should be recorded as provided") } @@ -38,18 +38,18 @@ func TestArgsAutoMapSmartMappingWithVal2Env(t *testing.T) { func TestArgsAutoMapSmartMappingWithArg2EnvProvided(t *testing.T) { strs := CmdTreeStrsForTest() tree := NewCmdTree(strs) - + // Create target command with smart mapping targetCmd := tree.AddSub("target2") target := targetCmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { return currCmdIdx, nil }, "target command") - + _, err := target.SetArg2EnvAutoMap([]string{"*"}) if err != nil { t.Fatalf("failed to set auto map: %v", err) } - + // Create source command with arg2env srcCmd := tree.AddSub("source2") src := srcCmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { @@ -57,16 +57,16 @@ func TestArgsAutoMapSmartMappingWithArg2EnvProvided(t *testing.T) { }, "source command") src.AddArg("myarg", "default-value") src.AddArg2Env("provided.arg.key", "myarg") - + // Create argv with provided argument argv := ArgVals{ "myarg": ArgVal{Raw: "provided-value", Provided: true}, } - + // Verify that the provided key is recorded when arg is provided status := target.GetArgsAutoMapStatus() status.MarkMetWithArgv(src, argv) - + if !status.providedKeys["provided.arg.key"] { t.Error("arg2env key should be recorded as provided when arg is provided") } @@ -75,18 +75,18 @@ func TestArgsAutoMapSmartMappingWithArg2EnvProvided(t *testing.T) { func TestArgsAutoMapSmartMappingWithArg2EnvNotProvided(t *testing.T) { strs := CmdTreeStrsForTest() tree := NewCmdTree(strs) - + // Create target command with smart mapping targetCmd := tree.AddSub("target3") target := targetCmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { return currCmdIdx, nil }, "target command") - + _, err := target.SetArg2EnvAutoMap([]string{"*"}) if err != nil { t.Fatalf("failed to set auto map: %v", err) } - + // Create source command with arg2env srcCmd := tree.AddSub("source3") src := srcCmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { @@ -94,16 +94,16 @@ func TestArgsAutoMapSmartMappingWithArg2EnvNotProvided(t *testing.T) { }, "source command") src.AddArg("myarg", "default-value") src.AddArg2Env("not.provided.key", "myarg") - + // Create argv without provided argument argv := ArgVals{ "myarg": ArgVal{Raw: "default-value", Provided: false}, } - + // Verify that the key is NOT recorded when arg is not provided status := target.GetArgsAutoMapStatus() status.MarkMetWithArgv(src, argv) - + if status.providedKeys["not.provided.key"] { t.Error("arg2env key should NOT be recorded as provided when arg is not provided") } @@ -112,22 +112,22 @@ func TestArgsAutoMapSmartMappingWithArg2EnvNotProvided(t *testing.T) { func TestArgsAutoMapSmartMappingWithGlobalEnv(t *testing.T) { strs := CmdTreeStrsForTest() tree := NewCmdTree(strs) - + // Create target command with smart mapping targetCmd := tree.AddSub("target4") target := targetCmd.RegPowerCmd(func(argv ArgVals, cc *Cli, env *Env, flow *ParsedCmds, currCmdIdx int) (int, error) { return currCmdIdx, nil }, "target command") - + _, err := target.SetArg2EnvAutoMap([]string{"*"}) if err != nil { t.Fatalf("failed to set auto map: %v", err) } - + // Verify that global env keys are recorded status := target.GetArgsAutoMapStatus() status.RecordGlobalEnvKey("global.env.key") - + if !status.providedKeys["global.env.key"] { t.Error("global env key should be recorded as provided") } From d9c7698d6e886519a355416b16f7a22e269aa362 Mon Sep 17 00:00:00 2001 From: Liu Cong Date: Thu, 19 Feb 2026 05:42:42 +0800 Subject: [PATCH 3/3] lint Signed-off-by: Liu Cong --- pkg/core/model/executing_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pkg/core/model/executing_test.go b/pkg/core/model/executing_test.go index 6d46b3d4..34ef8904 100644 --- a/pkg/core/model/executing_test.go +++ b/pkg/core/model/executing_test.go @@ -63,7 +63,7 @@ func (fs *testFS) open(path string, content string) { if fs.files[path] == nil { fs.files[path] = newMockStatusFile() } - fs.files[path].Write([]byte(content)) + _, _ = fs.files[path].Write([]byte(content)) fs.writeLog = append(fs.writeLog, path) } @@ -726,7 +726,7 @@ func TestExecutingFlow_ErrorRecovery(t *testing.T) { func() { defer func() { - recover() + _ = recover() }() executing.OnCmdStart(flow, 2, env, "") executing.OnCmdFinish(flow, 2, env, true, nil, false) @@ -826,7 +826,7 @@ func TestStatusFile_PanicDuringWrite(t *testing.T) { for i := 0; i < len(flow.Cmds); i++ { func() { defer func() { - recover() + _ = recover() }() executing.OnCmdStart(flow, i, env, "") executing.OnCmdFinish(flow, i, env, true, nil, false) @@ -858,7 +858,7 @@ func (fs *panicTestFS) open(path string, content string) { if fs.files[path] == nil { fs.files[path] = newMockStatusFile() } - fs.files[path].Write([]byte(content)) + _, _ = fs.files[path].Write([]byte(content)) fs.writeLog = append(fs.writeLog, path) } @@ -1372,7 +1372,7 @@ func TestExecutingFlow_PanicInCmd_DeepestLevel(t *testing.T) { case error: panicErr = v case string: - panicErr = fmt.Errorf(v) + panicErr = fmt.Errorf("%s", v) default: panicErr = fmt.Errorf("panic: %v", r) } @@ -1478,7 +1478,7 @@ func TestExecutingFlow_PanicRecovery_StatusFileIntegrity(t *testing.T) { func() { defer func() { - recover() + _ = recover() }() executing.OnCmdStart(flow, 2, env, "") executing.OnCmdFinish(flow, 2, env, true, nil, false)