pulumi_test.go 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
  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 pulumi
  14. import (
  15. "context"
  16. "encoding/json"
  17. "fmt"
  18. "net/http"
  19. "net/http/httptest"
  20. "reflect"
  21. "testing"
  22. esc "github.com/pulumi/esc-sdk/sdk/go"
  23. "github.com/stretchr/testify/assert"
  24. "github.com/stretchr/testify/require"
  25. esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
  26. )
  27. // Constants for content type and value.
  28. const contentTypeValue = "application/json"
  29. const contentType = "Content-Type"
  30. func newTestClient(t *testing.T, _, pattern string, handler func(w http.ResponseWriter, r *http.Request)) *client {
  31. const token = "test-token"
  32. mux := http.NewServeMux()
  33. mux.HandleFunc(pattern, handler)
  34. mux.HandleFunc("/environments/foo/default/bar/open/", func(w http.ResponseWriter, r *http.Request) {
  35. r.Header.Add(contentType, contentTypeValue)
  36. w.Header().Add(contentType, contentTypeValue)
  37. w.WriteHeader(http.StatusOK)
  38. err := json.NewEncoder(w).Encode(map[string]any{
  39. "id": "session-id",
  40. })
  41. require.NoError(t, err)
  42. })
  43. server := httptest.NewServer(mux)
  44. t.Cleanup(server.Close)
  45. configuration := esc.NewConfiguration()
  46. configuration.AddDefaultHeader("Authorization", "token "+token)
  47. configuration.UserAgent = "external-secrets-operator"
  48. configuration.Servers = esc.ServerConfigurations{
  49. esc.ServerConfiguration{
  50. URL: server.URL,
  51. },
  52. }
  53. ctx := esc.NewAuthContext(token)
  54. escClient := esc.NewClient(configuration)
  55. return &client{
  56. escClient: *escClient,
  57. authCtx: ctx,
  58. organization: "foo",
  59. environment: "bar",
  60. project: "default",
  61. }
  62. }
  63. func TestGetSecret(t *testing.T) {
  64. testmap := map[string]any{
  65. "b": "world",
  66. }
  67. client := newTestClient(t, http.MethodGet, "/environments/foo/default/bar/open/session-id", func(w http.ResponseWriter, r *http.Request) {
  68. r.Header.Add(contentType, contentTypeValue)
  69. w.Header().Add(contentType, contentTypeValue)
  70. err := json.NewEncoder(w).Encode(esc.NewValue(testmap, esc.Trace{}))
  71. require.NoError(t, err)
  72. })
  73. testCases := map[string]struct {
  74. ref esv1.ExternalSecretDataRemoteRef
  75. want []byte
  76. err error
  77. }{
  78. "querying for the key returns the value": {
  79. ref: esv1.ExternalSecretDataRemoteRef{
  80. Key: "b",
  81. },
  82. want: []byte(`{"b":"world"}`),
  83. },
  84. }
  85. for name, tc := range testCases {
  86. t.Run(name, func(t *testing.T) {
  87. got, err := client.GetSecret(context.TODO(), tc.ref)
  88. if tc.err == nil {
  89. assert.NoError(t, err)
  90. assert.Equal(t, tc.want, got)
  91. } else {
  92. assert.Nil(t, got)
  93. assert.ErrorIs(t, err, tc.err)
  94. assert.Equal(t, tc.err, err)
  95. }
  96. })
  97. }
  98. }
  99. func TestGetSecretMap(t *testing.T) {
  100. tests := []struct {
  101. name string
  102. ref esv1.ExternalSecretDataRemoteRef
  103. input map[string]any
  104. want map[string][]byte
  105. wantErr bool
  106. }{
  107. {
  108. name: "successful case (basic types)",
  109. ref: esv1.ExternalSecretDataRemoteRef{
  110. Key: "mysec",
  111. },
  112. input: map[string]any{
  113. "foo": map[string]any{
  114. "value": "bar",
  115. "trace": map[string]any{
  116. "def": map[string]any{
  117. "environment": "bar",
  118. "begin": map[string]any{
  119. "line": 3,
  120. "column": 9,
  121. "byte": 29,
  122. },
  123. "end": map[string]any{
  124. "line": 3,
  125. "column": 13,
  126. "byte": 33,
  127. },
  128. },
  129. },
  130. },
  131. "foobar": map[string]any{
  132. "value": "42",
  133. "trace": map[string]any{
  134. "def": map[string]any{
  135. "environment": "bar",
  136. "begin": map[string]any{
  137. "line": 4,
  138. "column": 9,
  139. "byte": 38,
  140. },
  141. "end": map[string]any{
  142. "line": 4,
  143. "column": 13,
  144. "byte": 42,
  145. },
  146. },
  147. },
  148. },
  149. "bar": map[string]any{
  150. "value": true,
  151. "trace": map[string]any{
  152. "def": map[string]any{
  153. "environment": "bar",
  154. "begin": map[string]any{
  155. "line": 5,
  156. "column": 9,
  157. "byte": 47,
  158. },
  159. "end": map[string]any{
  160. "line": 5,
  161. "column": 13,
  162. "byte": 51,
  163. },
  164. },
  165. },
  166. },
  167. },
  168. want: map[string][]byte{
  169. "foo": []byte("bar"),
  170. "foobar": []byte("42"),
  171. "bar": []byte(`true`),
  172. },
  173. wantErr: false,
  174. },
  175. {
  176. name: "successful case (nested)",
  177. ref: esv1.ExternalSecretDataRemoteRef{
  178. Key: "mysec",
  179. },
  180. input: map[string]any{
  181. "test22": map[string]any{
  182. "value": map[string]any{
  183. "my": map[string]any{
  184. "value": "hello",
  185. "trace": map[string]any{
  186. "def": map[string]any{
  187. "environment": "bar",
  188. "begin": map[string]any{
  189. "line": 6,
  190. "column": 11,
  191. "byte": 72,
  192. },
  193. "end": map[string]any{
  194. "line": 6,
  195. "column": 16,
  196. "byte": 77,
  197. },
  198. },
  199. },
  200. },
  201. },
  202. "trace": map[string]any{
  203. "def": map[string]any{
  204. "environment": "bar",
  205. "begin": map[string]any{
  206. "line": 6,
  207. "column": 7,
  208. "byte": 68,
  209. },
  210. "end": map[string]any{
  211. "line": 6,
  212. "column": 16,
  213. "byte": 77,
  214. },
  215. },
  216. },
  217. },
  218. "test33": map[string]any{
  219. "value": map[string]any{
  220. "world": map[string]any{
  221. "value": "hello",
  222. "trace": map[string]any{
  223. "def": map[string]any{
  224. "environment": "bar",
  225. "begin": map[string]any{
  226. "line": 8,
  227. "column": 14,
  228. "byte": 103,
  229. },
  230. "end": map[string]any{
  231. "line": 8,
  232. "column": 19,
  233. "byte": 108,
  234. },
  235. },
  236. },
  237. },
  238. },
  239. "trace": map[string]any{
  240. "def": map[string]any{
  241. "environment": "bar",
  242. "begin": map[string]any{
  243. "line": 8,
  244. "column": 7,
  245. "byte": 96,
  246. },
  247. "end": map[string]any{
  248. "line": 8,
  249. "column": 19,
  250. "byte": 108,
  251. },
  252. },
  253. },
  254. },
  255. },
  256. want: map[string][]byte{
  257. "test22": []byte(`{"my":{"trace":{"def":{"begin":{"byte":72,"column":11,"line":6},"end":{"byte":77,"column":16,"line":6},"environment":"bar"}},"value":"hello"}}`),
  258. "test33": []byte(`{"world":{"trace":{"def":{"begin":{"byte":103,"column":14,"line":8},"end":{"byte":108,"column":19,"line":8},"environment":"bar"}},"value":"hello"}}`),
  259. },
  260. wantErr: false,
  261. },
  262. {
  263. name: "successful case (basic + nested)",
  264. ref: esv1.ExternalSecretDataRemoteRef{
  265. Key: "mysec",
  266. },
  267. input: map[string]any{
  268. "foo": map[string]any{
  269. "value": "bar",
  270. "trace": map[string]any{
  271. "def": map[string]any{
  272. "environment": "bar",
  273. "begin": map[string]any{
  274. "line": 3,
  275. "column": 9,
  276. "byte": 29,
  277. },
  278. "end": map[string]any{
  279. "line": 3,
  280. "column": 13,
  281. "byte": 33,
  282. },
  283. },
  284. },
  285. },
  286. "test22": map[string]any{
  287. "value": map[string]any{
  288. "my": map[string]any{
  289. "value": "hello",
  290. "trace": map[string]any{
  291. "def": map[string]any{
  292. "environment": "bar",
  293. "begin": map[string]any{
  294. "line": 6,
  295. "column": 11,
  296. "byte": 72,
  297. },
  298. "end": map[string]any{
  299. "line": 6,
  300. "column": 16,
  301. "byte": 77,
  302. },
  303. },
  304. },
  305. },
  306. },
  307. "trace": map[string]any{
  308. "def": map[string]any{
  309. "environment": "bar",
  310. "begin": map[string]any{
  311. "line": 6,
  312. "column": 7,
  313. "byte": 68,
  314. },
  315. "end": map[string]any{
  316. "line": 6,
  317. "column": 16,
  318. "byte": 77,
  319. },
  320. },
  321. },
  322. },
  323. },
  324. want: map[string][]byte{
  325. "foo": []byte("bar"),
  326. "test22": []byte(`{"my":{"trace":{"def":{"begin":{"byte":72,"column":11,"line":6},"end":{"byte":77,"column":16,"line":6},"environment":"bar"}},"value":"hello"}}`),
  327. },
  328. wantErr: false,
  329. },
  330. }
  331. for _, tt := range tests {
  332. t.Run(tt.name, func(t *testing.T) {
  333. p := newTestClient(t, http.MethodGet, "/environments/foo/default/bar/open/session-id", func(w http.ResponseWriter, r *http.Request) {
  334. r.Header.Add(contentType, contentTypeValue)
  335. w.Header().Add(contentType, contentTypeValue)
  336. err2 := json.NewEncoder(w).Encode(esc.NewValue(tt.input, esc.Trace{}))
  337. require.NoError(t, err2)
  338. })
  339. got, err := p.GetSecretMap(context.TODO(), tt.ref)
  340. fmt.Print(got)
  341. if (err != nil) != tt.wantErr {
  342. t.Errorf("ProviderPulumi.GetSecretMap() error = %v, wantErr %v", err, tt.wantErr)
  343. return
  344. }
  345. if !reflect.DeepEqual(got, tt.want) {
  346. t.Errorf("ProviderPulumi.GetSecretMap() get = %v, want %v", got, tt.want)
  347. }
  348. })
  349. }
  350. }
  351. func TestCreateSubmaps(t *testing.T) {
  352. input := map[string]any{
  353. "a.b.c": 1,
  354. "a.b.d": 2,
  355. "a.e": 3,
  356. "f": 4,
  357. }
  358. expected := map[string]any{
  359. "a": map[string]any{
  360. "b": map[string]any{
  361. "c": 1,
  362. "d": 2,
  363. },
  364. "e": 3,
  365. },
  366. "f": 4,
  367. }
  368. result := createSubmaps(input)
  369. if !reflect.DeepEqual(result, expected) {
  370. t.Errorf("createSubmaps() = %v, want %v", result, expected)
  371. }
  372. // Test nested access
  373. a, ok := result["a"].(map[string]any)
  374. if !ok {
  375. t.Errorf("Expected 'a' to be a map")
  376. }
  377. b, ok := a["b"].(map[string]any)
  378. if !ok {
  379. t.Errorf("Expected 'a.b' to be a map")
  380. }
  381. c, ok := b["c"].(int)
  382. if !ok || c != 1 {
  383. t.Errorf("Expected 'a.b.c' to be 1, got %v", b["c"])
  384. }
  385. // Test non-nested key
  386. f, ok := result["f"].(int)
  387. if !ok || f != 4 {
  388. t.Errorf("Expected 'f' to be 4, got %v", result["f"])
  389. }
  390. }