provider_test.go 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662
  1. /*
  2. Copyright © The ESO Authors
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. https://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package beyondtrustworkloadcredentials
  14. import (
  15. "context"
  16. "encoding/json"
  17. "fmt"
  18. "path"
  19. "strings"
  20. "testing"
  21. "github.com/google/go-cmp/cmp"
  22. esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
  23. "github.com/external-secrets/external-secrets/providers/v1/beyondtrustworkloadcredentials/fake"
  24. btwcutil "github.com/external-secrets/external-secrets/providers/v1/beyondtrustworkloadcredentials/util"
  25. )
  26. const (
  27. validSecretName = "apikey"
  28. validFolderPath = "valid/folder/path"
  29. invalidSecretName = "INVALID_NAME"
  30. invalidFolderPath = "INVALID_PATH"
  31. )
  32. ///////////////////////
  33. // test case structs //
  34. ///////////////////////
  35. type beyondtrustworkloadcredentialsGetSecretTestCase struct {
  36. label string
  37. fakeBtsecretsClient *fake.BeyondtrustWorkloadCredentialsClient
  38. ctx context.Context
  39. name *string
  40. folderPath *string
  41. remoteRef esv1.ExternalSecretDataRemoteRef
  42. fakeBtsecretsResponse *btwcutil.KV
  43. fakeBtsecretsError *string
  44. expectedError *string
  45. expectedResponse []byte
  46. }
  47. type beyondtrustworkloadcredentialsGetAllSecretsTestCase struct {
  48. label string
  49. fakeBtsecretsClient *fake.BeyondtrustWorkloadCredentialsClient
  50. ctx context.Context
  51. name *string
  52. names []string
  53. folderPath *string
  54. remoteRef esv1.ExternalSecretFind
  55. fakeBtsecretsGetResponse *btwcutil.KV
  56. fakeBtsecretsGetResponses []btwcutil.KV
  57. fakeBtsecretsListResponse []btwcutil.KVListItem
  58. fakeBtsecretsGetError *string
  59. fakeBtsecretsListError *string
  60. expectedError *string
  61. expectedResponse map[string][]byte
  62. }
  63. ////////////
  64. // makers //
  65. ////////////
  66. // makeValidGetSecretTestCase creates a valid test case for GetSecret tests.
  67. func makeValidGetSecretTestCase() *beyondtrustworkloadcredentialsGetSecretTestCase {
  68. return &beyondtrustworkloadcredentialsGetSecretTestCase{
  69. fakeBtsecretsClient: &fake.BeyondtrustWorkloadCredentialsClient{},
  70. ctx: context.Background(),
  71. name: new(validSecretName),
  72. folderPath: new(validFolderPath),
  73. remoteRef: esv1.ExternalSecretDataRemoteRef{Key: validSecretName, Property: ""},
  74. }
  75. }
  76. // makeValidGetAllSecretsTestCase creates a valid test case for GetSecrets tests.
  77. func makeValidGetAllSecretsTestCase() *beyondtrustworkloadcredentialsGetAllSecretsTestCase {
  78. return &beyondtrustworkloadcredentialsGetAllSecretsTestCase{
  79. fakeBtsecretsClient: &fake.BeyondtrustWorkloadCredentialsClient{},
  80. ctx: context.Background(),
  81. name: new(validSecretName),
  82. folderPath: new(validFolderPath),
  83. remoteRef: esv1.ExternalSecretFind{},
  84. }
  85. }
  86. // makeValidGetSecretTestCaseWithValues injects values into the faked BeyondtrustWorkloadCredentialsClient for GetSecret tests.
  87. func makeValidGetSecretTestCaseWithValues(tweaks ...func(tc *beyondtrustworkloadcredentialsGetSecretTestCase)) *beyondtrustworkloadcredentialsGetSecretTestCase {
  88. vtc := makeValidGetSecretTestCase()
  89. for _, fn := range tweaks {
  90. fn(vtc)
  91. }
  92. vtc.fakeBtsecretsClient.WithValues(vtc.ctx, vtc.name, vtc.folderPath, vtc.fakeBtsecretsResponse, nil, vtc.fakeBtsecretsError, nil)
  93. return vtc
  94. }
  95. // makeValidGetAllSecretsTestCaseWithValues injects values into the faked BeyondtrustWorkloadCredentialsClient for GetSecrets tests.
  96. func makeValidGetAllSecretsTestCaseWithValues(tweaks ...func(tc *beyondtrustworkloadcredentialsGetAllSecretsTestCase)) *beyondtrustworkloadcredentialsGetAllSecretsTestCase {
  97. vtc := makeValidGetAllSecretsTestCase()
  98. for _, fn := range tweaks {
  99. fn(vtc)
  100. }
  101. vtc.fakeBtsecretsClient.WithValues(vtc.ctx, vtc.name, vtc.folderPath, vtc.fakeBtsecretsGetResponse, vtc.fakeBtsecretsListResponse, vtc.fakeBtsecretsGetError, vtc.fakeBtsecretsListError)
  102. return vtc
  103. }
  104. // makeValidGetAllSecretsTestCaseWithMultiValues injects values with multiple GET responses into the faked BeyondtrustWorkloadCredentialsClient for GetSecrets tests.
  105. func makeValidGetAllSecretsTestCaseWithMultiValues(tweaks ...func(tc *beyondtrustworkloadcredentialsGetAllSecretsTestCase)) *beyondtrustworkloadcredentialsGetAllSecretsTestCase {
  106. vtc := makeValidGetAllSecretsTestCase()
  107. for _, fn := range tweaks {
  108. fn(vtc)
  109. }
  110. vtc.fakeBtsecretsClient.WithMultiValues(vtc.ctx, vtc.names, vtc.folderPath, vtc.fakeBtsecretsGetResponses, vtc.fakeBtsecretsListResponse, vtc.fakeBtsecretsGetError, vtc.fakeBtsecretsListError)
  111. return vtc
  112. }
  113. ///////////
  114. // tests //
  115. ///////////
  116. func TestGetSecret(t *testing.T) {
  117. // happy paths
  118. validSecret := func(tc *beyondtrustworkloadcredentialsGetSecretTestCase) {
  119. fakeKV := &btwcutil.KV{
  120. Path: fmt.Sprintf("%s/%s", validFolderPath, validSecretName),
  121. Secret: map[string]any{"valid": "test"},
  122. }
  123. fakeKVBytes, err := json.Marshal(fakeKV.Secret)
  124. if err != nil {
  125. t.Errorf("failed to marshal fake KV: %#v, error: %q", fakeKV, err.Error())
  126. }
  127. tc.label = "GetSecret - Valid"
  128. tc.fakeBtsecretsResponse = fakeKV
  129. tc.expectedResponse = fakeKVBytes
  130. }
  131. validSecretProperty := func(tc *beyondtrustworkloadcredentialsGetSecretTestCase) {
  132. propertyValue := "test"
  133. fakeKV := &btwcutil.KV{
  134. Path: fmt.Sprintf("%s/%s", validFolderPath, validSecretName),
  135. Secret: map[string]any{"valid": propertyValue, "doNotInclude": "this"},
  136. }
  137. tc.label = "GetSecret - Valid Property"
  138. tc.remoteRef = esv1.ExternalSecretDataRemoteRef{Key: validSecretName, Property: "valid"}
  139. tc.fakeBtsecretsResponse = fakeKV
  140. tc.expectedResponse = []byte(propertyValue)
  141. }
  142. // sad paths
  143. clientError := func(tc *beyondtrustworkloadcredentialsGetSecretTestCase) {
  144. tc.label = "GetSecret - Client Error"
  145. tc.name = new(invalidSecretName)
  146. tc.folderPath = new(invalidFolderPath)
  147. tc.remoteRef = esv1.ExternalSecretDataRemoteRef{Key: invalidSecretName}
  148. tc.fakeBtsecretsError = new("beyondtrustworkloadcredentials error")
  149. tc.expectedError = new("failed to get secret")
  150. }
  151. nilSecret := func(tc *beyondtrustworkloadcredentialsGetSecretTestCase) {
  152. fakeKV := &btwcutil.KV{
  153. Path: fmt.Sprintf("%s/%s", invalidFolderPath, invalidSecretName),
  154. }
  155. tc.label = "GetSecret - Nil Secret"
  156. tc.name = new(invalidSecretName)
  157. tc.folderPath = new(invalidFolderPath)
  158. tc.remoteRef = esv1.ExternalSecretDataRemoteRef{Key: invalidSecretName}
  159. tc.fakeBtsecretsResponse = fakeKV
  160. tc.expectedError = new("secret value is nil")
  161. }
  162. invalidSecretProperty := func(tc *beyondtrustworkloadcredentialsGetSecretTestCase) {
  163. fakeKV := &btwcutil.KV{
  164. Path: fmt.Sprintf("%s/%s", invalidFolderPath, invalidSecretName),
  165. Secret: map[string]any{"invalid": "test"},
  166. }
  167. ref := esv1.ExternalSecretDataRemoteRef{Key: invalidSecretName, Property: "nonexistant"}
  168. tc.label = "GetSecret - Invalid Property"
  169. tc.name = new(invalidSecretName)
  170. tc.folderPath = new(invalidFolderPath)
  171. tc.remoteRef = ref
  172. tc.fakeBtsecretsResponse = fakeKV
  173. tc.expectedError = new(fmt.Sprintf("property %s not found in secret", ref.Property))
  174. }
  175. invalidSecret := func(tc *beyondtrustworkloadcredentialsGetSecretTestCase) {
  176. fakeKV := &btwcutil.KV{
  177. Path: fmt.Sprintf("%s/%s", invalidFolderPath, invalidSecretName),
  178. Secret: map[string]any{"invalid": func() {}},
  179. }
  180. tc.label = "GetSecret - Invalid"
  181. tc.name = new(invalidSecretName)
  182. tc.folderPath = new(invalidFolderPath)
  183. tc.remoteRef = esv1.ExternalSecretDataRemoteRef{Key: invalidSecretName}
  184. tc.fakeBtsecretsResponse = fakeKV
  185. tc.expectedError = new("failed to marshal secret")
  186. }
  187. testCases := []*beyondtrustworkloadcredentialsGetSecretTestCase{
  188. // happy paths
  189. makeValidGetSecretTestCaseWithValues(validSecret),
  190. makeValidGetSecretTestCaseWithValues(validSecretProperty),
  191. // sad paths
  192. makeValidGetSecretTestCaseWithValues(clientError),
  193. makeValidGetSecretTestCaseWithValues(nilSecret),
  194. makeValidGetSecretTestCaseWithValues(invalidSecretProperty),
  195. makeValidGetSecretTestCaseWithValues(invalidSecret),
  196. }
  197. c := Client{store: &esv1.BeyondtrustWorkloadCredentialsProvider{}}
  198. for i, tc := range testCases {
  199. t.Run(tc.label, func(t *testing.T) {
  200. c.beyondtrustWorkloadCredentialsClient = tc.fakeBtsecretsClient
  201. c.store.FolderPath = *tc.folderPath
  202. out, err := c.GetSecret(context.Background(), tc.remoteRef)
  203. // assert error
  204. if tc.expectedError == nil && err != nil {
  205. t.Errorf("[%d] unexpected error: expected nil, got %q", i, err.Error())
  206. }
  207. if tc.expectedError != nil && !ErrorContains(err, *tc.expectedError) {
  208. t.Errorf("[%d] unexpected error: expected %q, got %q", i, *tc.expectedError, err)
  209. }
  210. // assert response
  211. if tc.expectedResponse == nil && out != nil {
  212. t.Errorf("[%d] unexpected response: expected nil, got %#v", i, out)
  213. }
  214. if tc.expectedResponse != nil && !cmp.Equal(out, tc.expectedResponse) {
  215. t.Errorf("[%d] unexpected response: expected %#v, got %#v", i, tc.expectedResponse, out)
  216. }
  217. })
  218. }
  219. }
  220. func ErrorContains(out error, want string) bool {
  221. if out == nil {
  222. return want == ""
  223. }
  224. if want == "" {
  225. return false
  226. }
  227. return strings.Contains(out.Error(), want)
  228. }
  229. func TestGetAllSecrets(t *testing.T) {
  230. // happy paths
  231. validSecretKVs := func(tc *beyondtrustworkloadcredentialsGetAllSecretsTestCase) {
  232. fakeKV := &btwcutil.KV{
  233. Path: fmt.Sprintf("%s/%s", validFolderPath, validSecretName),
  234. Secret: map[string]any{"key1": "val1", "key2": "val2", "key3": "val3"},
  235. }
  236. fakeListItem := btwcutil.KVListItem{
  237. Path: fakeKV.Path,
  238. }
  239. fakeKVBytes := map[string][]byte{
  240. fakeKV.Path + "/key1": []byte("val1"),
  241. fakeKV.Path + "/key2": []byte("val2"),
  242. fakeKV.Path + "/key3": []byte("val3"),
  243. }
  244. tc.label = "GetAllSecrets - Secret KVs - Valid"
  245. tc.remoteRef = esv1.ExternalSecretFind{Path: new(validFolderPath)}
  246. tc.fakeBtsecretsListResponse = []btwcutil.KVListItem{fakeListItem}
  247. tc.fakeBtsecretsGetResponse = fakeKV
  248. tc.expectedResponse = fakeKVBytes
  249. }
  250. validNamedSecrets := func(tc *beyondtrustworkloadcredentialsGetAllSecretsTestCase) {
  251. findName := fmt.Sprintf("%s-include", validSecretName)
  252. fakeKV1 := btwcutil.KV{
  253. Path: fmt.Sprintf("%s/%s-fakeKV1", validFolderPath, findName),
  254. Secret: map[string]any{"key1": "val1", "key2": "val2", "key3": "val3"},
  255. }
  256. fakeKV2 := btwcutil.KV{
  257. Path: fmt.Sprintf("%s/%s", validFolderPath, validSecretName),
  258. }
  259. fakeKV3 := btwcutil.KV{
  260. Path: fmt.Sprintf("%s/%s-fakeKV3", validFolderPath, findName),
  261. Secret: map[string]any{"key6": "val6"},
  262. }
  263. fakeListItem1 := btwcutil.KVListItem{
  264. Path: fakeKV1.Path,
  265. }
  266. fakeListItem2 := btwcutil.KVListItem{
  267. Path: fakeKV2.Path,
  268. }
  269. fakeListItem3 := btwcutil.KVListItem{
  270. Path: fakeKV3.Path,
  271. }
  272. fakeResponseBytes := map[string][]byte{
  273. fakeKV1.Path + "/key1": []byte("val1"),
  274. fakeKV1.Path + "/key2": []byte("val2"),
  275. fakeKV1.Path + "/key3": []byte("val3"),
  276. fakeKV3.Path + "/key6": []byte("val6"),
  277. }
  278. _, name1 := path.Split(fakeListItem1.Path)
  279. _, name3 := path.Split(fakeListItem3.Path)
  280. tc.label = "GetAllSecrets - Secret KVs - Valid Find RegExp"
  281. tc.names = []string{name1, name3}
  282. tc.remoteRef = esv1.ExternalSecretFind{Name: &esv1.FindName{RegExp: fmt.Sprintf("^%s.*", findName)}}
  283. tc.fakeBtsecretsGetResponses = []btwcutil.KV{fakeKV1, fakeKV3}
  284. tc.fakeBtsecretsListResponse = []btwcutil.KVListItem{fakeListItem1, fakeListItem2, fakeListItem3}
  285. tc.expectedResponse = fakeResponseBytes
  286. }
  287. validAllSecrets := func(tc *beyondtrustworkloadcredentialsGetAllSecretsTestCase) {
  288. findPath := fmt.Sprintf("%s/%s-include", validFolderPath, validSecretName)
  289. fakeKV1 := btwcutil.KV{
  290. Path: fmt.Sprintf("%s-fakeKV1", findPath),
  291. Secret: map[string]any{"key1": "val1", "key2": "val2", "key3": "val3"},
  292. }
  293. fakeKV2 := btwcutil.KV{
  294. Path: fmt.Sprintf("%s/%s", validFolderPath, validSecretName),
  295. Secret: map[string]any{"key4": "val4", "key5": "val5"},
  296. }
  297. fakeKV3 := btwcutil.KV{
  298. Path: fmt.Sprintf("%s-fakeKV3", findPath),
  299. Secret: map[string]any{"key6": "val6"},
  300. }
  301. fakeListItem1 := btwcutil.KVListItem{
  302. Path: fmt.Sprintf("%s-fakeKV1", findPath),
  303. }
  304. fakeListItem2 := btwcutil.KVListItem{
  305. Path: fmt.Sprintf("%s/%s", validFolderPath, validSecretName),
  306. }
  307. fakeListItem3 := btwcutil.KVListItem{
  308. Path: fmt.Sprintf("%s-fakeKV3", findPath),
  309. }
  310. fakeResponseBytes := map[string][]byte{
  311. fakeKV1.Path + "/key1": []byte("val1"),
  312. fakeKV1.Path + "/key2": []byte("val2"),
  313. fakeKV1.Path + "/key3": []byte("val3"),
  314. fakeKV2.Path + "/key4": []byte("val4"),
  315. fakeKV2.Path + "/key5": []byte("val5"),
  316. fakeKV3.Path + "/key6": []byte("val6"),
  317. }
  318. _, name1 := path.Split(fakeListItem1.Path)
  319. _, name2 := path.Split(fakeListItem2.Path)
  320. _, name3 := path.Split(fakeListItem3.Path)
  321. tc.label = "GetAllSecrets - List Secrets - Valid"
  322. tc.names = []string{name1, name2, name3}
  323. tc.fakeBtsecretsGetResponses = []btwcutil.KV{fakeKV1, fakeKV2, fakeKV3}
  324. tc.fakeBtsecretsListResponse = []btwcutil.KVListItem{fakeListItem1, fakeListItem2, fakeListItem3}
  325. tc.expectedResponse = fakeResponseBytes
  326. }
  327. // sad paths
  328. clientGetError := func(tc *beyondtrustworkloadcredentialsGetAllSecretsTestCase) {
  329. tc.label = "GetAllSecrets - Secret KVs - Client Error"
  330. tc.name = new(invalidSecretName)
  331. tc.folderPath = new(invalidFolderPath)
  332. tc.remoteRef = esv1.ExternalSecretFind{Path: new(invalidFolderPath)}
  333. tc.fakeBtsecretsListError = new("beyondtrustworkloadcredentials list error")
  334. tc.expectedError = new("failed to list secrets:")
  335. }
  336. nilSecret := func(tc *beyondtrustworkloadcredentialsGetAllSecretsTestCase) {
  337. fakeKV := &btwcutil.KV{
  338. Path: fmt.Sprintf("%s/%s", invalidFolderPath, invalidSecretName),
  339. }
  340. fakeListItem := btwcutil.KVListItem{
  341. Path: fakeKV.Path,
  342. }
  343. tc.label = "GetAllSecrets - Secret KVs - Nil Secret"
  344. tc.name = new(invalidSecretName)
  345. tc.folderPath = new(invalidFolderPath)
  346. tc.remoteRef = esv1.ExternalSecretFind{Path: new(invalidFolderPath)}
  347. tc.fakeBtsecretsListResponse = []btwcutil.KVListItem{fakeListItem}
  348. tc.fakeBtsecretsGetResponse = fakeKV
  349. // Provider now returns NoSecretError when no results are found
  350. tc.expectedError = new("Secret does not exist")
  351. }
  352. invalidSecret := func(tc *beyondtrustworkloadcredentialsGetAllSecretsTestCase) {
  353. fakeKV := &btwcutil.KV{
  354. Path: fmt.Sprintf("%s/%s", invalidFolderPath, invalidSecretName),
  355. Secret: map[string]any{"invalid": func() {}},
  356. }
  357. fakeListItem := btwcutil.KVListItem{
  358. Path: fakeKV.Path,
  359. }
  360. tc.label = "GetAllSecrets - Secret KVs - Invalid Secret"
  361. tc.name = new(invalidSecretName)
  362. tc.folderPath = new(invalidFolderPath)
  363. tc.remoteRef = esv1.ExternalSecretFind{Path: new(invalidFolderPath)}
  364. tc.fakeBtsecretsListResponse = []btwcutil.KVListItem{fakeListItem}
  365. tc.fakeBtsecretsGetResponse = fakeKV
  366. tc.expectedError = new("failed to marshal secret value for key")
  367. }
  368. clientListError := func(tc *beyondtrustworkloadcredentialsGetAllSecretsTestCase) {
  369. tc.label = "GetAllSecrets - List Secrets - Client Error"
  370. tc.name = new(invalidSecretName)
  371. tc.folderPath = new(invalidFolderPath)
  372. tc.fakeBtsecretsListError = new("beyondtrustworkloadcredentials list error")
  373. tc.expectedError = new("failed to list secrets:")
  374. }
  375. invalidFindRegex := func(tc *beyondtrustworkloadcredentialsGetAllSecretsTestCase) {
  376. tc.label = "GetAllSecrets - List Secrets - Invalid Find RegExp"
  377. tc.name = new(invalidSecretName)
  378. tc.folderPath = new(invalidFolderPath)
  379. tc.remoteRef = esv1.ExternalSecretFind{Name: &esv1.FindName{RegExp: "[invalid-regex"}}
  380. tc.expectedError = new("invalid name regexp")
  381. }
  382. clientGetErrorInList := func(tc *beyondtrustworkloadcredentialsGetAllSecretsTestCase) {
  383. tc.label = "GetAllSecrets - List Secrets - Get KVs - Client Error"
  384. tc.name = new(invalidSecretName)
  385. tc.folderPath = new(invalidFolderPath)
  386. tc.fakeBtsecretsListResponse = []btwcutil.KVListItem{{}}
  387. tc.fakeBtsecretsGetError = new("beyondtrustworkloadcredentials get error in list")
  388. tc.expectedError = new("failed to get secret at path")
  389. }
  390. nilSecretInList := func(tc *beyondtrustworkloadcredentialsGetAllSecretsTestCase) {
  391. fakeKV := &btwcutil.KV{
  392. Path: fmt.Sprintf("%s/%s", invalidFolderPath, invalidSecretName),
  393. }
  394. tc.label = "GetAllSecrets - List Secrets - Get KVs - Nil Secret"
  395. tc.name = new(invalidSecretName)
  396. tc.folderPath = new(invalidFolderPath)
  397. tc.fakeBtsecretsGetResponse = fakeKV
  398. tc.fakeBtsecretsListResponse = []btwcutil.KVListItem{{Path: fakeKV.Path}}
  399. // In list mode, skip missing entries; when no results are found, return NoSecretError
  400. tc.expectedError = new("Secret does not exist")
  401. }
  402. invalidSecretInList := func(tc *beyondtrustworkloadcredentialsGetAllSecretsTestCase) {
  403. fakeKV := &btwcutil.KV{
  404. Path: fmt.Sprintf("%s/%s", invalidFolderPath, invalidSecretName),
  405. Secret: map[string]any{"invalid": func() {}},
  406. }
  407. tc.label = "GetAllSecrets - List Secrets - Get KVs - Invalid"
  408. tc.name = new(invalidSecretName)
  409. tc.folderPath = new(invalidFolderPath)
  410. tc.fakeBtsecretsGetResponse = fakeKV
  411. tc.fakeBtsecretsListResponse = []btwcutil.KVListItem{{Path: fakeKV.Path}}
  412. tc.expectedError = new("failed to marshal secret value for key")
  413. }
  414. testCases := []*beyondtrustworkloadcredentialsGetAllSecretsTestCase{
  415. // happy paths
  416. makeValidGetAllSecretsTestCaseWithValues(validSecretKVs),
  417. makeValidGetAllSecretsTestCaseWithMultiValues(validNamedSecrets),
  418. makeValidGetAllSecretsTestCaseWithMultiValues(validAllSecrets),
  419. // sad paths
  420. makeValidGetAllSecretsTestCaseWithValues(clientGetError),
  421. makeValidGetAllSecretsTestCaseWithValues(nilSecret),
  422. makeValidGetAllSecretsTestCaseWithValues(invalidSecret),
  423. makeValidGetAllSecretsTestCaseWithValues(clientListError),
  424. makeValidGetAllSecretsTestCaseWithValues(invalidFindRegex),
  425. makeValidGetAllSecretsTestCaseWithValues(clientGetErrorInList),
  426. makeValidGetAllSecretsTestCaseWithValues(nilSecretInList),
  427. makeValidGetAllSecretsTestCaseWithValues(invalidSecretInList),
  428. }
  429. c := Client{store: &esv1.BeyondtrustWorkloadCredentialsProvider{}}
  430. for i, tc := range testCases {
  431. t.Run(tc.label, func(t *testing.T) {
  432. c.beyondtrustWorkloadCredentialsClient = tc.fakeBtsecretsClient
  433. c.store.FolderPath = *tc.folderPath
  434. out, err := c.GetAllSecrets(context.Background(), tc.remoteRef)
  435. // assert error
  436. if tc.expectedError == nil && err != nil {
  437. t.Errorf("[%d] unexpected error: expected nil, got %q", i, err.Error())
  438. }
  439. if tc.expectedError != nil && !ErrorContains(err, *tc.expectedError) {
  440. t.Errorf("[%d] unexpected error: expected %q, got %q", i, *tc.expectedError, err)
  441. }
  442. // assert response
  443. if tc.expectedResponse == nil && out != nil {
  444. t.Errorf("[%d] unexpected response: expected nil, got %#v", i, out)
  445. }
  446. if tc.expectedResponse != nil && !cmp.Equal(out, tc.expectedResponse) {
  447. t.Errorf("[%d] unexpected response: expected %#v, got %#v", i, tc.expectedResponse, out)
  448. }
  449. })
  450. }
  451. }
  452. func TestGetSecretMap(t *testing.T) {
  453. // happy paths
  454. validSecretMap := func(tc *beyondtrustworkloadcredentialsGetSecretTestCase) {
  455. fakeKV := &btwcutil.KV{
  456. Path: fmt.Sprintf("%s/%s", validFolderPath, validSecretName),
  457. Secret: map[string]any{"username": "admin", "password": "secret123", "port": 5432},
  458. }
  459. tc.label = "GetSecretMap - Valid"
  460. tc.fakeBtsecretsResponse = fakeKV
  461. // For GetSecretMap tests, we use a map comparison, not a single byte response
  462. // This test will use a custom comparison below
  463. }
  464. validSecretMapWithTypes := func(tc *beyondtrustworkloadcredentialsGetSecretTestCase) {
  465. fakeKV := &btwcutil.KV{
  466. Path: fmt.Sprintf("%s/%s", validFolderPath, validSecretName),
  467. Secret: map[string]any{
  468. "username": "admin",
  469. "config": map[string]string{"env": "prod", "region": "us-east-1"},
  470. },
  471. }
  472. tc.label = "GetSecretMap - Valid With Complex Types"
  473. tc.fakeBtsecretsResponse = fakeKV
  474. }
  475. // sad paths
  476. clientError := func(tc *beyondtrustworkloadcredentialsGetSecretTestCase) {
  477. tc.label = "GetSecretMap - Client Error"
  478. tc.name = new(invalidSecretName)
  479. tc.folderPath = new(invalidFolderPath)
  480. tc.remoteRef = esv1.ExternalSecretDataRemoteRef{Key: invalidSecretName}
  481. tc.fakeBtsecretsError = new("beyondtrustworkloadcredentials error")
  482. tc.expectedError = new("failed to get secret")
  483. }
  484. nilSecret := func(tc *beyondtrustworkloadcredentialsGetSecretTestCase) {
  485. fakeKV := &btwcutil.KV{
  486. Path: fmt.Sprintf("%s/%s", invalidFolderPath, invalidSecretName),
  487. }
  488. tc.label = "GetSecretMap - Nil Secret"
  489. tc.name = new(invalidSecretName)
  490. tc.folderPath = new(invalidFolderPath)
  491. tc.remoteRef = esv1.ExternalSecretDataRemoteRef{Key: invalidSecretName}
  492. tc.fakeBtsecretsResponse = fakeKV
  493. tc.expectedError = new("secret value is nil")
  494. }
  495. invalidSecret := func(tc *beyondtrustworkloadcredentialsGetSecretTestCase) {
  496. fakeKV := &btwcutil.KV{
  497. Path: fmt.Sprintf("%s/%s", invalidFolderPath, invalidSecretName),
  498. Secret: map[string]any{
  499. "valid": "test",
  500. "invalid": func() {}, // Unmarshalable type
  501. },
  502. }
  503. tc.label = "GetSecretMap - Invalid"
  504. tc.name = new(invalidSecretName)
  505. tc.folderPath = new(invalidFolderPath)
  506. tc.remoteRef = esv1.ExternalSecretDataRemoteRef{Key: invalidSecretName}
  507. tc.fakeBtsecretsResponse = fakeKV
  508. tc.expectedError = new("failed to marshal secret value for key")
  509. }
  510. testCases := []*beyondtrustworkloadcredentialsGetSecretTestCase{
  511. // happy paths
  512. makeValidGetSecretTestCaseWithValues(validSecretMap),
  513. makeValidGetSecretTestCaseWithValues(validSecretMapWithTypes),
  514. // sad paths
  515. makeValidGetSecretTestCaseWithValues(clientError),
  516. makeValidGetSecretTestCaseWithValues(nilSecret),
  517. makeValidGetSecretTestCaseWithValues(invalidSecret),
  518. }
  519. c := Client{store: &esv1.BeyondtrustWorkloadCredentialsProvider{}}
  520. for i, tc := range testCases {
  521. t.Run(tc.label, func(t *testing.T) {
  522. c.beyondtrustWorkloadCredentialsClient = tc.fakeBtsecretsClient
  523. c.store.FolderPath = *tc.folderPath
  524. out, err := c.GetSecretMap(context.Background(), tc.remoteRef)
  525. // assert error
  526. if tc.expectedError == nil && err != nil {
  527. t.Errorf("[%d] unexpected error: expected nil, got %q", i, err.Error())
  528. }
  529. if tc.expectedError != nil && !ErrorContains(err, *tc.expectedError) {
  530. t.Errorf("[%d] unexpected error: expected %q, got %q", i, *tc.expectedError, err)
  531. }
  532. // For happy path tests with complex types, just verify no error and non-nil response
  533. if i < 2 && tc.expectedError == nil {
  534. if out == nil {
  535. t.Errorf("[%d] unexpected response: expected non-nil map, got nil", i)
  536. }
  537. if len(out) == 0 {
  538. t.Errorf("[%d] unexpected response: expected non-empty map, got empty", i)
  539. }
  540. }
  541. })
  542. }
  543. }