1 package emulatorsvc
2
3 import (
4 "bytes"
5 "context"
6 "encoding/json"
7 "fmt"
8 "io"
9 "net/http"
10 "net/http/httptest"
11 "net/url"
12 "os"
13 "reflect"
14 "regexp"
15 "testing"
16 "time"
17
18 "github.com/stretchr/testify/assert"
19 "golang.org/x/oauth2"
20
21 "edge-infra.dev/pkg/lib/fog"
22 "edge-infra.dev/pkg/sds/emergencyaccess/eaconst"
23 "edge-infra.dev/pkg/sds/emergencyaccess/msgdata"
24 "edge-infra.dev/pkg/sds/emergencyaccess/types"
25 )
26
27 const apiV2 = "/api/v2"
28 const https = "https://"
29
30 var (
31 defaultAccessToken = "accessToken"
32 defaultSessionTarget = types.Target{Projectid: "", Bannerid: "bannerID", Storeid: "storeID", Terminalid: "terminalID"}
33 defaultSessionResolvedTarget = types.Target{
34 Projectid: "project-UUID",
35 Bannerid: "banner-UUID",
36 Storeid: "store-UUID",
37 Terminalid: "terminal-UUID",
38 }
39 testSessionID string
40 defaultResponseDataJSON = []byte(`
41 {
42 "type": "Output",
43 "exitCode": 0,
44 "output": "hello\n",
45 "timestamp": "01-01-2023 00:00:00",
46 "duration": 0.1
47 }`)
48 defaultAttrMap = map[string]string{
49 "bannerId": "banner",
50 "storeId": "store",
51 "terminalId": "terminal",
52 "sessionId": "orderingKey",
53 "identity": "identity",
54 "version": "1.0",
55 "signature": "signature",
56 "request-message-uuid": "test",
57 "commandId": "testID",
58 }
59 )
60
61 type tHelper interface {
62 Helper()
63 }
64
65 func EqualError(message string) assert.ErrorAssertionFunc {
66 return func(t assert.TestingT, err error, i ...interface{}) bool {
67 if tt, ok := t.(tHelper); ok {
68 tt.Helper()
69 }
70 return assert.EqualError(t, err, message, i...)
71 }
72 }
73
74
75 func MatchesError(message string) assert.ErrorAssertionFunc {
76 return func(t assert.TestingT, err error, i ...interface{}) bool {
77 if tt, ok := t.(tHelper); ok {
78 tt.Helper()
79 }
80
81 return assert.Regexp(t, regexp.MustCompile(message), err.Error(), i...)
82 }
83 }
84
85 func TestMain(m *testing.M) {
86
87 dir, err := os.MkdirTemp("", "")
88 if err != nil {
89 panic(err)
90 }
91 os.Setenv(envFilePathDir, dir)
92
93 m.Run()
94
95
96 os.Unsetenv(envFilePathDir)
97 err = os.RemoveAll(dir)
98 if err != nil {
99 panic(err)
100 }
101 }
102
103
104
105
106 func startSessionServer(t *testing.T) *httptest.Server {
107 mux := http.NewServeMux()
108 mux.HandleFunc("/startSession", func(w http.ResponseWriter, r *http.Request) {
109 data, err := io.ReadAll(r.Body)
110 assert.NoError(t, err)
111 var payload types.StartSessionPayload
112 err = json.Unmarshal(data, &payload)
113 assert.NoError(t, err)
114 assert.EqualValues(t, defaultSessionTarget, payload.Target)
115
116 w.Header().Add("X-EA-ProjectID", defaultSessionResolvedTarget.Projectid)
117 w.Header().Add("X-EA-BannerID", defaultSessionResolvedTarget.Bannerid)
118 w.Header().Add("X-EA-StoreID", defaultSessionResolvedTarget.Storeid)
119 w.Header().Add("X-EA-TerminalID", defaultSessionResolvedTarget.Terminalid)
120 })
121 server := httptest.NewServer(mux)
122 return server
123 }
124
125
126 func TestConnect(t *testing.T) {
127 ctx, cancel := context.WithCancel(context.Background())
128 defer cancel()
129
130 server := startSessionServer(t)
131 defer server.Close()
132 t.Setenv("RCLI_GATEWAY_HOST", server.URL)
133 es, err := New(ctx, Config{})
134 assert.NoError(t, err)
135 es.idToken = &oauth2.Token{
136 AccessToken: defaultAccessToken,
137 Expiry: time.Now().Add(24 * time.Hour),
138 }
139 err = es.Connect(ctx, "", "bannerID", "storeID", "terminalID")
140 assert.NoError(t, err)
141 assert.Equal(t, defaultSessionResolvedTarget, es.session.target)
142 }
143
144
145 func connectionFailureServer(status int) *httptest.Server {
146 mux := http.NewServeMux()
147 mux.HandleFunc("/empty/startSession", func(w http.ResponseWriter, _ *http.Request) {
148 w.WriteHeader(status)
149 })
150 mux.HandleFunc("/json/startSession", func(w http.ResponseWriter, _ *http.Request) {
151 w.WriteHeader(status)
152 _, _ = w.Write([]byte(`
153 {
154 "errorCode": 61111,
155 "errorMessage": "Bad things happen",
156 "details": ["lots", "of", "bad", "things"]
157 }
158 `))
159 })
160 server := httptest.NewServer(mux)
161 return server
162 }
163
164 func TestConnectFail(t *testing.T) {
165 cases := map[string]struct {
166 path string
167 errAssert assert.ErrorAssertionFunc
168 }{
169 "Error on non-OK": {
170 path: "/empty/",
171 errAssert: MatchesError(`error calling startSession API \(http://127.0.0.1:.*/empty/startSession\), status \(403 Forbidden\)`),
172 },
173 "APIError response": {
174 path: "/json/",
175 errAssert: EqualError("61111: Bad things happen"),
176 },
177 }
178
179 for name, tc := range cases {
180 t.Run(name, func(t *testing.T) {
181 ctx, cancel := context.WithCancel(context.Background())
182 defer cancel()
183
184 server := connectionFailureServer(403)
185 defer server.Close()
186
187 host := server.URL
188 if tc.path != "" {
189 host += tc.path
190 }
191
192 t.Setenv("RCLI_GATEWAY_HOST", host)
193 es, err := New(ctx, Config{})
194 assert.NoError(t, err)
195
196 es.idToken = &oauth2.Token{
197 AccessToken: defaultAccessToken,
198 }
199 err = es.Connect(ctx, "", "bannerID", "storeID", "terminalID")
200 tc.errAssert(t, err)
201 })
202 }
203 }
204
205
206
207 func TestSend(t *testing.T) {
208 ctx, cancel := context.WithCancel(context.Background())
209 defer cancel()
210 server := sendCommandServer(t)
211 defer server.Close()
212 t.Setenv("RCLI_GATEWAY_HOST", server.URL)
213 es, err := New(ctx, Config{})
214 assert.NoError(t, err)
215 es.idToken = &oauth2.Token{
216 AccessToken: defaultAccessToken,
217 }
218 err = es.Connect(ctx, "", "bannerID", "storeID", "terminalID")
219
220 testSessionID = es.session.ID
221 assert.NoError(t, err)
222 commandID, err := es.Send("test")
223 assert.NoError(t, err)
224
225 assert.Equal(t, "abcd", commandID)
226 }
227
228 func TestDarkmode(t *testing.T) {
229 tests := map[string]struct {
230 darkmode bool
231 }{
232 "Darkmode True": {
233 darkmode: true,
234 },
235 "Darkmode False": {
236 darkmode: false,
237 },
238 }
239 for name, tc := range tests {
240 t.Run(name, func(t *testing.T) {
241 ctx, cancel := context.WithCancel(context.Background())
242 defer cancel()
243
244 server := sendCommandServer(t, assertDarkmode(tc.darkmode))
245 defer server.Close()
246 t.Setenv("RCLI_GATEWAY_HOST", server.URL)
247 es, err := New(ctx, Config{})
248 assert.NoError(t, err)
249 es.idToken = &oauth2.Token{
250 AccessToken: defaultAccessToken,
251 Expiry: time.Now().Add(24 * time.Hour),
252 }
253
254 es.SetDarkmode(tc.darkmode)
255 err = es.Connect(ctx, "", "bannerID", "storeID", "terminalID")
256
257 testSessionID = es.session.ID
258 assert.NoError(t, err)
259 _, err = es.Send("test")
260 assert.NoError(t, err)
261 })
262 }
263 }
264
265
266 func TestEnd(t *testing.T) {
267 ctx, cancel := context.WithCancel(context.Background())
268 defer cancel()
269 server := endSessionServer(t)
270 defer server.Close()
271 t.Setenv("RCLI_GATEWAY_HOST", server.URL)
272 es, err := New(ctx, Config{})
273 es.idToken = &oauth2.Token{
274 AccessToken: defaultAccessToken,
275 }
276 assert.NoError(t, err)
277 err = es.Connect(ctx, "projectID", "bannerID", "storeID", "terminalID")
278 assert.NoError(t, err)
279 testSessionID = es.session.ID
280 err = es.End()
281 assert.NoError(t, err)
282 done := <-es.session.context.Done()
283 assert.NotNil(t, done)
284 }
285
286 func TestSetGatewayURLs(t *testing.T) {
287 t.Parallel()
288
289 ctx, cancel := context.WithCancel(context.Background())
290 defer cancel()
291
292 log := fog.New(fog.To(io.Discard))
293 ctx = fog.IntoContext(ctx, log)
294
295
296 _, ok := os.LookupEnv(envGatewayHost)
297 assert.False(t, ok, "Test Requires %s to not be set", envGatewayHost)
298
299 host := "dev1.edge-preprod.dev"
300
301 tests := map[string]struct {
302 uri string
303 }{
304 "Simple": {
305 uri: https + host + apiV2,
306 },
307 "Trailing Slash": {
308 uri: https + host + "/api/v2/",
309 },
310 "Just host": {
311 uri: https + host,
312 },
313 "Different path": {
314 uri: https + host + "/different/path",
315 },
316 }
317
318 for name, tc := range tests {
319 tc := tc
320 t.Run(name, func(t *testing.T) {
321 t.Parallel()
322
323 es := EmulatorService{
324 config: &Config{
325 Profile: Profile{
326 API: tc.uri,
327 },
328 },
329 }
330
331
332 err := es.setGatewayURLs(ctx)
333 assert.NoError(t, err)
334 expectedValue := gatewayURLs{
335 send: &url.URL{Host: host, Path: "/api/ea/sendCommand", Scheme: "https"},
336 start: &url.URL{Host: host, Path: "/api/ea/startSession", Scheme: "https"},
337 end: &url.URL{Host: host, Path: "/api/ea/endSession", Scheme: "https"},
338 }
339 assert.EqualValues(t, &expectedValue, es.gatewayURLs)
340 })
341 }
342 }
343
344
345 func TestSetGatewayURLsEnvVar(t *testing.T) {
346 tests := map[string]struct {
347 envVar string
348 expVal gatewayURLs
349 }{
350 "Simple": {
351 envVar: "https://testhostURL",
352 expVal: gatewayURLs{
353 start: &url.URL{Host: "testhostURL", Path: "/startSession", Scheme: "https"},
354 end: &url.URL{Host: "testhostURL", Path: "/endSession", Scheme: "https"},
355 send: &url.URL{Host: "testhostURL", Path: "/sendCommand", Scheme: "https"},
356 },
357 },
358 "Simple with port": {
359 envVar: "https://testhostURL:8080",
360 expVal: gatewayURLs{
361 start: &url.URL{Host: "testhostURL:8080", Path: "/startSession", Scheme: "https"},
362 end: &url.URL{Host: "testhostURL:8080", Path: "/endSession", Scheme: "https"},
363 send: &url.URL{Host: "testhostURL:8080", Path: "/sendCommand", Scheme: "https"},
364 },
365 },
366 "With Path": {
367 envVar: "https://testhostURL/abcd/",
368 expVal: gatewayURLs{
369 start: &url.URL{Host: "testhostURL", Path: "/abcd/startSession", Scheme: "https"},
370 end: &url.URL{Host: "testhostURL", Path: "/abcd/endSession", Scheme: "https"},
371 send: &url.URL{Host: "testhostURL", Path: "/abcd/sendCommand", Scheme: "https"},
372 },
373 },
374 "With Port and Path": {
375 envVar: "https://testhostURL:8080/abcd/",
376 expVal: gatewayURLs{
377 start: &url.URL{Host: "testhostURL:8080", Path: "/abcd/startSession", Scheme: "https"},
378 end: &url.URL{Host: "testhostURL:8080", Path: "/abcd/endSession", Scheme: "https"},
379 send: &url.URL{Host: "testhostURL:8080", Path: "/abcd/sendCommand", Scheme: "https"},
380 },
381 },
382
383
384
385
386 "Missing scheme": {
387 envVar: "testhostURL:8080",
388
389
390 expVal: gatewayURLs{
391 start: &url.URL{Host: "", Path: "/startSession", Scheme: "testhosturl"},
392 end: &url.URL{Host: "", Path: "/endSession", Scheme: "testhosturl"},
393 send: &url.URL{Host: "", Path: "/sendCommand", Scheme: "testhosturl"},
394 },
395 },
396 "Just host": {
397 envVar: "testhostURL",
398
399 expVal: gatewayURLs{
400 send: &url.URL{Host: "dev1.edge-preprod.dev", Path: "/api/ea/sendCommand", Scheme: "https"},
401 start: &url.URL{Host: "dev1.edge-preprod.dev", Path: "/api/ea/startSession", Scheme: "https"},
402 end: &url.URL{Host: "dev1.edge-preprod.dev", Path: "/api/ea/endSession", Scheme: "https"},
403 },
404 },
405 "Missing Trailing slash": {
406
407 envVar: "https://testhostURL/abcd",
408 expVal: gatewayURLs{
409 start: &url.URL{Host: "testhostURL", Path: "/startSession", Scheme: "https"},
410 end: &url.URL{Host: "testhostURL", Path: "/endSession", Scheme: "https"},
411 send: &url.URL{Host: "testhostURL", Path: "/sendCommand", Scheme: "https"},
412 },
413 },
414 }
415
416 for name, tc := range tests {
417 tc := tc
418 t.Run(name, func(t *testing.T) {
419 ctx, cancel := context.WithCancel(context.Background())
420 defer cancel()
421
422 t.Setenv(envGatewayHost, tc.envVar)
423
424 es := EmulatorService{config: &Config{Profile: Profile{API: "https://dev1.edge-preprod.dev/api/v2/"}}}
425
426 err := es.setGatewayURLs(ctx)
427 assert.NoError(t, err)
428
429 assert.EqualValues(t, &tc.expVal, es.gatewayURLs)
430 })
431 }
432 }
433
434
435 func TestPostToDisplayChan(t *testing.T) {
436 ctx, cancel := context.WithCancel(context.Background())
437 defer cancel()
438 server := postToDisplayChannelServer(t)
439 defer server.Close()
440 t.Setenv("RCLI_GATEWAY_HOST", server.URL)
441
442 es, err := New(ctx, Config{})
443 assert.NoError(t, err)
444 es.idToken = &oauth2.Token{
445 AccessToken: defaultAccessToken,
446 }
447 assert.NoError(t, err)
448
449 err = es.Connect(ctx, "projectID", "bannerID", "storeID", "terminalID")
450 assert.NoError(t, err)
451 msg := <-es.dispChan
452
453 expectedMsg, err := msgdata.NewCommandResponse(defaultResponseDataJSON, defaultAttrMap)
454 assert.NoError(t, err)
455 assert.EqualValues(t, expectedMsg, msg)
456 }
457
458 func TestGetSessionContext(t *testing.T) {
459 ctx, cancel := context.WithCancel(context.Background())
460 defer cancel()
461 server := startSessionServer(t)
462 defer server.Close()
463 t.Setenv("RCLI_GATEWAY_HOST", server.URL)
464
465 es, err := New(ctx, Config{})
466 assert.NoError(t, err)
467 es.idToken = &oauth2.Token{
468 AccessToken: defaultAccessToken,
469 }
470 assert.NoError(t, err)
471 err = es.Connect(ctx, "", "bannerID", "storeID", "terminalID")
472 assert.NoError(t, err)
473 expectedSessionID := es.session.ID
474 assert.Equal(t, expectedSessionID, es.GetSessionContext().Value(sessionID))
475 }
476
477 func assertDarkmode(val bool) payloadAssertions {
478 if val {
479 return func(t *testing.T, sp types.SendPayload) {
480 assert.True(t, sp.AuthDetails.DarkMode)
481 }
482 }
483 return func(t *testing.T, sp types.SendPayload) {
484 assert.False(t, sp.AuthDetails.DarkMode)
485 }
486 }
487
488 type payloadAssertions func(*testing.T, types.SendPayload)
489
490
491
492
493
494 func sendCommandServer(t *testing.T, asserts ...payloadAssertions) *httptest.Server {
495 mux := http.NewServeMux()
496 mux.HandleFunc("/startSession", func(w http.ResponseWriter, _ *http.Request) {
497 w.Header().Add("X-EA-ProjectID", defaultSessionResolvedTarget.Projectid)
498 w.Header().Add("X-EA-BannerID", defaultSessionResolvedTarget.Bannerid)
499 w.Header().Add("X-EA-StoreID", defaultSessionResolvedTarget.Storeid)
500 w.Header().Add("X-EA-TerminalID", defaultSessionResolvedTarget.Terminalid)
501 })
502 mux.HandleFunc("/sendCommand", func(w http.ResponseWriter, r *http.Request) {
503 data, err := io.ReadAll(r.Body)
504 assert.NoError(t, err)
505 var payload types.SendPayload
506 err = json.Unmarshal(data, &payload)
507 assert.NoError(t, err)
508 assert.EqualValues(t, defaultSessionResolvedTarget, payload.Target)
509 assert.Equal(t, "test", payload.Command)
510 assert.Equal(t, payload.SessionID, testSessionID)
511 for _, assert := range asserts {
512 assert(t, payload)
513 }
514
515 w.Header().Add(`X-Correlation-ID`, "abcd")
516 })
517 server := httptest.NewServer(mux)
518 return server
519 }
520
521
522 func postToDisplayChannelServer(t *testing.T) *httptest.Server {
523
524 mux := http.NewServeMux()
525 mux.HandleFunc("/startSession", func(w http.ResponseWriter, _ *http.Request) {
526 w.(http.Flusher).Flush()
527
528 time.Sleep(1 * time.Second)
529 msg, err := msgdata.NewCommandResponse(defaultResponseDataJSON, defaultAttrMap)
530 assert.NoError(t, err)
531 resp := types.ConnectionPayload{Message: msg}
532 bytes, err := json.Marshal(resp)
533 assert.NoError(t, err)
534 _, err = w.Write(bytes)
535 assert.NoError(t, err)
536 })
537 server := httptest.NewServer(mux)
538 return server
539 }
540
541
542 func endSessionServer(t *testing.T) *httptest.Server {
543 mux := http.NewServeMux()
544 mux.HandleFunc("/startSession", func(_ http.ResponseWriter, _ *http.Request) {
545 })
546 mux.HandleFunc("/sendCommand", func(_ http.ResponseWriter, _ *http.Request) {
547 })
548 mux.HandleFunc("/endSession", func(_ http.ResponseWriter, r *http.Request) {
549 data, err := io.ReadAll(r.Body)
550 assert.NoError(t, err)
551 var payload types.EndSessionPayload
552 err = json.Unmarshal(data, &payload)
553 assert.NoError(t, err)
554 assert.Equal(t, testSessionID, payload.SessionID)
555 })
556 server := httptest.NewServer(mux)
557 return server
558 }
559
560 const (
561 validQueryF = "{\"query\":\"mutation($organization:String!$password:String!$username:String!){login(username: $username, password: $password, organization: $organization){token,banners{bannerEdgeId}}}\",\"variables\":{\"organization\":\"%s\",\"password\":\"%s\",\"username\":\"%s\"}}\n"
562 validResponseF = `{"login": {"token": "%s"}}`
563
564 validToken = "ewogICJhbGciOiAiSFM1MTIiLAogICJ0eXAiOiAiSldUIgp9.ewogICJhdXRoUHJvdmlkZXIiOiAiYnNsIiwKICAiZW1haWwiOiAiYWNjb3VudCIsCiAgIm9yZ2FuaXphdGlvbiI6ICJvcmdhbml6YXRpb24iLAogICJyZWZyZXNoVG9rZW4iOiAiIiwKICAicm9sZXMiOiBbCiAgICAiUk9MRSIKICBdLAogICJ0b2tlbiI6ICJld29nSUNKMGVYQWlPaUFpU2xkVUlpd0tJQ0FpWVd4bklqb2dJa1ZUTWpVMklncDkuZXdvZ0lDSnRkR2dpT2lCYkNpQWdJQ0FpY0dGemMzZHZjbVFpQ2lBZ1hTd0tJQ0FpYzNWaUlqb2dJbUZqWTI5MWJuUWlMQW9nSUNKdVltWWlPaUF4TnpFeE1UQXlOamcyTEFvZ0lDSnZjbWNpT2lBaWIzSm5ZVzVwZW1GMGFXOXVJaXdLSUNBaWFYTnpJam9nSW1semMzVmxjaUlzQ2lBZ0luSnNjeUk2SUNKbFNuaE9hVGhyVG1kRVFVMUNUamt5VG1KU1oydFdWM2RyU25wSmJVeFFMMUZxUVdZMFJGZGhNV1Y2UVdoek5tdENVbGxoU25oT2RHbG9NMDlHUnpKSGNHVk5XVW8zU0RaVVRtNUJkU3QxUTFnd1psWjRVMHcxVEhNeWNtMUdORk15ZFhkTVJqUXlPR3BSTlRWdFEyaE5ZWGs1Y0U0M2JWbElaMEU5SWl3S0lDQWlaWGh3SWpvZ01UY3hNVEV3TXpVNE5pd0tJQ0FpYVdGMElqb2dNVGN4TVRFd01qWTROaXdLSUNBaWFuUnBJam9nSWpNeE1XRXdaamcwTFRsa05USXROREU0TUMxaVpHUXdMV1psWlRoak5UaGpZemRsTUNJS2ZRLlUybG5ibUYwZFhKbE9pQmhkRE5PZWtNM2RXbDNTelE0V1VVNWJTMVBRMUV6VFZGcFRVMVVabDlxUkY4d2EzUmFWaTAxV0ZKSWNHaGpPWFJTVURSSlJGVk1WVWhDUlhSMmVVMDRPRTQwV1RadE1rRTNXVXAzYlY5MVpYQTFWM0JEWnciLAogICJ1c2VybmFtZSI6ICJ1c2VyIgp9.signature"
565 invalidToken = "invalid"
566 )
567
568 type graphQLErr struct {
569 Message string
570 Locations []struct {
571 Line int
572 Column int
573 }
574 }
575
576 func startSessionReadCookieServer(t *testing.T, expected *http.Cookie) *httptest.Server {
577 mux := http.NewServeMux()
578 mux.HandleFunc("/startSession", func(w http.ResponseWriter, r *http.Request) {
579 cookie, err := r.Cookie("edge-session")
580 assert.NoError(t, err)
581 if reflect.DeepEqual(cookie, expected) {
582 w.WriteHeader(http.StatusOK)
583 } else {
584 w.WriteHeader(http.StatusForbidden)
585 }
586 })
587 server := httptest.NewServer(mux)
588 return server
589 }
590
591 func edgeAPIMockServer(t *testing.T, expQuery string, token string) *httptest.Server {
592 type output struct {
593 Data *json.RawMessage `json:"data"`
594 Errors []graphQLErr `json:"errors,omitempty"`
595 }
596
597 generateErr := func(key string) graphQLErr {
598 message := fmt.Sprintf("Field \"login\" argument \"%s\" of type \"String!\" is required but not provided.", key)
599 return graphQLErr{
600 Message: message,
601 Locations: []struct {
602 Line int
603 Column int
604 }{
605 {Line: 2, Column: 3},
606 },
607 }
608 }
609
610 mux := http.NewServeMux()
611 mux.HandleFunc(apiV2, func(w http.ResponseWriter, r *http.Request) {
612 var err error
613
614
615 data, err := io.ReadAll(r.Body)
616 assert.NoError(t, err)
617 assert.Equal(t, expQuery, string(data))
618
619
620 variables := bytes.SplitAfter(bytes.Split(data, []byte(`"variables":`))[1], []byte("}"))[0]
621 var m map[string]string
622 err = json.Unmarshal(variables, &m)
623 assert.NoError(t, err)
624
625
626 var errs []graphQLErr
627 if m["username"] == "" {
628 errs = append(errs, generateErr("username"))
629 }
630 if m["password"] == "" {
631 errs = append(errs, generateErr("password"))
632 }
633 if m["organization"] == "" {
634 errs = append(errs, generateErr("organization"))
635 }
636
637
638 var outputData *json.RawMessage
639 if len(errs) == 0 {
640 d := json.RawMessage(fmt.Sprintf(validResponseF, token))
641 outputData = &d
642 }
643 out := output{
644 Data: outputData,
645 Errors: errs,
646 }
647 res, err := json.Marshal(out)
648 assert.NoError(t, err)
649
650
651 http.SetCookie(w, &http.Cookie{
652 Name: "edge-session",
653 Value: "MTcxMjIyMzUzOHxOd3dBTkVkWFRVOVhOelpDVEV4UVdqTmFXRlpOTkZoRU4xa3pTMUJLVkVJelQwMUJNa2hTUzA1UVRWaFJSbFJPVVZGR1VWRTNRa0U9fIUdYK4-Qw-mlW3KIOInztkT-v8RpJyoTiMNVUsiA9Qx",
654 Path: "/",
655 })
656 w.Header().Add("content-type", "application/json")
657 w.WriteHeader(http.StatusOK)
658 _, err = w.Write(res)
659 assert.NoError(t, err)
660 })
661 server := httptest.NewServer(mux)
662 return server
663 }
664
665 func TestRetrieveIdentityJWT(t *testing.T) {
666 server := edgeAPIMockServer(t, fmt.Sprintf(validQueryF, "c", "b", "a"), validToken)
667 defer server.Close()
668
669 es, err := New(context.Background(), Config{
670 Profile: Profile{
671 Username: "a",
672 Password: "b",
673 Organization: "c",
674 API: server.URL + apiV2,
675 },
676 })
677 assert.NoError(t, err)
678
679 err = es.RetrieveIdentity(context.Background())
680 assert.NoError(t, err)
681 assert.Equal(t, validToken, es.idToken.AccessToken)
682 assert.NotEmpty(t, es.userID)
683 assert.Equal(t, "Bearer", es.idToken.TokenType)
684 assert.NotNil(t, es.idToken.Expiry)
685 }
686
687 func TestProfileCookie(t *testing.T) {
688
689
690
691
692 ctx, cancel := context.WithCancel(context.Background())
693 defer cancel()
694
695 expectedCookie := &http.Cookie{
696 Name: "edge-session",
697 Value: "MTcxMjIyMzUzOHxOd3dBTkVkWFRVOVhOelpDVEV4UVdqTmFXRlpOTkZoRU4xa3pTMUJLVkVJelQwMUJNa2hTUzA1UVRWaFJSbFJPVVZGR1VWRTNRa0U9fIUdYK4-Qw-mlW3KIOInztkT-v8RpJyoTiMNVUsiA9Qx",
698 Path: "",
699 }
700
701 startSeshServer := startSessionReadCookieServer(t, expectedCookie)
702 defer startSeshServer.Close()
703
704 t.Setenv("RCLI_GATEWAY_HOST", startSeshServer.URL)
705 api, err := url.Parse(startSeshServer.URL)
706 assert.NoError(t, err)
707
708 es, err := New(context.Background(), Config{
709 Profile: Profile{
710 Username: "a",
711 Password: "b",
712 Organization: "c",
713 API: api.String(),
714 SessionCookie: expectedCookie.String(),
715 },
716 })
717 assert.NoError(t, err)
718
719
720
721
722 u, err := url.Parse(startSeshServer.URL)
723 assert.NoError(t, err)
724 cookie := es.client.Jar.Cookies(u)[0]
725 assert.Equal(t, expectedCookie, cookie)
726
727
728 err = es.Connect(ctx, "", "bannerID", "storeID", "terminalID")
729 assert.NoError(t, err)
730 }
731
732 func TestRetrieveIdentityCookie(t *testing.T) {
733
734 ctx, cancel := context.WithCancel(context.Background())
735 defer cancel()
736
737 expectedCookie := &http.Cookie{
738 Name: "edge-session",
739 Value: "MTcxMjIyMzUzOHxOd3dBTkVkWFRVOVhOelpDVEV4UVdqTmFXRlpOTkZoRU4xa3pTMUJLVkVJelQwMUJNa2hTUzA1UVRWaFJSbFJPVVZGR1VWRTNRa0U9fIUdYK4-Qw-mlW3KIOInztkT-v8RpJyoTiMNVUsiA9Qx",
740 Path: "",
741 }
742
743 startSeshServer := startSessionReadCookieServer(t, expectedCookie)
744 defer startSeshServer.Close()
745
746 apiServer := edgeAPIMockServer(t, fmt.Sprintf(validQueryF, "c", "b", "a"), validToken)
747 defer apiServer.Close()
748
749 t.Setenv("RCLI_GATEWAY_HOST", startSeshServer.URL)
750 api, err := url.Parse(apiServer.URL + "/api/v2")
751 assert.NoError(t, err)
752
753 es, err := New(context.Background(), Config{
754 Profile: Profile{
755 Username: "a",
756 Password: "b",
757 Organization: "c",
758 API: api.String(),
759 },
760 })
761 assert.NoError(t, err)
762
763
764 err = es.RetrieveIdentity(context.Background())
765 assert.NoError(t, err)
766
767
768 u, err := url.Parse(apiServer.URL)
769 assert.NoError(t, err)
770 cookie := es.client.Jar.Cookies(u)[0]
771 assert.Equal(t, expectedCookie, cookie)
772
773
774 assert.Equal(t, expectedCookie.String(), es.config.Profile.SessionCookie)
775
776
777 err = es.Connect(ctx, "", "bannerID", "storeID", "terminalID")
778 assert.NoError(t, err)
779
780
781 expEnv := []string{
782 fmt.Sprintf("RCLI_API_ENDPOINT=%s/api/v2", apiServer.URL),
783 "RCLI_COOKIE=edge-session=MTcxMjIyMzUzOHxOd3dBTkVkWFRVOVhOelpDVEV4UVdqTmFXRlpOTkZoRU4xa3pTMUJLVkVJelQwMUJNa2hTUzA1UVRWaFJSbFJPVVZGR1VWRTNRa0U9fIUdYK4-Qw-mlW3KIOInztkT-v8RpJyoTiMNVUsiA9Qx",
784 }
785 assert.Equal(t, expEnv, es.Env())
786 }
787
788 func TestRetrieveIdentityInvalid(t *testing.T) {
789 cases := map[string]struct {
790 config Config
791 expQuery string
792 errAssert assert.ErrorAssertionFunc
793 }{
794 "Missing Username Field": {
795 config: Config{
796 Profile: Profile{
797 Password: "b",
798 Organization: "c",
799 },
800 },
801 expQuery: fmt.Sprintf(validQueryF, "c", "b", ""),
802 errAssert: EqualError("error calling Edge API: Field \"login\" argument \"username\" of type \"String!\" is required but not provided."),
803 },
804 "Missing Password Field": {
805 config: Config{
806 Profile: Profile{
807 Username: "a",
808 Organization: "c",
809 },
810 },
811 expQuery: fmt.Sprintf(validQueryF, "c", "", "a"),
812 errAssert: EqualError("error calling Edge API: Field \"login\" argument \"password\" of type \"String!\" is required but not provided."),
813 },
814 "Missing Organization Field": {
815 config: Config{
816 Profile: Profile{
817 Username: "a",
818 Password: "b",
819 },
820 },
821 expQuery: fmt.Sprintf(validQueryF, "", "b", "a"),
822 errAssert: EqualError("error calling Edge API: Field \"login\" argument \"organization\" of type \"String!\" is required but not provided."),
823 },
824 "Missing All Fields": {
825 config: Config{},
826 expQuery: fmt.Sprintf(validQueryF, "", "", ""),
827 errAssert: EqualError("error calling Edge API: Field \"login\" argument \"username\" of type \"String!\" is required but not provided."),
828 },
829 }
830
831 for name, tc := range cases {
832 t.Run(name, func(t *testing.T) {
833 server := edgeAPIMockServer(t, tc.expQuery, invalidToken)
834 defer server.Close()
835
836 tc.config.Profile.API = server.URL + apiV2
837 es, err := New(context.Background(), tc.config)
838 assert.NoError(t, err)
839
840 err = es.RetrieveIdentity(context.Background())
841 tc.errAssert(t, err)
842 assert.Nil(t, es.idToken)
843 })
844 }
845 }
846
847 func checkVersionServer(t *testing.T, expectedVer string) *httptest.Server {
848 mux := http.NewServeMux()
849 mux.HandleFunc("/", func(_ http.ResponseWriter, r *http.Request) {
850 ver := r.Header.Get(eaconst.APIVersionKey)
851 assert.Equal(t, expectedVer, ver)
852 })
853 server := httptest.NewServer(mux)
854 return server
855 }
856
857 func TestVersionHeader(t *testing.T) {
858 ver := eaconst.APIVersion
859
860 es, err := New(context.Background(), Config{})
861 assert.NoError(t, err)
862
863 server := checkVersionServer(t, ver)
864 defer server.Close()
865
866 req, err := http.NewRequest(http.MethodGet, server.URL, nil)
867 assert.NoError(t, err)
868 _, err = es.client.Do(req)
869 assert.NoError(t, err)
870 }
871
View as plain text