diff --git a/cmd/config_envs.go b/cmd/config_envs.go index 4d03a0f347..94a0c535b3 100644 --- a/cmd/config_envs.go +++ b/cmd/config_envs.go @@ -120,6 +120,9 @@ set environment variable from a secret } if np != nil || vp != nil { + if np != nil && vp == nil { + return fmt.Errorf("--value is required when --name is provided") + } if np != nil { if err := utils.ValidateEnvVarName(*np); err != nil { return err diff --git a/cmd/config_labels.go b/cmd/config_labels.go index 389a8a0735..900e1c62db 100644 --- a/cmd/config_labels.go +++ b/cmd/config_labels.go @@ -3,6 +3,7 @@ package cmd import ( "context" "encoding/json" + "errors" "fmt" "io" "os" @@ -92,6 +93,13 @@ the local machine. return loaderSaver.Save(function) } + if np != nil { + return errors.New("--value is required when --name is provided") + } + if vp != nil { + return errors.New("--name is required when --value is provided") + } + return runAddLabelsPrompt(cmd.Context(), function, loaderSaver) }, } diff --git a/cmd/config_test.go b/cmd/config_test.go index 5317567613..130cb00e05 100644 --- a/cmd/config_test.go +++ b/cmd/config_test.go @@ -113,6 +113,82 @@ func TestListEnvAdd(t *testing.T) { } } +// TestEnvsAddNameWithoutValue ensures that providing --name without --value returns an error +// rather than silently writing a broken fn.Env{Name: &key, Value: nil} to func.yaml. +func TestEnvsAddNameWithoutValue(t *testing.T) { + mock := common.NewMockLoaderSaver() + mock.LoadFn = func(path string) (fn.Function, error) { + return fn.Function{}, nil + } + mock.SaveFn = func(f fn.Function) error { + t.Error("Save must not be called when validation fails") + return nil + } + + cmd := setupConfigEnvCmd(mock, "add", "--name=API_KEY") + cmd.SetOut(io.Discard) + cmd.SetErr(io.Discard) + + err := cmd.Execute() + if err == nil { + t.Fatal("expected error when --name is provided without --value, got nil") + } +} + +func setupConfigLabelsCmd(mock common.FunctionLoaderSaver, args ...string) *cobra.Command { + cmd := fnCmd.NewConfigCmd( + mock, + ci.NewBufferWriter(), + common.CurrentBranchStub("", nil), + common.WorkDirStub("", nil), + fnCmd.NewClient, + ) + cmd.SetArgs(append([]string{"labels"}, args...)) + return cmd +} + +// TestLabelsAddNameWithoutValue ensures that providing --name without --value returns an error. +func TestLabelsAddNameWithoutValue(t *testing.T) { + mock := common.NewMockLoaderSaver() + mock.LoadFn = func(path string) (fn.Function, error) { + return fn.Function{}, nil + } + mock.SaveFn = func(f fn.Function) error { + t.Error("Save must not be called when validation fails") + return nil + } + + cmd := setupConfigLabelsCmd(mock, "add", "--name=env") + cmd.SetOut(io.Discard) + cmd.SetErr(io.Discard) + + err := cmd.Execute() + if err == nil { + t.Fatal("expected error when --name is provided without --value, got nil") + } +} + +// TestLabelsAddValueWithoutName ensures that providing --value without --name returns an error. +func TestLabelsAddValueWithoutName(t *testing.T) { + mock := common.NewMockLoaderSaver() + mock.LoadFn = func(path string) (fn.Function, error) { + return fn.Function{}, nil + } + mock.SaveFn = func(f fn.Function) error { + t.Error("Save must not be called when validation fails") + return nil + } + + cmd := setupConfigLabelsCmd(mock, "add", "--value=prod") + cmd.SetOut(io.Discard) + cmd.SetErr(io.Discard) + + err := cmd.Execute() + if err == nil { + t.Fatal("expected error when --value is provided without --name, got nil") + } +} + func envsEqual(a, b []fn.Env) bool { if len(a) != len(b) { return false