Repository: skupperproject/skupper
Pull request #1728 (2024-10-16 18:21:02)

Run go tests with race detection enabled
Updates makefile and CI to use the go test `-race` flag, fixes a race in the fs watcher test harness, and skips compat tests that depend on an external container engine.


Patch:
diff --git a/.circleci/config.yml b/.circleci/config.yml
index 281e2eb2..9fa6d185 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -8,8 +8,6 @@ executors:
   go_cimg:
     docker:
       - image: cimg/go:1.22
-    environment:
-      CGO_ENABLED: 0
 
   local_cluster_test_executor:
     machine:
@@ -476,18 +474,17 @@ jobs:
                 exit 1
               fi
       - go/mod-download-cached
-      - run: make
       - run:
           name: Creating artifacts directory
           command: mkdir /tmp/artifacts
-      - go/test:
-          coverpkg: "./internal/...,./pkg/...,./cmd/..."
-          verbose: true
-          coverprofile: "/tmp/artifacts/cover.out"
-          parallel: "3"
+      - run:
+          name: Run tests
+          command: make cover
       - run:
           name: Generating HTML coverage report
-          command: go tool cover -html /tmp/artifacts/cover.out -o /tmp/artifacts/cover.html
+          command: |
+            mv ./cover.out /tmp/artifacts
+            go tool cover -html /tmp/artifacts/cover.out -o /tmp/artifacts/cover.html
       - store_artifacts:
           path: /tmp/artifacts
           destination: test-artifacts
@@ -519,6 +516,8 @@ jobs:
   build-all:
     executor:
       name: go_cimg
+    environment:
+      CGO_ENABLED: 0
     steps:
       - checkout
       - go/mod-download-cached
diff --git a/Makefile b/Makefile
index f09ad5e3..c45f6b99 100644
--- a/Makefile
+++ b/Makefile
@@ -7,6 +7,7 @@ TEST_IMAGE := quay.io/skupper/skupper-tests:v2-latest
 TEST_BINARIES_FOLDER := ${PWD}/test/integration/bin
 DOCKER := docker
 LDFLAGS := -X github.com/skupperproject/skupper/pkg/version.Version=${VERSION}
+TESTFLAGS := -v -race -short
 PLATFORMS ?= linux/amd64,linux/arm64
 GOOS ?= linux
 GOARCH ?= amd64
@@ -86,18 +87,16 @@ force-generate-client:
 vet:
 	go vet ./...
 
-cmd-test:
-	go test -v -count=1 ./cmd/...
-
-pkg-test:
-	go test -v -count=1 ./pkg/...
-
-internal-test:
-	go test -v -count=1 ./internal/...
-
 .PHONY: test
 test:
-	go test -v -count=1 ./pkg/... ./internal/... ./cmd/...
+	go test ${TESTFLAGS} ./...
+
+.PHONY: cover
+cover:
+	go test ${TESTFLAGS} \
+		-cover \
+		-coverprofile cover.out \
+		./...
 
 clean:
 	rm -rf skupper controller release config-sync manifest bootstrap network-console-collector ${TEST_BINARIES_FOLDER}
