mirror of
				https://github.com/optim-enterprises-bv/vault.git
				synced 2025-10-31 18:48:08 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			294 lines
		
	
	
		
			8.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			294 lines
		
	
	
		
			8.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright (c) HashiCorp, Inc.
 | |
| // SPDX-License-Identifier: MPL-2.0
 | |
| 
 | |
| package command
 | |
| 
 | |
| import (
 | |
| 	"bytes"
 | |
| 	"fmt"
 | |
| 	"os"
 | |
| 	"strings"
 | |
| 	"testing"
 | |
| 	"time"
 | |
| 
 | |
| 	"github.com/ghodss/yaml"
 | |
| 	"github.com/hashicorp/vault/api"
 | |
| 	"github.com/hashicorp/vault/sdk/helper/jsonutil"
 | |
| )
 | |
| 
 | |
| type mockUi struct {
 | |
| 	t          *testing.T
 | |
| 	SampleData string
 | |
| 	outputData *string
 | |
| }
 | |
| 
 | |
| func (m mockUi) Ask(_ string) (string, error) {
 | |
| 	m.t.FailNow()
 | |
| 	return "", nil
 | |
| }
 | |
| 
 | |
| func (m mockUi) AskSecret(_ string) (string, error) {
 | |
| 	m.t.FailNow()
 | |
| 	return "", nil
 | |
| }
 | |
| func (m mockUi) Output(s string) { *m.outputData = s }
 | |
| func (m mockUi) Info(s string)   { m.t.Log(s) }
 | |
| func (m mockUi) Error(s string)  { m.t.Log(s) }
 | |
| func (m mockUi) Warn(s string)   { m.t.Log(s) }
 | |
| 
 | |
