From 2c553b92418f239538891c40d95ce16541dae3d2 Mon Sep 17 00:00:00 2001 From: Gyan Ranjan Panda Date: Sat, 16 May 2026 01:08:01 +0530 Subject: [PATCH] fix(test): replace os.Args guard with cobra.ExactArgs(3) to prevent panic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The test command validated argument count using len(os.Args) < 4, which counts global flags like --microcksURL and --keycloakClientId as part of the argument vector. When a user invokes the command with global flags but omits the positional argument (the typical CI pattern from the README), os.Args has enough entries to pass the guard, but cobra's parsed args slice only has 2 elements. The subsequent access to args[2] causes a runtime panic with a stack trace instead of a clean error. Fix: - Use cobra.ExactArgs(3) on the command definition — idiomatic and handles count validation before Run is ever called. - Remove three now-dead HasPrefix guards that were unreachable since cobra strips flags before handing args to Run. - Remove a redundant var testResultID string pre-declaration that was immediately shadowed by :=. - Fix the runner error message which listed SOAP (not a valid value) instead of SOAP_HTTP. - Update Use string to show positional args in help output. Fixes #390 Signed-off-by: gyanranjanpanda --- cmd/test.go | 26 ++++----------------- cmd/test_test.go | 59 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 22 deletions(-) create mode 100644 cmd/test_test.go diff --git a/cmd/test.go b/cmd/test.go index 70ecb4f..8eaf33b 100644 --- a/cmd/test.go +++ b/cmd/test.go @@ -42,35 +42,18 @@ func NewTestCommand(globalClientOpts *connectors.ClientOptions) *cobra.Command { ) var testCmd = &cobra.Command{ - Use: "test", + Use: "test ", Short: "Run tests on Microcks", Long: `Run tests on Microcks`, + Args: cobra.ExactArgs(3), Run: func(cmd *cobra.Command, args []string) { - // Parse subcommand args first. - if len(os.Args) < 4 { - fmt.Println("test command require args") - os.Exit(1) - } serviceRef := args[0] testEndpoint := args[1] runnerType := args[2] - // Validate presence and values of args. - if len(serviceRef) == 0 || strings.HasPrefix(serviceRef, "-") { - fmt.Println("test command require args") - os.Exit(1) - } - if len(testEndpoint) == 0 || strings.HasPrefix(testEndpoint, "-") { - fmt.Println("test command require args") - os.Exit(1) - } - if len(runnerType) == 0 || strings.HasPrefix(runnerType, "-") { - fmt.Println("test command require args") - os.Exit(1) - } - if _, validChoice := runnerChoices[runnerType]; !validChoice { - fmt.Println(" should be one of: HTTP, SOAP, SOAP_UI, POSTMAN, OPEN_API_SCHEMA, ASYNC_API_SCHEMA, GRPC_PROTOBUF, GRAPHQL_SCHEMA") + if _, ok := runnerChoices[runnerType]; !ok { + fmt.Println(" should be one of: HTTP, SOAP_HTTP, SOAP_UI, POSTMAN, OPEN_API_SCHEMA, ASYNC_API_SCHEMA, GRPC_PROTOBUF, GRAPHQL_SCHEMA") os.Exit(1) } @@ -169,7 +152,6 @@ func NewTestCommand(globalClientOpts *connectors.ClientOptions) *cobra.Command { serverAddr = ctx.Server.Server } - var testResultID string testResultID, err := mc.CreateTestResult(serviceRef, testEndpoint, runnerType, secretName, waitForMilliseconds, filteredOperations, operationsHeaders, oAuth2Context) if err != nil { fmt.Printf("Got error when invoking Microcks client creating Test: %s", err) diff --git a/cmd/test_test.go b/cmd/test_test.go new file mode 100644 index 0000000..01d7caf --- /dev/null +++ b/cmd/test_test.go @@ -0,0 +1,59 @@ +/* + * Copyright The Microcks Authors. + * + * 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 cmd + +import ( + "os" + "os/exec" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +const missingTestArgsMessage = "accepts 3 arg(s), received 2" + +func TestTestCommandMissingRunnerWithGlobalFlagsDoesNotPanic(t *testing.T) { + if os.Getenv("MICROCKS_CLI_TEST_MISSING_RUNNER") == "1" { + os.Args = []string{ + os.Args[0], + "test", + "--microcksURL=http://localhost:8080", + "--keycloakClientId=foo", + "--keycloakClientSecret=bar", + "MyAPI:1.0", + "http://localhost:3000", + } + + err := NewCommad().Execute() + if err != nil { + t.Fatal(err) + } + return + } + + cmd := exec.Command(os.Args[0], "-test.run=TestTestCommandMissingRunnerWithGlobalFlagsDoesNotPanic") + cmd.Env = append(os.Environ(), "MICROCKS_CLI_TEST_MISSING_RUNNER=1") + + output, err := cmd.CombinedOutput() + require.Error(t, err) + + exitError, ok := err.(*exec.ExitError) + require.True(t, ok) + assert.Equal(t, 1, exitError.ExitCode()) + assert.Contains(t, string(output), missingTestArgsMessage) + assert.NotContains(t, string(output), "panic:") // core regression guard: must never crash with a stack trace +}