Repository: scaleway/scaleway-cli
Pull request #1126 (2020-06-24 14:34:12)

test: fix test on new version warning
### Community Note

* Please vote on this pull request by adding a 👍 [reaction](https://blog.github.com/2016-03-10-add-reactions-to-pull-requests-issues-and-comments/) to the original pull request comment to help the community and maintainers prioritize this request.
* Please do not leave "+1" or other comments that do not add relevant new information or questions, they generate extra noise for pull request followers and do not help prioritize the request

<!--- Thank you for keeping this note for the community --->

<!--- If your PR fully resolves and should automatically close the linked issue, use Closes. Otherwise, use Relates --->
Relates OR Closes #0000

Release note for [CHANGELOG](https://github.com/scaleway/scaleway-cli/blob/v2/CHANGELOG.md):
<!--
If the change is not user facing, just write "NONE" in the release-note block below.
-->

```release-note

```



Patch:
diff --git a/internal/core/bootstrap.go b/internal/core/bootstrap.go
index e5e59844..ff381747 100644
--- a/internal/core/bootstrap.go
+++ b/internal/core/bootstrap.go
@@ -4,9 +4,11 @@ import (
 	"context"
 	"fmt"
 	"io"
+	"net/http"
 	"os"
 	"time"
 
+	"github.com/scaleway/scaleway-cli/internal/account"
 	"github.com/scaleway/scaleway-cli/internal/interactive"
 	"github.com/scaleway/scaleway-cli/internal/matomo"
 	"github.com/scaleway/scaleway-sdk-go/logger"
@@ -56,6 +58,10 @@ type BootstrapConfig struct {
 
 	// Optional we use it if defined
 	Logger *Logger
+
+	// Default HTTPClient to use. If not provided it will use a basic http client with a simple retry policy
+	// This client will be used to create SDK client, account call, version checking and telemetry
+	HTTPClient *http.Client
 }
 
 // Bootstrap is the main entry point. It is directly called from main.
@@ -116,12 +122,19 @@ func Bootstrap(config *BootstrapConfig) (exitCode int, result interface{}, err e
 	}
 	interactive.SetOutputWriter(config.Stderr) // set printer for interactive function (always stderr).
 
+	httpClient := config.HTTPClient
+	if httpClient == nil {
+		httpClient = &http.Client{
+			Transport: &retryableHTTPTransport{transport: http.DefaultTransport},
+		}
+	}
+
 	// An authenticated client will be created later if required.
 	client := config.Client
 	isClientFromBootstrapConfig := true
 	if client == nil {
 		isClientFromBootstrapConfig = false
-		client, err = createAnonymousClient(config.BuildInfo)
+		client, err = createAnonymousClient(httpClient, config.BuildInfo)
 		if err != nil {
 			printErr := printer.Print(err, nil)
 			if printErr != nil {
@@ -142,12 +155,14 @@ func Bootstrap(config *BootstrapConfig) (exitCode int, result interface{}, err e
 		OverrideEnv:    config.OverrideEnv,
 		OverrideExec:   config.OverrideExec,
 		ConfigPathFlag: configPathFlag,
+		Logger:         log,
 
 		stdout:                      config.Stdout,
 		stderr:                      config.Stderr,
 		stdin:                       config.Stdin,
 		result:                      nil, // result is later injected by cobra_utils.go/cobraRun()
 		command:                     nil, // command is later injected by cobra_utils.go/cobraRun()
+		httpClient:                  httpClient,
 		isClientFromBootstrapConfig: isClientFromBootstrapConfig,
 	}
 	// We make sure OverrideEnv is never nil in meta.
@@ -164,6 +179,7 @@ func Bootstrap(config *BootstrapConfig) (exitCode int, result interface{}, err e
 	if ctx == nil {
 		ctx = context.Background()
 	}
+	ctx = account.InjectHTTPClient(ctx, httpClient)
 	ctx = injectMeta(ctx, meta)
 
 	// Send Matomo telemetry when exiting the bootstrap
@@ -195,7 +211,7 @@ func Bootstrap(config *BootstrapConfig) (exitCode int, result interface{}, err e
 
 	// Check CLI new version when exiting the bootstrap
 	defer func() { // if we plan to remove defer, do not forget logger is not set until cobra pre init func
-		config.BuildInfo.checkVersion()
+		config.BuildInfo.checkVersion(ctx)
 	}()
 
 	// cobraBuilder will build a Cobra root command from a list of Command
diff --git a/internal/core/build_info.go b/internal/core/build_info.go
index 14875406..62aaca64 100644
--- a/internal/core/build_info.go
+++ b/internal/core/build_info.go
@@ -1,7 +1,9 @@
 package core
 
 import (
+	"context"
 	"encoding/json"
+	"fmt"
 	"io/ioutil"
 	"net/http"
 	"os"
@@ -10,8 +12,6 @@ import (
 	"time"
 
 	"github.com/hashicorp/go-version"
-	"github.com/scaleway/scaleway-sdk-go/logger"
-	"github.com/scaleway/scaleway-sdk-go/scw"
 )
 
 type BuildInfo struct {
@@ -56,48 +56,55 @@ func (b *BuildInfo) GetUserAgent() string {
 	return userAgentPrefix
 }
 
-func (b *BuildInfo) checkVersion() {
-	if !b.IsRelease() || os.Getenv(scwDisableCheckVersionEnv) == "true" {
-		logger.Debugf("skipping check version")
+func (b *BuildInfo) checkVersion(ctx context.Context) {
+	if !b.IsRelease() || ExtractEnv(ctx, scwDisableCheckVersionEnv) == "true" {
+		ExtractLogger(ctx).Debug("skipping check version")
 		return
 	}
 
-	latestVersionUpdateFilePath := getLatestVersionUpdateFilePath()
+	latestVersionUpdateFilePath := getLatestVersionUpdateFilePath(ExtractCacheDir(ctx))
 
 	// do nothing if last refresh at during the last 24h
 	if wasFileModifiedLast24h(latestVersionUpdateFilePath) {
-		logger.Debugf("version was already checked during past 24 hours")
+		ExtractLogger(ctx).Debug("version was already checked during past 24 hours")
 		return
 	}
 
 	// do nothing if we cannot create the file
-	if !createAndCloseFile(latestVersionUpdateFilePath) {
+	err := createAndCloseFile(latestVersionUpdateFilePath)
+	if err != nil {
+		ExtractLogger(ctx).Debug(err.Error())
 		return
 	}
 
 	// pull latest version
-	latestVersion, err := getLatestVersion()
+	latestVersion, err := getLatestVersion(ExtractHTTPClient(ctx))
 	if err != nil {
-		logger.Debugf("failed to retrieve latest version: %s", err)
+		ExtractLogger(ctx).Debugf("failed to retrieve latest version: %s\n", err)
 		return
 	}
 
 	if b.Version.LessThan(latestVersion) {
-		logger.Warningf("a new version of scw is available (%s), beware that you are currently running %v", latestVersion, b.Version)
+		ExtractLogger(ctx).Warningf("a new version of scw is available (%s), beware that you are currently running %s\n", latestVersion, b.Version)
 	} else {
-		logger.Debugf("version is up to date (%s)", b.Version)
+		ExtractLogger(ctx).Debugf("version is up to date (%s)\n", b.Version)
 	}
 }
 
-func getLatestVersionUpdateFilePath() string {
-	return filepath.Join(scw.GetCacheDirectory(), latestVersionUpdateFileLocalName)
+func getLatestVersionUpdateFilePath(cacheDir string) string {
+	return filepath.Join(cacheDir, latestVersionUpdateFileLocalName)
 }
 
 // getLatestVersion attempt to read the latest version of the remote file at latestVersionFileURL.
-func getLatestVersion() (*version.Version, error) {
-	resp, err := (&http.Client{
-		Timeout: latestVersionRequestTimeout,
-	}).Get(latestVersionFileURL)
+func getLatestVersion(client *http.Client) (*version.Version, error) {
+	ctx, cancelTimeout := context.WithTimeout(context.Background(), latestVersionRequestTimeout)
+	defer cancelTimeout()
+
+	req, err := http.NewRequestWithContext(ctx, http.MethodGet, latestVersionFileURL, nil)
+	if err != nil {
+		return nil, err
+	}
+	resp, err := client.Do(req)
 	if resp != nil {
 		defer resp.Body.Close()
 	}
@@ -125,17 +132,15 @@ func wasFileModifiedLast24h(path string) bool {
 }
 
 // createAndCloseFile creates a file and closes it. It returns true on succeed, false on failure.
-func createAndCloseFile(path string) bool {
+func createAndCloseFile(path string) error {
 	err := os.MkdirAll(filepath.Dir(path), 0700)
 	if err != nil {
-		logger.Debugf("failed creating path %s: %s", path, err)
+		return fmt.Errorf("failed creating path %s: %s", path, err)
 	}
 	newFile, err := os.OpenFile(path, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0600)
 	if err != nil {
-		logger.Debugf("failed creating file %s: %s", path, err)
-		return false
+		return fmt.Errorf("failed creating file %s: %s", path, err)
 	}
 
-	newFile.Close()
-	return true
+	return newFile.Close()
 }
diff --git a/internal/core/client.go b/internal/core/client.go
index 71aea49c..8d3fff32 100644
--- a/internal/core/client.go
+++ b/internal/core/client.go
@@ -11,7 +11,7 @@ import (
 )
 
 // createClient creates a Scaleway SDK client.
-func createClient(buildInfo *BuildInfo, profileName string) (*scw.Client, error) {
+func createClient(httpClient *http.Client, buildInfo *BuildInfo, profileName string) (*scw.Client, error) {
 	_, err := scw.MigrateLegacyConfig()
 	if err != nil {
 		return nil, err
@@ -62,9 +62,7 @@ func createClient(buildInfo *BuildInfo, profileName string) (*scw.Client, error)
 		scw.WithDefaultZone(scw.ZoneFrPar1),
 		scw.WithUserAgent(buildInfo.GetUserAgent()),
 		scw.WithProfile(profile),
-		scw.WithHTTPClient(&http.Client{
-			Transport: &retryableHTTPTransport{transport: http.DefaultTransport},
-		}),
+		scw.WithHTTPClient(httpClient),
 	}
 
 	client, err := scw.NewClient(opts...)
@@ -75,14 +73,12 @@ func createClient(buildInfo *BuildInfo, profileName string) (*scw.Client, error)
 	return client, nil
 }
 
-func createAnonymousClient(buildInfo *BuildInfo) (*scw.Client, error) {
+func createAnonymousClient(httpClient *http.Client, buildInfo *BuildInfo) (*scw.Client, error) {
 	opts := []scw.ClientOption{
 		scw.WithDefaultRegion(scw.RegionFrPar),
 		scw.WithDefaultZone(scw.ZoneFrPar1),
 		scw.WithUserAgent(buildInfo.GetUserAgent()),
-		scw.WithHTTPClient(&http.Client{
-			Transport: &retryableHTTPTransport{transport: http.DefaultTransport},
-		}),
+		scw.WithHTTPClient(httpClient),
 	}
 
 	client, err := scw.NewClient(opts...)
diff --git a/internal/core/cobra_utils.go b/internal/core/cobra_utils.go
index 4e05ef6d..6f88b0a5 100644
--- a/internal/core/cobra_utils.go
+++ b/internal/core/cobra_utils.go
@@ -21,7 +21,7 @@ func cobraRun(ctx context.Context, cmd *Command) func(*cobra.Command, []string)
 
 		// If command requires authentication and the client was not directly provided in the bootstrap config, we create a new client and overwrite the existing one
 		if !cmd.AllowAnonymousClient && !meta.isClientFromBootstrapConfig {
-			client, err := createClient(meta.BuildInfo, ExtractProfileName(ctx))
+			client, err := createClient(meta.httpClient, meta.BuildInfo, ExtractProfileName(ctx))
 			if err != nil {
 				return err
 			}
diff --git a/internal/core/context.go b/internal/core/context.go
index 16590e98..5467aac1 100644
--- a/internal/core/context.go
+++ b/internal/core/context.go
@@ -3,6 +3,7 @@ package core
 import (
 	"context"
 	"io"
+	"net/http"
 	"os"
 	"path"
 
@@ -28,6 +29,7 @@ type meta struct {
 	stderr                      io.Writer
 	stdin                       io.Reader
 	result                      interface{}
+	httpClient                  *http.Client
 	isClientFromBootstrapConfig bool
 }
 
@@ -90,6 +92,14 @@ func ExtractUserHomeDir(ctx context.Context) string {
 	return ExtractEnv(ctx, "HOME")
 }
 
+func ExtractCacheDir(ctx context.Context) string {
+	env := ExtractEnv(ctx, scw.ScwCacheDirEnv)
+	if env != "" {
+		return env
+	}
+	return scw.GetCacheDirectory()
+}
+
 func ExtractBinaryName(ctx context.Context) string {
 	return extractMeta(ctx).BinaryName
 }
@@ -105,6 +115,10 @@ func ExtractProfileName(ctx context.Context) string {
 	return ExtractEnv(ctx, scw.ScwActiveProfileEnv)
 }
 
+func ExtractHTTPClient(ctx context.Context) *http.Client {
+	return extractMeta(ctx).httpClient
+}
+
 func ExtractConfigPath(ctx context.Context) string {
 	meta := extractMeta(ctx)
 	if meta.ConfigPathFlag != "" {
@@ -125,7 +139,7 @@ func ReloadClient(ctx context.Context) error {
 	if meta.isClientFromBootstrapConfig {
 		return nil
 	}
-	meta.Client, err = createClient(meta.BuildInfo, "")
+	meta.Client, err = createClient(meta.httpClient, meta.BuildInfo, "")
 	return err
 }
 


Test patch:
diff --git a/internal/core/build_info_test.go b/internal/core/build_info_test.go
index d4dd54c2..0de1b569 100644
--- a/internal/core/build_info_test.go
+++ b/internal/core/build_info_test.go
@@ -2,13 +2,16 @@ package core
 
 import (
 	"context"
-	"fmt"
 	"os"
 	"reflect"
 	"testing"
+	"time"
 
+	"github.com/alecthomas/assert"
 	"github.com/hashicorp/go-version"
 	"github.com/scaleway/scaleway-cli/internal/args"
+	"github.com/scaleway/scaleway-sdk-go/scw"
+	"github.com/stretchr/testify/require"
 )
 
 var fakeCommand = &Command{
@@ -20,11 +23,6 @@ var fakeCommand = &Command{
 	},
 }
 
-func deleteLatestVersionUpdateFile(*BeforeFuncCtx) error {
-	os.Remove(getLatestVersionUpdateFilePath())
-	return nil
-}
-
 // These tests needs to run in sequence since they are modifying a file on the filesystem
 func Test_CheckVersion(t *testing.T) {
 	t.Run("Outdated version", Test(&TestConfig{
@@ -32,13 +30,13 @@ func Test_CheckVersion(t *testing.T) {
 		BuildInfo: &BuildInfo{
 			Version: version.Must(version.NewSemver("v1.20")),
 		},
-		BeforeFunc: deleteLatestVersionUpdateFile,
-		Cmd:        "scw plop",
+		Cmd: "scw plop",
 		Check: TestCheckCombine(
-			TestCheckGolden(),
-			TestCheckExitCode(0),
+			func(t *testing.T, ctx *CheckFuncCtx) {
+				assert.Equal(t, "a new version of scw is available (2.0.0-beta.4), beware that you are currently running 1.20.0\n", ctx.LogBuffer)
+			},
 		),
-		DisableParallel: true,
+		TmpHomeDir: true,
 	}))
 
 	t.Run("Up to date version", Test(&TestConfig{
@@ -46,13 +44,13 @@ func Test_CheckVersion(t *testing.T) {
 		BuildInfo: &BuildInfo{
 			Version: version.Must(version.NewSemver("v99.99")),
 		},
-		BeforeFunc: deleteLatestVersionUpdateFile,
-		Cmd:        "scw plop -D",
+		Cmd: "scw plop -D",
 		Check: TestCheckCombine(
-			TestCheckGolden(),
-			TestCheckExitCode(0),
+			func(t *testing.T, ctx *CheckFuncCtx) {
+				assert.Contains(t, ctx.LogBuffer, "version is up to date (99.99.0)\n")
+			},
 		),
-		DisableParallel: true,
+		TmpHomeDir: true,
 	}))
 
 	t.Run("Already checked", Test(&TestConfig{
@@ -61,16 +59,35 @@ func Test_CheckVersion(t *testing.T) {
 			Version: version.Must(version.NewSemver("v1.0")),
 		},
 		BeforeFunc: func(ctx *BeforeFuncCtx) error {
-			if createAndCloseFile(getLatestVersionUpdateFilePath()) {
-				return nil
-			}
-			return fmt.Errorf("failed to create latestVersionUpdateFile")
+			return createAndCloseFile(getLatestVersionUpdateFilePath(ctx.OverrideEnv[scw.ScwCacheDirEnv]))
 		},
 		Cmd: "scw plop -D",
 		Check: TestCheckCombine(
-			TestCheckGolden(),
-			TestCheckExitCode(0),
+			func(t *testing.T, ctx *CheckFuncCtx) {
+				assert.Contains(t, ctx.LogBuffer, "version was already checked during past 24 hours\n")
+			},
+		),
+		TmpHomeDir: true,
+	}))
+
+	t.Run("Check more than 24h ago", Test(&TestConfig{
+		Commands: NewCommands(fakeCommand),
+		BuildInfo: &BuildInfo{
+			Version: version.Must(version.NewSemver("v1.0")),
+		},
+		BeforeFunc: func(ctx *BeforeFuncCtx) error {
+			filePath := getLatestVersionUpdateFilePath(ctx.OverrideEnv[scw.ScwCacheDirEnv])
+			err := createAndCloseFile(filePath)
+			require.NoError(t, err)
+			twoDaysAgo := time.Now().Local().Add(-2 * time.Hour * 24)
+			return os.Chtimes(filePath, twoDaysAgo, twoDaysAgo)
+		},
+		Cmd: "scw plop",
+		Check: TestCheckCombine(
+			func(t *testing.T, ctx *CheckFuncCtx) {
+				assert.Contains(t, ctx.LogBuffer, "a new version of scw is available (2.0.0-beta.4), beware that you are currently running 1.0.0\n")
+			},
 		),
-		DisableParallel: true,
+		TmpHomeDir: true,
 	}))
 }
diff --git a/internal/core/testdata/test-check-version-already-checked.golden b/internal/core/testdata/test-check-version-already-checked.golden
deleted file mode 100644
index 87818d7a..00000000
--- a/internal/core/testdata/test-check-version-already-checked.golden
+++ /dev/null
@@ -1,8 +0,0 @@
-🎲🎲🎲 EXIT CODE: 0 🎲🎲🎲
-🟩🟩🟩 STDOUT️ 🟩🟩🟩️
-✅ Success.
-🟩🟩🟩 JSON STDOUT 🟩🟩🟩
-{
-  "message": "Success",
-  "details": ""
-}
diff --git a/internal/core/testdata/test-check-version-check-more-than24h-ago.cassette.yaml b/internal/core/testdata/test-check-version-check-more-than24h-ago.cassette.yaml
new file mode 100644
index 00000000..86a09ef9
--- /dev/null
+++ b/internal/core/testdata/test-check-version-check-more-than24h-ago.cassette.yaml
@@ -0,0 +1,33 @@
+---
+version: 1
+interactions:
+- request:
+    body: ""
+    form: {}
+    headers: {}
+    url: https://scw-devtools.s3.nl-ams.scw.cloud/scw-cli-v2-version
+    method: GET
+  response:
+    body: 2.0.0-beta.4
+    headers:
+      Accept-Ranges:
+      - bytes
+      Content-Length:
+      - "12"
+      Content-Type:
+      - application/octet-stream
+      Date:
+      - Wed, 24 Jun 2020 13:56:16 GMT
+      Etag:
+      - '"c557c270fc02f78c44f2ccbe75faf6ae"'
+      Last-Modified:
+      - Mon, 08 Jun 2020 15:34:08 GMT
+      X-Amz-Id-2:
+      - tx18917f4b74a44e5196d6a-005ef35b80
+      X-Amz-Request-Id:
+      - tx18917f4b74a44e5196d6a-005ef35b80
+      X-Amz-Version-Id:
+      - "1591630448205209"
+    status: 200 OK
+    code: 200
+    duration: ""
diff --git a/internal/core/testdata/test-check-version-outdated-version.cassette.yaml b/internal/core/testdata/test-check-version-outdated-version.cassette.yaml
new file mode 100644
index 00000000..628a68a4
--- /dev/null
+++ b/internal/core/testdata/test-check-version-outdated-version.cassette.yaml
@@ -0,0 +1,33 @@
+---
+version: 1
+interactions:
+- request:
+    body: ""
+    form: {}
+    headers: {}
+    url: https://scw-devtools.s3.nl-ams.scw.cloud/scw-cli-v2-version
+    method: GET
+  response:
+    body: 2.0.0-beta.4
+    headers:
+      Accept-Ranges:
+      - bytes
+      Content-Length:
+      - "12"
+      Content-Type:
+      - application/octet-stream
+      Date:
+      - Wed, 24 Jun 2020 12:45:08 GMT
+      Etag:
+      - '"c557c270fc02f78c44f2ccbe75faf6ae"'
+      Last-Modified:
+      - Mon, 08 Jun 2020 15:34:08 GMT
+      X-Amz-Id-2:
+      - txec799400299f459dbad48-005ef34ad3
+      X-Amz-Request-Id:
+      - txec799400299f459dbad48-005ef34ad3
+      X-Amz-Version-Id:
+      - "1591630448205209"
+    status: 200 OK
+    code: 200
+    duration: ""
diff --git a/internal/core/testdata/test-check-version-outdated-version.golden b/internal/core/testdata/test-check-version-outdated-version.golden
deleted file mode 100644
index 87818d7a..00000000
--- a/internal/core/testdata/test-check-version-outdated-version.golden
+++ /dev/null
@@ -1,8 +0,0 @@
-🎲🎲🎲 EXIT CODE: 0 🎲🎲🎲
-🟩🟩🟩 STDOUT️ 🟩🟩🟩️
-✅ Success.
-🟩🟩🟩 JSON STDOUT 🟩🟩🟩
-{
-  "message": "Success",
-  "details": ""
-}
diff --git a/internal/core/testdata/test-check-version-up-to-date-version.cassette.yaml b/internal/core/testdata/test-check-version-up-to-date-version.cassette.yaml
new file mode 100644
index 00000000..66ed0488
--- /dev/null
+++ b/internal/core/testdata/test-check-version-up-to-date-version.cassette.yaml
@@ -0,0 +1,33 @@
+---
+version: 1
+interactions:
+- request:
+    body: ""
+    form: {}
+    headers: {}
+    url: https://scw-devtools.s3.nl-ams.scw.cloud/scw-cli-v2-version
+    method: GET
+  response:
+    body: 2.0.0-beta.4
+    headers:
+      Accept-Ranges:
+      - bytes
+      Content-Length:
+      - "12"
+      Content-Type:
+      - application/octet-stream
+      Date:
+      - Wed, 24 Jun 2020 13:21:48 GMT
+      Etag:
+      - '"c557c270fc02f78c44f2ccbe75faf6ae"'
+      Last-Modified:
+      - Mon, 08 Jun 2020 15:34:08 GMT
+      X-Amz-Id-2:
+      - tx35e3dce5738b427395609-005ef3536c
+      X-Amz-Request-Id:
+      - tx35e3dce5738b427395609-005ef3536c
+      X-Amz-Version-Id:
+      - "1591630448205209"
+    status: 200 OK
+    code: 200
+    duration: ""
diff --git a/internal/core/testdata/test-check-version-up-to-date-version.golden b/internal/core/testdata/test-check-version-up-to-date-version.golden
deleted file mode 100644
index 87818d7a..00000000
--- a/internal/core/testdata/test-check-version-up-to-date-version.golden
+++ /dev/null
@@ -1,8 +0,0 @@
-🎲🎲🎲 EXIT CODE: 0 🎲🎲🎲
-🟩🟩🟩 STDOUT️ 🟩🟩🟩️
-✅ Success.
-🟩🟩🟩 JSON STDOUT 🟩🟩🟩
-{
-  "message": "Success",
-  "details": ""
-}
diff --git a/internal/core/testing.go b/internal/core/testing.go
index f9f4c185..d0beb1f5 100644
--- a/internal/core/testing.go
+++ b/internal/core/testing.go
@@ -21,7 +21,6 @@ import (
 	"github.com/dnaeon/go-vcr/cassette"
 	"github.com/dnaeon/go-vcr/recorder"
 	"github.com/hashicorp/go-version"
-	"github.com/scaleway/scaleway-cli/internal/account"
 	"github.com/scaleway/scaleway-cli/internal/human"
 	"github.com/scaleway/scaleway-cli/internal/interactive"
 	"github.com/scaleway/scaleway-sdk-go/api/test/v1"
@@ -72,6 +71,9 @@ type CheckFuncCtx struct {
 	OverrideEnv map[string]string
 
 	Logger *Logger
+
+	// The content logged by the command
+	LogBuffer string
 }
 
 // testMetadata contains arbitrary data that can be passed along a test lifecycle.
@@ -160,6 +162,8 @@ type TestConfig struct {
 
 	// If set, it will create a temporary home directory during the tests.
 	// Get this folder with ExtractUserHomeDir()
+	// This will also use this temporary directory as a cache directory.
+	// Get this folder with ExtractCacheDir()
 	TmpHomeDir bool
 
 	// OverrideEnv contains environment variables that will be overridden during the test.
@@ -263,12 +267,13 @@ func Test(config *TestConfig) func(t *testing.T) {
 			t.Parallel()
 		}
 
-		log := &Logger{
+		testLogger := &Logger{
 			writer: os.Stderr,
 			level:  logger.LogLevelInfo,
 		}
+
 		if *Debug {
-			log.level = logger.LogLevelDebug
+			testLogger.level = logger.LogLevelDebug
 		}
 
 		// Because human marshal of date is relative (e.g 3 minutes ago) we must make sure it stay consistent for golden to works.
@@ -293,7 +298,6 @@ func Test(config *TestConfig) func(t *testing.T) {
 		httpClient, cleanup, err := getHTTPRecoder(t, *UpdateCassettes)
 		require.NoError(t, err)
 		defer cleanup()
-		ctx = account.InjectHTTPClient(ctx, httpClient)
 
 		// We try to use the client provided in the config
 		// if no client is provided in the config we create a test client
@@ -319,7 +323,9 @@ func Test(config *TestConfig) func(t *testing.T) {
 				assert.NoError(t, err)
 			}()
 			overrideEnv["HOME"] = dir
+			overrideEnv[scw.ScwCacheDirEnv] = dir
 			meta["HOME"] = dir
+			meta[scw.ScwCacheDirEnv] = dir
 		}
 
 		overrideExec := defaultOverrideExec
@@ -341,7 +347,7 @@ func Test(config *TestConfig) func(t *testing.T) {
 		buildInfo := config.BuildInfo
 		if buildInfo == nil {
 			buildInfo = &BuildInfo{
-				Version:   version.Must(version.NewSemver("v0.0.0")),
+				Version:   version.Must(version.NewSemver("v0.0.0+test")),
 				BuildDate: "unknown",
 				GoVersion: "runtime.Version()",
 				GitBranch: "unknown",
@@ -365,7 +371,8 @@ func Test(config *TestConfig) func(t *testing.T) {
 				OverrideEnv:      overrideEnv,
 				OverrideExec:     overrideExec,
 				Ctx:              ctx,
-				Logger:           log,
+				Logger:           testLogger,
+				HTTPClient:       httpClient,
 			})
 			require.NoError(t, err, "error executing cmd (%s)\nstdout: %s\nstderr: %s", args, stdoutBuffer.String(), stderrBuffer.String())
 
@@ -374,16 +381,16 @@ func Test(config *TestConfig) func(t *testing.T) {
 
 		// Run config.BeforeFunc
 		if config.BeforeFunc != nil {
-			log.Debug("Start BeforeFunc")
+			testLogger.Debug("Start BeforeFunc")
 			require.NoError(t, config.BeforeFunc(&BeforeFuncCtx{
 				T:           t,
 				Client:      client,
 				ExecuteCmd:  executeCmd,
 				Meta:        meta,
 				OverrideEnv: overrideEnv,
-				Logger:      log,
+				Logger:      testLogger,
 			}))
-			log.Debug("End BeforeFunc")
+			testLogger.Debug("End BeforeFunc")
 		}
 
 		// Run config.Cmd
@@ -393,6 +400,15 @@ func Test(config *TestConfig) func(t *testing.T) {
 		if config.Cmd != "" {
 			args = cmdToArgs(meta, config.Cmd)
 		}
+
+		// We create a separate logger for the command we want to test.
+		// This separate logger allow check function to test content log by a command
+		// without content log by the test-engine (Before/After func, ...).
+		cmdLoggerBuffer := &bytes.Buffer{}
+		cmdLogger := &Logger{
+			writer: io.MultiWriter(cmdLoggerBuffer, os.Stderr),
+			level:  testLogger.level,
+		}
 		if len(args) > 0 {
 			stdout := &bytes.Buffer{}
 			stderr := &bytes.Buffer{}
@@ -408,7 +424,8 @@ func Test(config *TestConfig) func(t *testing.T) {
 				OverrideEnv:      overrideEnv,
 				OverrideExec:     overrideExec,
 				Ctx:              ctx,
-				Logger:           log,
+				Logger:           cmdLogger,
+				HTTPClient:       httpClient,
 			})
 
 			meta["CmdResult"] = result
@@ -421,13 +438,14 @@ func Test(config *TestConfig) func(t *testing.T) {
 				Err:         err,
 				Client:      client,
 				OverrideEnv: overrideEnv,
-				Logger:      log,
+				Logger:      testLogger,
+				LogBuffer:   cmdLoggerBuffer.String(),
 			})
 		}
 
 		// Run config.AfterFunc
 		if config.AfterFunc != nil {
-			log.Debug("Start AfterFunc")
+			testLogger.Debug("Start AfterFunc")
 			require.NoError(t, config.AfterFunc(&AfterFuncCtx{
 				T:           t,
 				Client:      client,
@@ -435,9 +453,9 @@ func Test(config *TestConfig) func(t *testing.T) {
 				Meta:        meta,
 				CmdResult:   result,
 				OverrideEnv: overrideEnv,
-				Logger:      log,
+				Logger:      testLogger,
 			}))
-			log.Debug("End AfterFunc")
+			testLogger.Debug("End AfterFunc")
 		}
 	}
 }
@@ -522,7 +540,6 @@ func ExecAfterCmd(cmd string) AfterFunc {
 // TestCheckCombine combines multiple check functions into one.
 func TestCheckCombine(checks ...TestCheck) TestCheck {
 	return func(t *testing.T, ctx *CheckFuncCtx) {
-		assert.Equal(t, true, len(checks) > 1, "TestCheckCombine must be used to combine more than one TestCheck")
 		for _, check := range checks {
 			check(t, ctx)
 		}
diff --git a/internal/namespaces/feedback/custom_test.go b/internal/namespaces/feedback/custom_test.go
index 8c37ee9b..0c4cee76 100644
--- a/internal/namespaces/feedback/custom_test.go
+++ b/internal/namespaces/feedback/custom_test.go
@@ -23,7 +23,7 @@ func Test_FeedbackBug(t *testing.T) {
 				observed = cmd.Args[1]
 			}
 			assert.Equal(t,
-				"https://github.com/scaleway/scaleway-cli/issues/new?body=%0A%23%23+Description%3A%0A%0A%23%23+How+to+reproduce%3A%0A%0A%23%23%23+Command+attempted%0A%0A%23%23%23+Expected+Behavior%0A%0A%23%23%23+Actual+Behavior%0A%0A%23%23+More+info%0A%0A%23%23+Version%0A%0AVersion++++0.0.0%0ABuildDate++unknown%0AGoVersion++runtime.Version%28%29%0AGitBranch++unknown%0AGitCommit++unknown%0AGoArch+++++runtime.GOARCH%0AGoOS+++++++runtime.GOOS%0A&issueTemplate=bug_report.md&labels=bug",
+				"https://github.com/scaleway/scaleway-cli/issues/new?body=%0A%23%23+Description%3A%0A%0A%23%23+How+to+reproduce%3A%0A%0A%23%23%23+Command+attempted%0A%0A%23%23%23+Expected+Behavior%0A%0A%23%23%23+Actual+Behavior%0A%0A%23%23+More+info%0A%0A%23%23+Version%0A%0AVersion++++0.0.0%26%2343%3Btest%0ABuildDate++unknown%0AGoVersion++runtime.Version%28%29%0AGitBranch++unknown%0AGitCommit++unknown%0AGoArch+++++runtime.GOARCH%0AGoOS+++++++runtime.GOOS%0A&issueTemplate=bug_report.md&labels=bug",
 				observed)
 
 			return 0, nil
@@ -49,7 +49,7 @@ func Test_FeedbackFeature(t *testing.T) {
 				observed = cmd.Args[1]
 			}
 			assert.Equal(t,
-				"https://github.com/scaleway/scaleway-cli/issues/new?body=%0A%23%23+Description%0A%0A%23%23+How+this+functionality+would+be+exposed%0A%0A%23%23+References%0A%0A%23%23+Version%0A%0AVersion++++0.0.0%0ABuildDate++unknown%0AGoVersion++runtime.Version%28%29%0AGitBranch++unknown%0AGitCommit++unknown%0AGoArch+++++runtime.GOARCH%0AGoOS+++++++runtime.GOOS%0A&issueTemplate=feature_request.md&labels=enhancement",
+				"https://github.com/scaleway/scaleway-cli/issues/new?body=%0A%23%23+Description%0A%0A%23%23+How+this+functionality+would+be+exposed%0A%0A%23%23+References%0A%0A%23%23+Version%0A%0AVersion++++0.0.0%26%2343%3Btest%0ABuildDate++unknown%0AGoVersion++runtime.Version%28%29%0AGitBranch++unknown%0AGitCommit++unknown%0AGoArch+++++runtime.GOARCH%0AGoOS+++++++runtime.GOOS%0A&issueTemplate=feature_request.md&labels=enhancement",
 				observed)
 
 			return 0, nil
diff --git a/internal/namespaces/feedback/testdata/test-feedback-bug-simple.golden b/internal/namespaces/feedback/testdata/test-feedback-bug-simple.golden
index 6486ac0b..c4ff8fb7 100644
--- a/internal/namespaces/feedback/testdata/test-feedback-bug-simple.golden
+++ b/internal/namespaces/feedback/testdata/test-feedback-bug-simple.golden
@@ -1,9 +1,9 @@
 🎲🎲🎲 EXIT CODE: 0 🎲🎲🎲
 🟩🟩🟩 STDOUT️ 🟩🟩🟩️
 ✅ Successfully opened the page.
-  https://github.com/scaleway/scaleway-cli/issues/new?body=%0A%23%23+Description%3A%0A%0A%23%23+How+to+reproduce%3A%0A%0A%23%23%23+Command+attempted%0A%0A%23%23%23+Expected+Behavior%0A%0A%23%23%23+Actual+Behavior%0A%0A%23%23+More+info%0A%0A%23%23+Version%0A%0AVersion++++0.0.0%0ABuildDate++unknown%0AGoVersion++runtime.Version%28%29%0AGitBranch++unknown%0AGitCommit++unknown%0AGoArch+++++runtime.GOARCH%0AGoOS+++++++runtime.GOOS%0A&issueTemplate=bug_report.md&labels=bug
+  https://github.com/scaleway/scaleway-cli/issues/new?body=%0A%23%23+Description%3A%0A%0A%23%23+How+to+reproduce%3A%0A%0A%23%23%23+Command+attempted%0A%0A%23%23%23+Expected+Behavior%0A%0A%23%23%23+Actual+Behavior%0A%0A%23%23+More+info%0A%0A%23%23+Version%0A%0AVersion++++0.0.0%26%2343%3Btest%0ABuildDate++unknown%0AGoVersion++runtime.Version%28%29%0AGitBranch++unknown%0AGitCommit++unknown%0AGoArch+++++runtime.GOARCH%0AGoOS+++++++runtime.GOOS%0A&issueTemplate=bug_report.md&labels=bug
 🟩🟩🟩 JSON STDOUT 🟩🟩🟩
 {
   "message": "Successfully opened the page",
-  "details": "https://github.com/scaleway/scaleway-cli/issues/new?body=%0A%23%23+Description%3A%0A%0A%23%23+How+to+reproduce%3A%0A%0A%23%23%23+Command+attempted%0A%0A%23%23%23+Expected+Behavior%0A%0A%23%23%23+Actual+Behavior%0A%0A%23%23+More+info%0A%0A%23%23+Version%0A%0AVersion++++0.0.0%0ABuildDate++unknown%0AGoVersion++runtime.Version%28%29%0AGitBranch++unknown%0AGitCommit++unknown%0AGoArch+++++runtime.GOARCH%0AGoOS+++++++runtime.GOOS%0A\u0026issueTemplate=bug_report.md\u0026labels=bug"
+  "details": "https://github.com/scaleway/scaleway-cli/issues/new?body=%0A%23%23+Description%3A%0A%0A%23%23+How+to+reproduce%3A%0A%0A%23%23%23+Command+attempted%0A%0A%23%23%23+Expected+Behavior%0A%0A%23%23%23+Actual+Behavior%0A%0A%23%23+More+info%0A%0A%23%23+Version%0A%0AVersion++++0.0.0%26%2343%3Btest%0ABuildDate++unknown%0AGoVersion++runtime.Version%28%29%0AGitBranch++unknown%0AGitCommit++unknown%0AGoArch+++++runtime.GOARCH%0AGoOS+++++++runtime.GOOS%0A\u0026issueTemplate=bug_report.md\u0026labels=bug"
 }
diff --git a/internal/namespaces/feedback/testdata/test-feedback-feature-simple.golden b/internal/namespaces/feedback/testdata/test-feedback-feature-simple.golden
index 32071554..387145ed 100644
--- a/internal/namespaces/feedback/testdata/test-feedback-feature-simple.golden
+++ b/internal/namespaces/feedback/testdata/test-feedback-feature-simple.golden
@@ -1,9 +1,9 @@
 🎲🎲🎲 EXIT CODE: 0 🎲🎲🎲
 🟩🟩🟩 STDOUT️ 🟩🟩🟩️
 ✅ Successfully opened the page.
-  https://github.com/scaleway/scaleway-cli/issues/new?body=%0A%23%23+Description%0A%0A%23%23+How+this+functionality+would+be+exposed%0A%0A%23%23+References%0A%0A%23%23+Version%0A%0AVersion++++0.0.0%0ABuildDate++unknown%0AGoVersion++runtime.Version%28%29%0AGitBranch++unknown%0AGitCommit++unknown%0AGoArch+++++runtime.GOARCH%0AGoOS+++++++runtime.GOOS%0A&issueTemplate=feature_request.md&labels=enhancement
+  https://github.com/scaleway/scaleway-cli/issues/new?body=%0A%23%23+Description%0A%0A%23%23+How+this+functionality+would+be+exposed%0A%0A%23%23+References%0A%0A%23%23+Version%0A%0AVersion++++0.0.0%26%2343%3Btest%0ABuildDate++unknown%0AGoVersion++runtime.Version%28%29%0AGitBranch++unknown%0AGitCommit++unknown%0AGoArch+++++runtime.GOARCH%0AGoOS+++++++runtime.GOOS%0A&issueTemplate=feature_request.md&labels=enhancement
 🟩🟩🟩 JSON STDOUT 🟩🟩🟩
 {
   "message": "Successfully opened the page",
-  "details": "https://github.com/scaleway/scaleway-cli/issues/new?body=%0A%23%23+Description%0A%0A%23%23+How+this+functionality+would+be+exposed%0A%0A%23%23+References%0A%0A%23%23+Version%0A%0AVersion++++0.0.0%0ABuildDate++unknown%0AGoVersion++runtime.Version%28%29%0AGitBranch++unknown%0AGitCommit++unknown%0AGoArch+++++runtime.GOARCH%0AGoOS+++++++runtime.GOOS%0A\u0026issueTemplate=feature_request.md\u0026labels=enhancement"
+  "details": "https://github.com/scaleway/scaleway-cli/issues/new?body=%0A%23%23+Description%0A%0A%23%23+How+this+functionality+would+be+exposed%0A%0A%23%23+References%0A%0A%23%23+Version%0A%0AVersion++++0.0.0%26%2343%3Btest%0ABuildDate++unknown%0AGoVersion++runtime.Version%28%29%0AGitBranch++unknown%0AGitCommit++unknown%0AGoArch+++++runtime.GOARCH%0AGoOS+++++++runtime.GOOS%0A\u0026issueTemplate=feature_request.md\u0026labels=enhancement"
 }
diff --git a/internal/namespaces/info/testdata/test-info-show-secret.golden b/internal/namespaces/info/testdata/test-info-show-secret.golden
index 0bbd3fe0..2f99149a 100644
--- a/internal/namespaces/info/testdata/test-info-show-secret.golden
+++ b/internal/namespaces/info/testdata/test-info-show-secret.golden
@@ -1,7 +1,7 @@
 🎲🎲🎲 EXIT CODE: 0 🎲🎲🎲
 🟩🟩🟩 STDOUT️ 🟩🟩🟩️
 Build Info:
-Version    0.0.0
+Version    0.0.0+test
 BuildDate  unknown
 GoVersion  runtime.Version()
 GitBranch  unknown
@@ -27,7 +27,7 @@ secret_key               22222222-2222-2222-2222-222222222222  env (SCW_SECRET_K
     "git_commit": "unknown",
     "go_arch": "runtime.GOARCH",
     "go_os": "runtime.GOOS",
-    "version": "0.0.0"
+    "version": "0.0.0+test"
   },
   "settings": [
     {
diff --git a/internal/namespaces/info/testdata/test-info-simple.golden b/internal/namespaces/info/testdata/test-info-simple.golden
index 190e2837..f6ce9de5 100644
--- a/internal/namespaces/info/testdata/test-info-simple.golden
+++ b/internal/namespaces/info/testdata/test-info-simple.golden
@@ -1,7 +1,7 @@
 🎲🎲🎲 EXIT CODE: 0 🎲🎲🎲
 🟩🟩🟩 STDOUT️ 🟩🟩🟩️
 Build Info:
-Version    0.0.0
+Version    0.0.0+test
 BuildDate  unknown
 GoVersion  runtime.Version()
 GitBranch  unknown
@@ -27,7 +27,7 @@ secret_key               22222222-xxxx-xxxx-xxxx-xxxxxxxxxxxx  env (SCW_SECRET_K
     "git_commit": "unknown",
     "go_arch": "runtime.GOARCH",
     "go_os": "runtime.GOOS",
-    "version": "0.0.0"
+    "version": "0.0.0+test"
   },
   "settings": [
     {