| func TestJsonFormatter(t *testing.T) {
 | |
| 	os.Setenv(EnvVaultFormat, "json")
 | |
| 	var output string
 | |
| 	ui := mockUi{t: t, SampleData: "something", outputData: &output}
 | |
| 	if err := outputWithFormat(ui, nil, ui); err != 0 {
 | |
| 		t.Fatal(err)
 | |
| 	}
 | |
| 	var newUi mockUi
 | |
| 	if err := jsonutil.DecodeJSON([]byte(output), &newUi); err != nil {
 | |
| 		t.Fatal(err)
 | |
| 	}
 | |
| 	if newUi.SampleData != ui.SampleData {
 | |
| 		t.Fatalf(`values not equal ("%s" != "%s")`,
 | |
| 			newUi.SampleData,
 | |
| 			ui.SampleData)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestYamlFormatter(t *testing.T) {
 | |
| 	os.Setenv(EnvVaultFormat, "yaml")
 | |
| 	var output string
 | |
| 	ui := mockUi{t: t, SampleData: "something", outputData: &output}
 | |
| 	if err := outputWithFormat(ui, nil, ui); err != 0 {
 | |
| 		t.Fatal(err)
 | |
| 	}
 | |
| 	var newUi mockUi
 | |
| 	err := yaml.Unmarshal([]byte(output), &newUi)
 | |
| 	if err != nil {
 | |
| 		t.Fatal(err)
 | |
| 	}
 | |
| 	if newUi.SampleData != ui.SampleData {
 | |
| 		t.Fatalf(`values not equal ("%s" != "%s")`,
 | |
| 			newUi.SampleData,
 | |
| 			ui.SampleData)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestTableFormatter(t *testing.T) {
 | |
| 	os.Setenv(EnvVaultFormat, "table")
 | |
| 	var output string
 | |
| 	ui := mockUi{t: t, outputData: &output}
 | |
| 
 | |
| 	// Testing secret formatting
 | |
| 	s := api.Secret{Data: map[string]interface{}{"k": "something"}}
 | |
| 	if err := outputWithFormat(ui, &s, &s); err != 0 {
 | |
| 		t.Fatal(err)
 | |
| 	}
 | |
| 	if !strings.Contains(output, "something") {
 | |
| 		t.Fatal("did not find 'something'")
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // TestStatusFormat tests to verify that the embedded struct
 | |
| // SealStatusOutput ignores omitEmpty fields and prints out
 | |
| // fields in the embedded struct explicitly. It also checks the spacing,
 | |
| // indentation, and delimiters of table formatting explicitly.
 | |
| func TestStatusFormat(t *testing.T) {
 | |
| 	var output string
 | |
| 	ui := mockUi{t: t, outputData: &output}
 | |
| 	os.Setenv(EnvVaultFormat, "table")
 | |
| 
 | |
| 	statusHA := getMockStatusData(false)
 | |
| 	statusOmitEmpty := getMockStatusData(true)
 | |
| 
 | |
| 	// Testing that HA fields are formatted properly for table.
 | |
| 	// All fields (including new HA fields) are expected
 | |
| 	if err := outputWithFormat(ui, nil, statusHA); err != 0 {
 | |
| 		t.Fatal(err)
 | |
| 	}
 | |
| 
 | |
| 	expectedOutputString := `Key                           Value
 | |
| ---                           -----
 | |
| Recovery Seal Type            type
 | |
| Initialized                   true
 | |
| Sealed                        true
 | |
| Total Recovery Shares         2
 | |
| Threshold                     1
 | |
| Unseal Progress               3/1
 | |
| Unseal Nonce                  nonce
 | |
| Seal Migration in Progress    true
 | |
| Version                       version
 | |
| Build Date                    build date
 | |
| Storage Type                  storage type
 | |
| Cluster Name                  cluster name
 | |
| Cluster ID                    cluster id
 | |
| HA Enabled                    true
 | |
| Raft Committed Index          3
 | |
| Raft Applied Index            4
 | |
| Last WAL                      2
 | |
| Warnings                      [warning]`
 | |
| 
 | |
| 	if expectedOutputString != output {
 | |
| 		fmt.Printf("%s\n%+v\n %s\n%+v\n", "output found was: ", output, "versus", expectedOutputString)
 | |
| 		t.Fatal("format output for status does not match expected format. Check print statements above.")
 | |
| 	}
 | |
| 
 | |
| 	// Testing that omitEmpty fields are omitted from status
 | |
| 	// no HA fields are expected, except HA Enabled
 | |
| 	if err := outputWithFormat(ui, nil, statusOmitEmpty); err != 0 {
 | |
| 		t.Fatal(err)
 | |
| 	}
 | |
| 
 | |
| 	expectedOutputString = `Key                           Value
 | |
| ---                           -----
 | |
| Recovery Seal Type            type
 | |
| Initialized                   true
 | |
| Sealed                        true
 | |
| Total Recovery Shares         2
 | |
| Threshold                     1
 | |
| Unseal Progress               3/1
 | |
| Unseal Nonce                  nonce
 | |
| Seal Migration in Progress    true
 | |
| Version                       version
 | |
| Build Date                    build date
 | |
| Storage Type                  n/a
 | |
| HA Enabled                    false`
 | |
| 
 | |
| 	if expectedOutputString != output {
 | |
| 		fmt.Printf("%s\n%+v\n %s\n%+v\n", "output found was: ", output, "versus", expectedOutputString)
 | |
| 		t.Fatal("format output for status does not match expected format. Check print statements above.")
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // getMockStatusData outputs a SealStatusOutput struct from format.go to be used
 | |
| // for testing. The emptyfields parameter specifies whether the struct will be
 | |
| // initialized with all the omitempty fields as empty or not.
 | |
| func getMockStatusData(emptyFields bool) SealStatusOutput {
 | |
| 	var status SealStatusOutput
 | |
| 	var sealStatusResponseMock api.SealStatusResponse
 | |
| 	if !emptyFields {
 | |
| 		sealStatusResponseMock = api.SealStatusResponse{
 | |
| 			Type:         "type",
 | |
| 			Initialized:  true,
 | |
| 			Sealed:       true,
 | |
| 			T:            1,
 | |
| 			N:            2,
 | |
| 			Progress:     3,
 | |
| 			Nonce:        "nonce",
 | |
| 			Version:      "version",
 | |
| 			BuildDate:    "build date",
 | |
| 			Migration:    true,
 | |
| 			ClusterName:  "cluster name",
 | |
| 			ClusterID:    "cluster id",
 | |
| 			RecoverySeal: true,
 | |
| 			StorageType:  "storage type",
 | |
| 			Warnings:     []string{"warning"},
 | |
| 		}
 | |
| 
 | |
| 		// must initialize this struct without explicit field names due to embedding
 | |
| 		status = SealStatusOutput{
 | |
| 			sealStatusResponseMock,
 | |
| 			true,                     // HAEnabled
 | |
| 			true,                     // IsSelf
 | |
| 			time.Time{}.UTC(),        // ActiveTime
 | |
| 			"leader address",         // LeaderAddress
 | |
| 			"leader cluster address", // LeaderClusterAddress
 | |
| 			true,                     // PerfStandby
 | |
| 			1,                        // PerfStandbyLastRemoteWAL
 | |
| 			2,                        // LastWAL
 | |
| 			3,                        // RaftCommittedIndex
 | |
| 			4,                        // RaftAppliedIndex
 | |
| 		}
 | |
| 	} else {
 | |
| 		sealStatusResponseMock = api.SealStatusResponse{
 | |
| 			Type:         "type",
 | |
| 			Initialized:  true,
 | |
| 			Sealed:       true,
 | |
| 			T:            1,
 | |
| 			N:            2,
 | |
| 			Progress:     3,
 | |
| 			Nonce:        "nonce",
 | |
| 			Version:      "version",
 | |
| 			BuildDate:    "build date",
 | |
| 			Migration:    true,
 | |
| 			ClusterName:  "",
 | |
| 			ClusterID:    "",
 | |
| 			RecoverySeal: true,
 | |
| 			StorageType:  "",
 | |
| 		}
 | |
| 
 | |
| 		// must initialize this struct without explicit field names due to embedding
 | |
| 		status = SealStatusOutput{
 | |
| 			sealStatusResponseMock,
 | |
| 			false,             // HAEnabled
 | |
| 			false,             // IsSelf
 | |
| 			time.Time{}.UTC(), // ActiveTime
 | |
| 			"",                // LeaderAddress
 | |
| 			"",                // LeaderClusterAddress
 | |
| 			false,             // PerfStandby
 | |
| 			0,                 // PerfStandbyLastRemoteWAL
 | |
| 			0,                 // LastWAL
 | |
| 			0,                 // RaftCommittedIndex
 | |
| 			0,                 // RaftAppliedIndex
 | |
| 		}
 | |
| 	}
 | |
| 	return status
 | |
| }
 | |
| 
 | |
| func Test_Format_Parsing(t *testing.T) {
 | |
| 	defer func() {
 | |
| 		os.Setenv(EnvVaultCLINoColor, "")
 | |
| 		os.Setenv(EnvVaultFormat, "")
 | |
| 	}()
 | |
| 
 | |
| 	cases := []struct {
 | |
| 		name string
 | |
| 		args []string
 | |
| 		out  string
 | |
| 		code int
 | |
| 	}{
 | |
| 		{
 | |
| 			"format",
 | |
| 			[]string{"token", "renew", "-format", "json"},
 | |
| 			"{",
 | |
| 			0,
 | |
| 		},
 | |
| 		{
 | |
| 			"format_bad",
 | |
| 			[]string{"token", "renew", "-format", "nope-not-real"},
 | |
| 			"Invalid output format",
 | |
| 			1,
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	for _, tc := range cases {
 | |
| 		tc := tc
 | |
| 
 | |
| 		t.Run(tc.name, func(t *testing.T) {
 | |
| 			client, closer := testVaultServer(t)
 | |
| 			defer closer()
 | |
| 
 | |
| 			stdout := bytes.NewBuffer(nil)
 | |
| 			stderr := bytes.NewBuffer(nil)
 | |
| 			runOpts := &RunOptions{
 | |
| 				Stdout: stdout,
 | |
| 				Stderr: stderr,
 | |
| 				Client: client,
 | |
| 			}
 | |
| 
 | |
| 			// Login with the token so we can renew-self.
 | |
| 			token, _ := testTokenAndAccessor(t, client)
 | |
| 			client.SetToken(token)
 | |
| 
 | |
| 			code := RunCustom(tc.args, runOpts)
 | |
| 			if code != tc.code {
 | |
| 				t.Errorf("expected %d to be %d", code, tc.code)
 | |
| 			}
 | |
| 
 | |
| 			combined := stdout.String() + stderr.String()
 | |
| 			if !strings.Contains(combined, tc.out) {
 | |
| 				t.Errorf("expected %q to contain %q", combined, tc.out)
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | 
