diff --git a/.github/workflows/release_build_infisical_cli.yml b/.github/workflows/release_build_infisical_cli.yml index d01d56198..40137a455 100644 --- a/.github/workflows/release_build_infisical_cli.yml +++ b/.github/workflows/release_build_infisical_cli.yml @@ -1,60 +1,64 @@ name: Build and release CLI on: - push: - # run only against tags - tags: - - "infisical-cli/v*.*.*" + push: + # run only against tags + tags: + - "infisical-cli/v*.*.*" permissions: - contents: write - # packages: write - # issues: write - + contents: write + # packages: write + # issues: write jobs: - goreleaser: - runs-on: ubuntu-20.04 - steps: - - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - name: 🐋 Login to Docker Hub - uses: docker/login-action@v2 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: 🔧 Set up Docker Buildx - uses: docker/setup-buildx-action@v2 - - run: git fetch --force --tags - - run: echo "Ref name ${{github.ref_name}}" - - uses: actions/setup-go@v3 - with: - go-version: ">=1.19.3" - cache: true - cache-dependency-path: cli/go.sum - - name: libssl1.1 => libssl1.0-dev for OSXCross - run: | - echo 'deb http://security.ubuntu.com/ubuntu bionic-security main' | sudo tee -a /etc/apt/sources.list - sudo apt update && apt-cache policy libssl1.0-dev - sudo apt-get install libssl1.0-dev - - name: OSXCross for CGO Support - run: | - mkdir ../../osxcross - git clone https://github.com/plentico/osxcross-target.git ../../osxcross/target - - uses: goreleaser/goreleaser-action@v4 - with: - distribution: goreleaser-pro - version: latest - args: release --clean - env: - GITHUB_TOKEN: ${{ secrets.GO_RELEASER_GITHUB_TOKEN }} - POSTHOG_API_KEY_FOR_CLI: ${{ secrets.POSTHOG_API_KEY_FOR_CLI }} - FURY_TOKEN: ${{ secrets.FURYPUSHTOKEN }} - AUR_KEY: ${{ secrets.AUR_KEY }} - GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }} - - uses: actions/setup-python@v4 - - run: pip install --upgrade cloudsmith-cli - - name: Publish to CloudSmith - run: sh cli/upload_to_cloudsmith.sh - env: - CLOUDSMITH_API_KEY: ${{ secrets.CLOUDSMITH_API_KEY }} + cli-integration-tests: + name: Run tests before deployment + uses: ./.github/workflows/run-cli-tests.yml + + goreleaser: + runs-on: ubuntu-20.04 + needs: [cli-integration-tests] + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: 🐋 Login to Docker Hub + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: 🔧 Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + - run: git fetch --force --tags + - run: echo "Ref name ${{github.ref_name}}" + - uses: actions/setup-go@v3 + with: + go-version: ">=1.19.3" + cache: true + cache-dependency-path: cli/go.sum + - name: libssl1.1 => libssl1.0-dev for OSXCross + run: | + echo 'deb http://security.ubuntu.com/ubuntu bionic-security main' | sudo tee -a /etc/apt/sources.list + sudo apt update && apt-cache policy libssl1.0-dev + sudo apt-get install libssl1.0-dev + - name: OSXCross for CGO Support + run: | + mkdir ../../osxcross + git clone https://github.com/plentico/osxcross-target.git ../../osxcross/target + - uses: goreleaser/goreleaser-action@v4 + with: + distribution: goreleaser-pro + version: latest + args: release --clean + env: + GITHUB_TOKEN: ${{ secrets.GO_RELEASER_GITHUB_TOKEN }} + POSTHOG_API_KEY_FOR_CLI: ${{ secrets.POSTHOG_API_KEY_FOR_CLI }} + FURY_TOKEN: ${{ secrets.FURYPUSHTOKEN }} + AUR_KEY: ${{ secrets.AUR_KEY }} + GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }} + - uses: actions/setup-python@v4 + - run: pip install --upgrade cloudsmith-cli + - name: Publish to CloudSmith + run: sh cli/upload_to_cloudsmith.sh + env: + CLOUDSMITH_API_KEY: ${{ secrets.CLOUDSMITH_API_KEY }} diff --git a/.github/workflows/run-cli-tests.yml b/.github/workflows/run-cli-tests.yml new file mode 100644 index 000000000..846998528 --- /dev/null +++ b/.github/workflows/run-cli-tests.yml @@ -0,0 +1,34 @@ +name: Go CLI Tests + +on: + pull_request: + types: [opened, synchronize] + paths: + - "cli/**" + + workflow_call: + +jobs: + test: + defaults: + run: + working-directory: ./cli + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - name: Setup Go + uses: actions/setup-go@v4 + with: + go-version: "1.21.x" + - name: Install dependencies + run: go get . + - name: Test with the Go CLI + env: + CLI_TESTS_UA_CLIENT_ID: ${{ secrets.CLI_TESTS_UA_CLIENT_ID }} + CLI_TESTS_UA_CLIENT_SECRET: ${{ secrets.CLI_TESTS_UA_CLIENT_SECRET }} + CLI_TESTS_SERVICE_TOKEN: ${{ secrets.CLI_TESTS_SERVICE_TOKEN }} + CLI_TESTS_PROJECT_ID: ${{ secrets.CLI_TESTS_PROJECT_ID }} + CLI_TESTS_ENV_SLUG: ${{ secrets.CLI_TESTS_ENV_SLUG }} + + run: go test -v -count=1 ./test diff --git a/.gitignore b/.gitignore index 4a12c15f2..b04860071 100644 --- a/.gitignore +++ b/.gitignore @@ -67,3 +67,5 @@ yarn-error.log* frontend-build *.tgz +cli/infisical-merge +cli/test/infisical-merge diff --git a/cli/go.mod b/cli/go.mod index d3b8eff0c..833745eff 100644 --- a/cli/go.mod +++ b/cli/go.mod @@ -29,6 +29,7 @@ require ( require ( github.com/alessio/shellescape v1.4.1 // indirect github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef // indirect + github.com/bradleyjkemp/cupaloy/v2 v2.8.0 // indirect github.com/chzyer/readline v1.5.1 // indirect github.com/danieljoos/wincred v1.2.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect diff --git a/cli/go.sum b/cli/go.sum index a73ac5185..353579136 100644 --- a/cli/go.sum +++ b/cli/go.sum @@ -51,6 +51,8 @@ github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef h1:46PFijGL github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= +github.com/bradleyjkemp/cupaloy/v2 v2.8.0 h1:any4BmKE+jGIaMpnU8YgH/I2LPiLBufr6oMMlVBbn9M= +github.com/bradleyjkemp/cupaloy/v2 v2.8.0/go.mod h1:bm7JXdkRd4BHJk9HpwqAI8BoAY1lps46Enkdqw6aRX0= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/charmbracelet/lipgloss v0.5.0 h1:lulQHuVeodSgDez+3rGiuxlPVXSnhth442DATR2/8t8= github.com/charmbracelet/lipgloss v0.5.0/go.mod h1:EZLha/HbzEt7cYqdFPovlqy5FZPj0xFhg5SaqxScmgs= @@ -324,6 +326,7 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An github.com/spf13/viper v1.8.1 h1:Kq1fyeebqsBfbjZj4EL7gj2IO0mMaiyjYUWcUsl2O44= github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= diff --git a/cli/packages/api/api.go b/cli/packages/api/api.go index d45a42db4..01f29a03a 100644 --- a/cli/packages/api/api.go +++ b/cli/packages/api/api.go @@ -512,16 +512,23 @@ func CallUniversalAuthRefreshAccessToken(httpClient *resty.Client, request Unive func CallGetRawSecretsV3(httpClient *resty.Client, request GetRawSecretsV3Request) (GetRawSecretsV3Response, error) { var getRawSecretsV3Response GetRawSecretsV3Response - response, err := httpClient. + req := httpClient. R(). SetResult(&getRawSecretsV3Response). SetHeader("User-Agent", USER_AGENT). SetBody(request). SetQueryParam("workspaceId", request.WorkspaceId). SetQueryParam("environment", request.Environment). - SetQueryParam("secretPath", request.SecretPath). - SetQueryParam("include_imports", "false"). - Get(fmt.Sprintf("%v/v3/secrets/raw", config.INFISICAL_URL)) + SetQueryParam("secretPath", request.SecretPath) + + if request.IncludeImport { + req.SetQueryParam("include_imports", "true") + } + if request.Recursive { + req.SetQueryParam("recursive", "true") + } + + response, err := req.Get(fmt.Sprintf("%v/v3/secrets/raw", config.INFISICAL_URL)) if err != nil { return GetRawSecretsV3Response{}, fmt.Errorf("CallGetRawSecretsV3: Unable to complete api request [err=%w]", err) diff --git a/cli/packages/api/model.go b/cli/packages/api/model.go index 0a5bfee6d..56b9807f7 100644 --- a/cli/packages/api/model.go +++ b/cli/packages/api/model.go @@ -371,6 +371,22 @@ type ImportedSecretV3 struct { Secrets []EncryptedSecretV3 `json:"secrets"` } +type ImportedRawSecretV3 struct { + SecretPath string `json:"secretPath"` + Environment string `json:"environment"` + FolderId string `json:"folderId"` + Secrets []struct { + ID string `json:"id"` + Workspace string `json:"workspace"` + Environment string `json:"environment"` + Version int `json:"version"` + Type string `json:"type"` + SecretKey string `json:"secretKey"` + SecretValue string `json:"secretValue"` + SecretComment string `json:"secretComment"` + } `json:"secrets"` +} + type GetEncryptedSecretsV3Response struct { Secrets []EncryptedSecretV3 `json:"secrets"` ImportedSecrets []ImportedSecretV3 `json:"imports,omitempty"` @@ -542,6 +558,6 @@ type GetRawSecretsV3Response struct { SecretValue string `json:"secretValue"` SecretComment string `json:"secretComment"` } `json:"secrets"` - Imports []any `json:"imports"` + Imports []ImportedRawSecretV3 `json:"imports"` ETag string } diff --git a/cli/packages/cmd/export.go b/cli/packages/cmd/export.go index 84015cc02..983c19255 100644 --- a/cli/packages/cmd/export.go +++ b/cli/packages/cmd/export.go @@ -149,6 +149,8 @@ var exportCmd = &cobra.Command{ secrets = util.ExpandSecrets(secrets, authParams, "") } secrets = util.FilterSecretsByTag(secrets, tagSlugs) + secrets = util.SortSecretsByKeys(secrets) + output, err = formatEnvs(secrets, format) if err != nil { util.HandleError(err) diff --git a/cli/packages/cmd/secrets.go b/cli/packages/cmd/secrets.go index 7ba6e5411..f2f694075 100644 --- a/cli/packages/cmd/secrets.go +++ b/cli/packages/cmd/secrets.go @@ -116,6 +116,9 @@ var secretsCmd = &cobra.Command{ secrets = util.ExpandSecrets(secrets, authParams, "") } + // Sort the secrets by key so we can create a consistent output + secrets = util.SortSecretsByKeys(secrets) + visualize.PrintAllSecretDetails(secrets) Telemetry.CaptureEvent("cli-command:secrets", posthog.NewProperties().Set("secretCount", len(secrets)).Set("version", util.CLI_VERSION)) }, diff --git a/cli/packages/util/helper.go b/cli/packages/util/helper.go index e5b8ab6d9..9a4d960db 100644 --- a/cli/packages/util/helper.go +++ b/cli/packages/util/helper.go @@ -8,6 +8,7 @@ import ( "os" "os/exec" "path" + "sort" "strings" "time" @@ -53,6 +54,14 @@ func GetBase64DecodedSymmetricEncryptionDetails(key string, cipher string, IV st }, nil } +// Helper function to sort the secrets by key so we can create a consistent output +func SortSecretsByKeys(secrets []models.SingleEnvironmentVariable) []models.SingleEnvironmentVariable { + sort.Slice(secrets, func(i, j int) bool { + return secrets[i].Key < secrets[j].Key + }) + return secrets +} + func IsSecretEnvironmentValid(env string) bool { if env == "prod" || env == "dev" || env == "test" || env == "staging" { return true diff --git a/cli/packages/util/secrets.go b/cli/packages/util/secrets.go index 59d80ea77..bedd77572 100644 --- a/cli/packages/util/secrets.go +++ b/cli/packages/util/secrets.go @@ -186,12 +186,12 @@ func GetPlainTextSecretsViaMachineIdentity(accessToken string, workspaceId strin plainTextSecrets = append(plainTextSecrets, models.SingleEnvironmentVariable{Key: secret.SecretKey, Value: secret.SecretValue, Type: secret.Type, WorkspaceId: secret.Workspace}) } - // if includeImports { - // plainTextSecrets, err = InjectImportedSecret(plainTextWorkspaceKey, plainTextSecrets, encryptedSecrets.ImportedSecrets) - // if err != nil { - // return nil, err - // } - // } + if includeImports { + plainTextSecrets, err = InjectRawImportedSecret(plainTextSecrets, rawSecrets.Imports) + if err != nil { + return models.PlaintextSecretResult{}, err + } + } return models.PlaintextSecretResult{ Secrets: plainTextSecrets, @@ -252,6 +252,36 @@ func InjectImportedSecret(plainTextWorkspaceKey []byte, secrets []models.SingleE return secrets, nil } +func InjectRawImportedSecret(secrets []models.SingleEnvironmentVariable, importedSecrets []api.ImportedRawSecretV3) ([]models.SingleEnvironmentVariable, error) { + if importedSecrets == nil { + return secrets, nil + } + + hasOverriden := make(map[string]bool) + for _, sec := range secrets { + hasOverriden[sec.Key] = true + } + + for i := len(importedSecrets) - 1; i >= 0; i-- { + importSec := importedSecrets[i] + plainTextImportedSecrets := importSec.Secrets + + for _, sec := range plainTextImportedSecrets { + if _, ok := hasOverriden[sec.SecretKey]; !ok { + secrets = append(secrets, models.SingleEnvironmentVariable{ + Key: sec.SecretKey, + WorkspaceId: sec.Workspace, + Value: sec.SecretValue, + Type: sec.Type, + ID: sec.ID, + }) + hasOverriden[sec.SecretKey] = true + } + } + } + return secrets, nil +} + func FilterSecretsByTag(plainTextSecrets []models.SingleEnvironmentVariable, tagSlugs string) []models.SingleEnvironmentVariable { if tagSlugs == "" { return plainTextSecrets diff --git a/cli/test/.snapshots/test-TestServiceToken_ExportSecretsWithImports b/cli/test/.snapshots/test-TestServiceToken_ExportSecretsWithImports new file mode 100644 index 000000000..679cd91ad --- /dev/null +++ b/cli/test/.snapshots/test-TestServiceToken_ExportSecretsWithImports @@ -0,0 +1,5 @@ +STAGING-SECRET-1='staging-value-1' +STAGING-SECRET-2='staging-value-2' +TEST-SECRET-1='test-value-1' +TEST-SECRET-2='test-value-2' +TEST-SECRET-3='test-value-3' diff --git a/cli/test/.snapshots/test-TestServiceToken_ExportSecretsWithoutImports b/cli/test/.snapshots/test-TestServiceToken_ExportSecretsWithoutImports new file mode 100644 index 000000000..c803e591e --- /dev/null +++ b/cli/test/.snapshots/test-TestServiceToken_ExportSecretsWithoutImports @@ -0,0 +1,3 @@ +TEST-SECRET-1='test-value-1' +TEST-SECRET-2='test-value-2' +TEST-SECRET-3='test-value-3' diff --git a/cli/test/.snapshots/test-TestServiceToken_GetSecretsByNameRecursive b/cli/test/.snapshots/test-TestServiceToken_GetSecretsByNameRecursive new file mode 100644 index 000000000..f2f26ae19 --- /dev/null +++ b/cli/test/.snapshots/test-TestServiceToken_GetSecretsByNameRecursive @@ -0,0 +1,7 @@ +┌─────────────────┬────────────────┬─────────────┐ +│ SECRET NAME │ SECRET VALUE │ SECRET TYPE │ +├─────────────────┼────────────────┼─────────────┤ +│ TEST-SECRET-1 │ test-value-1 │ shared │ +│ TEST-SECRET-2 │ test-value-2 │ shared │ +│ FOLDER-SECRET-1 │ folder-value-1 │ shared │ +└─────────────────┴────────────────┴─────────────┘ diff --git a/cli/test/.snapshots/test-TestServiceToken_GetSecretsByNameWithImports b/cli/test/.snapshots/test-TestServiceToken_GetSecretsByNameWithImports new file mode 100644 index 000000000..ff488466d --- /dev/null +++ b/cli/test/.snapshots/test-TestServiceToken_GetSecretsByNameWithImports @@ -0,0 +1,7 @@ +┌──────────────────┬─────────────────┬─────────────┐ +│ SECRET NAME │ SECRET VALUE │ SECRET TYPE │ +├──────────────────┼─────────────────┼─────────────┤ +│ TEST-SECRET-1 │ test-value-1 │ shared │ +│ STAGING-SECRET-2 │ staging-value-2 │ shared │ +│ FOLDER-SECRET-1 │ folder-value-1 │ shared │ +└──────────────────┴─────────────────┴─────────────┘ diff --git a/cli/test/.snapshots/test-TestServiceToken_GetSecretsByNameWithNotFoundSecret b/cli/test/.snapshots/test-TestServiceToken_GetSecretsByNameWithNotFoundSecret new file mode 100644 index 000000000..afe3bffa8 --- /dev/null +++ b/cli/test/.snapshots/test-TestServiceToken_GetSecretsByNameWithNotFoundSecret @@ -0,0 +1,8 @@ +┌─────────────────┬────────────────┬─────────────┐ +│ SECRET NAME │ SECRET VALUE │ SECRET TYPE │ +├─────────────────┼────────────────┼─────────────┤ +│ TEST-SECRET-1 │ test-value-1 │ shared │ +│ TEST-SECRET-2 │ test-value-2 │ shared │ +│ FOLDER-SECRET-1 │ folder-value-1 │ shared │ +│ DOES-NOT-EXIST │ *not found* │ *not found* │ +└─────────────────┴────────────────┴─────────────┘ diff --git a/cli/test/.snapshots/test-TestServiceToken_RunCmdRecursiveAndImports b/cli/test/.snapshots/test-TestServiceToken_RunCmdRecursiveAndImports new file mode 100644 index 000000000..4106549cd --- /dev/null +++ b/cli/test/.snapshots/test-TestServiceToken_RunCmdRecursiveAndImports @@ -0,0 +1,2 @@ + Injecting 6 Infisical secrets into your application process +hello world diff --git a/cli/test/.snapshots/test-TestServiceToken_RunCmdWithImports b/cli/test/.snapshots/test-TestServiceToken_RunCmdWithImports new file mode 100644 index 000000000..86475d3e5 --- /dev/null +++ b/cli/test/.snapshots/test-TestServiceToken_RunCmdWithImports @@ -0,0 +1,2 @@ + Injecting 5 Infisical secrets into your application process +hello world diff --git a/cli/test/.snapshots/test-TestServiceToken_RunCmdWithoutImports b/cli/test/.snapshots/test-TestServiceToken_RunCmdWithoutImports new file mode 100644 index 000000000..dfe56064a --- /dev/null +++ b/cli/test/.snapshots/test-TestServiceToken_RunCmdWithoutImports @@ -0,0 +1,2 @@ + Injecting 3 Infisical secrets into your application process +hello world diff --git a/cli/test/.snapshots/test-TestServiceToken_SecretsGetWithImportsAndRecursiveCmd b/cli/test/.snapshots/test-TestServiceToken_SecretsGetWithImportsAndRecursiveCmd new file mode 100644 index 000000000..dd2bc317c --- /dev/null +++ b/cli/test/.snapshots/test-TestServiceToken_SecretsGetWithImportsAndRecursiveCmd @@ -0,0 +1,10 @@ +┌──────────────────┬─────────────────┬─────────────┐ +│ SECRET NAME │ SECRET VALUE │ SECRET TYPE │ +├──────────────────┼─────────────────┼─────────────┤ +│ FOLDER-SECRET-1 │ folder-value-1 │ shared │ +│ STAGING-SECRET-1 │ staging-value-1 │ shared │ +│ STAGING-SECRET-2 │ staging-value-2 │ shared │ +│ TEST-SECRET-1 │ test-value-1 │ shared │ +│ TEST-SECRET-2 │ test-value-2 │ shared │ +│ TEST-SECRET-3 │ test-value-3 │ shared │ +└──────────────────┴─────────────────┴─────────────┘ diff --git a/cli/test/.snapshots/test-TestServiceToken_SecretsGetWithoutImportsAndWithoutRecursiveCmd b/cli/test/.snapshots/test-TestServiceToken_SecretsGetWithoutImportsAndWithoutRecursiveCmd new file mode 100644 index 000000000..260607e97 --- /dev/null +++ b/cli/test/.snapshots/test-TestServiceToken_SecretsGetWithoutImportsAndWithoutRecursiveCmd @@ -0,0 +1,7 @@ +┌───────────────┬──────────────┬─────────────┐ +│ SECRET NAME │ SECRET VALUE │ SECRET TYPE │ +├───────────────┼──────────────┼─────────────┤ +│ TEST-SECRET-1 │ test-value-1 │ shared │ +│ TEST-SECRET-2 │ test-value-2 │ shared │ +│ TEST-SECRET-3 │ test-value-3 │ shared │ +└───────────────┴──────────────┴─────────────┘ diff --git a/cli/test/.snapshots/test-TestUniversalAuth_ExportSecretsWithImports b/cli/test/.snapshots/test-TestUniversalAuth_ExportSecretsWithImports new file mode 100644 index 000000000..679cd91ad --- /dev/null +++ b/cli/test/.snapshots/test-TestUniversalAuth_ExportSecretsWithImports @@ -0,0 +1,5 @@ +STAGING-SECRET-1='staging-value-1' +STAGING-SECRET-2='staging-value-2' +TEST-SECRET-1='test-value-1' +TEST-SECRET-2='test-value-2' +TEST-SECRET-3='test-value-3' diff --git a/cli/test/.snapshots/test-TestUniversalAuth_ExportSecretsWithoutImports b/cli/test/.snapshots/test-TestUniversalAuth_ExportSecretsWithoutImports new file mode 100644 index 000000000..c803e591e --- /dev/null +++ b/cli/test/.snapshots/test-TestUniversalAuth_ExportSecretsWithoutImports @@ -0,0 +1,3 @@ +TEST-SECRET-1='test-value-1' +TEST-SECRET-2='test-value-2' +TEST-SECRET-3='test-value-3' diff --git a/cli/test/.snapshots/test-TestUniversalAuth_GetSecretsByNameRecursive b/cli/test/.snapshots/test-TestUniversalAuth_GetSecretsByNameRecursive new file mode 100644 index 000000000..f2f26ae19 --- /dev/null +++ b/cli/test/.snapshots/test-TestUniversalAuth_GetSecretsByNameRecursive @@ -0,0 +1,7 @@ +┌─────────────────┬────────────────┬─────────────┐ +│ SECRET NAME │ SECRET VALUE │ SECRET TYPE │ +├─────────────────┼────────────────┼─────────────┤ +│ TEST-SECRET-1 │ test-value-1 │ shared │ +│ TEST-SECRET-2 │ test-value-2 │ shared │ +│ FOLDER-SECRET-1 │ folder-value-1 │ shared │ +└─────────────────┴────────────────┴─────────────┘ diff --git a/cli/test/.snapshots/test-TestUniversalAuth_GetSecretsByNameWithImports b/cli/test/.snapshots/test-TestUniversalAuth_GetSecretsByNameWithImports new file mode 100644 index 000000000..ff488466d --- /dev/null +++ b/cli/test/.snapshots/test-TestUniversalAuth_GetSecretsByNameWithImports @@ -0,0 +1,7 @@ +┌──────────────────┬─────────────────┬─────────────┐ +│ SECRET NAME │ SECRET VALUE │ SECRET TYPE │ +├──────────────────┼─────────────────┼─────────────┤ +│ TEST-SECRET-1 │ test-value-1 │ shared │ +│ STAGING-SECRET-2 │ staging-value-2 │ shared │ +│ FOLDER-SECRET-1 │ folder-value-1 │ shared │ +└──────────────────┴─────────────────┴─────────────┘ diff --git a/cli/test/.snapshots/test-TestUniversalAuth_GetSecretsByNameWithNotFoundSecret b/cli/test/.snapshots/test-TestUniversalAuth_GetSecretsByNameWithNotFoundSecret new file mode 100644 index 000000000..afe3bffa8 --- /dev/null +++ b/cli/test/.snapshots/test-TestUniversalAuth_GetSecretsByNameWithNotFoundSecret @@ -0,0 +1,8 @@ +┌─────────────────┬────────────────┬─────────────┐ +│ SECRET NAME │ SECRET VALUE │ SECRET TYPE │ +├─────────────────┼────────────────┼─────────────┤ +│ TEST-SECRET-1 │ test-value-1 │ shared │ +│ TEST-SECRET-2 │ test-value-2 │ shared │ +│ FOLDER-SECRET-1 │ folder-value-1 │ shared │ +│ DOES-NOT-EXIST │ *not found* │ *not found* │ +└─────────────────┴────────────────┴─────────────┘ diff --git a/cli/test/.snapshots/test-TestUniversalAuth_RunCmdRecursiveAndImports b/cli/test/.snapshots/test-TestUniversalAuth_RunCmdRecursiveAndImports new file mode 100644 index 000000000..4106549cd --- /dev/null +++ b/cli/test/.snapshots/test-TestUniversalAuth_RunCmdRecursiveAndImports @@ -0,0 +1,2 @@ + Injecting 6 Infisical secrets into your application process +hello world diff --git a/cli/test/.snapshots/test-TestUniversalAuth_RunCmdWithImports b/cli/test/.snapshots/test-TestUniversalAuth_RunCmdWithImports new file mode 100644 index 000000000..86475d3e5 --- /dev/null +++ b/cli/test/.snapshots/test-TestUniversalAuth_RunCmdWithImports @@ -0,0 +1,2 @@ + Injecting 5 Infisical secrets into your application process +hello world diff --git a/cli/test/.snapshots/test-TestUniversalAuth_RunCmdWithoutImports b/cli/test/.snapshots/test-TestUniversalAuth_RunCmdWithoutImports new file mode 100644 index 000000000..dfe56064a --- /dev/null +++ b/cli/test/.snapshots/test-TestUniversalAuth_RunCmdWithoutImports @@ -0,0 +1,2 @@ + Injecting 3 Infisical secrets into your application process +hello world diff --git a/cli/test/.snapshots/test-TestUniversalAuth_SecretsGetWithImportsAndRecursiveCmd b/cli/test/.snapshots/test-TestUniversalAuth_SecretsGetWithImportsAndRecursiveCmd new file mode 100644 index 000000000..dd2bc317c --- /dev/null +++ b/cli/test/.snapshots/test-TestUniversalAuth_SecretsGetWithImportsAndRecursiveCmd @@ -0,0 +1,10 @@ +┌──────────────────┬─────────────────┬─────────────┐ +│ SECRET NAME │ SECRET VALUE │ SECRET TYPE │ +├──────────────────┼─────────────────┼─────────────┤ +│ FOLDER-SECRET-1 │ folder-value-1 │ shared │ +│ STAGING-SECRET-1 │ staging-value-1 │ shared │ +│ STAGING-SECRET-2 │ staging-value-2 │ shared │ +│ TEST-SECRET-1 │ test-value-1 │ shared │ +│ TEST-SECRET-2 │ test-value-2 │ shared │ +│ TEST-SECRET-3 │ test-value-3 │ shared │ +└──────────────────┴─────────────────┴─────────────┘ diff --git a/cli/test/.snapshots/test-TestUniversalAuth_SecretsGetWithoutImportsAndWithoutRecursiveCmd b/cli/test/.snapshots/test-TestUniversalAuth_SecretsGetWithoutImportsAndWithoutRecursiveCmd new file mode 100644 index 000000000..260607e97 --- /dev/null +++ b/cli/test/.snapshots/test-TestUniversalAuth_SecretsGetWithoutImportsAndWithoutRecursiveCmd @@ -0,0 +1,7 @@ +┌───────────────┬──────────────┬─────────────┐ +│ SECRET NAME │ SECRET VALUE │ SECRET TYPE │ +├───────────────┼──────────────┼─────────────┤ +│ TEST-SECRET-1 │ test-value-1 │ shared │ +│ TEST-SECRET-2 │ test-value-2 │ shared │ +│ TEST-SECRET-3 │ test-value-3 │ shared │ +└───────────────┴──────────────┴─────────────┘ diff --git a/cli/test/.snapshots/test-TestUniversalAuth_SecretsGetWrongEnvironment b/cli/test/.snapshots/test-TestUniversalAuth_SecretsGetWrongEnvironment new file mode 100644 index 000000000..c3811bd22 --- /dev/null +++ b/cli/test/.snapshots/test-TestUniversalAuth_SecretsGetWrongEnvironment @@ -0,0 +1,4 @@ +error: CallGetRawSecretsV3: Unsuccessful response [GET https://app.infisical.com/api/v3/secrets/raw?environment=invalid-env&include_imports=true&recursive=true&secretPath=%2F&workspaceId=bef697d4-849b-4a75-b284-0922f87f8ba2] [status-code=500] [response={"statusCode":500,"error":"Internal Server Error","message":"'invalid-env' environment not found in project with ID bef697d4-849b-4a75-b284-0922f87f8ba2"}] + + +If this issue continues, get support at https://infisical.com/slack diff --git a/cli/test/export_test.go b/cli/test/export_test.go new file mode 100644 index 000000000..9a936871d --- /dev/null +++ b/cli/test/export_test.go @@ -0,0 +1,73 @@ +package tests + +import ( + "testing" + + "github.com/bradleyjkemp/cupaloy/v2" +) + +func TestUniversalAuth_ExportSecretsWithImports(t *testing.T) { + MachineIdentityLoginCmd(t) + SetupCli(t) + + output, err := ExecuteCliCommand(FORMATTED_CLI_NAME, "export", "--token", creds.UAAccessToken, "--projectId", creds.ProjectID, "--env", creds.EnvSlug, "--silent") + + if err != nil { + t.Fatalf("error running CLI command: %v", err) + } + + // Use cupaloy to snapshot test the output + err = cupaloy.Snapshot(output) + if err != nil { + t.Fatalf("snapshot failed: %v", err) + } +} + +func TestServiceToken_ExportSecretsWithImports(t *testing.T) { + SetupCli(t) + + output, err := ExecuteCliCommand(FORMATTED_CLI_NAME, "export", "--token", creds.ServiceToken, "--projectId", creds.ProjectID, "--env", creds.EnvSlug, "--silent") + + if err != nil { + t.Fatalf("error running CLI command: %v", err) + } + + // Use cupaloy to snapshot test the output + err = cupaloy.Snapshot(output) + if err != nil { + t.Fatalf("snapshot failed: %v", err) + } +} + +func TestUniversalAuth_ExportSecretsWithoutImports(t *testing.T) { + MachineIdentityLoginCmd(t) + SetupCli(t) + + output, err := ExecuteCliCommand(FORMATTED_CLI_NAME, "export", "--token", creds.UAAccessToken, "--projectId", creds.ProjectID, "--env", creds.EnvSlug, "--silent", "--include-imports=false") + + if err != nil { + t.Fatalf("error running CLI command: %v", err) + } + + // Use cupaloy to snapshot test the output + err = cupaloy.Snapshot(output) + if err != nil { + t.Fatalf("snapshot failed: %v", err) + } +} + +func TestServiceToken_ExportSecretsWithoutImports(t *testing.T) { + SetupCli(t) + + output, err := ExecuteCliCommand(FORMATTED_CLI_NAME, "export", "--token", creds.ServiceToken, "--projectId", creds.ProjectID, "--env", creds.EnvSlug, "--silent", "--include-imports=false") + + if err != nil { + t.Fatalf("error running CLI command: %v", err) + } + + // Use cupaloy to snapshot test the output + err = cupaloy.Snapshot(output) + if err != nil { + t.Fatalf("snapshot failed: %v", err) + } +} diff --git a/cli/test/helper.go b/cli/test/helper.go new file mode 100644 index 000000000..995367c4b --- /dev/null +++ b/cli/test/helper.go @@ -0,0 +1,64 @@ +package tests + +import ( + "fmt" + "os" + "os/exec" + "strings" + "testing" +) + +const ( + CLI_NAME = "infisical-merge" +) + +var ( + FORMATTED_CLI_NAME = fmt.Sprintf("./%s", CLI_NAME) +) + +type Credentials struct { + ClientID string + ClientSecret string + UAAccessToken string + ServiceToken string + ProjectID string + EnvSlug string +} + +var creds = Credentials{ + UAAccessToken: "", + ClientID: os.Getenv("CLI_TESTS_UA_CLIENT_ID"), + ClientSecret: os.Getenv("CLI_TESTS_UA_CLIENT_SECRET"), + ServiceToken: os.Getenv("CLI_TESTS_SERVICE_TOKEN"), + ProjectID: os.Getenv("CLI_TESTS_PROJECT_ID"), + EnvSlug: os.Getenv("CLI_TESTS_ENV_SLUG"), +} + +func ExecuteCliCommand(command string, args ...string) (string, error) { + cmd := exec.Command(command, args...) + output, err := cmd.CombinedOutput() + if err != nil { + return strings.TrimSpace(string(output)), err + } + return strings.TrimSpace(string(output)), nil +} + +func SetupCli(t *testing.T) { + + if creds.ClientID == "" || creds.ClientSecret == "" || creds.ServiceToken == "" || creds.ProjectID == "" || creds.EnvSlug == "" { + panic("Missing required environment variables") + } + + // check if the CLI is already built, if not build it + alreadyBuilt := false + if _, err := os.Stat(FORMATTED_CLI_NAME); err == nil { + alreadyBuilt = true + } + + if !alreadyBuilt { + if err := exec.Command("go", "build", "../.").Run(); err != nil { + t.Fatal(err) + } + } + +} diff --git a/cli/test/login_test.go b/cli/test/login_test.go new file mode 100644 index 000000000..0f4591413 --- /dev/null +++ b/cli/test/login_test.go @@ -0,0 +1,29 @@ +package tests + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func MachineIdentityLoginCmd(t *testing.T) { + SetupCli(t) + + if creds.UAAccessToken != "" { + return + } + + jwtPattern := `^[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+\.[A-Za-z0-9-_]*$` + + output, err := ExecuteCliCommand(FORMATTED_CLI_NAME, "login", "--method=universal-auth", "--client-id", creds.ClientID, "--client-secret", creds.ClientSecret, "--plain", "--silent") + + if err != nil { + t.Fatalf("error running CLI command: %v", err) + } + + assert.Regexp(t, jwtPattern, output) + + creds.UAAccessToken = output + + // We can't use snapshot testing here because the output will be different every time +} diff --git a/cli/test/run_test.go b/cli/test/run_test.go new file mode 100644 index 000000000..808f4f14f --- /dev/null +++ b/cli/test/run_test.go @@ -0,0 +1,120 @@ +package tests + +import ( + "bytes" + "testing" + + "github.com/bradleyjkemp/cupaloy/v2" +) + +func TestServiceToken_RunCmdRecursiveAndImports(t *testing.T) { + SetupCli(t) + + output, err := ExecuteCliCommand(FORMATTED_CLI_NAME, "run", "--token", creds.ServiceToken, "--projectId", creds.ProjectID, "--env", creds.EnvSlug, "--recursive", "--silent", "--", "echo", "hello world") + + if err != nil { + t.Fatalf("error running CLI command: %v", err) + } + + output = string(bytes.Split([]byte(output), []byte("INF"))[1]) + + // Use cupaloy to snapshot test the output + err = cupaloy.Snapshot(output) + if err != nil { + t.Fatalf("snapshot failed: %v", err) + } +} +func TestServiceToken_RunCmdWithImports(t *testing.T) { + SetupCli(t) + + output, err := ExecuteCliCommand(FORMATTED_CLI_NAME, "run", "--token", creds.ServiceToken, "--projectId", creds.ProjectID, "--env", creds.EnvSlug, "--silent", "--", "echo", "hello world") + + if err != nil { + t.Fatalf("error running CLI command: %v", err) + } + + output = string(bytes.Split([]byte(output), []byte("INF"))[1]) + + // Use cupaloy to snapshot test the output + err = cupaloy.Snapshot(output) + if err != nil { + t.Fatalf("snapshot failed: %v", err) + } +} + +func TestUniversalAuth_RunCmdRecursiveAndImports(t *testing.T) { + MachineIdentityLoginCmd(t) + SetupCli(t) + + output, err := ExecuteCliCommand(FORMATTED_CLI_NAME, "run", "--token", creds.UAAccessToken, "--projectId", creds.ProjectID, "--env", creds.EnvSlug, "--recursive", "--silent", "--", "echo", "hello world") + + if err != nil { + t.Fatalf("error running CLI command: %v", err) + } + + output = string(bytes.Split([]byte(output), []byte("INF"))[1]) + + // Use cupaloy to snapshot test the output + err = cupaloy.Snapshot(output) + if err != nil { + t.Fatalf("snapshot failed: %v", err) + } +} + +func TestUniversalAuth_RunCmdWithImports(t *testing.T) { + MachineIdentityLoginCmd(t) + SetupCli(t) + + output, err := ExecuteCliCommand(FORMATTED_CLI_NAME, "run", "--token", creds.UAAccessToken, "--projectId", creds.ProjectID, "--env", creds.EnvSlug, "--silent", "--", "echo", "hello world") + + if err != nil { + t.Fatalf("error running CLI command: %v", err) + } + + // remove the first few characters from the output because we don't care about the time, and it will change every time + output = string(bytes.Split([]byte(output), []byte("INF"))[1]) + + // Use cupaloy to snapshot test the output + err = cupaloy.Snapshot(output) + if err != nil { + t.Fatalf("snapshot failed: %v", err) + } +} + +func TestUniversalAuth_RunCmdWithoutImports(t *testing.T) { + MachineIdentityLoginCmd(t) + SetupCli(t) + + output, err := ExecuteCliCommand(FORMATTED_CLI_NAME, "run", "--token", creds.UAAccessToken, "--projectId", creds.ProjectID, "--env", creds.EnvSlug, "--silent", "--include-imports=false", "--", "echo", "hello world") + + if err != nil { + t.Fatalf("error running CLI command: %v", err) + } + + output = string(bytes.Split([]byte(output), []byte("INF"))[1]) + + // Use cupaloy to snapshot test the output + err = cupaloy.Snapshot(output) + if err != nil { + t.Fatalf("snapshot failed: %v", err) + } +} + +func TestServiceToken_RunCmdWithoutImports(t *testing.T) { + SetupCli(t) + + output, err := ExecuteCliCommand(FORMATTED_CLI_NAME, "run", "--token", creds.ServiceToken, "--projectId", creds.ProjectID, "--env", creds.EnvSlug, "--silent", "--include-imports=false", "--", "echo", "hello world") + + if err != nil { + t.Fatalf("error running CLI command: %v", err) + } + + // Remove everything before "INF" because it's not relevant to the test + output = string(bytes.Split([]byte(output), []byte("INF"))[1]) + + // Use cupaloy to snapshot test the output + err = cupaloy.Snapshot(output) + if err != nil { + t.Fatalf("snapshot failed: %v", err) + } +} diff --git a/cli/test/secrets_by_name_test.go b/cli/test/secrets_by_name_test.go new file mode 100644 index 000000000..440324e1a --- /dev/null +++ b/cli/test/secrets_by_name_test.go @@ -0,0 +1,106 @@ +package tests + +import ( + "testing" + + "github.com/bradleyjkemp/cupaloy/v2" +) + +func TestServiceToken_GetSecretsByNameRecursive(t *testing.T) { + SetupCli(t) + + output, err := ExecuteCliCommand(FORMATTED_CLI_NAME, "secrets", "get", "TEST-SECRET-1", "TEST-SECRET-2", "FOLDER-SECRET-1", "--token", creds.ServiceToken, "--projectId", creds.ProjectID, "--env", creds.EnvSlug, "--recursive", "--silent") + + if err != nil { + t.Fatalf("error running CLI command: %v", err) + } + + // Use cupaloy to snapshot test the output + err = cupaloy.Snapshot(output) + if err != nil { + t.Fatalf("snapshot failed: %v", err) + } +} + +func TestServiceToken_GetSecretsByNameWithNotFoundSecret(t *testing.T) { + SetupCli(t) + + output, err := ExecuteCliCommand(FORMATTED_CLI_NAME, "secrets", "get", "TEST-SECRET-1", "TEST-SECRET-2", "FOLDER-SECRET-1", "DOES-NOT-EXIST", "--token", creds.ServiceToken, "--projectId", creds.ProjectID, "--env", creds.EnvSlug, "--recursive", "--silent") + + if err != nil { + t.Fatalf("error running CLI command: %v", err) + } + + // Use cupaloy to snapshot test the output + err = cupaloy.Snapshot(output) + if err != nil { + t.Fatalf("snapshot failed: %v", err) + } +} + +func TestServiceToken_GetSecretsByNameWithImports(t *testing.T) { + SetupCli(t) + + output, err := ExecuteCliCommand(FORMATTED_CLI_NAME, "secrets", "get", "TEST-SECRET-1", "STAGING-SECRET-2", "FOLDER-SECRET-1", "--token", creds.ServiceToken, "--projectId", creds.ProjectID, "--env", creds.EnvSlug, "--recursive", "--silent") + + if err != nil { + t.Fatalf("error running CLI command: %v", err) + } + + // Use cupaloy to snapshot test the output + err = cupaloy.Snapshot(output) + if err != nil { + t.Fatalf("snapshot failed: %v", err) + } +} + +func TestUniversalAuth_GetSecretsByNameRecursive(t *testing.T) { + MachineIdentityLoginCmd(t) + SetupCli(t) + + output, err := ExecuteCliCommand(FORMATTED_CLI_NAME, "secrets", "get", "TEST-SECRET-1", "TEST-SECRET-2", "FOLDER-SECRET-1", "--token", creds.UAAccessToken, "--projectId", creds.ProjectID, "--env", creds.EnvSlug, "--recursive", "--silent") + + if err != nil { + t.Fatalf("error running CLI command: %v", err) + } + + // Use cupaloy to snapshot test the output + err = cupaloy.Snapshot(output) + if err != nil { + t.Fatalf("snapshot failed: %v", err) + } +} + +func TestUniversalAuth_GetSecretsByNameWithNotFoundSecret(t *testing.T) { + MachineIdentityLoginCmd(t) + SetupCli(t) + + output, err := ExecuteCliCommand(FORMATTED_CLI_NAME, "secrets", "get", "TEST-SECRET-1", "TEST-SECRET-2", "FOLDER-SECRET-1", "DOES-NOT-EXIST", "--token", creds.UAAccessToken, "--projectId", creds.ProjectID, "--env", creds.EnvSlug, "--recursive", "--silent") + + if err != nil { + t.Fatalf("error running CLI command: %v", err) + } + + // Use cupaloy to snapshot test the output + err = cupaloy.Snapshot(output) + if err != nil { + t.Fatalf("snapshot failed: %v", err) + } +} + +func TestUniversalAuth_GetSecretsByNameWithImports(t *testing.T) { + MachineIdentityLoginCmd(t) + SetupCli(t) + + output, err := ExecuteCliCommand(FORMATTED_CLI_NAME, "secrets", "get", "TEST-SECRET-1", "STAGING-SECRET-2", "FOLDER-SECRET-1", "--token", creds.UAAccessToken, "--projectId", creds.ProjectID, "--env", creds.EnvSlug, "--recursive", "--silent") + + if err != nil { + t.Fatalf("error running CLI command: %v", err) + } + + // Use cupaloy to snapshot test the output + err = cupaloy.Snapshot(output) + if err != nil { + t.Fatalf("snapshot failed: %v", err) + } +} diff --git a/cli/test/secrets_test.go b/cli/test/secrets_test.go new file mode 100644 index 000000000..453666406 --- /dev/null +++ b/cli/test/secrets_test.go @@ -0,0 +1,87 @@ +package tests + +import ( + "testing" + + "github.com/bradleyjkemp/cupaloy/v2" +) + +func TestServiceToken_SecretsGetWithImportsAndRecursiveCmd(t *testing.T) { + SetupCli(t) + + output, err := ExecuteCliCommand(FORMATTED_CLI_NAME, "secrets", "--token", creds.ServiceToken, "--projectId", creds.ProjectID, "--env", creds.EnvSlug, "--recursive", "--silent") + + if err != nil { + t.Fatalf("error running CLI command: %v", err) + } + + // Use cupaloy to snapshot test the output + err = cupaloy.Snapshot(output) + if err != nil { + t.Fatalf("snapshot failed: %v", err) + } +} + +func TestServiceToken_SecretsGetWithoutImportsAndWithoutRecursiveCmd(t *testing.T) { + SetupCli(t) + + output, err := ExecuteCliCommand(FORMATTED_CLI_NAME, "secrets", "--token", creds.ServiceToken, "--projectId", creds.ProjectID, "--env", creds.EnvSlug, "--include-imports=false", "--silent") + + if err != nil { + t.Fatalf("error running CLI command: %v", err) + } + + // Use cupaloy to snapshot test the output + err = cupaloy.Snapshot(output) + if err != nil { + t.Fatalf("snapshot failed: %v", err) + } +} + +func TestUniversalAuth_SecretsGetWithImportsAndRecursiveCmd(t *testing.T) { + SetupCli(t) + MachineIdentityLoginCmd(t) + + output, err := ExecuteCliCommand(FORMATTED_CLI_NAME, "secrets", "--token", creds.UAAccessToken, "--projectId", creds.ProjectID, "--env", creds.EnvSlug, "--recursive", "--silent") + + if err != nil { + t.Fatalf("error running CLI command: %v", err) + } + + // Use cupaloy to snapshot test the output + err = cupaloy.Snapshot(output) + if err != nil { + t.Fatalf("snapshot failed: %v", err) + } +} + +func TestUniversalAuth_SecretsGetWithoutImportsAndWithoutRecursiveCmd(t *testing.T) { + SetupCli(t) + MachineIdentityLoginCmd(t) + + output, err := ExecuteCliCommand(FORMATTED_CLI_NAME, "secrets", "--token", creds.UAAccessToken, "--projectId", creds.ProjectID, "--env", creds.EnvSlug, "--include-imports=false", "--silent") + + if err != nil { + t.Fatalf("error running CLI command: %v", err) + } + + // Use cupaloy to snapshot test the output + err = cupaloy.Snapshot(output) + if err != nil { + t.Fatalf("snapshot failed: %v", err) + } +} + +func TestUniversalAuth_SecretsGetWrongEnvironment(t *testing.T) { + SetupCli(t) + MachineIdentityLoginCmd(t) + + output, _ := ExecuteCliCommand(FORMATTED_CLI_NAME, "secrets", "--token", creds.UAAccessToken, "--projectId", creds.ProjectID, "--env", "invalid-env", "--recursive", "--silent") + + // Use cupaloy to snapshot test the output + err := cupaloy.Snapshot(output) + if err != nil { + t.Fatalf("snapshot failed: %v", err) + } + +}