From aa02c0e623fd508968df0aa29c0d3f3c68a2beff Mon Sep 17 00:00:00 2001 From: km Date: Mon, 14 Sep 2026 09:06:56 +0900 Subject: [PATCH 1/2] fix(cli): exit non-zero from unimplemented commands validate, convert, plugin install and plugin remove printed "not yet implemented" to stdout and returned nil, so they exited 0 while doing nothing. In a pipeline such as `ossie validate model.yaml && deploy` that reads as a pass. Each stub now returns an error naming the command, which cobra reports on stderr and main turns into exit 1. Usage is silenced since the arguments were not the problem. Generated-by: Claude Code --- cli/cmd/convert.go | 4 +-- cli/cmd/plugin/install.go | 4 +-- cli/cmd/plugin/remove.go | 4 +-- cli/cmd/stubs_test.go | 54 +++++++++++++++++++++++++++++++++++++++ cli/cmd/validate.go | 4 +-- 5 files changed, 62 insertions(+), 8 deletions(-) create mode 100644 cli/cmd/stubs_test.go diff --git a/cli/cmd/convert.go b/cli/cmd/convert.go index 1d0734fc..5de4064b 100644 --- a/cli/cmd/convert.go +++ b/cli/cmd/convert.go @@ -43,6 +43,6 @@ func init() { } func runConvert(cmd *cobra.Command, args []string) error { - fmt.Fprintln(cmd.OutOrStdout(), "not yet implemented") - return nil + cmd.SilenceUsage = true + return fmt.Errorf("%s is not yet implemented", cmd.CommandPath()) } diff --git a/cli/cmd/plugin/install.go b/cli/cmd/plugin/install.go index 7a77f235..eebc59f9 100644 --- a/cli/cmd/plugin/install.go +++ b/cli/cmd/plugin/install.go @@ -33,6 +33,6 @@ func init() { } func runPluginInstall(cmd *cobra.Command, args []string) error { - fmt.Fprintln(cmd.OutOrStdout(), "not yet implemented") - return nil + cmd.SilenceUsage = true + return fmt.Errorf("%s is not yet implemented", cmd.CommandPath()) } diff --git a/cli/cmd/plugin/remove.go b/cli/cmd/plugin/remove.go index 38df2259..1f1cfd89 100644 --- a/cli/cmd/plugin/remove.go +++ b/cli/cmd/plugin/remove.go @@ -30,6 +30,6 @@ var removeCmd = &cobra.Command{ } func runPluginRemove(cmd *cobra.Command, args []string) error { - fmt.Fprintln(cmd.OutOrStdout(), "not yet implemented") - return nil + cmd.SilenceUsage = true + return fmt.Errorf("%s is not yet implemented", cmd.CommandPath()) } diff --git a/cli/cmd/stubs_test.go b/cli/cmd/stubs_test.go new file mode 100644 index 00000000..0bcd8ce6 --- /dev/null +++ b/cli/cmd/stubs_test.go @@ -0,0 +1,54 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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 cmd + +import ( + "bytes" + "strings" + "testing" +) + +func TestUnimplementedCommandsFail(t *testing.T) { + t.Setenv("OSSIE_PLUGIN_DIR", t.TempDir()) + + tests := [][]string{ + {"validate", "model.yaml"}, + {"convert", "--from", "dbt", "--input", "model.yaml"}, + {"plugin", "install", "dbt"}, + {"plugin", "remove", "dbt"}, + } + + for _, args := range tests { + t.Run(strings.Join(args, " "), func(t *testing.T) { + out := new(bytes.Buffer) + rootCmd.SetOut(out) + rootCmd.SetErr(out) + rootCmd.SetArgs(args) + + err := rootCmd.Execute() + if err == nil { + t.Fatalf("Execute(%q) returned nil, want not-implemented error", args) + } + if !strings.Contains(err.Error(), "not yet implemented") { + t.Errorf("Execute(%q) error = %q, want it to mention not yet implemented", args, err) + } + if strings.Contains(out.String(), "Usage:") { + t.Errorf("Execute(%q) printed usage:\n%s", args, out.String()) + } + }) + } +} diff --git a/cli/cmd/validate.go b/cli/cmd/validate.go index 08a3be55..0d096e60 100644 --- a/cli/cmd/validate.go +++ b/cli/cmd/validate.go @@ -35,6 +35,6 @@ func init() { } func runValidate(cmd *cobra.Command, args []string) error { - fmt.Fprintln(cmd.OutOrStdout(), "not yet implemented") - return nil + cmd.SilenceUsage = true + return fmt.Errorf("%s is not yet implemented", cmd.CommandPath()) } From 48ecea48ada77f4d3ac61778ad97e81143e9da05 Mon Sep 17 00:00:00 2001 From: km Date: Mon, 14 Sep 2026 09:19:49 +0900 Subject: [PATCH 2/2] test(cli): reset flags between stub test cases The convert case leaves --from and --input set on the shared rootCmd, so a later test running convert without flags would skip the required flag check. Generated-by: Claude Code --- cli/cmd/stubs_test.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/cli/cmd/stubs_test.go b/cli/cmd/stubs_test.go index 0bcd8ce6..e405a22b 100644 --- a/cli/cmd/stubs_test.go +++ b/cli/cmd/stubs_test.go @@ -20,6 +20,9 @@ import ( "bytes" "strings" "testing" + + "github.com/spf13/cobra" + "github.com/spf13/pflag" ) func TestUnimplementedCommandsFail(t *testing.T) { @@ -34,6 +37,8 @@ func TestUnimplementedCommandsFail(t *testing.T) { for _, args := range tests { t.Run(strings.Join(args, " "), func(t *testing.T) { + t.Cleanup(func() { clearFlags(rootCmd) }) + out := new(bytes.Buffer) rootCmd.SetOut(out) rootCmd.SetErr(out) @@ -52,3 +57,14 @@ func TestUnimplementedCommandsFail(t *testing.T) { }) } } + +// Flag values set by one Execute call stay on the shared rootCmd tree. +func clearFlags(cmd *cobra.Command) { + cmd.Flags().Visit(func(f *pflag.Flag) { + _ = f.Value.Set(f.DefValue) + f.Changed = false + }) + for _, sub := range cmd.Commands() { + clearFlags(sub) + } +}