golang.org/x/build@v0.0.0-20240506185731-218518f32b70/internal/secret/gcp_secret_manager_test.go (about)

     1  // Copyright 2020 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package secret
     6  
     7  import (
     8  	"context"
     9  	"flag"
    10  	"fmt"
    11  	"io"
    12  	"reflect"
    13  	"testing"
    14  
    15  	"cloud.google.com/go/secretmanager/apiv1/secretmanagerpb"
    16  	gax "github.com/googleapis/gax-go/v2"
    17  	"google.golang.org/grpc/codes"
    18  	"google.golang.org/grpc/status"
    19  )
    20  
    21  type fakeSecretClient struct {
    22  	accessReturnError error
    23  	accessSecretMap   map[string]string // map[path] = secret
    24  
    25  	closeReturnError error
    26  }
    27  
    28  func (fsc *fakeSecretClient) AccessSecretVersion(ctx context.Context, req *secretmanagerpb.AccessSecretVersionRequest, opts ...gax.CallOption) (*secretmanagerpb.AccessSecretVersionResponse, error) {
    29  	if ctx == nil || req == nil {
    30  		return nil, status.Error(codes.InvalidArgument, "ctx or req are nil")
    31  	}
    32  	if secret, ok := fsc.accessSecretMap[req.GetName()]; ok {
    33  		return &secretmanagerpb.AccessSecretVersionResponse{
    34  			Payload: &secretmanagerpb.SecretPayload{
    35  				Data: []byte(secret),
    36  			},
    37  		}, nil
    38  	}
    39  	return nil, status.Error(codes.NotFound, "secret not found")
    40  }
    41  
    42  func (fsc *fakeSecretClient) Close() error {
    43  	return fsc.closeReturnError
    44  }
    45  
    46  func TestRetrieve(t *testing.T) {
    47  	testCases := []struct {
    48  		desc          string
    49  		fakeClient    secretClient
    50  		ctx           context.Context
    51  		name          string
    52  		projectID     string
    53  		wantSecret    string
    54  		wantErrorCode codes.Code
    55  	}{
    56  		{
    57  			desc:          "nil-params",
    58  			fakeClient:    &fakeSecretClient{},
    59  			ctx:           nil,
    60  			name:          "x",
    61  			projectID:     "y",
    62  			wantSecret:    "",
    63  			wantErrorCode: codes.InvalidArgument,
    64  		},
    65  		{
    66  			desc:          "secret-not-found",
    67  			fakeClient:    &fakeSecretClient{},
    68  			ctx:           context.Background(),
    69  			name:          "x",
    70  			projectID:     "y",
    71  			wantSecret:    "",
    72  			wantErrorCode: codes.NotFound,
    73  		},
    74  		{
    75  			desc: "secret-found",
    76  			fakeClient: &fakeSecretClient{
    77  				accessReturnError: nil,
    78  				accessSecretMap: map[string]string{
    79  					buildNamePath("projecto", "nombre", "latest"): "secreto",
    80  				},
    81  			},
    82  			ctx:           context.Background(),
    83  			name:          "nombre",
    84  			projectID:     "projecto",
    85  			wantSecret:    "secreto",
    86  			wantErrorCode: codes.OK,
    87  		},
    88  	}
    89  	for _, tc := range testCases {
    90  		t.Run(tc.desc, func(t *testing.T) {
    91  			c := &Client{
    92  				client:    tc.fakeClient,
    93  				projectID: tc.projectID,
    94  			}
    95  			gotSecret, gotErr := c.Retrieve(tc.ctx, tc.name)
    96  			gotErrStatus, _ := status.FromError(gotErr)
    97  			if gotErrStatus.Code() != tc.wantErrorCode || gotSecret != tc.wantSecret {
    98  				t.Errorf("Retrieve(%v, %q) = %q, %v, wanted %q, %v", tc.ctx, tc.name, gotSecret, gotErr, tc.wantSecret, tc.wantErrorCode)
    99  			}
   100  		})
   101  	}
   102  }
   103  
   104  func TestClose(t *testing.T) {
   105  	randomErr := fmt.Errorf("close error")
   106  
   107  	testCases := []struct {
   108  		desc       string
   109  		fakeClient secretClient
   110  		wantError  error
   111  	}{
   112  		{
   113  			desc:       "no-error",
   114  			fakeClient: &fakeSecretClient{},
   115  			wantError:  nil,
   116  		},
   117  		{
   118  			desc: "error",
   119  			fakeClient: &fakeSecretClient{
   120  				closeReturnError: randomErr,
   121  			},
   122  			wantError: randomErr,
   123  		},
   124  	}
   125  	for _, tc := range testCases {
   126  		t.Run(tc.desc, func(t *testing.T) {
   127  			c := &Client{
   128  				client: tc.fakeClient,
   129  			}
   130  			if gotErr := c.Close(); gotErr != tc.wantError {
   131  				t.Errorf("Close() = %v, wanted %v", gotErr, tc.wantError)
   132  			}
   133  		})
   134  	}
   135  }
   136  
   137  func TestBuildNamePath(t *testing.T) {
   138  	want := "projects/x/secrets/y/versions/z"
   139  	got := buildNamePath("x", "y", "z")
   140  	if got != want {
   141  		t.Errorf("BuildVersionNumber(%s, %s, %s) = %q; want=%q", "x", "y", "z", got, want)
   142  	}
   143  }
   144  
   145  func TestFlag(t *testing.T) {
   146  	r := &FlagResolver{
   147  		Context: context.Background(),
   148  		Client: &fakeSecretClient{
   149  			accessSecretMap: map[string]string{
   150  				buildNamePath("project1", "secret1", "latest"): "supersecret",
   151  				buildNamePath("project2", "secret2", "latest"): "tippytopsecret",
   152  			},
   153  		},
   154  		DefaultProjectID: "project1",
   155  	}
   156  
   157  	tests := []struct {
   158  		flagVal, wantVal string
   159  		wantErr          bool
   160  	}{
   161  		{"hey", "hey", false},
   162  		{"secret:secret1", "supersecret", false},
   163  		{"secret:project2/secret2", "tippytopsecret", false},
   164  		{"secret:foo", "", true},
   165  	}
   166  
   167  	for _, tt := range tests {
   168  		t.Run(tt.flagVal, func(t *testing.T) {
   169  			fs := flag.NewFlagSet("", flag.ContinueOnError)
   170  			fs.SetOutput(io.Discard)
   171  			flagVal := r.Flag(fs, "testflag", "usage")
   172  			err := fs.Parse([]string{"--testflag", tt.flagVal})
   173  			if tt.wantErr {
   174  				if err == nil {
   175  					t.Fatalf("flag parsing succeeded, should have failed")
   176  				}
   177  				return
   178  			}
   179  			if err != nil {
   180  				t.Fatalf("flag parsing failed: %v", err)
   181  			}
   182  			if *flagVal != tt.wantVal {
   183  				t.Errorf("flag value = %q, want %q", *flagVal, tt.wantVal)
   184  			}
   185  		})
   186  	}
   187  }
   188  
   189  type jsonValue struct {
   190  	Foo, Bar int
   191  }
   192  
   193  func TestJSONFlag(t *testing.T) {
   194  	r := &FlagResolver{
   195  		Context: context.Background(),
   196  		Client: &fakeSecretClient{
   197  			accessSecretMap: map[string]string{
   198  				buildNamePath("project1", "secret1", "latest"): `{"Foo": 1, "Bar": 2}`,
   199  				buildNamePath("project1", "secret2", "latest"): `i am not json`,
   200  			},
   201  		},
   202  		DefaultProjectID: "project1",
   203  	}
   204  	tests := []struct {
   205  		flagVal   string
   206  		wantValue *jsonValue
   207  		wantErr   bool
   208  	}{
   209  		{"secret:secret1", &jsonValue{Foo: 1, Bar: 2}, false},
   210  		{"secret:secret2", nil, true},
   211  		{`{"Foo":0, "Bar":1}`, &jsonValue{Foo: 0, Bar: 1}, false},
   212  	}
   213  
   214  	for _, tt := range tests {
   215  		t.Run(tt.flagVal, func(t *testing.T) {
   216  			fs := flag.NewFlagSet("", flag.ContinueOnError)
   217  			fs.SetOutput(io.Discard)
   218  			value := &jsonValue{}
   219  			r.JSONVarFlag(fs, value, "testflag", "usage")
   220  			err := fs.Parse([]string{"--testflag", tt.flagVal})
   221  			if tt.wantErr {
   222  				if err == nil {
   223  					t.Fatalf("flag parsing succeeded, should have failed")
   224  				}
   225  				return
   226  			}
   227  			if err != nil {
   228  				t.Fatalf("flag parsing failed: %v", err)
   229  			}
   230  			if !reflect.DeepEqual(value, tt.wantValue) {
   231  				t.Errorf("flag value = %q, want %q", value, tt.wantValue)
   232  			}
   233  		})
   234  	}
   235  
   236  }