diff --git a/internal/cmd/skupper/connector/kube/connector_create.go b/internal/cmd/skupper/connector/kube/connector_create.go
index bdf1f914..f84c24b7 100644
--- a/internal/cmd/skupper/connector/kube/connector_create.go
+++ b/internal/cmd/skupper/connector/kube/connector_create.go
@@ -58,6 +58,7 @@ func (cmd *CmdConnectorCreate) ValidateInput(args []string) []error {
 	connectorTypeValidator := validator.NewOptionValidator(common.ConnectorTypes)
 	outputTypeValidator := validator.NewOptionValidator(common.OutputTypes)
 	workloadStringValidator := validator.NewWorkloadStringValidator()
+	timeoutValidator := validator.NewTimeoutInSecondsValidator()
 
 	// Validate arguments name and port
 	if len(args) < 2 {
@@ -95,33 +96,33 @@ func (cmd *CmdConnectorCreate) ValidateInput(args []string) []error {
 	}
 
 	// Validate flags
-	if cmd.Flags.RoutingKey != "" {
+	if cmd.Flags != nil && cmd.Flags.RoutingKey != "" {
 		ok, err := resourceStringValidator.Evaluate(cmd.Flags.RoutingKey)
 		if !ok {
 			validationErrors = append(validationErrors, fmt.Errorf("routing key is not valid: %s", err))
 		}
 	}
-	if cmd.Flags.TlsSecret != "" {
+	if cmd.Flags != nil && cmd.Flags.TlsSecret != "" {
 		// check that the secret exists
 		_, err := cmd.KubeClient.CoreV1().Secrets(cmd.namespace).Get(context.TODO(), cmd.Flags.TlsSecret, metav1.GetOptions{})
 		if err != nil {
 			validationErrors = append(validationErrors, fmt.Errorf("tls-secret is not valid: does not exist"))
 		}
 	}
-	if cmd.Flags.ConnectorType != "" {
+	if cmd.Flags != nil && cmd.Flags.ConnectorType != "" {
 		ok, err := connectorTypeValidator.Evaluate(cmd.Flags.ConnectorType)
 		if !ok {
 			validationErrors = append(validationErrors, fmt.Errorf("connector type is not valid: %s", err))
 		}
 	}
 	// only one of workload, selector or host can be specified
-	if cmd.Flags.Host != "" {
+	if cmd.Flags != nil && cmd.Flags.Host != "" {
 		if cmd.Flags.Workload != "" || cmd.Flags.Selector != "" {
 			validationErrors = append(validationErrors, fmt.Errorf("If host is configured, cannot configure workload or selector"))
 		}
 		//TBD what characters are not allowed for host flag
 	}
-	if cmd.Flags.Selector != "" {
+	if cmd.Flags != nil && cmd.Flags.Selector != "" {
 		if cmd.Flags.Workload != "" || cmd.Flags.Host != "" {
 			validationErrors = append(validationErrors, fmt.Errorf("If selector is configured, cannot configure workload or host"))
 		}
@@ -130,7 +131,7 @@ func (cmd *CmdConnectorCreate) ValidateInput(args []string) []error {
 			validationErrors = append(validationErrors, fmt.Errorf("selector is not valid: %s", err))
 		}
 	}
-	if cmd.Flags.Workload != "" {
+	if cmd.Flags != nil && cmd.Flags.Workload != "" {
 		if cmd.Flags.Selector != "" || cmd.Flags.Host != "" {
 			validationErrors = append(validationErrors, fmt.Errorf("If workload is configured, cannot configure selector or host"))
 		}
@@ -140,10 +141,13 @@ func (cmd *CmdConnectorCreate) ValidateInput(args []string) []error {
 		}
 	}
 	//TBD what is valid timeout
-	if cmd.Flags.Timeout <= 0*time.Minute {
-		validationErrors = append(validationErrors, fmt.Errorf("timeout is not valid"))
+	if cmd.Flags != nil && cmd.Flags.Timeout.String() != "" {
+		ok, err := timeoutValidator.Evaluate(cmd.Flags.Timeout)
+		if !ok {
+			validationErrors = append(validationErrors, fmt.Errorf("timeout is not valid: %s", err))
+		}
 	}
-	if cmd.Flags.Output != "" {
+	if cmd.Flags != nil && cmd.Flags.Output != "" {
 		ok, err := outputTypeValidator.Evaluate(cmd.Flags.Output)
 		if !ok {
 			validationErrors = append(validationErrors, fmt.Errorf("output type is not valid: %s", err))
diff --git a/internal/cmd/skupper/connector/kube/connector_delete.go b/internal/cmd/skupper/connector/kube/connector_delete.go
index c245e418..63ebff13 100644
--- a/internal/cmd/skupper/connector/kube/connector_delete.go
+++ b/internal/cmd/skupper/connector/kube/connector_delete.go
@@ -3,9 +3,9 @@ package kube
 import (
 	"context"
 	"fmt"
+
 	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
 	"github.com/skupperproject/skupper/internal/cmd/skupper/common/utils"
-	"time"
 
 	"github.com/skupperproject/skupper/internal/kube/client"
 	skupperv1alpha1 "github.com/skupperproject/skupper/pkg/generated/client/clientset/versioned/typed/skupper/v1alpha1"
@@ -38,6 +38,7 @@ func (cmd *CmdConnectorDelete) NewClient(cobraCommand *cobra.Command, args []str
 func (cmd *CmdConnectorDelete) ValidateInput(args []string) []error {
 	var validationErrors []error
 	resourceStringValidator := validator.NewResourceStringValidator()
+	timeoutValidator := validator.NewTimeoutInSecondsValidator()
 
 	// Validate arguments name
 	if len(args) < 1 {
@@ -61,9 +62,11 @@ func (cmd *CmdConnectorDelete) ValidateInput(args []string) []error {
 			}
 		}
 
-		//TBD what is valid timeout --> use timeout validator
-		if cmd.Flags.Timeout <= 0*time.Minute {
-			validationErrors = append(validationErrors, fmt.Errorf("timeout is not valid"))
+		if cmd.Flags != nil && cmd.Flags.Timeout.String() != "" {
+			ok, err := timeoutValidator.Evaluate(cmd.Flags.Timeout)
+			if !ok {
+				validationErrors = append(validationErrors, fmt.Errorf("timeout is not valid: %s", err))
+			}
 		}
 	}
 
diff --git a/internal/cmd/skupper/connector/kube/connector_status.go b/internal/cmd/skupper/connector/kube/connector_status.go
index a2b0d587..21002373 100644
--- a/internal/cmd/skupper/connector/kube/connector_status.go
+++ b/internal/cmd/skupper/connector/kube/connector_status.go
@@ -3,11 +3,12 @@ package kube
 import (
 	"context"
 	"fmt"
-	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
-	"github.com/skupperproject/skupper/internal/cmd/skupper/common/utils"
 	"os"
 	"text/tabwriter"
 
+	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
+	"github.com/skupperproject/skupper/internal/cmd/skupper/common/utils"
+
 	"github.com/skupperproject/skupper/internal/kube/client"
 	skupperv1alpha1 "github.com/skupperproject/skupper/pkg/generated/client/clientset/versioned/typed/skupper/v1alpha1"
 	"github.com/skupperproject/skupper/pkg/utils/validator"
@@ -67,7 +68,7 @@ func (cmd *CmdConnectorStatus) ValidateInput(args []string) []error {
 		}
 	}
 
-	if cmd.Flags.Output != "" {
+	if cmd.Flags != nil && cmd.Flags.Output != "" {
 		ok, err := outputTypeValidator.Evaluate(cmd.Flags.Output)
 		if !ok {
 			validationErrors = append(validationErrors, fmt.Errorf("output type is not valid: %s", err))
diff --git a/internal/cmd/skupper/connector/kube/connector_update.go b/internal/cmd/skupper/connector/kube/connector_update.go
index 0e751fbb..3a5e3019 100644
--- a/internal/cmd/skupper/connector/kube/connector_update.go
+++ b/internal/cmd/skupper/connector/kube/connector_update.go
@@ -64,6 +64,7 @@ func (cmd *CmdConnectorUpdate) ValidateInput(args []string) []error {
 	connectorTypeValidator := validator.NewOptionValidator(common.ConnectorTypes)
 	outputTypeValidator := validator.NewOptionValidator(common.OutputTypes)
 	workloadStringValidator := validator.NewWorkloadStringValidator()
+	timeoutValidator := validator.NewTimeoutInSecondsValidator()
 
 	// Validate arguments name
 	if len(args) < 1 {
@@ -99,7 +100,7 @@ func (cmd *CmdConnectorUpdate) ValidateInput(args []string) []error {
 	}
 
 	// Validate flags
-	if cmd.Flags.RoutingKey != "" {
+	if cmd.Flags != nil && cmd.Flags.RoutingKey != "" {
 		ok, err := resourceStringValidator.Evaluate(cmd.Flags.RoutingKey)
 		if !ok {
 			validationErrors = append(validationErrors, fmt.Errorf("routing key is not valid: %s", err))
@@ -107,7 +108,7 @@ func (cmd *CmdConnectorUpdate) ValidateInput(args []string) []error {
 			cmd.newSettings.routingKey = cmd.Flags.RoutingKey
 		}
 	}
-	if cmd.Flags.TlsSecret != "" {
+	if cmd.Flags != nil && cmd.Flags.TlsSecret != "" {
 		_, err := cmd.KubeClient.CoreV1().Secrets(cmd.namespace).Get(context.TODO(), cmd.Flags.TlsSecret, metav1.GetOptions{})
 		if err != nil {
 			validationErrors = append(validationErrors, fmt.Errorf("tls-secret is not valid: does not exist"))
@@ -115,7 +116,7 @@ func (cmd *CmdConnectorUpdate) ValidateInput(args []string) []error {
 			cmd.newSettings.tlsSecret = cmd.Flags.TlsSecret
 		}
 	}
-	if cmd.Flags.ConnectorType != "" {
+	if cmd.Flags != nil && cmd.Flags.ConnectorType != "" {
 		ok, err := connectorTypeValidator.Evaluate(cmd.Flags.ConnectorType)
 		if !ok {
 			validationErrors = append(validationErrors, fmt.Errorf("connector type is not valid: %s", err))
@@ -123,7 +124,7 @@ func (cmd *CmdConnectorUpdate) ValidateInput(args []string) []error {
 			cmd.newSettings.connectorType = cmd.Flags.ConnectorType
 		}
 	}
-	if cmd.Flags.Port != 0 {
+	if cmd.Flags != nil && cmd.Flags.Port != 0 {
 		ok, err := numberValidator.Evaluate(cmd.Flags.Port)
 		if !ok {
 			validationErrors = append(validationErrors, fmt.Errorf("connector port is not valid: %s", err))
@@ -131,23 +132,24 @@ func (cmd *CmdConnectorUpdate) ValidateInput(args []string) []error {
 			cmd.newSettings.port = cmd.Flags.Port
 		}
 	}
-	//TBD what is valid timeout ---> use timevalidator
-	if cmd.Flags.Timeout <= 0*time.Minute {
-		validationErrors = append(validationErrors, fmt.Errorf("timeout is not valid"))
+	if cmd.Flags != nil && cmd.Flags.Timeout.String() != "" {
+		ok, err := timeoutValidator.Evaluate(cmd.Flags.Timeout)
+		if !ok {
+			validationErrors = append(validationErrors, fmt.Errorf("timeout is not valid: %s", err))
+		}
 	}
 	//TBD what characters are not allowed for host flag
-	if cmd.Flags.Host != "" {
+	if cmd.Flags != nil && cmd.Flags.Host != "" {
 		cmd.newSettings.host = cmd.Flags.Host
 	}
-	//TBD what are valid values here
-	if cmd.Flags.Selector != "" {
+	if cmd.Flags != nil && cmd.Flags.Selector != "" {
 		ok, err := workloadStringValidator.Evaluate(cmd.Flags.Selector)
 		if !ok {
 			validationErrors = append(validationErrors, fmt.Errorf("selector is not valid: %s", err))
 		}
 		cmd.newSettings.selector = cmd.Flags.Selector
 	}
-	if cmd.Flags.Workload != "" {
+	if cmd.Flags != nil && cmd.Flags.Workload != "" {
 		ok, err := workloadStringValidator.Evaluate(cmd.Flags.Workload)
 		if !ok {
 			validationErrors = append(validationErrors, fmt.Errorf("workload is not valid: %s", err))
@@ -155,22 +157,22 @@ func (cmd *CmdConnectorUpdate) ValidateInput(args []string) []error {
 		cmd.newSettings.workload = cmd.Flags.Workload
 	}
 	//Can only have one workload/Selector/host set
-	if cmd.newSettings.host != "" {
+	if cmd.Flags != nil && cmd.newSettings.host != "" {
 		if cmd.newSettings.workload != "" || cmd.newSettings.selector != "" {
 			validationErrors = append(validationErrors, fmt.Errorf("If host is configured, cannot configure workload or selector"))
 		}
 	}
-	if cmd.newSettings.selector != "" {
+	if cmd.Flags != nil && cmd.newSettings.selector != "" {
 		if cmd.newSettings.host != "" || cmd.newSettings.workload != "" {
 			validationErrors = append(validationErrors, fmt.Errorf("If selector is configured, cannot configure workload or host"))
 		}
 	}
-	if cmd.newSettings.workload != "" {
+	if cmd.Flags != nil && cmd.newSettings.workload != "" {
 		if cmd.newSettings.selector != "" || cmd.newSettings.host != "" {
 			validationErrors = append(validationErrors, fmt.Errorf("If workload is configured, cannot configure selector or host"))
 		}
 	}
-	if cmd.Flags.Output != "" {
+	if cmd.Flags != nil && cmd.Flags.Output != "" {
 		ok, err := outputTypeValidator.Evaluate(cmd.Flags.Output)
 		if !ok {
 			validationErrors = append(validationErrors, fmt.Errorf("output type is not valid: %s", err))
diff --git a/internal/cmd/skupper/listener/kube/listener_create.go b/internal/cmd/skupper/listener/kube/listener_create.go
index ee84f42e..f0650327 100644
--- a/internal/cmd/skupper/listener/kube/listener_create.go
+++ b/internal/cmd/skupper/listener/kube/listener_create.go
@@ -56,6 +56,7 @@ func (cmd *CmdListenerCreate) ValidateInput(args []string) []error {
 	numberValidator := validator.NewNumberValidator()
 	listenerTypeValidator := validator.NewOptionValidator(common.ListenerTypes)
 	outputTypeValidator := validator.NewOptionValidator(common.OutputTypes)
+	timeoutValidator := validator.NewTimeoutInSecondsValidator()
 
 	// Validate arguments name and port
 	if len(args) < 2 {
@@ -93,16 +94,14 @@ func (cmd *CmdListenerCreate) ValidateInput(args []string) []error {
 	}
 
 	// Validate flags
-	if cmd.Flags.RoutingKey != "" {
+	if cmd.Flags != nil && cmd.Flags.RoutingKey != "" {
 		ok, err := resourceStringValidator.Evaluate(cmd.Flags.RoutingKey)
 		if !ok {
 			validationErrors = append(validationErrors, fmt.Errorf("routing key is not valid: %s", err))
 		}
 	}
 
-	//TBD what characters are not allowed for host flag
-
-	if cmd.Flags.TlsSecret != "" {
+	if cmd.Flags != nil && cmd.Flags.TlsSecret != "" {
 		// check that the secret exists
 		_, err := cmd.KubeClient.CoreV1().Secrets(cmd.namespace).Get(context.TODO(), cmd.Flags.TlsSecret, metav1.GetOptions{})
 		if err != nil {
@@ -110,19 +109,21 @@ func (cmd *CmdListenerCreate) ValidateInput(args []string) []error {
 		}
 	}
 
-	if cmd.Flags.ListenerType != "" {
+	if cmd.Flags != nil && cmd.Flags.ListenerType != "" {
 		ok, err := listenerTypeValidator.Evaluate(cmd.Flags.ListenerType)
 		if !ok {
 			validationErrors = append(validationErrors, fmt.Errorf("listener type is not valid: %s", err))
 		}
 	}
 
-	//TBD what is valid timeout
-	if cmd.Flags.Timeout <= 0*time.Minute {
-		validationErrors = append(validationErrors, fmt.Errorf("timeout is not valid"))
+	if cmd.Flags != nil && cmd.Flags.Timeout.String() != "" {
+		ok, err := timeoutValidator.Evaluate(cmd.Flags.Timeout)
+		if !ok {
+			validationErrors = append(validationErrors, fmt.Errorf("timeout is not valid: %s", err))
+		}
 	}
 
-	if cmd.Flags.Output != "" {
+	if cmd.Flags != nil && cmd.Flags.Output != "" {
 		ok, err := outputTypeValidator.Evaluate(cmd.Flags.Output)
 		if !ok {
 			validationErrors = append(validationErrors, fmt.Errorf("output type is not valid: %s", err))
diff --git a/internal/cmd/skupper/listener/kube/listener_delete.go b/internal/cmd/skupper/listener/kube/listener_delete.go
index 3287f59c..81e06f36 100644
--- a/internal/cmd/skupper/listener/kube/listener_delete.go
+++ b/internal/cmd/skupper/listener/kube/listener_delete.go
@@ -3,9 +3,9 @@ package kube
 import (
 	"context"
 	"fmt"
+
 	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
 	"github.com/skupperproject/skupper/internal/cmd/skupper/common/utils"
-	"time"
 
 	"github.com/skupperproject/skupper/internal/kube/client"
 	skupperv1alpha1 "github.com/skupperproject/skupper/pkg/generated/client/clientset/versioned/typed/skupper/v1alpha1"
@@ -38,6 +38,7 @@ func (cmd *CmdListenerDelete) NewClient(cobraCommand *cobra.Command, args []stri
 func (cmd *CmdListenerDelete) ValidateInput(args []string) []error {
 	var validationErrors []error
 	resourceStringValidator := validator.NewResourceStringValidator()
+	timeoutValidator := validator.NewTimeoutInSecondsValidator()
 
 	// Validate arguments name
 	if len(args) < 1 {
@@ -62,9 +63,11 @@ func (cmd *CmdListenerDelete) ValidateInput(args []string) []error {
 			}
 		}
 
-		//TBD what is valid timeout --> use timeout validator
-		if cmd.Flags.Timeout <= 0*time.Minute {
-			validationErrors = append(validationErrors, fmt.Errorf("timeout is not valid"))
+		if cmd.Flags != nil && cmd.Flags.Timeout.String() != "" {
+			ok, err := timeoutValidator.Evaluate(cmd.Flags.Timeout)
+			if !ok {
+				validationErrors = append(validationErrors, fmt.Errorf("timeout is not valid: %s", err))
+			}
 		}
 	}
 
diff --git a/internal/cmd/skupper/listener/kube/listener_status.go b/internal/cmd/skupper/listener/kube/listener_status.go
index f60329c7..d86efe25 100644
--- a/internal/cmd/skupper/listener/kube/listener_status.go
+++ b/internal/cmd/skupper/listener/kube/listener_status.go
@@ -3,11 +3,12 @@ package kube
 import (
 	"context"
 	"fmt"
-	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
-	"github.com/skupperproject/skupper/internal/cmd/skupper/common/utils"
 	"os"
 	"text/tabwriter"
 
+	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
+	"github.com/skupperproject/skupper/internal/cmd/skupper/common/utils"
+
 	"github.com/skupperproject/skupper/internal/kube/client"
 	skupperv1alpha1 "github.com/skupperproject/skupper/pkg/generated/client/clientset/versioned/typed/skupper/v1alpha1"
 	"github.com/skupperproject/skupper/pkg/utils/validator"
@@ -67,7 +68,7 @@ func (cmd *CmdListenerStatus) ValidateInput(args []string) []error {
 		}
 	}
 
-	if cmd.Flags.Output != "" {
+	if cmd.Flags != nil && cmd.Flags.Output != "" {
 		ok, err := outputTypeValidator.Evaluate(cmd.Flags.Output)
 		if !ok {
 			validationErrors = append(validationErrors, fmt.Errorf("output type is not valid: %s", err))
diff --git a/internal/cmd/skupper/listener/kube/listener_update.go b/internal/cmd/skupper/listener/kube/listener_update.go
index dd42bba4..fab9e8ed 100644
--- a/internal/cmd/skupper/listener/kube/listener_update.go
+++ b/internal/cmd/skupper/listener/kube/listener_update.go
@@ -3,9 +3,10 @@ package kube
 import (
 	"context"
 	"fmt"
+	"time"
+
 	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
 	"github.com/skupperproject/skupper/internal/cmd/skupper/common/utils"
-	"time"
 
 	"github.com/skupperproject/skupper/internal/kube/client"
 	"github.com/skupperproject/skupper/pkg/apis/skupper/v1alpha1"
@@ -57,6 +58,7 @@ func (cmd *CmdListenerUpdate) ValidateInput(args []string) []error {
 	numberValidator := validator.NewNumberValidator()
 	listenerTypeValidator := validator.NewOptionValidator(common.ListenerTypes)
 	outputTypeValidator := validator.NewOptionValidator(common.OutputTypes)
+	timeoutValidator := validator.NewTimeoutInSecondsValidator()
 
 	// Validate arguments name
 	if len(args) < 1 {
@@ -90,7 +92,7 @@ func (cmd *CmdListenerUpdate) ValidateInput(args []string) []error {
 	}
 
 	// Validate flags
-	if cmd.Flags.RoutingKey != "" {
+	if cmd.Flags != nil && cmd.Flags.RoutingKey != "" {
 		ok, err := resourceStringValidator.Evaluate(cmd.Flags.RoutingKey)
 		if !ok {
 			validationErrors = append(validationErrors, fmt.Errorf("routing key is not valid: %s", err))
@@ -99,10 +101,10 @@ func (cmd *CmdListenerUpdate) ValidateInput(args []string) []error {
 		}
 	}
 	// TBD what validation should be done
-	if cmd.Flags.Host != "" {
+	if cmd.Flags != nil && cmd.Flags.Host != "" {
 		cmd.newSettings.host = cmd.Flags.Host
 	}
-	if cmd.Flags.TlsSecret != "" {
+	if cmd.Flags != nil && cmd.Flags.TlsSecret != "" {
 		_, err := cmd.KubeClient.CoreV1().Secrets(cmd.namespace).Get(context.TODO(), cmd.Flags.TlsSecret, metav1.GetOptions{})
 		if err != nil {
 			validationErrors = append(validationErrors, fmt.Errorf("tls-secret is not valid: does not exist"))
@@ -110,7 +112,7 @@ func (cmd *CmdListenerUpdate) ValidateInput(args []string) []error {
 			cmd.newSettings.tlsSecret = cmd.Flags.TlsSecret
 		}
 	}
-	if cmd.Flags.ListenerType != "" {
+	if cmd.Flags != nil && cmd.Flags.ListenerType != "" {
 		ok, err := listenerTypeValidator.Evaluate(cmd.Flags.ListenerType)
 		if !ok {
 			validationErrors = append(validationErrors, fmt.Errorf("listener type is not valid: %s", err))
@@ -118,7 +120,7 @@ func (cmd *CmdListenerUpdate) ValidateInput(args []string) []error {
 			cmd.newSettings.listenerType = cmd.Flags.ListenerType
 		}
 	}
-	if cmd.Flags.Port != 0 {
+	if cmd.Flags != nil && cmd.Flags.Port != 0 {
 		ok, err := numberValidator.Evaluate(cmd.Flags.Port)
 		if !ok {
 			validationErrors = append(validationErrors, fmt.Errorf("listener port is not valid: %s", err))
@@ -126,11 +128,13 @@ func (cmd *CmdListenerUpdate) ValidateInput(args []string) []error {
 			cmd.newSettings.port = cmd.Flags.Port
 		}
 	}
-	//TBD what is valid timeout --> use timeoutvalidator
-	if cmd.Flags.Timeout <= 0*time.Minute {
-		validationErrors = append(validationErrors, fmt.Errorf("timeout is not valid"))
+	if cmd.Flags != nil && cmd.Flags.Timeout.String() != "" {
+		ok, err := timeoutValidator.Evaluate(cmd.Flags.Timeout)
+		if !ok {
+			validationErrors = append(validationErrors, fmt.Errorf("timeout is not valid: %s", err))
+		}
 	}
-	if cmd.Flags.Output != "" {
+	if cmd.Flags != nil && cmd.Flags.Output != "" {
 		ok, err := outputTypeValidator.Evaluate(cmd.Flags.Output)
 		if !ok {
 			validationErrors = append(validationErrors, fmt.Errorf("output type is not valid: %s", err))
diff --git a/internal/cmd/skupper/token/kube/token_issue.go b/internal/cmd/skupper/token/kube/token_issue.go
index c5bd95ca..51b8c0bd 100644
--- a/internal/cmd/skupper/token/kube/token_issue.go
+++ b/internal/cmd/skupper/token/kube/token_issue.go
@@ -4,7 +4,6 @@ import (
 	"context"
 	"fmt"
 	"os"
-	"time"
 
 	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
 	"github.com/skupperproject/skupper/internal/cmd/skupper/common/utils"
@@ -46,6 +45,7 @@ func (cmd *CmdTokenIssue) ValidateInput(args []string) []error {
 	var validationErrors []error
 	resourceStringValidator := validator.NewResourceStringValidator()
 	tokenStringValidator := validator.NewFilePathStringValidator()
+	timeoutValidator := validator.NewTimeoutInSecondsValidator()
 
 	// Validate token file name
 	if len(args) < 1 {
@@ -96,18 +96,22 @@ func (cmd *CmdTokenIssue) ValidateInput(args []string) []error {
 
 	// Validate flags
 	//TBD is there a limit to number of redemptions
-	if cmd.Flags.RedemptionsAllowed < 1 {
+	if cmd.Flags != nil && cmd.Flags.RedemptionsAllowed < 1 {
 		validationErrors = append(validationErrors, fmt.Errorf("number of redemptions is not valid"))
 	}
 
-	//TBD what is valid times
-	if cmd.Flags.ExpirationWindow <= 0*time.Minute {
-		validationErrors = append(validationErrors, fmt.Errorf("expiration time is not valid"))
+	if cmd.Flags != nil && cmd.Flags.ExpirationWindow.String() != "" {
+		ok, err := timeoutValidator.Evaluate(cmd.Flags.ExpirationWindow)
+		if !ok {
+			validationErrors = append(validationErrors, fmt.Errorf("expiration time is not valid: %s", err))
+		}
 	}
 
-	//TBD what is valid timeout --> use timeoutValidator
-	if cmd.Flags.Timeout <= 0*time.Minute {
-		validationErrors = append(validationErrors, fmt.Errorf("timeout is not valid"))
+	if cmd.Flags != nil && cmd.Flags.Timeout.String() != "" {
+		ok, err := timeoutValidator.Evaluate(cmd.Flags.Timeout)
+		if !ok {
+			validationErrors = append(validationErrors, fmt.Errorf("timeout is not valid: %s", err))
+		}
 	}
 
 	return validationErrors
@@ -134,7 +138,8 @@ func (cmd *CmdTokenIssue) Run() error {
 }
 
 func (cmd *CmdTokenIssue) WaitUntil() error {
-	err := utils.NewSpinnerWithTimeout("Waiting for token status ...", int(cmd.Flags.Timeout.Seconds()), func() error {
+	waitTime := int(cmd.Flags.Timeout.Seconds())
+	err := utils.NewSpinnerWithTimeout("Waiting for token status ...", waitTime, func() error {
 
 		accessGrant, err := cmd.client.AccessGrants(cmd.namespace).Get(context.TODO(), cmd.grantName, metav1.GetOptions{})
 		if err != nil {
diff --git a/internal/cmd/skupper/token/kube/token_redeem.go b/internal/cmd/skupper/token/kube/token_redeem.go
index 072ebc5b..1658aa15 100644
--- a/internal/cmd/skupper/token/kube/token_redeem.go
+++ b/internal/cmd/skupper/token/kube/token_redeem.go
@@ -4,7 +4,6 @@ import (
 	"context"
 	"fmt"
 	"os"
-	"time"
 
 	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
 	"github.com/skupperproject/skupper/internal/cmd/skupper/common/utils"
@@ -43,6 +42,7 @@ func (cmd *CmdTokenRedeem) NewClient(cobraCommand *cobra.Command, args []string)
 func (cmd *CmdTokenRedeem) ValidateInput(args []string) []error {
 	var validationErrors []error
 	tokenStringValidator := validator.NewFilePathStringValidator()
+	timeoutValidator := validator.NewTimeoutInSecondsValidator()
 
 	// Validate token file name
 	if len(args) < 1 {
@@ -79,9 +79,11 @@ func (cmd *CmdTokenRedeem) ValidateInput(args []string) []error {
 		}
 	}
 
-	//TBD what is valid times --> use TimeoutValidator
-	if cmd.Flags.Timeout <= 0*time.Minute {
-		validationErrors = append(validationErrors, fmt.Errorf("timeout is not valid"))
+	if cmd.Flags != nil && cmd.Flags.Timeout.String() != "" {
+		ok, err := timeoutValidator.Evaluate(cmd.Flags.Timeout)
+		if !ok {
+			validationErrors = append(validationErrors, fmt.Errorf("timeout is not valid: %s", err))
+		}
 	}
 
 	return validationErrors
@@ -108,8 +110,8 @@ func (cmd *CmdTokenRedeem) Run() error {
 }
 
 func (cmd *CmdTokenRedeem) WaitUntil() error {
-
-	err := utils.NewSpinnerWithTimeout("Waiting for token status ...", int(cmd.Flags.Timeout.Seconds()), func() error {
+	waitTime := int(cmd.Flags.Timeout.Seconds())
+	err := utils.NewSpinnerWithTimeout("Waiting for token status ...", waitTime, func() error {
 
 		token, err := cmd.client.AccessTokens(cmd.namespace).Get(context.TODO(), cmd.name, metav1.GetOptions{})
 		if err != nil {
diff --git a/pkg/vanflow/eventsource/client.go b/pkg/vanflow/eventsource/client.go
index 990c269b..96fb5153 100644
--- a/pkg/vanflow/eventsource/client.go
+++ b/pkg/vanflow/eventsource/client.go
@@ -136,10 +136,10 @@ func (c *Client) Listen(ctx context.Context, attributes ListenerConfigProvider)
 // Close stops all listeners
 func (c *Client) Close() {
 	c.lock.Lock()
-	defer c.lock.Unlock()
 	for _, cancel := range c.cleanup {
 		cancel()
 	}
+	c.lock.Unlock()
 	c.wg.Wait()
 }
 
diff --git a/pkg/vanflow/session/mock.go b/pkg/vanflow/session/mock.go
index fced8ad0..e99532d6 100644
--- a/pkg/vanflow/session/mock.go
+++ b/pkg/vanflow/session/mock.go
@@ -19,6 +19,8 @@ type mockContainer struct {
 	Router *MockRouter
 }
 
+func (c *mockContainer) IsMock() {}
+
 func (c *mockContainer) Start(ctx context.Context) {
 }
 


Test patch:
diff --git a/internal/cmd/skupper/connector/kube/connector_create_test.go b/internal/cmd/skupper/connector/kube/connector_create_test.go
index 48d36ea8..84ee98a2 100644
--- a/internal/cmd/skupper/connector/kube/connector_create_test.go
+++ b/internal/cmd/skupper/connector/kube/connector_create_test.go
@@ -199,14 +199,14 @@ func TestCmdConnectorCreate_ValidateInput(t *testing.T) {
 				Workload: "workload",
 				Timeout:  0 * time.Second,
 			},
-			expectedErrors: []string{"timeout is not valid"},
+			expectedErrors: []string{"timeout is not valid: duration must not be less than 10s; got 0s"},
 		},
 		{
 			name: "output is not valid",
 			args: []string{"bad-output", "1234"},
 			flags: common.CommandConnectorCreateFlags{
 				Workload: "workload",
-				Timeout:  1 * time.Second,
+				Timeout:  10 * time.Second,
 				Output:   "not-supported",
 			},
 			expectedErrors: []string{
@@ -216,7 +216,7 @@ func TestCmdConnectorCreate_ValidateInput(t *testing.T) {
 			name: "selector/host",
 			args: []string{"selector", "1234"},
 			flags: common.CommandConnectorCreateFlags{
-				Timeout:  1 * time.Second,
+				Timeout:  10 * time.Second,
 				Output:   "json",
 				Selector: "app=test",
 				Host:     "test",
@@ -229,7 +229,7 @@ func TestCmdConnectorCreate_ValidateInput(t *testing.T) {
 			name: "workload/host",
 			args: []string{"workload", "1234"},
 			flags: common.CommandConnectorCreateFlags{
-				Timeout:  1 * time.Second,
+				Timeout:  10 * time.Second,
 				Output:   "json",
 				Workload: "deployment/test",
 				Host:     "test",
@@ -297,33 +297,33 @@ func TestCmdConnectorCreate_InputToOptions(t *testing.T) {
 	testTable := []test{
 		{
 			name:                  "test1",
-			flags:                 common.CommandConnectorCreateFlags{"backend", "", "app=backend", "secret", "tcp", true, "", 2 * time.Second, "json"},
+			flags:                 common.CommandConnectorCreateFlags{"backend", "", "app=backend", "secret", "tcp", true, "", 20 * time.Second, "json"},
 			expectedTlsSecret:     "secret",
 			expectedHost:          "",
 			expectedRoutingKey:    "backend",
-			expectedTimeout:       2 * time.Second,
+			expectedTimeout:       20 * time.Second,
 			expectedConnectorType: "tcp",
 			expectedOutput:        "json",
 			expectedSelector:      "app=backend",
 		},
 		{
 			name:                  "test2",
-			flags:                 common.CommandConnectorCreateFlags{"backend", "backend", "", "secret", "tcp", true, "", 2 * time.Second, "json"},
+			flags:                 common.CommandConnectorCreateFlags{"backend", "backend", "", "secret", "tcp", true, "", 20 * time.Second, "json"},
 			expectedTlsSecret:     "secret",
 			expectedHost:          "backend",
 			expectedRoutingKey:    "backend",
-			expectedTimeout:       2 * time.Second,
+			expectedTimeout:       20 * time.Second,
 			expectedConnectorType: "tcp",
 			expectedOutput:        "json",
 			expectedSelector:      "",
 		},
 		{
 			name:                  "test3",
-			flags:                 common.CommandConnectorCreateFlags{"", "", "", "secret", "tcp", false, "", 3 * time.Second, "yaml"},
+			flags:                 common.CommandConnectorCreateFlags{"", "", "", "secret", "tcp", false, "", 30 * time.Second, "yaml"},
 			expectedTlsSecret:     "secret",
 			expectedHost:          "",
 			expectedRoutingKey:    "test3",
-			expectedTimeout:       3 * time.Second,
+			expectedTimeout:       30 * time.Second,
 			expectedConnectorType: "tcp",
 			expectedOutput:        "yaml",
 			expectedSelector:      "app=test3",
@@ -375,7 +375,7 @@ func TestCmdConnectorCreate_Run(t *testing.T) {
 				TlsSecret:       "secretname",
 				IncludeNotReady: true,
 				Selector:        "app=backend",
-				Timeout:         1 * time.Second,
+				Timeout:         10 * time.Second,
 			},
 		},
 		{
@@ -388,7 +388,7 @@ func TestCmdConnectorCreate_Run(t *testing.T) {
 				RoutingKey:      "keyname",
 				TlsSecret:       "secretname",
 				IncludeNotReady: true,
-				Timeout:         1 * time.Second,
+				Timeout:         10 * time.Second,
 				Output:          "json",
 			},
 		},
diff --git a/internal/cmd/skupper/connector/kube/connector_delete_test.go b/internal/cmd/skupper/connector/kube/connector_delete_test.go
index 73299fde..1a4ada80 100644
--- a/internal/cmd/skupper/connector/kube/connector_delete_test.go
+++ b/internal/cmd/skupper/connector/kube/connector_delete_test.go
@@ -1,11 +1,12 @@
 package kube
 
 import (
-	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
-	"github.com/skupperproject/skupper/internal/cmd/skupper/common/utils"
 	"testing"
 	"time"
 
+	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
+	"github.com/skupperproject/skupper/internal/cmd/skupper/common/utils"
+
 	fakeclient "github.com/skupperproject/skupper/internal/kube/client/fake"
 	"github.com/skupperproject/skupper/pkg/apis/skupper/v1alpha1"
 	"gotest.tools/assert"
@@ -33,31 +34,31 @@ func TestCmdConnectorDelete_ValidateInput(t *testing.T) {
 		{
 			name:           "connector name is not specified",
 			args:           []string{},
-			flags:          common.CommandConnectorDeleteFlags{Timeout: 1 * time.Second},
+			flags:          common.CommandConnectorDeleteFlags{Timeout: 10 * time.Second},
 			expectedErrors: []string{"connector name must be specified"},
 		},
 		{
 			name:           "connector name is nil",
 			args:           []string{""},
-			flags:          common.CommandConnectorDeleteFlags{Timeout: 1 * time.Second},
+			flags:          common.CommandConnectorDeleteFlags{Timeout: 10 * time.Second},
 			expectedErrors: []string{"connector name must not be empty"},
 		},
 		{
 			name:           "connector name is not valid",
 			args:           []string{"my name"},
-			flags:          common.CommandConnectorDeleteFlags{Timeout: 1 * time.Second},
+			flags:          common.CommandConnectorDeleteFlags{Timeout: 10 * time.Second},
 			expectedErrors: []string{"connector name is not valid: value does not match this regular expression: ^[a-z0-9]([-a-z0-9]*[a-z0-9])*(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])*)*$"},
 		},
 		{
 			name:           "more than one argument is specified",
 			args:           []string{"my", "connector"},
-			flags:          common.CommandConnectorDeleteFlags{Timeout: 1 * time.Second},
+			flags:          common.CommandConnectorDeleteFlags{Timeout: 10 * time.Second},
 			expectedErrors: []string{"only one argument is allowed for this command"},
 		},
 		{
 			name:  "timeout is not valid",
 			args:  []string{"bad-timeout"},
-			flags: common.CommandConnectorDeleteFlags{Timeout: 0 * time.Second},
+			flags: common.CommandConnectorDeleteFlags{Timeout: 5 * time.Second},
 			skupperObjects: []runtime.Object{
 				&v1alpha1.Connector{
 					ObjectMeta: v1.ObjectMeta{
@@ -76,7 +77,7 @@ func TestCmdConnectorDelete_ValidateInput(t *testing.T) {
 					},
 				},
 			},
-			expectedErrors: []string{"timeout is not valid"},
+			expectedErrors: []string{"timeout is not valid: duration must not be less than 10s; got 5s"},
 		},
 	}
 
diff --git a/internal/cmd/skupper/connector/kube/connector_update_test.go b/internal/cmd/skupper/connector/kube/connector_update_test.go
index 62ab0aed..3f716aa3 100644
--- a/internal/cmd/skupper/connector/kube/connector_update_test.go
+++ b/internal/cmd/skupper/connector/kube/connector_update_test.go
@@ -29,31 +29,31 @@ func TestCmdConnectorUpdate_ValidateInput(t *testing.T) {
 		{
 			name:           "connector is not updated because get connector returned error",
 			args:           []string{"my-connector"},
-			flags:          common.CommandConnectorUpdateFlags{Timeout: 1 * time.Second},
+			flags:          common.CommandConnectorUpdateFlags{Timeout: 10 * time.Second},
 			expectedErrors: []string{"connector my-connector must exist in namespace test to be updated"},
 		},
 		{
 			name:           "connector name is not specified",
 			args:           []string{},
-			flags:          common.CommandConnectorUpdateFlags{Timeout: 1 * time.Second},
+			flags:          common.CommandConnectorUpdateFlags{Timeout: 10 * time.Second},
 			expectedErrors: []string{"connector name must be configured"},
 		},
 		{
 			name:           "connector name is nil",
 			args:           []string{""},
-			flags:          common.CommandConnectorUpdateFlags{Timeout: 1 * time.Minute},
+			flags:          common.CommandConnectorUpdateFlags{Timeout: 10 * time.Minute},
 			expectedErrors: []string{"connector name must not be empty"},
 		},
 		{
 			name:           "more than one argument is specified",
 			args:           []string{"my", "connector"},
-			flags:          common.CommandConnectorUpdateFlags{Timeout: 1 * time.Minute},
+			flags:          common.CommandConnectorUpdateFlags{Timeout: 10 * time.Minute},
 			expectedErrors: []string{"only one argument is allowed for this command"},
 		},
 		{
 			name:           "connector name is not valid.",
 			args:           []string{"my new connector"},
-			flags:          common.CommandConnectorUpdateFlags{Timeout: 1 * time.Minute},
+			flags:          common.CommandConnectorUpdateFlags{Timeout: 10 * time.Minute},
 			expectedErrors: []string{"connector name is not valid: value does not match this regular expression: ^[a-z0-9]([-a-z0-9]*[a-z0-9])*(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])*)*$"},
 		},
 		{
@@ -117,7 +117,7 @@ func TestCmdConnectorUpdate_ValidateInput(t *testing.T) {
 			args: []string{"my-connector-tls"},
 			flags: common.CommandConnectorUpdateFlags{
 				TlsSecret: "test-tls",
-				Timeout:   5 * time.Second,
+				Timeout:   50 * time.Second,
 			},
 			skupperObjects: []runtime.Object{
 				&v1alpha1.Connector{
@@ -227,7 +227,7 @@ func TestCmdConnectorUpdate_ValidateInput(t *testing.T) {
 			name: "selector/host",
 			args: []string{"selector"},
 			flags: common.CommandConnectorUpdateFlags{
-				Timeout:  1 * time.Second,
+				Timeout:  10 * time.Second,
 				Output:   "json",
 				Selector: "app=test",
 				Host:     "test",
@@ -258,7 +258,7 @@ func TestCmdConnectorUpdate_ValidateInput(t *testing.T) {
 			name: "workload/host",
 			args: []string{"workload"},
 			flags: common.CommandConnectorUpdateFlags{
-				Timeout:  1 * time.Second,
+				Timeout:  10 * time.Second,
 				Output:   "json",
 				Workload: "deployment/test",
 				Host:     "test",
@@ -290,7 +290,7 @@ func TestCmdConnectorUpdate_ValidateInput(t *testing.T) {
 			args: []string{"bad-timeout"},
 			flags: common.CommandConnectorUpdateFlags{
 				Selector: "selector",
-				Timeout:  0 * time.Second,
+				Timeout:  1 * time.Second,
 			},
 			skupperObjects: []runtime.Object{
 				&v1alpha1.Connector{
@@ -310,14 +310,14 @@ func TestCmdConnectorUpdate_ValidateInput(t *testing.T) {
 					},
 				},
 			},
-			expectedErrors: []string{"timeout is not valid"},
+			expectedErrors: []string{"timeout is not valid: duration must not be less than 10s; got 1s"},
 		},
 		{
 			name: "output is not valid",
 			args: []string{"bad-output"},
 			flags: common.CommandConnectorUpdateFlags{
 				Output:  "not-supported",
-				Timeout: 1 * time.Minute,
+				Timeout: 10 * time.Minute,
 			},
 			skupperObjects: []runtime.Object{
 				&v1alpha1.Connector{
@@ -349,7 +349,7 @@ func TestCmdConnectorUpdate_ValidateInput(t *testing.T) {
 				Port:            1234,
 				ConnectorType:   "tcp",
 				IncludeNotReady: false,
-				Timeout:         5 * time.Second,
+				Timeout:         50 * time.Second,
 				Output:          "json",
 			},
 			skupperObjects: []runtime.Object{
@@ -424,7 +424,7 @@ func TestCmdConnectorUpdate_Run(t *testing.T) {
 				includeNotReady: true,
 				workload:        "deployment/backend",
 				selector:        "backend",
-				timeout:         1 * time.Second,
+				timeout:         10 * time.Second,
 			},
 			skupperObjects: []runtime.Object{
 				&v1alpha1.Connector{
@@ -457,7 +457,7 @@ func TestCmdConnectorUpdate_Run(t *testing.T) {
 				includeNotReady: true,
 				workload:        "deployment/backend",
 				selector:        "backend",
-				timeout:         1 * time.Second,
+				timeout:         10 * time.Second,
 				output:          "json",
 			},
 			skupperObjects: []runtime.Object{
@@ -582,7 +582,7 @@ func TestCmdConnectorUpdate_WaitUntil(t *testing.T) {
 
 		cmd.name = "my-connector"
 		cmd.Flags = &common.CommandConnectorUpdateFlags{
-			Timeout: 1 * time.Second,
+			Timeout: 10 * time.Second,
 			Output:  test.output,
 		}
 		cmd.namespace = "test"
diff --git a/internal/cmd/skupper/listener/kube/listener_create_test.go b/internal/cmd/skupper/listener/kube/listener_create_test.go
index 0197ccf3..6cefc5d3 100644
--- a/internal/cmd/skupper/listener/kube/listener_create_test.go
+++ b/internal/cmd/skupper/listener/kube/listener_create_test.go
@@ -142,7 +142,7 @@ func TestCmdListenerCreate_ValidateInput(t *testing.T) {
 			name:           "timeout is not valid",
 			args:           []string{"bad-timeout", "8080"},
 			flags:          common.CommandListenerCreateFlags{Timeout: 0 * time.Second},
-			expectedErrors: []string{"timeout is not valid"},
+			expectedErrors: []string{"timeout is not valid: duration must not be less than 10s; got 0s"},
 		},
 		{
 			name: "output is not valid",
@@ -230,21 +230,21 @@ func TestCmdListenerCreate_InputToOptions(t *testing.T) {
 	testTable := []test{
 		{
 			name:                 "test1",
-			flags:                common.CommandListenerCreateFlags{"backend", "backend", "secret", "tcp", 2 * time.Second, "json"},
+			flags:                common.CommandListenerCreateFlags{"backend", "backend", "secret", "tcp", 20 * time.Second, "json"},
 			expectedTlsSecret:    "secret",
 			expectedHost:         "backend",
 			expectedRoutingKey:   "backend",
-			expectedTimeout:      2 * time.Second,
+			expectedTimeout:      20 * time.Second,
 			expectedListenerType: "tcp",
 			expectedOutput:       "json",
 		},
 		{
 			name:                 "test2",
-			flags:                common.CommandListenerCreateFlags{"", "", "secret", "tcp", 3 * time.Second, "yaml"},
+			flags:                common.CommandListenerCreateFlags{"", "", "secret", "tcp", 30 * time.Second, "yaml"},
 			expectedTlsSecret:    "secret",
 			expectedHost:         "test2",
 			expectedRoutingKey:   "test2",
-			expectedTimeout:      3 * time.Second,
+			expectedTimeout:      30 * time.Second,
 			expectedListenerType: "tcp",
 			expectedOutput:       "yaml",
 		},
diff --git a/internal/cmd/skupper/listener/kube/listener_delete_test.go b/internal/cmd/skupper/listener/kube/listener_delete_test.go
index a5f056b3..b06f56ce 100644
--- a/internal/cmd/skupper/listener/kube/listener_delete_test.go
+++ b/internal/cmd/skupper/listener/kube/listener_delete_test.go
@@ -1,11 +1,12 @@
 package kube
 
 import (
-	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
-	"github.com/skupperproject/skupper/internal/cmd/skupper/common/utils"
 	"testing"
 	"time"
 
+	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
+	"github.com/skupperproject/skupper/internal/cmd/skupper/common/utils"
+
 	fakeclient "github.com/skupperproject/skupper/internal/kube/client/fake"
 	"github.com/skupperproject/skupper/pkg/apis/skupper/v1alpha1"
 	"gotest.tools/assert"
@@ -78,7 +79,7 @@ func TestCmdListenerDelete_ValidateInput(t *testing.T) {
 			},
 			flags:               common.CommandListenerDeleteFlags{Timeout: 0 * time.Minute},
 			skupperErrorMessage: "timeout is not valid",
-			expectedErrors:      []string{"timeout is not valid"},
+			expectedErrors:      []string{"timeout is not valid: duration must not be less than 10s; got 0s"},
 		},
 	}
 
diff --git a/internal/cmd/skupper/listener/kube/listener_update_test.go b/internal/cmd/skupper/listener/kube/listener_update_test.go
index d271c1cb..96303c5d 100644
--- a/internal/cmd/skupper/listener/kube/listener_update_test.go
+++ b/internal/cmd/skupper/listener/kube/listener_update_test.go
@@ -1,11 +1,12 @@
 package kube
 
 import (
-	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
-	"github.com/skupperproject/skupper/internal/cmd/skupper/common/utils"
 	"testing"
 	"time"
 
+	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
+	"github.com/skupperproject/skupper/internal/cmd/skupper/common/utils"
+
 	fakeclient "github.com/skupperproject/skupper/internal/kube/client/fake"
 	"github.com/skupperproject/skupper/pkg/apis/skupper/v1alpha1"
 	"gotest.tools/assert"
@@ -115,7 +116,7 @@ func TestCmdListenerUpdate_ValidateInput(t *testing.T) {
 			args: []string{"my-listener-tls"},
 			flags: common.CommandListenerUpdateFlags{
 				TlsSecret: ":not-valid",
-				Timeout:   5 * time.Minute,
+				Timeout:   50 * time.Minute,
 			},
 			skupperObjects: []runtime.Object{
 				&v1alpha1.Listener{
@@ -167,7 +168,7 @@ func TestCmdListenerUpdate_ValidateInput(t *testing.T) {
 		{
 			name:  "timeout is not valid",
 			args:  []string{"bad-timeout"},
-			flags: common.CommandListenerUpdateFlags{Timeout: 0 * time.Minute},
+			flags: common.CommandListenerUpdateFlags{Timeout: 5 * time.Second},
 			skupperObjects: []runtime.Object{
 				&v1alpha1.Listener{
 					ObjectMeta: v1.ObjectMeta{
@@ -186,14 +187,14 @@ func TestCmdListenerUpdate_ValidateInput(t *testing.T) {
 					},
 				},
 			},
-			expectedErrors: []string{"timeout is not valid"},
+			expectedErrors: []string{"timeout is not valid: duration must not be less than 10s; got 5s"},
 		},
 		{
 			name: "output is not valid",
 			args: []string{"bad-output"},
 			flags: common.CommandListenerUpdateFlags{
 				Output:  "not-supported",
-				Timeout: 1 * time.Second,
+				Timeout: 10 * time.Second,
 			},
 			skupperObjects: []runtime.Object{
 				&v1alpha1.Listener{
@@ -225,7 +226,7 @@ func TestCmdListenerUpdate_ValidateInput(t *testing.T) {
 				TlsSecret:    "secretname",
 				Port:         1234,
 				ListenerType: "tcp",
-				Timeout:      1 * time.Second,
+				Timeout:      10 * time.Second,
 				Output:       "json",
 			},
 			skupperObjects: []runtime.Object{
diff --git a/internal/cmd/skupper/token/kube/token_issue_test.go b/internal/cmd/skupper/token/kube/token_issue_test.go
index 3d3b9a2f..8ea3d3a0 100644
--- a/internal/cmd/skupper/token/kube/token_issue_test.go
+++ b/internal/cmd/skupper/token/kube/token_issue_test.go
@@ -394,7 +394,7 @@ func TestCmdTokenIssue_ValidateInput(t *testing.T) {
 					},
 				},
 			},
-			expectedErrors: []string{"expiration time is not valid"},
+			expectedErrors: []string{"expiration time is not valid: duration must not be less than 10s; got 0s"},
 		},
 		{
 			name: "timeout is not valid",
@@ -430,7 +430,7 @@ func TestCmdTokenIssue_ValidateInput(t *testing.T) {
 					},
 				},
 			},
-			expectedErrors: []string{"timeout is not valid"},
+			expectedErrors: []string{"timeout is not valid: duration must not be less than 10s; got 0s"},
 		},
 		{
 			name: "flags all valid",
diff --git a/internal/cmd/skupper/token/kube/token_redeem_test.go b/internal/cmd/skupper/token/kube/token_redeem_test.go
index 3eaf2499..14e16d68 100644
--- a/internal/cmd/skupper/token/kube/token_redeem_test.go
+++ b/internal/cmd/skupper/token/kube/token_redeem_test.go
@@ -2,12 +2,13 @@ package kube
 
 import (
 	"fmt"
-	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
-	"github.com/skupperproject/skupper/internal/cmd/skupper/common/utils"
 	"os"
 	"testing"
 	"time"
 
+	"github.com/skupperproject/skupper/internal/cmd/skupper/common"
+	"github.com/skupperproject/skupper/internal/cmd/skupper/common/utils"
+
 	fakeclient "github.com/skupperproject/skupper/internal/kube/client/fake"
 	"github.com/skupperproject/skupper/pkg/apis/skupper/v1alpha1"
 	"github.com/skupperproject/skupper/pkg/generated/client/clientset/versioned/scheme"
@@ -224,7 +225,7 @@ func TestCmdTokenRedeem_ValidateInput(t *testing.T) {
 			},
 			expectedErrors: []string{
 				"token file does not exist: stat ~/token.yaml: no such file or directory",
-				"timeout is not valid"},
+				"timeout is not valid: duration must not be less than 10s; got 0s"},
 		},
 		{
 			name:  "flags all valid",
diff --git a/internal/nonkube/client/compat/container_test.go b/internal/nonkube/client/compat/container_test.go
index 9fba557b..438b5f68 100644
--- a/internal/nonkube/client/compat/container_test.go
+++ b/internal/nonkube/client/compat/container_test.go
@@ -256,6 +256,9 @@ func TestContainer(t *testing.T) {
 }
 
 func NewClientOrSkip(t *testing.T, endpoint string, ctx context.Context) (*CompatClient, *sync.WaitGroup) {
+	if testing.Short() {
+		t.Skip("short: skipping test with dependency on podman")
+	}
 	var cli *CompatClient
 	var err error
 	var wg *sync.WaitGroup
diff --git a/internal/nonkube/client/compat/rest_test.go b/internal/nonkube/client/compat/rest_test.go
index 1f604622..f3662b01 100644
--- a/internal/nonkube/client/compat/rest_test.go
+++ b/internal/nonkube/client/compat/rest_test.go
@@ -51,6 +51,9 @@ func TestNewPodmanClient(t *testing.T) {
 // PodmanSkipValidation skips the current test if podman binary is not
 // available or if the client version found is lesser than 4.0.0.
 func PodmanSkipValidation(t *testing.T) {
+	if testing.Short() {
+		t.Skip("short: skipping test with dependency on podman")
+	}
 	stdout := new(bytes.Buffer)
 	cmd := exec.Command("podman", "version", "--format=json")
 	cmd.Stdout = stdout
diff --git a/pkg/fs/watcher_test.go b/pkg/fs/watcher_test.go
index 86f87ca2..7785d8ed 100644
--- a/pkg/fs/watcher_test.go
+++ b/pkg/fs/watcher_test.go
@@ -69,6 +69,8 @@ func TestFileWatcher(t *testing.T) {
 
 			// validate results
 			watcher.waitDone(test.expected)
+			watcher.mutex.Lock()
+			defer watcher.mutex.Unlock()
 			assert.DeepEqual(t, test.expected.created, watcher.results.created)
 			assert.DeepEqual(t, test.expected.updated, watcher.results.updated)
 			assert.DeepEqual(t, test.expected.deleted, watcher.results.deleted)
@@ -154,6 +156,8 @@ func (m *ModificationHandler) OnRemove(s string) {
 
 func (m *ModificationHandler) waitDone(expected modificationResult) {
 	_ = utils.RetryError(time.Millisecond*200, 10, func() error {
+		m.mutex.Lock()
+		defer m.mutex.Unlock()
 		if reflect.DeepEqual(m.results.created, expected.created) &&
 			reflect.DeepEqual(m.results.updated, expected.updated) &&
 			reflect.DeepEqual(m.results.deleted, expected.deleted) {
diff --git a/pkg/vanflow/eventsource/discovery_test.go b/pkg/vanflow/eventsource/discovery_test.go
index df047322..33651e04 100644
--- a/pkg/vanflow/eventsource/discovery_test.go
+++ b/pkg/vanflow/eventsource/discovery_test.go
@@ -50,9 +50,13 @@ func TestDiscoveryBasic(t *testing.T) {
 	tstSender.Send(tstCtx, beaconA.Encode())
 	tstSender.Send(tstCtx, beaconB.Encode())
 
-	// when listeners are not yet present the router can drop beacon messages
-	// retry sending initial beacons once
-	retryOnceAfter := time.Now().Add(500 * rtt)
+	// a "real" skupper router can drop beacon messages when there is not yet a
+	// consumer ready. retry sending initial beacons once when the
+	// implemenation is not a mock
+	retryOnceAfter := time.Now().Add(time.Hour * 48)
+	if _, isMock := tstSender.(interface{ IsMock() }); !isMock {
+		retryOnceAfter = time.Now().Add(500 * rtt)
+	}
 	// wait for discovery.List to return two sources
 	poll.WaitOn(t,
 		func(t poll.LogT) poll.Result {
@@ -60,7 +64,7 @@ func TestDiscoveryBasic(t *testing.T) {
 			if actual == desired {
 				return poll.Success()
 			}
-			if time.Now().After(retryOnceAfter) {
+			if _, isMock := tstSender.(interface{ IsMock() }); !isMock && time.Now().After(retryOnceAfter) {
 				tstSender.Send(tstCtx, beaconA.Encode())
 				tstSender.Send(tstCtx, beaconB.Encode())
 				retryOnceAfter = retryOnceAfter.Add(time.Hour)
@@ -84,12 +88,8 @@ func TestDiscoveryBasic(t *testing.T) {
 	assert.DeepEqual(t, sourceA, eventA, cmpopts.IgnoreFields(Info{}, "LastSeen"))
 	assert.DeepEqual(t, sourceB, eventB, cmpopts.IgnoreFields(Info{}, "LastSeen"))
 
-	tstSender.Send(tstCtx, beaconA.Encode())
-	tstSender.Send(tstCtx, beaconA.Encode())
-	tstSender.Send(tstCtx, beaconA.Encode())
 	tstSender.Send(tstCtx, beaconA.Encode())
 	tstSender.Send(tstCtx, beaconB.Encode())
-	tstSender.Send(tstCtx, beaconA.Encode())
 
 	// wait for LastSeen to update
 	poll.WaitOn(t,
@@ -124,7 +124,7 @@ func TestDiscoveryBasic(t *testing.T) {
 			if actual == desired {
 				return poll.Success()
 			}
-			return poll.Continue("number of event sources is %d, not %d", actual, desired)
+			return poll.Continue("number of event sources is %d, not %d: got %v", actual, desired, discovery.List())
 		}, poll.WithTimeout(1000*rtt),
 	)
 
@@ -183,9 +183,13 @@ func TestDiscoveryWatch(t *testing.T) {
 			MessageProps: vanflow.MessageProps{To: mcsfe(sourceAID)},
 		}
 		for {
-			time.Sleep(rtt)
-			heartbeatSender.Send(tstCtx, heartbeat.Encode())
-			heartbeat.Now++
+			select {
+			case <-time.After(rtt):
+				heartbeatSender.Send(tstCtx, heartbeat.Encode())
+				heartbeat.Now++
+			case <-tstCtx.Done():
+				return
+			}
 		}
 	}()
 
@@ -236,7 +240,7 @@ func TestDiscoveryWatch(t *testing.T) {
 	select {
 	case event := <-forgottenOut:
 		assert.Check(t, event.ID == sourceAID)
-	case <-time.After(80 * rtt):
+	case <-time.After(200 * rtt):
 		t.Error("expected source to be forgotten after starting watch client with no activity")
 	}
 }