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 }