From c42f339fc0af1f868907f5fec90410c073f408f7 Mon Sep 17 00:00:00 2001 From: Falk Harrison Date: Tue, 29 Sep 2026 10:46:31 +0200 Subject: [PATCH] fix(headless): preserve skipped conditional values --- internal/headless/conditions_test.go | 72 ++++++++++++++++++++++++++++ internal/headless/vars.go | 3 ++ 2 files changed, 75 insertions(+) create mode 100644 internal/headless/conditions_test.go diff --git a/internal/headless/conditions_test.go b/internal/headless/conditions_test.go new file mode 100644 index 0000000..1846b2a --- /dev/null +++ b/internal/headless/conditions_test.go @@ -0,0 +1,72 @@ +package headless + +import ( + "bytes" + "encoding/json" + "reflect" + "strings" + "testing" + + "github.com/cheatmd-dev/cheatmd/pkg/config" + "github.com/cheatmd-dev/cheatmd/pkg/executor" + "github.com/cheatmd-dev/cheatmd/pkg/parser" +) + +type conditionalExecutor struct { + *executor.Executor + commands []string +} + +func (e *conditionalExecutor) RunShell(command string) (string, error) { + e.commands = append(e.commands, command) + return "chosen", nil +} + +func TestConditionalDefinitionsRespectSkippedValues(t *testing.T) { + old := *config.Get() + t.Cleanup(func() { *config.Get() = old }) + *config.Get() = config.DefaultConfig + config.Get().AutoContinue = true + t.Setenv("a", "") + + for _, tc := range []struct { + name, literal, shell, prefill, condition, want string + commands []string + }{ + {name: "false literal", literal: "should-be-skipped", condition: "$a == zeta"}, + {name: "false shell with prefill", shell: "printf 'should-be-skipped'", prefill: "cached", condition: "$a == zeta"}, + {name: "matching literal", literal: "chosen", condition: "$a == alpha", want: "chosen"}, + {name: "matching shell", shell: "printf 'chosen'", condition: "$a == alpha", want: "chosen", commands: []string{"printf 'chosen'"}}, + } { + t.Run(tc.name, func(t *testing.T) { + t.Setenv("b", tc.prefill) + cheat := &parser.Cheat{ + Header: "Conditional", Command: "echo [$b]", + Vars: []parser.VarDef{ + {Name: "a"}, + {Name: "b", Literal: tc.literal, Shell: tc.shell, Condition: tc.condition}, + }, + } + index := parser.NewCheatIndex() + index.Cheats = []*parser.Cheat{cheat} + exec := &conditionalExecutor{Executor: executor.NewExecutor(index)} + var output bytes.Buffer + session := &RunnerSession{ + Index: index, Exec: exec, Out: &output, + Decoder: json.NewDecoder(strings.NewReader("{\"result\":{\"values\":{\"a\":\"alpha\"}}}\n{\"result\":{\"values\":{\"b\":\"chosen\"}}}\n")), + } + + if err := session.Execute("Conditional", ""); err != nil { + t.Fatal(err) + } + + wantScope := map[string]string{"a": "alpha", "b": tc.want} + if !reflect.DeepEqual(cheat.Scope, wantScope) { + t.Errorf("scope = %v, want %v", cheat.Scope, wantScope) + } + if !reflect.DeepEqual(exec.commands, tc.commands) { + t.Errorf("shell commands = %v, want %v", exec.commands, tc.commands) + } + }) + } +} diff --git a/internal/headless/vars.go b/internal/headless/vars.go index 1e63b51..17f2c57 100644 --- a/internal/headless/vars.go +++ b/internal/headless/vars.go @@ -102,6 +102,9 @@ func (s *RunnerSession) tryResolveVariable(vs *resolver.VarState, scope map[stri } s.updateVariableDefinition(vs, scope) + if vs.Resolved { + return true + } if s.tryAutoContinue(vs) { return true