pulumi_test.go 10 KB

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