From 23010c7929f98652a1d0d521cf54a4c45a6fb6d5 Mon Sep 17 00:00:00 2001 From: Jonathan Remy Date: Wed, 3 Dec 2025 14:37:15 +0100 Subject: [PATCH 1/7] fix(mnq): fix XDG_CONFIG_HOME compliance in NATS context directory --- .../mnq/v1beta1/custom_nats_helpers.go | 3 +- .../mnq/v1beta1/custom_nats_test.go | 95 +++++++++++++++++++ 2 files changed, 96 insertions(+), 2 deletions(-) diff --git a/internal/namespaces/mnq/v1beta1/custom_nats_helpers.go b/internal/namespaces/mnq/v1beta1/custom_nats_helpers.go index 36a174c3a3..1f05f02751 100644 --- a/internal/namespaces/mnq/v1beta1/custom_nats_helpers.go +++ b/internal/namespaces/mnq/v1beta1/custom_nats_helpers.go @@ -85,7 +85,6 @@ func writeFile( func getNATSContextDir(ctx context.Context) (string, error) { xdgConfigHome := core.ExtractEnv(ctx, "XDG_CONFIG_HOME") - interactive.Println("xdgConfigHome:", xdgConfigHome) if xdgConfigHome == "" { homeDir := core.ExtractEnv(ctx, "HOME") if homeDir == "" { @@ -95,7 +94,7 @@ func getNATSContextDir(ctx context.Context) (string, error) { return filepath.Join(homeDir, ".config", "nats", "context"), nil } - return xdgConfigHome, nil + return filepath.Join(xdgConfigHome, "nats", "context"), nil } func saveNATSCredentials( diff --git a/internal/namespaces/mnq/v1beta1/custom_nats_test.go b/internal/namespaces/mnq/v1beta1/custom_nats_test.go index 7ec4fcf014..4f8b28f26d 100644 --- a/internal/namespaces/mnq/v1beta1/custom_nats_test.go +++ b/internal/namespaces/mnq/v1beta1/custom_nats_test.go @@ -2,6 +2,7 @@ package mnq_test import ( "os" + "path/filepath" "reflect" "regexp" "testing" @@ -106,3 +107,97 @@ func Test_CreateContextNoInteractiveTermAndMultiAccount(t *testing.T) { AfterFunc: core.AfterFuncCombine(deleteNATSAccount("NATS"), deleteNATSAccount("NATS2")), })) } + +func Test_CreateContextWithXDGConfigHome(t *testing.T) { + t.Run("XDG_CONFIG_HOME compliance", core.Test(&core.TestConfig{ + Commands: mnq.GetCommands(), + BeforeFunc: createNATSAccount("NATS"), + Cmd: "scw mnq nats create-context nats-account-id={{ .NATS.ID }}", + OverrideEnv: func() map[string]string { + xdgConfigHome := t.TempDir() + realHomeDir, _ := os.UserHomeDir() + + return map[string]string{ + "XDG_CONFIG_HOME": xdgConfigHome, + "HOME": realHomeDir, + } + }(), + Check: core.TestCheckCombine( + core.TestCheckExitCode(0), + core.TestCheckGoldenAndReplacePatterns( + core.GoldenReplacement{ + Pattern: regexp.MustCompile(`cli[\w-]*creds[\w-]*`), + Replacement: "credential-placeholder", + }, + core.GoldenReplacement{ + Pattern: regexp.MustCompile( + "(Select context using `nats context select )cli[\\w-]*`", + ), + Replacement: "Select context using `nats context select context-placeholder`", + }, + ), + func(t *testing.T, ctx *core.CheckFuncCtx) { + t.Helper() + result, isSuccessResult := ctx.Result.(*core.SuccessResult) + assert.True( + t, + isSuccessResult, + "Expected result to be of type *core.SuccessResult, got %s", + reflect.TypeOf(result).String(), + ) + assert.NotNil(t, result) + + xdgConfigHome := ctx.OverrideEnv["XDG_CONFIG_HOME"] + realHomeDir := ctx.OverrideEnv["HOME"] + + expectedContextFile := result.Resource + assert.True( + t, + mnq.FileExists(expectedContextFile), + "Expected context file not found: %s", + expectedContextFile, + ) + + expectedContextDir := filepath.Join(xdgConfigHome, "nats", "context") + assert.Contains( + t, + expectedContextFile, + expectedContextDir, + "Context file should be in XDG_CONFIG_HOME/nats/context, got: %s", + expectedContextFile, + ) + + realHomeNatsDir := filepath.Join(realHomeDir, ".config", "nats", "context") + realHomeNatsDirExists := false + if _, err := os.Stat(realHomeNatsDir); err == nil { + realHomeNatsDirExists = true + } + assert.False( + t, + realHomeNatsDirExists, + "Files should not be created in real home directory: %s", + realHomeNatsDir, + ) + + ctx.Meta["deleteFiles"] = []string{expectedContextFile} + }, + ), + AfterFunc: core.AfterFuncCombine( + deleteNATSAccount("NATS"), + func(ctx *core.AfterFuncCtx) error { + if ctx.Meta["deleteFiles"] == nil { + return nil + } + filesToDelete := ctx.Meta["deleteFiles"].([]string) + for _, file := range filesToDelete { + err := os.Remove(file) + if err != nil { + t.Errorf("Failed to delete the file : %s", err) + } + } + + return nil + }, + ), + })) +} From 5faf21bf7aeba83c1ba48858072f2878fa05bf5d Mon Sep 17 00:00:00 2001 From: Jonathan Remy Date: Wed, 3 Dec 2025 16:54:47 +0100 Subject: [PATCH 2/7] test(mnq): improve XDG_CONFIG_HOME test to use TmpHomeDir --- .github/workflows/lint.yml | 2 +- .../mnq/v1beta1/custom_nats_test.go | 31 +++++++++---------- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index a6a00507b7..0cc2f5c9f8 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -35,7 +35,7 @@ jobs: uses: golangci/golangci-lint-action@v9 with: # Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version. - version: latest + version: v2.6 args: --timeout 10m spelling: diff --git a/internal/namespaces/mnq/v1beta1/custom_nats_test.go b/internal/namespaces/mnq/v1beta1/custom_nats_test.go index 4f8b28f26d..0fdfd338b4 100644 --- a/internal/namespaces/mnq/v1beta1/custom_nats_test.go +++ b/internal/namespaces/mnq/v1beta1/custom_nats_test.go @@ -109,19 +109,16 @@ func Test_CreateContextNoInteractiveTermAndMultiAccount(t *testing.T) { } func Test_CreateContextWithXDGConfigHome(t *testing.T) { + xdgConfigHomeDir := t.TempDir() + t.Run("XDG_CONFIG_HOME compliance", core.Test(&core.TestConfig{ Commands: mnq.GetCommands(), BeforeFunc: createNATSAccount("NATS"), Cmd: "scw mnq nats create-context nats-account-id={{ .NATS.ID }}", - OverrideEnv: func() map[string]string { - xdgConfigHome := t.TempDir() - realHomeDir, _ := os.UserHomeDir() - - return map[string]string{ - "XDG_CONFIG_HOME": xdgConfigHome, - "HOME": realHomeDir, - } - }(), + TmpHomeDir: true, + OverrideEnv: map[string]string{ + "XDG_CONFIG_HOME": xdgConfigHomeDir, + }, Check: core.TestCheckCombine( core.TestCheckExitCode(0), core.TestCheckGoldenAndReplacePatterns( @@ -148,7 +145,7 @@ func Test_CreateContextWithXDGConfigHome(t *testing.T) { assert.NotNil(t, result) xdgConfigHome := ctx.OverrideEnv["XDG_CONFIG_HOME"] - realHomeDir := ctx.OverrideEnv["HOME"] + tmpHomeDir := ctx.OverrideEnv["HOME"] expectedContextFile := result.Resource assert.True( @@ -167,16 +164,16 @@ func Test_CreateContextWithXDGConfigHome(t *testing.T) { expectedContextFile, ) - realHomeNatsDir := filepath.Join(realHomeDir, ".config", "nats", "context") - realHomeNatsDirExists := false - if _, err := os.Stat(realHomeNatsDir); err == nil { - realHomeNatsDirExists = true + tmpHomeNatsDir := filepath.Join(tmpHomeDir, ".config", "nats", "context") + tmpHomeNatsDirExists := false + if _, err := os.Stat(tmpHomeNatsDir); err == nil { + tmpHomeNatsDirExists = true } assert.False( t, - realHomeNatsDirExists, - "Files should not be created in real home directory: %s", - realHomeNatsDir, + tmpHomeNatsDirExists, + "Files should not be created in HOME/.config/nats/context when XDG_CONFIG_HOME is set: %s", + tmpHomeNatsDir, ) ctx.Meta["deleteFiles"] = []string{expectedContextFile} From f3200d90e8c66cda0f156ab6c8ac3e6ef4bee15d Mon Sep 17 00:00:00 2001 From: Jonathan Remy Date: Thu, 4 Dec 2025 06:35:15 +0100 Subject: [PATCH 3/7] fix(mnq): fix XDG_CONFIG_HOME compliance in NATS context directory --- .../mnq/v1beta1/custom_nats_test.go | 59 ++++- ...ome-xdgconfighome-compliance.cassette.yaml | 201 ++++++++++++++++++ ...onfig-home-xdgconfighome-compliance.golden | 10 + 3 files changed, 268 insertions(+), 2 deletions(-) create mode 100644 internal/namespaces/mnq/v1beta1/testdata/test-create-context-with-xdg-config-home-xdgconfighome-compliance.cassette.yaml create mode 100644 internal/namespaces/mnq/v1beta1/testdata/test-create-context-with-xdg-config-home-xdgconfighome-compliance.golden diff --git a/internal/namespaces/mnq/v1beta1/custom_nats_test.go b/internal/namespaces/mnq/v1beta1/custom_nats_test.go index 0fdfd338b4..22c5912aa8 100644 --- a/internal/namespaces/mnq/v1beta1/custom_nats_test.go +++ b/internal/namespaces/mnq/v1beta1/custom_nats_test.go @@ -9,6 +9,7 @@ import ( "github.com/scaleway/scaleway-cli/v2/core" mnq "github.com/scaleway/scaleway-cli/v2/internal/namespaces/mnq/v1beta1" + "github.com/scaleway/scaleway-sdk-go/scw" "github.com/stretchr/testify/assert" ) @@ -108,12 +109,66 @@ func Test_CreateContextNoInteractiveTermAndMultiAccount(t *testing.T) { })) } +func beforeFuncCopyConfigToTmpHome() core.BeforeFunc { + return core.BeforeFuncWhenUpdatingCassette(func(ctx *core.BeforeFuncCtx) error { + realHomeDir, err := os.UserHomeDir() + if err != nil { + return err + } + + realConfigPath := filepath.Join(realHomeDir, ".config", "scw", "config.yaml") + if _, err := os.Stat(realConfigPath); os.IsNotExist(err) { + return nil + } + + config, err := scw.LoadConfigFromPath(realConfigPath) + if err != nil { + return err + } + + activeProfile, err := config.GetActiveProfile() + if err != nil { + return err + } + + tmpHomeDir := ctx.OverrideEnv["HOME"] + tmpConfigDir := filepath.Join(tmpHomeDir, ".config", "scw") + if err := os.MkdirAll(tmpConfigDir, 0o0755); err != nil { + return err + } + + tmpConfigPath := filepath.Join(tmpConfigDir, "config.yaml") + + if err := config.SaveTo(tmpConfigPath); err != nil { + return err + } + + if activeProfile.AccessKey != nil { + ctx.OverrideEnv[scw.ScwAccessKeyEnv] = *activeProfile.AccessKey + } + if activeProfile.SecretKey != nil { + ctx.OverrideEnv[scw.ScwSecretKeyEnv] = *activeProfile.SecretKey + } + if activeProfile.DefaultOrganizationID != nil { + ctx.OverrideEnv[scw.ScwDefaultOrganizationIDEnv] = *activeProfile.DefaultOrganizationID + } + if activeProfile.DefaultProjectID != nil { + ctx.OverrideEnv[scw.ScwDefaultProjectIDEnv] = *activeProfile.DefaultProjectID + } + + return nil + }) +} + func Test_CreateContextWithXDGConfigHome(t *testing.T) { xdgConfigHomeDir := t.TempDir() t.Run("XDG_CONFIG_HOME compliance", core.Test(&core.TestConfig{ - Commands: mnq.GetCommands(), - BeforeFunc: createNATSAccount("NATS"), + Commands: mnq.GetCommands(), + BeforeFunc: core.BeforeFuncCombine( + beforeFuncCopyConfigToTmpHome(), + createNATSAccount("NATS"), + ), Cmd: "scw mnq nats create-context nats-account-id={{ .NATS.ID }}", TmpHomeDir: true, OverrideEnv: map[string]string{ diff --git a/internal/namespaces/mnq/v1beta1/testdata/test-create-context-with-xdg-config-home-xdgconfighome-compliance.cassette.yaml b/internal/namespaces/mnq/v1beta1/testdata/test-create-context-with-xdg-config-home-xdgconfighome-compliance.cassette.yaml new file mode 100644 index 0000000000..ebc1076535 --- /dev/null +++ b/internal/namespaces/mnq/v1beta1/testdata/test-create-context-with-xdg-config-home-xdgconfighome-compliance.cassette.yaml @@ -0,0 +1,201 @@ +--- +version: 1 +interactions: +- request: + body: '{"project_id":"46fd79d8-1a35-4548-bfb8-03df51a0ebae", "region":"fr-par", + "created_at":"2025-12-04T05:08:07.797431533Z", "updated_at":"2025-12-04T05:08:07.797431533Z", + "id":"AAFJH7WGGU2YRNIXDC5IRRBOFYMPU6D5UJLGPWCVI3VWIJQFLOX2JB75", "name":"cli-mnq-vibrant-antonelli", + "endpoint":"nats://nats.mnq.fr-par.scaleway.com:4222"}' + form: {} + headers: + Content-Type: + - application/json + User-Agent: + - scaleway-sdk-go/v1.0.0-beta.7+dev (go1.25.3; darwin; amd64) cli-e2e-test + url: https://api.scaleway.com/mnq/v1beta1/regions/fr-par/nats-accounts + method: POST + response: + body: '{"project_id":"46fd79d8-1a35-4548-bfb8-03df51a0ebae", "region":"fr-par", + "created_at":"2025-12-04T05:08:07.797431533Z", "updated_at":"2025-12-04T05:08:07.797431533Z", + "id":"AAFJH7WGGU2YRNIXDC5IRRBOFYMPU6D5UJLGPWCVI3VWIJQFLOX2JB75", "name":"cli-mnq-vibrant-antonelli", + "endpoint":"nats://nats.mnq.fr-par.scaleway.com:4222"}' + headers: + Content-Length: + - "322" + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none' + Content-Type: + - application/json + Date: + - Thu, 04 Dec 2025 05:08:07 GMT + Server: + - Scaleway API Gateway (fr-par-2;edge03) + Strict-Transport-Security: + - max-age=63072000 + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Request-Id: + - edffd555-1943-4ed1-a5bf-76b020ab1d9b + status: 200 OK + code: 200 + duration: "" +- request: + body: '{"access_key":"SCW8XSQX472BP8SNX6HA", "secret_key":null, "description":"", + "created_at":"2025-11-21T09:36:03.459154Z", "updated_at":"2025-11-21T09:36:03.459154Z", + "expires_at":null, "default_project_id":"46fd79d8-1a35-4548-bfb8-03df51a0ebae", + "editable":true, "deletable":true, "managed":false, "creation_ip":"51.159.46.153", + "user_id":"a52be78d-a4f2-4d4f-a4e6-8bf74589bfd8"}' + form: {} + headers: + User-Agent: + - scaleway-sdk-go/v1.0.0-beta.7+dev (go1.25.3; darwin; amd64) cli-e2e-test + url: https://api.scaleway.com/iam/v1alpha1/api-keys/SCW8XSQX472BP8SNX6HA + method: GET + response: + body: '{"access_key":"SCW8XSQX472BP8SNX6HA", "secret_key":null, "description":"", + "created_at":"2025-11-21T09:36:03.459154Z", "updated_at":"2025-11-21T09:36:03.459154Z", + "expires_at":null, "default_project_id":"46fd79d8-1a35-4548-bfb8-03df51a0ebae", + "editable":true, "deletable":true, "managed":false, "creation_ip":"51.159.46.153", + "user_id":"a52be78d-a4f2-4d4f-a4e6-8bf74589bfd8"}' + headers: + Content-Length: + - "375" + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none' + Content-Type: + - application/json + Date: + - Thu, 04 Dec 2025 05:08:08 GMT + Server: + - Scaleway API Gateway (fr-par-2;edge03) + Strict-Transport-Security: + - max-age=63072000 + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Request-Id: + - 8031e99a-c130-455c-a74b-a48a68c9c92a + status: 200 OK + code: 200 + duration: "" +- request: + body: '{"project_id":"46fd79d8-1a35-4548-bfb8-03df51a0ebae", "region":"fr-par", + "created_at":"2025-12-04T05:08:07.797431Z", "updated_at":"2025-12-04T05:08:07.797431Z", + "id":"AAFJH7WGGU2YRNIXDC5IRRBOFYMPU6D5UJLGPWCVI3VWIJQFLOX2JB75", "name":"cli-mnq-vibrant-antonelli", + "endpoint":"nats://nats.mnq.fr-par.scaleway.com:4222"}' + form: {} + headers: + User-Agent: + - scaleway-sdk-go/v1.0.0-beta.7+dev (go1.25.3; darwin; amd64) cli-e2e-test + url: https://api.scaleway.com/mnq/v1beta1/regions/fr-par/nats-accounts/AAFJH7WGGU2YRNIXDC5IRRBOFYMPU6D5UJLGPWCVI3VWIJQFLOX2JB75 + method: GET + response: + body: '{"project_id":"46fd79d8-1a35-4548-bfb8-03df51a0ebae", "region":"fr-par", + "created_at":"2025-12-04T05:08:07.797431Z", "updated_at":"2025-12-04T05:08:07.797431Z", + "id":"AAFJH7WGGU2YRNIXDC5IRRBOFYMPU6D5UJLGPWCVI3VWIJQFLOX2JB75", "name":"cli-mnq-vibrant-antonelli", + "endpoint":"nats://nats.mnq.fr-par.scaleway.com:4222"}' + headers: + Content-Length: + - "316" + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none' + Content-Type: + - application/json + Date: + - Thu, 04 Dec 2025 05:08:08 GMT + Server: + - Scaleway API Gateway (fr-par-2;edge03) + Strict-Transport-Security: + - max-age=63072000 + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Request-Id: + - 6eeac215-6850-424b-b7c3-8ef6fda6184e + status: 200 OK + code: 200 + duration: "" +- request: + body: '{"nats_account_id":"AAFJH7WGGU2YRNIXDC5IRRBOFYMPU6D5UJLGPWCVI3VWIJQFLOX2JB75", + "region":"fr-par", "created_at":"2025-12-04T05:08:08.325079040Z", "updated_at":"2025-12-04T05:08:08.325079290Z", + "id":"d21c9f77-e065-490e-b4fc-a2feb3e4d90f", "name":"cli-mnq-vibrant-antonellicli-creds-priceless-bhabha", + "credentials":{"name":"user.creds", "content":"-----BEGIN NATS USER JWT-----\neyJ0eXAiOiJKV1QiLCJhbGciOiJlZDI1NTE5LW5rZXkifQ.eyJqdGkiOiJSNDNZRVhBT1NZTExJUkFDR01WUDJMU1JDWEMyWVk2UlVQN1hQRUxDNlBJQ1pQNlRLNEtBIiwiaWF0IjoxNzY0ODI0ODg4LCJpc3MiOiJBREFXV0dMVVJLTVdYWlYyVldJWktDNkMzUUNZMjNKN1BJRU5BTDZaUlhSRzJLTUtKT0FUSEJNSSIsIm5hbWUiOiJjbGktbW5xLXZpYnJhbnQtYW50b25lbGxpY2xpLWNyZWRzLXByaWNlbGVzcy1iaGFiaGEiLCJzdWIiOiJVQkFYVzMzQTc2UFY3SUQ0UUtHWFdaUkpSQlJRT1ZHWVNCM1NRNDVQQldNUFBBT0VOMlFMSlJZVyIsIm5hdHMiOnsicHViIjp7fSwic3ViIjp7fSwic3VicyI6LTEsImRhdGEiOi0xLCJwYXlsb2FkIjotMSwiaXNzdWVyX2FjY291bnQiOiJBQUZKSDdXR0dVMllSTklYREM1SVJSQk9GWU1QVTZENVVKTEdQV0NWSTNWV0lKUUZMT1gySkI3NSIsInR5cGUiOiJ1c2VyIiwidmVyc2lvbiI6Mn19.zaqi9Sf5iBSWXbj-THzcti2GFxpim8O_MhgWsYbDIEc0M39Er-Wgyspw4APxYDsqqBQRNnKfaYSxmUbz-mPUBg\n------END + NATS USER JWT------\n\n************************* IMPORTANT *************************\nNKEY + Seed printed below can be used to sign and prove identity.\nNKEYs are sensitive + and should be treated as secrets.\n\n-----BEGIN USER NKEY SEED-----\nSUAGEIKEUA7L46FYGMVMDQSQEBIVRPJAHEEC23SEMCAR4X5VFAXQPPPC7Q\n------END + USER NKEY SEED------\n\n*************************************************************\n"}, + "checksum":"1313cedb5035d6df3ad1b43112fd6b0ba32dd1dd"}' + form: {} + headers: + Content-Type: + - application/json + User-Agent: + - scaleway-sdk-go/v1.0.0-beta.7+dev (go1.25.3; darwin; amd64) cli-e2e-test + url: https://api.scaleway.com/mnq/v1beta1/regions/fr-par/nats-credentials + method: POST + response: + body: '{"nats_account_id":"AAFJH7WGGU2YRNIXDC5IRRBOFYMPU6D5UJLGPWCVI3VWIJQFLOX2JB75", + "region":"fr-par", "created_at":"2025-12-04T05:08:08.325079040Z", "updated_at":"2025-12-04T05:08:08.325079290Z", + "id":"d21c9f77-e065-490e-b4fc-a2feb3e4d90f", "name":"cli-mnq-vibrant-antonellicli-creds-priceless-bhabha", + "credentials":{"name":"user.creds", "content":"-----BEGIN NATS USER JWT-----\neyJ0eXAiOiJKV1QiLCJhbGciOiJlZDI1NTE5LW5rZXkifQ.eyJqdGkiOiJSNDNZRVhBT1NZTExJUkFDR01WUDJMU1JDWEMyWVk2UlVQN1hQRUxDNlBJQ1pQNlRLNEtBIiwiaWF0IjoxNzY0ODI0ODg4LCJpc3MiOiJBREFXV0dMVVJLTVdYWlYyVldJWktDNkMzUUNZMjNKN1BJRU5BTDZaUlhSRzJLTUtKT0FUSEJNSSIsIm5hbWUiOiJjbGktbW5xLXZpYnJhbnQtYW50b25lbGxpY2xpLWNyZWRzLXByaWNlbGVzcy1iaGFiaGEiLCJzdWIiOiJVQkFYVzMzQTc2UFY3SUQ0UUtHWFdaUkpSQlJRT1ZHWVNCM1NRNDVQQldNUFBBT0VOMlFMSlJZVyIsIm5hdHMiOnsicHViIjp7fSwic3ViIjp7fSwic3VicyI6LTEsImRhdGEiOi0xLCJwYXlsb2FkIjotMSwiaXNzdWVyX2FjY291bnQiOiJBQUZKSDdXR0dVMllSTklYREM1SVJSQk9GWU1QVTZENVVKTEdQV0NWSTNWV0lKUUZMT1gySkI3NSIsInR5cGUiOiJ1c2VyIiwidmVyc2lvbiI6Mn19.zaqi9Sf5iBSWXbj-THzcti2GFxpim8O_MhgWsYbDIEc0M39Er-Wgyspw4APxYDsqqBQRNnKfaYSxmUbz-mPUBg\n------END + NATS USER JWT------\n\n************************* IMPORTANT *************************\nNKEY + Seed printed below can be used to sign and prove identity.\nNKEYs are sensitive + and should be treated as secrets.\n\n-----BEGIN USER NKEY SEED-----\nSUAGEIKEUA7L46FYGMVMDQSQEBIVRPJAHEEC23SEMCAR4X5VFAXQPPPC7Q\n------END + USER NKEY SEED------\n\n*************************************************************\n"}, + "checksum":"1313cedb5035d6df3ad1b43112fd6b0ba32dd1dd"}' + headers: + Content-Length: + - "1554" + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none' + Content-Type: + - application/json + Date: + - Thu, 04 Dec 2025 05:08:08 GMT + Server: + - Scaleway API Gateway (fr-par-2;edge03) + Strict-Transport-Security: + - max-age=63072000 + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Request-Id: + - 628cf359-ba2b-4963-9d5d-4b20dec718e4 + status: 200 OK + code: 200 + duration: "" +- request: + body: "" + form: {} + headers: + User-Agent: + - scaleway-sdk-go/v1.0.0-beta.7+dev (go1.25.3; darwin; amd64) cli-e2e-test + url: https://api.scaleway.com/mnq/v1beta1/regions/fr-par/nats-accounts/AAFJH7WGGU2YRNIXDC5IRRBOFYMPU6D5UJLGPWCVI3VWIJQFLOX2JB75 + method: DELETE + response: + body: "" + headers: + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none' + Content-Type: + - application/json + Date: + - Thu, 04 Dec 2025 05:08:08 GMT + Server: + - Scaleway API Gateway (fr-par-2;edge03) + Strict-Transport-Security: + - max-age=63072000 + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Request-Id: + - 19e34f27-9202-436b-9f8b-884e58cb49a4 + status: 204 No Content + code: 204 + duration: "" diff --git a/internal/namespaces/mnq/v1beta1/testdata/test-create-context-with-xdg-config-home-xdgconfighome-compliance.golden b/internal/namespaces/mnq/v1beta1/testdata/test-create-context-with-xdg-config-home-xdgconfighome-compliance.golden new file mode 100644 index 0000000000..fd84f10544 --- /dev/null +++ b/internal/namespaces/mnq/v1beta1/testdata/test-create-context-with-xdg-config-home-xdgconfighome-compliance.golden @@ -0,0 +1,10 @@ +🎲🎲🎲 EXIT CODE: 0 🎲🎲🎲 +🟩🟩🟩 STDOUT️ 🟩🟩🟩️ +βœ… Nats context successfully created. + credential-placeholder nats credentials was created + Select context using `nats context select context-placeholder` +🟩🟩🟩 JSON STDOUT 🟩🟩🟩 +{ + "message": "Nats context successfully created", + "details": "credential-placeholder nats credentials was created\nSelect context using `nats context select context-placeholder`" +} From c99445bd7f0cb848d4accb5d5ac9ddc3ebb2475b Mon Sep 17 00:00:00 2001 From: Jonathan Remy Date: Thu, 4 Dec 2025 07:53:07 +0100 Subject: [PATCH 4/7] test(mnq): simplify config copy to use file copy instead of load/save --- .../mnq/v1beta1/custom_nats_test.go | 35 ++++++------------- 1 file changed, 11 insertions(+), 24 deletions(-) diff --git a/internal/namespaces/mnq/v1beta1/custom_nats_test.go b/internal/namespaces/mnq/v1beta1/custom_nats_test.go index 22c5912aa8..f9bb5b1e11 100644 --- a/internal/namespaces/mnq/v1beta1/custom_nats_test.go +++ b/internal/namespaces/mnq/v1beta1/custom_nats_test.go @@ -1,6 +1,7 @@ package mnq_test import ( + "io" "os" "path/filepath" "reflect" @@ -9,7 +10,6 @@ import ( "github.com/scaleway/scaleway-cli/v2/core" mnq "github.com/scaleway/scaleway-cli/v2/internal/namespaces/mnq/v1beta1" - "github.com/scaleway/scaleway-sdk-go/scw" "github.com/stretchr/testify/assert" ) @@ -121,16 +121,6 @@ func beforeFuncCopyConfigToTmpHome() core.BeforeFunc { return nil } - config, err := scw.LoadConfigFromPath(realConfigPath) - if err != nil { - return err - } - - activeProfile, err := config.GetActiveProfile() - if err != nil { - return err - } - tmpHomeDir := ctx.OverrideEnv["HOME"] tmpConfigDir := filepath.Join(tmpHomeDir, ".config", "scw") if err := os.MkdirAll(tmpConfigDir, 0o0755); err != nil { @@ -139,24 +129,21 @@ func beforeFuncCopyConfigToTmpHome() core.BeforeFunc { tmpConfigPath := filepath.Join(tmpConfigDir, "config.yaml") - if err := config.SaveTo(tmpConfigPath); err != nil { + src, err := os.Open(realConfigPath) + if err != nil { return err } + defer src.Close() - if activeProfile.AccessKey != nil { - ctx.OverrideEnv[scw.ScwAccessKeyEnv] = *activeProfile.AccessKey - } - if activeProfile.SecretKey != nil { - ctx.OverrideEnv[scw.ScwSecretKeyEnv] = *activeProfile.SecretKey - } - if activeProfile.DefaultOrganizationID != nil { - ctx.OverrideEnv[scw.ScwDefaultOrganizationIDEnv] = *activeProfile.DefaultOrganizationID - } - if activeProfile.DefaultProjectID != nil { - ctx.OverrideEnv[scw.ScwDefaultProjectIDEnv] = *activeProfile.DefaultProjectID + dst, err := os.Create(tmpConfigPath) + if err != nil { + return err } + defer dst.Close() + + _, err = io.Copy(dst, src) - return nil + return err }) } From c7b77d80b770f6683c2434269a25f187062e56ae Mon Sep 17 00:00:00 2001 From: Jonathan Remy Date: Thu, 4 Dec 2025 11:17:39 +0100 Subject: [PATCH 5/7] test(mnq): simplify config copy using ReadFile/WriteFile --- .github/workflows/lint.yml | 2 +- .../namespaces/mnq/v1beta1/custom_nats_test.go | 16 ++-------------- 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 0cc2f5c9f8..a6a00507b7 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -35,7 +35,7 @@ jobs: uses: golangci/golangci-lint-action@v9 with: # Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version. - version: v2.6 + version: latest args: --timeout 10m spelling: diff --git a/internal/namespaces/mnq/v1beta1/custom_nats_test.go b/internal/namespaces/mnq/v1beta1/custom_nats_test.go index f9bb5b1e11..31596fdd0d 100644 --- a/internal/namespaces/mnq/v1beta1/custom_nats_test.go +++ b/internal/namespaces/mnq/v1beta1/custom_nats_test.go @@ -1,7 +1,6 @@ package mnq_test import ( - "io" "os" "path/filepath" "reflect" @@ -127,23 +126,12 @@ func beforeFuncCopyConfigToTmpHome() core.BeforeFunc { return err } - tmpConfigPath := filepath.Join(tmpConfigDir, "config.yaml") - - src, err := os.Open(realConfigPath) - if err != nil { - return err - } - defer src.Close() - - dst, err := os.Create(tmpConfigPath) + data, err := os.ReadFile(realConfigPath) if err != nil { return err } - defer dst.Close() - - _, err = io.Copy(dst, src) - return err + return os.WriteFile(filepath.Join(tmpConfigDir, "config.yaml"), data, 0o644) }) } From 63ac200da441c276fe472a657b95faa338585543 Mon Sep 17 00:00:00 2001 From: Jonathan Remy Date: Thu, 4 Dec 2025 14:49:15 +0100 Subject: [PATCH 6/7] test(mnq): extract helper functions to simplify tests --- .../mnq/v1beta1/custom_nats_test.go | 141 ++++++------------ 1 file changed, 48 insertions(+), 93 deletions(-) diff --git a/internal/namespaces/mnq/v1beta1/custom_nats_test.go b/internal/namespaces/mnq/v1beta1/custom_nats_test.go index 31596fdd0d..827e20f4b6 100644 --- a/internal/namespaces/mnq/v1beta1/custom_nats_test.go +++ b/internal/namespaces/mnq/v1beta1/custom_nats_test.go @@ -32,43 +32,11 @@ func Test_CreateContext(t *testing.T) { Replacement: "Select context using `nats context select context-placeholder`", }, ), - func(t *testing.T, ctx *core.CheckFuncCtx) { - t.Helper() - result, isSuccessResult := ctx.Result.(*core.SuccessResult) - assert.True( - t, - isSuccessResult, - "Expected result to be of type *core.SuccessResult, got %s", - reflect.TypeOf(result).String(), - ) - assert.NotNil(t, result) - expectedContextFile := result.Resource - if !mnq.FileExists(expectedContextFile) { - t.Errorf( - "Expected credentials file not found expected [%s] ", - expectedContextFile, - ) - } else { - ctx.Meta["deleteFiles"] = []string{expectedContextFile} - } - }, + checkContextFile, ), AfterFunc: core.AfterFuncCombine( deleteNATSAccount("NATS"), - func(ctx *core.AfterFuncCtx) error { - if ctx.Meta["deleteFiles"] == nil { - return nil - } - filesToDelete := ctx.Meta["deleteFiles"].([]string) - for _, file := range filesToDelete { - err := os.Remove(file) - if err != nil { - t.Errorf("Failed to delete the file : %s", err) - } - } - - return nil - }, + afterFuncDeleteFiles, ), })) } @@ -135,6 +103,50 @@ func beforeFuncCopyConfigToTmpHome() core.BeforeFunc { }) } +func checkContextFile(t *testing.T, ctx *core.CheckFuncCtx) { + t.Helper() + result, isSuccessResult := ctx.Result.(*core.SuccessResult) + assert.True(t, isSuccessResult, "Expected result to be of type *core.SuccessResult, got %s", reflect.TypeOf(result).String()) + assert.NotNil(t, result) + expectedContextFile := result.Resource + if !mnq.FileExists(expectedContextFile) { + t.Errorf("Expected credentials file not found expected [%s]", expectedContextFile) + } else { + ctx.Meta["deleteFiles"] = []string{expectedContextFile} + } +} + +func checkContextFileInXDGConfigHome(t *testing.T, ctx *core.CheckFuncCtx) { + t.Helper() + checkContextFile(t, ctx) + + result := ctx.Result.(*core.SuccessResult) + expectedContextFile := result.Resource + xdgConfigHome := ctx.OverrideEnv["XDG_CONFIG_HOME"] + tmpHomeDir := ctx.OverrideEnv["HOME"] + + expectedContextDir := filepath.Join(xdgConfigHome, "nats", "context") + assert.Contains(t, expectedContextFile, expectedContextDir, "Context file should be in XDG_CONFIG_HOME/nats/context, got: %s", expectedContextFile) + + tmpHomeNatsDir := filepath.Join(tmpHomeDir, ".config", "nats", "context") + _, err := os.Stat(tmpHomeNatsDir) + assert.True(t, os.IsNotExist(err), "Files should not be created in HOME/.config/nats/context when XDG_CONFIG_HOME is set: %s", tmpHomeNatsDir) +} + +func afterFuncDeleteFiles(ctx *core.AfterFuncCtx) error { + if ctx.Meta["deleteFiles"] == nil { + return nil + } + filesToDelete := ctx.Meta["deleteFiles"].([]string) + for _, file := range filesToDelete { + if err := os.Remove(file); err != nil { + return err + } + } + + return nil +} + func Test_CreateContextWithXDGConfigHome(t *testing.T) { xdgConfigHomeDir := t.TempDir() @@ -163,68 +175,11 @@ func Test_CreateContextWithXDGConfigHome(t *testing.T) { Replacement: "Select context using `nats context select context-placeholder`", }, ), - func(t *testing.T, ctx *core.CheckFuncCtx) { - t.Helper() - result, isSuccessResult := ctx.Result.(*core.SuccessResult) - assert.True( - t, - isSuccessResult, - "Expected result to be of type *core.SuccessResult, got %s", - reflect.TypeOf(result).String(), - ) - assert.NotNil(t, result) - - xdgConfigHome := ctx.OverrideEnv["XDG_CONFIG_HOME"] - tmpHomeDir := ctx.OverrideEnv["HOME"] - - expectedContextFile := result.Resource - assert.True( - t, - mnq.FileExists(expectedContextFile), - "Expected context file not found: %s", - expectedContextFile, - ) - - expectedContextDir := filepath.Join(xdgConfigHome, "nats", "context") - assert.Contains( - t, - expectedContextFile, - expectedContextDir, - "Context file should be in XDG_CONFIG_HOME/nats/context, got: %s", - expectedContextFile, - ) - - tmpHomeNatsDir := filepath.Join(tmpHomeDir, ".config", "nats", "context") - tmpHomeNatsDirExists := false - if _, err := os.Stat(tmpHomeNatsDir); err == nil { - tmpHomeNatsDirExists = true - } - assert.False( - t, - tmpHomeNatsDirExists, - "Files should not be created in HOME/.config/nats/context when XDG_CONFIG_HOME is set: %s", - tmpHomeNatsDir, - ) - - ctx.Meta["deleteFiles"] = []string{expectedContextFile} - }, + checkContextFileInXDGConfigHome, ), AfterFunc: core.AfterFuncCombine( deleteNATSAccount("NATS"), - func(ctx *core.AfterFuncCtx) error { - if ctx.Meta["deleteFiles"] == nil { - return nil - } - filesToDelete := ctx.Meta["deleteFiles"].([]string) - for _, file := range filesToDelete { - err := os.Remove(file) - if err != nil { - t.Errorf("Failed to delete the file : %s", err) - } - } - - return nil - }, + afterFuncDeleteFiles, ), })) } From add87f1fdbc09eacd2381ae0522a47e73c5cd284 Mon Sep 17 00:00:00 2001 From: Jonathan Remy Date: Fri, 5 Dec 2025 07:09:29 +0100 Subject: [PATCH 7/7] test(mnq): fix golines formatting --- .../mnq/v1beta1/custom_nats_test.go | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/internal/namespaces/mnq/v1beta1/custom_nats_test.go b/internal/namespaces/mnq/v1beta1/custom_nats_test.go index 827e20f4b6..944f4a09eb 100644 --- a/internal/namespaces/mnq/v1beta1/custom_nats_test.go +++ b/internal/namespaces/mnq/v1beta1/custom_nats_test.go @@ -106,7 +106,12 @@ func beforeFuncCopyConfigToTmpHome() core.BeforeFunc { func checkContextFile(t *testing.T, ctx *core.CheckFuncCtx) { t.Helper() result, isSuccessResult := ctx.Result.(*core.SuccessResult) - assert.True(t, isSuccessResult, "Expected result to be of type *core.SuccessResult, got %s", reflect.TypeOf(result).String()) + assert.True( + t, + isSuccessResult, + "Expected result to be of type *core.SuccessResult, got %s", + reflect.TypeOf(result).String(), + ) assert.NotNil(t, result) expectedContextFile := result.Resource if !mnq.FileExists(expectedContextFile) { @@ -126,11 +131,22 @@ func checkContextFileInXDGConfigHome(t *testing.T, ctx *core.CheckFuncCtx) { tmpHomeDir := ctx.OverrideEnv["HOME"] expectedContextDir := filepath.Join(xdgConfigHome, "nats", "context") - assert.Contains(t, expectedContextFile, expectedContextDir, "Context file should be in XDG_CONFIG_HOME/nats/context, got: %s", expectedContextFile) + assert.Contains( + t, + expectedContextFile, + expectedContextDir, + "Context file should be in XDG_CONFIG_HOME/nats/context, got: %s", + expectedContextFile, + ) tmpHomeNatsDir := filepath.Join(tmpHomeDir, ".config", "nats", "context") _, err := os.Stat(tmpHomeNatsDir) - assert.True(t, os.IsNotExist(err), "Files should not be created in HOME/.config/nats/context when XDG_CONFIG_HOME is set: %s", tmpHomeNatsDir) + assert.True( + t, + os.IsNotExist(err), + "Files should not be created in HOME/.config/nats/context when XDG_CONFIG_HOME is set: %s", + tmpHomeNatsDir, + ) } func afterFuncDeleteFiles(ctx *core.AfterFuncCtx) error {