pulumi_test.go 10 KB

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