auth
auth/oauth2_test.go
More tests, login redirect bugfix. Add tests for our cookie checking helper and our helper for generating login redirection URIs. Fix a bug where the URL to redirect to was being URL-encoded twice when included in the login redirect URI.
1 package auth
3 import (
4 "bytes"
5 "html/template"
6 "io/ioutil"
7 "net/http"
8 "net/http/httptest"
9 "net/url"
10 "testing"
11 "time"
13 "code.secondbit.org/uuid"
14 )
16 const (
17 scopeSet = 1 << iota
18 stateSet
19 uriSet
20 )
22 func stripParam(param string, u *url.URL) {
23 q := u.Query()
24 q.Del(param)
25 u.RawQuery = q.Encode()
26 }
28 func TestGetGrantCodeSuccess(t *testing.T) {
29 t.Parallel()
30 store := NewMemstore()
31 testContext := Context{
32 template: template.Must(template.New(getGrantTemplateName).Parse("Get auth grant")),
33 clients: store,
34 grants: store,
35 profiles: store,
36 tokens: store,
37 sessions: store,
38 }
39 client := Client{
40 ID: uuid.NewID(),
41 Secret: "super secret!",
42 OwnerID: uuid.NewID(),
43 Name: "My test client",
44 Logo: "https://secondbit.org/logo.png",
45 Website: "https://secondbit.org",
46 Type: "public",
47 }
48 uri, err := url.Parse("https://test.secondbit.org/redirect")
49 if err != nil {
50 t.Fatal("Can't parse URL:", err)
51 }
52 endpoint := Endpoint{
53 ID: uuid.NewID(),
54 ClientID: client.ID,
55 URI: *uri,
56 Added: time.Now(),
57 }
58 err = testContext.SaveClient(client)
59 if err != nil {
60 t.Fatal("Can't store client:", err)
61 }
62 err = testContext.AddEndpoint(client.ID, endpoint)
63 if err != nil {
64 t.Fatal("Can't store endpoint:", err)
65 }
66 session := Session{
67 ID: "testsession",
68 Active: true,
69 }
70 err = testContext.CreateSession(session)
71 if err != nil {
72 t.Fatal("Can't store session:", err)
73 }
74 req, err := http.NewRequest("GET", "https://test.auth.secondbit.org/oauth2/grant", nil)
75 if err != nil {
76 t.Fatal("Can't build request:", err)
77 }
78 cookie := &http.Cookie{
79 Name: authCookieName,
80 Value: session.ID,
81 }
82 req.AddCookie(cookie)
83 for i := 0; i < 1<<3; i++ {
84 w := httptest.NewRecorder()
85 params := url.Values{}
86 // see OAuth 2.0 spec, section 4.1.1
87 params.Set("response_type", "code")
88 params.Set("client_id", client.ID.String())
89 if i&uriSet != 0 {
90 params.Set("redirect_uri", endpoint.URI.String())
91 }
92 if i&scopeSet != 0 {
93 params.Set("scope", "testscope")
94 }
95 if i&stateSet != 0 {
96 params.Set("state", "my super secure state string")
97 }
98 req.URL.RawQuery = params.Encode()
99 req.Method = "GET"
100 req.Body = nil
101 req.Header.Del("Content-Type")
102 GetGrantHandler(w, req, testContext)
103 if w.Code != http.StatusOK {
104 t.Errorf("Expected status code to be %d, got %d for %s", http.StatusOK, w.Code, req.URL.String())
105 }
106 if w.Body.String() != "Get auth grant" {
107 t.Errorf("Expected body to be `%s`, got `%s` for %s", "Get auth grant", w.Body.String(), req.URL.String())
108 }
109 req.Method = "POST"
110 req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
111 w = httptest.NewRecorder()
112 data := url.Values{}
113 data.Set("grant", "approved")
114 body := bytes.NewBufferString(data.Encode())
115 req.Body = ioutil.NopCloser(body)
116 GetGrantHandler(w, req, testContext)
117 if w.Code != http.StatusFound {
118 t.Errorf("Expected status code to be %d, got %d for %s", http.StatusFound, w.Code, req.URL.String())
119 }
120 redirectedTo := w.Header().Get("Location")
121 red, err := url.Parse(redirectedTo)
122 if err != nil {
123 t.Fatalf(`Being redirected to a non-URL "%s" threw error "%s" for "%s"\n`, redirectedTo, err, req.URL.String())
124 }
125 t.Log("Redirected to", redirectedTo)
126 if red.Query().Get("code") == "" {
127 t.Fatalf(`Expected code param in redirect URL to be set, but it wasn't for %s`, req.URL.String())
128 }
129 if _, err := testContext.GetGrant(red.Query().Get("code")); err != nil {
130 t.Fatalf(`Unexpected error "%s: retrieving the grant "%s" supplied in the redirect URL for %s`, err, red.Query().Get("code"), req.URL.String())
131 }
132 err = testContext.DeleteGrant(red.Query().Get("code"))
133 if err != nil {
134 t.Log(`Unexpected error "%s" deleting grant "%s" for %s`, err, red.Query().Get("code"), req.URL.String())
135 }
136 stripParam("code", red)
137 if red.Query().Get("state") != params.Get("state") {
138 t.Errorf(`Expected state param in redirect URL to be "%s", got "%s" for %s`, params.Get("state"), red.Query().Get("state"), req.URL.String())
139 }
140 stripParam("state", red)
141 if red.String() != endpoint.URI.String() {
142 t.Errorf(`Expected redirect URL to be "%s", got "%s"`, endpoint.URI.String(), red.String())
143 }
144 }
145 }
147 func TestGetGrantCodeInvalidClient(t *testing.T) {
148 t.Parallel()
149 store := NewMemstore()
150 testContext := Context{
151 template: template.Must(template.New(getGrantTemplateName).Parse("{{ .error }}")),
152 clients: store,
153 grants: store,
154 profiles: store,
155 tokens: store,
156 sessions: store,
157 }
158 client := Client{
159 ID: uuid.NewID(),
160 Secret: "super secret!",
161 OwnerID: uuid.NewID(),
162 Name: "My test client",
163 Type: "public",
164 }
165 err := testContext.SaveClient(client)
166 if err != nil {
167 t.Fatal("Can't store client:", err)
168 }
169 session := Session{
170 ID: "testsession",
171 Active: true,
172 }
173 err = testContext.CreateSession(session)
174 if err != nil {
175 t.Fatal("Can't store session:", err)
176 }
177 req, err := http.NewRequest("GET", "https://test.auth.secondbit.org/oauth2/grant", nil)
178 if err != nil {
179 t.Fatal("Can't build request:", err)
180 }
181 w := httptest.NewRecorder()
182 params := url.Values{}
183 params.Set("response_type", "code")
184 params.Set("redirect_uri", "https://test.secondbit.org/")
185 req.URL.RawQuery = params.Encode()
186 cookie := &http.Cookie{
187 Name: authCookieName,
188 Value: session.ID,
189 }
190 req.AddCookie(cookie)
191 GetGrantHandler(w, req, testContext)
192 if w.Code != http.StatusBadRequest {
193 t.Errorf("Expected status code to be %d, got %d", http.StatusBadRequest, w.Code)
194 }
195 if w.Body.String() != "Client ID must be specified in the request." {
196 t.Errorf(`Expected output to be "%s", got "%s" instead.`, "Client ID must be specified in the request.", w.Body.String())
197 }
198 w = httptest.NewRecorder()
199 params.Set("client_id", "Not an ID")
200 req.URL.RawQuery = params.Encode()
201 GetGrantHandler(w, req, testContext)
202 if w.Code != http.StatusBadRequest {
203 t.Errorf("Expected status code to be %d, got %d", http.StatusBadRequest, w.Code)
204 }
205 if w.Body.String() != "client_id is not a valid Client ID." {
206 t.Errorf(`Expected output to be "%s", got "%s" instead.`, "client_id is not a valid Client ID.", w.Body.String())
207 }
208 w = httptest.NewRecorder()
209 params.Set("client_id", uuid.NewID().String())
210 req.URL.RawQuery = params.Encode()
211 GetGrantHandler(w, req, testContext)
212 if w.Code != http.StatusBadRequest {
213 t.Errorf("Expected status code to be %d, got %d", http.StatusBadRequest, w.Code)
214 }
215 if w.Body.String() != "The specified Client couldn’t be found." {
216 t.Errorf(`Expected output to be "%s", got "%s" instead.`, "The specified Client couldn’t be found.", w.Body.String())
217 }
218 }
220 func TestGetGrantCodeInvalidURI(t *testing.T) {
221 t.Parallel()
222 store := NewMemstore()
223 testContext := Context{
224 template: template.Must(template.New(getGrantTemplateName).Parse("{{ .error }}")),
225 clients: store,
226 grants: store,
227 profiles: store,
228 tokens: store,
229 sessions: store,
230 }
231 client := Client{
232 ID: uuid.NewID(),
233 Secret: "super secret!",
234 OwnerID: uuid.NewID(),
235 Name: "My test client",
236 Type: "public",
237 }
238 uri, err := url.Parse("https://test.secondbit.org/redirect")
239 if err != nil {
240 t.Fatal("Can't parse URL:", err)
241 }
242 err = testContext.SaveClient(client)
243 if err != nil {
244 t.Fatal("Can't store client:", err)
245 }
246 session := Session{
247 ID: "testsession",
248 Active: true,
249 }
250 err = testContext.CreateSession(session)
251 if err != nil {
252 t.Fatal("Can't store session:", err)
253 }
254 req, err := http.NewRequest("GET", "https://test.auth.secondbit.org/oauth2/grant", nil)
255 if err != nil {
256 t.Fatal("Can't build request:", err)
257 }
258 cookie := &http.Cookie{
259 Name: authCookieName,
260 Value: session.ID,
261 }
262 req.AddCookie(cookie)
263 w := httptest.NewRecorder()
264 params := url.Values{}
265 params.Set("response_type", "code")
266 params.Set("client_id", client.ID.String())
267 req.URL.RawQuery = params.Encode()
268 GetGrantHandler(w, req, testContext)
269 if w.Code != http.StatusBadRequest {
270 t.Errorf("Expected status code to be %d, got %d", http.StatusBadRequest, w.Code)
271 }
272 if w.Body.String() != "The redirect_uri specified is not valid." {
273 t.Errorf(`Expected output to be "%s", got "%s" instead.`, "The redirect_uri specified is not valid.", w.Body.String())
274 }
275 endpoint := Endpoint{
276 ID: uuid.NewID(),
277 ClientID: client.ID,
278 URI: *uri,
279 Added: time.Now(),
280 }
281 err = testContext.AddEndpoint(client.ID, endpoint)
282 if err != nil {
283 t.Fatal("Can't store endpoint:", err)
284 }
285 w = httptest.NewRecorder()
286 params.Set("redirect_uri", "https://test.secondbit.org/wrong")
287 req.URL.RawQuery = params.Encode()
288 GetGrantHandler(w, req, testContext)
289 if w.Code != http.StatusBadRequest {
290 t.Errorf("Expected status code to be %d, got %d", http.StatusBadRequest, w.Code)
291 }
292 if w.Body.String() != "The redirect_uri specified is not valid." {
293 t.Errorf(`Expected output to be "%s", got "%s" instead.`, "The redirect_uri specified is not valid.", w.Body.String())
294 }
295 endpoint2 := Endpoint{
296 ID: uuid.NewID(),
297 ClientID: client.ID,
298 URI: *uri,
299 Added: time.Now(),
300 }
301 err = testContext.AddEndpoint(client.ID, endpoint2)
302 if err != nil {
303 t.Fatal("Can't store endpoint:", err)
304 }
305 w = httptest.NewRecorder()
306 params.Set("redirect_uri", "")
307 req.URL.RawQuery = params.Encode()
308 GetGrantHandler(w, req, testContext)
309 if w.Code != http.StatusBadRequest {
310 t.Errorf("Expected status code to be %d, got %d", http.StatusBadRequest, w.Code)
311 }
312 if w.Body.String() != "The redirect_uri specified is not valid." {
313 t.Errorf(`Expected output to be "%s", got "%s" instead.`, "The redirect_uri specified is not valid.", w.Body.String())
314 }
315 w = httptest.NewRecorder()
316 params.Set("redirect_uri", "://not a URL")
317 req.URL.RawQuery = params.Encode()
318 GetGrantHandler(w, req, testContext)
319 if w.Code != http.StatusBadRequest {
320 t.Errorf("Expected status code to be %d, got %d", http.StatusBadRequest, w.Code)
321 }
322 if w.Body.String() != "The redirect_uri specified is not valid." {
323 t.Errorf(`Expected output to be "%s", got "%s" instead.`, "The redirect_uri specified is not valid.", w.Body.String())
324 }
325 }
327 func TestGetGrantCodeInvalidResponseType(t *testing.T) {
328 t.Parallel()
329 store := NewMemstore()
330 testContext := Context{
331 template: template.Must(template.New(getGrantTemplateName).Parse("{{ .error }}")),
332 clients: store,
333 grants: store,
334 profiles: store,
335 tokens: store,
336 sessions: store,
337 }
338 client := Client{
339 ID: uuid.NewID(),
340 Secret: "super secret!",
341 OwnerID: uuid.NewID(),
342 Name: "My test client",
343 Logo: "https://secondbit.org/logo.png",
344 Website: "https://secondbit.org",
345 Type: "public",
346 }
347 uri, err := url.Parse("https://test.secondbit.org/redirect")
348 if err != nil {
349 t.Fatal("Can't parse URL:", err)
350 }
351 endpoint := Endpoint{
352 ID: uuid.NewID(),
353 ClientID: client.ID,
354 URI: *uri,
355 Added: time.Now(),
356 }
357 err = testContext.SaveClient(client)
358 if err != nil {
359 t.Fatal("Can't store client:", err)
360 }
361 err = testContext.AddEndpoint(client.ID, endpoint)
362 if err != nil {
363 t.Fatal("Can't store endpoint:", err)
364 }
365 session := Session{
366 ID: "testsession",
367 Active: true,
368 }
369 err = testContext.CreateSession(session)
370 if err != nil {
371 t.Fatal("Can't store session:", err)
372 }
373 req, err := http.NewRequest("GET", "https://test.auth.secondbit.org/oauth2/grant", nil)
374 if err != nil {
375 t.Fatal("Can't build request:", err)
376 }
377 cookie := &http.Cookie{
378 Name: authCookieName,
379 Value: session.ID,
380 }
381 req.AddCookie(cookie)
382 params := url.Values{}
383 params.Set("response_type", "totally not code")
384 params.Set("client_id", client.ID.String())
385 params.Set("redirect_uri", endpoint.URI.String())
386 params.Set("scope", "testscope")
387 params.Set("state", "my super secure state string")
388 req.URL.RawQuery = params.Encode()
389 w := httptest.NewRecorder()
390 GetGrantHandler(w, req, testContext)
391 if w.Code != http.StatusFound {
392 t.Errorf("Expected status code to be %d, got %d", http.StatusFound, w.Code)
393 }
394 redirectedTo := w.Header().Get("Location")
395 red, err := url.Parse(redirectedTo)
396 if err != nil {
397 t.Fatalf("Being redirected to a non-URL (%s) threw error: %s\n", redirectedTo, err)
398 }
399 if red.Query().Get("error") != "invalid_request" {
400 t.Errorf(`Expected error param in redirect URL to be "%s", got "%s"`, "invalid_request", red.Query().Get("error"))
401 }
402 stripParam("error", red)
403 if red.Query().Get("state") != params.Get("state") {
404 t.Errorf(`Expected state param in redirect URL to be "%s", got "%s"`, params.Get("state"), red.Query().Get("state"))
405 }
406 stripParam("state", red)
407 if red.String() != endpoint.URI.String() {
408 t.Errorf(`Expected redirect URL to be "%s", got "%s"`, endpoint.URI.String(), red.String())
409 }
410 stripParam("response_type", req.URL)
411 w = httptest.NewRecorder()
412 GetGrantHandler(w, req, testContext)
413 if w.Code != http.StatusFound {
414 t.Errorf("Expected status code to be %d, got %d", http.StatusFound, w.Code)
415 }
416 redirectedTo = w.Header().Get("Location")
417 red, err = url.Parse(redirectedTo)
418 if err != nil {
419 t.Fatalf("Being redirected to a non-URL (%s) threw error: %s\n", redirectedTo, err)
420 }
421 if red.Query().Get("error") != "invalid_request" {
422 t.Errorf(`Expected error param in redirect URL to be "%s", got "%s"`, "invalid_request", red.Query().Get("error"))
423 }
424 stripParam("error", red)
425 if red.Query().Get("state") != params.Get("state") {
426 t.Errorf(`Expected state param in redirect URL to be "%s", got "%s"`, params.Get("state"), red.Query().Get("state"))
427 }
428 stripParam("state", red)
429 if red.String() != endpoint.URI.String() {
430 t.Errorf(`Expected redirect URL to be "%s", got "%s"`, endpoint.URI.String(), red.String())
431 }
432 }
434 func TestGetGrantCodeDenied(t *testing.T) {
435 t.Parallel()
436 store := NewMemstore()
437 testContext := Context{
438 template: template.Must(template.New(getGrantTemplateName).Parse("{{ .error }}")),
439 clients: store,
440 grants: store,
441 profiles: store,
442 tokens: store,
443 sessions: store,
444 }
445 client := Client{
446 ID: uuid.NewID(),
447 Secret: "super secret!",
448 OwnerID: uuid.NewID(),
449 Name: "My test client",
450 Logo: "https://secondbit.org/logo.png",
451 Website: "https://secondbit.org",
452 Type: "public",
453 }
454 uri, err := url.Parse("https://test.secondbit.org/redirect")
455 if err != nil {
456 t.Fatal("Can't parse URL:", err)
457 }
458 endpoint := Endpoint{
459 ID: uuid.NewID(),
460 ClientID: client.ID,
461 URI: *uri,
462 Added: time.Now(),
463 }
464 err = testContext.SaveClient(client)
465 if err != nil {
466 t.Fatal("Can't store client:", err)
467 }
468 err = testContext.AddEndpoint(client.ID, endpoint)
469 if err != nil {
470 t.Fatal("Can't store endpoint:", err)
471 }
472 session := Session{
473 ID: "testsession",
474 Active: true,
475 }
476 err = testContext.CreateSession(session)
477 if err != nil {
478 t.Fatal("Can't store session:", err)
479 }
480 req, err := http.NewRequest("GET", "https://test.auth.secondbit.org/oauth2/grant", nil)
481 if err != nil {
482 t.Fatal("Can't build request:", err)
483 }
484 cookie := &http.Cookie{
485 Name: authCookieName,
486 Value: session.ID,
487 }
488 req.AddCookie(cookie)
489 params := url.Values{}
490 params.Set("response_type", "code")
491 params.Set("client_id", client.ID.String())
492 params.Set("redirect_uri", endpoint.URI.String())
493 params.Set("scope", "testscope")
494 params.Set("state", "my super secure state string")
495 data := url.Values{}
496 data.Set("grant", "denied")
497 req.URL.RawQuery = params.Encode()
498 req.Body = ioutil.NopCloser(bytes.NewBufferString(data.Encode()))
499 req.Method = "POST"
500 w := httptest.NewRecorder()
501 GetGrantHandler(w, req, testContext)
502 if w.Code != http.StatusFound {
503 t.Errorf("Expected status code to be %d, got %d", http.StatusFound, w.Code)
504 }
505 redirectedTo := w.Header().Get("Location")
506 red, err := url.Parse(redirectedTo)
507 if err != nil {
508 t.Fatalf("Being redirected to a non-URL (%s) threw error: %s\n", redirectedTo, err)
509 }
510 if red.Query().Get("error") != "access_denied" {
511 t.Errorf(`Expected error param in redirect URL to be "%s", got "%s"`, "access_denied", red.Query().Get("error"))
512 }
513 stripParam("error", red)
514 if red.Query().Get("state") != params.Get("state") {
515 t.Errorf(`Expected state param in redirect URL to be "%s", got "%s"`, params.Get("state"), red.Query().Get("state"))
516 }
517 stripParam("state", red)
518 if red.String() != endpoint.URI.String() {
519 t.Errorf(`Expected redirect URL to be "%s", got "%s"`, endpoint.URI.String(), red.String())
520 }
521 }
523 func TestGetBasicAuth(t *testing.T) {
524 tests := map[string]struct {
525 un string
526 pass string
527 err error
528 }{
529 "Basic dGVzdHVzZXI6cGFzc3dvcmQx": {"testuser", "password1", nil},
530 "": {"", "", ErrNoAuth},
531 "dGVzdHVzZXI6cGFzc3dvcmQx": {"", "", ErrInvalidAuthFormat},
532 "Basic _*&^##$@#$@&!!@": {"", "", ErrInvalidAuthFormat},
533 "Basic abcdefgh": {"", "", ErrInvalidAuthFormat},
534 "Basic dXNlcjo=": {"user", "", nil},
535 }
537 for header, test := range tests {
538 req, err := http.NewRequest("GET", "https://auth.secondbit.org", nil)
539 if err != nil {
540 t.Error("Unexpected error creating base request:", err)
541 }
542 req.Header.Set("Authorization", header)
543 un, pass, err := getBasicAuth(req)
544 if un != test.un {
545 t.Errorf(`Expected header "%s" to return username "%s", got "%s"`, header, test.un, un)
546 }
547 if pass != test.pass {
548 t.Errorf(`Expected header "%s" to return password "%s", got "%s"`, header, test.pass, pass)
549 }
550 if err != test.err {
551 t.Errorf(`Expected header "%s" to return error "%s", got "%s"`, header, test.err, err)
552 }
553 }
554 }
556 func TestCheckCookie(t *testing.T) {
557 t.Parallel()
558 req, err := http.NewRequest("GET", "https://auth.secondbit.org", nil)
559 if err != nil {
560 t.Error("Unexpected error creating base request:", err)
561 }
562 store := NewMemstore()
563 testContext := Context{
564 sessions: store,
565 }
566 session, err := checkCookie(req, testContext)
567 if err != ErrNoSession {
568 t.Errorf("Expected ErrNoSession, got %s", err)
569 }
570 session = Session{
571 ID: "testsession",
572 Active: true,
573 }
574 err = testContext.CreateSession(session)
575 if err != nil {
576 t.Error("Unexpected error persisting session:", err)
577 }
578 invalidSession := Session{
579 ID: "testsession2",
580 Active: false,
581 }
582 err = testContext.CreateSession(invalidSession)
583 if err != nil {
584 t.Error("Unexpected error persisting session:", err)
585 }
586 result, err := checkCookie(req, testContext)
587 if err != ErrNoSession {
588 t.Errorf("Expected ErrNoSession, got %s", err)
589 }
590 req.AddCookie(&http.Cookie{
591 Name: "wrongcookie",
592 Value: "wrong value",
593 })
594 result, err = checkCookie(req, testContext)
595 if err != ErrNoSession {
596 t.Error("Expected ErrNoSession, got", err)
597 }
598 req, err = http.NewRequest("GET", "https://auth.secondbit.org", nil)
599 if err != nil {
600 t.Error("Unexpected error creating base request:", err)
601 }
602 req.AddCookie(&http.Cookie{
603 Name: "Stillwrongcookie",
604 Value: session.ID,
605 })
606 result, err = checkCookie(req, testContext)
607 if err != ErrNoSession {
608 t.Error("Expected ErrNoSession, got", err)
609 }
610 req, err = http.NewRequest("GET", "https://auth.secondbit.org", nil)
611 if err != nil {
612 t.Error("Unexpected error creating base request:", err)
613 }
614 req.AddCookie(&http.Cookie{
615 Name: authCookieName,
616 Value: "wrong value",
617 })
618 result, err = checkCookie(req, testContext)
619 if err != ErrInvalidSession {
620 t.Error("Expected ErrInvalidSession, got", err)
621 }
622 req, err = http.NewRequest("GET", "https://auth.secondbit.org", nil)
623 if err != nil {
624 t.Error("Unexpected error creating base request:", err)
625 }
626 req.AddCookie(&http.Cookie{
627 Name: authCookieName,
628 Value: invalidSession.ID,
629 })
630 result, err = checkCookie(req, testContext)
631 if err != ErrInvalidSession {
632 t.Error("Expected ErrInvalidSession, got", err)
633 }
634 req, err = http.NewRequest("GET", "https://auth.secondbit.org", nil)
635 if err != nil {
636 t.Error("Unexpected error creating base request:", err)
637 }
638 req.AddCookie(&http.Cookie{
639 Name: authCookieName,
640 Value: session.ID,
641 })
642 result, err = checkCookie(req, testContext)
643 if err != nil {
644 t.Error("Unexpected error:", err)
645 }
646 success, field, expectation, outcome := compareSessions(session, result)
647 if !success {
648 t.Errorf(`Expected field %s to be %v, but got %v`, field, expectation, outcome)
649 }
650 }
652 func TestBuildLoginRedirect(t *testing.T) {
653 t.Parallel()
654 req, err := http.NewRequest("GET", "https://client.secondbit.org/my/awesome/path?has=query¶ms=to&screw=this&all=up", nil)
655 if err != nil {
656 t.Error("Unexpected error creating base request:", err)
657 }
658 result := buildLoginRedirect(req, Context{})
659 if result != "" {
660 t.Error("Expected empty string as the result, got", result)
661 }
662 uri, err := url.Parse("https://auth.secondbit.org/login?query=string&other=param")
663 if err != nil {
664 t.Error("Unexpected error parsing URL:", err)
665 }
666 c := Context{loginURI: uri}
667 result = buildLoginRedirect(req, c)
668 expectation := "https://auth.secondbit.org/login?from=https%3A%2F%2Fclient.secondbit.org%2Fmy%2Fawesome%2Fpath%3Fhas%3Dquery%26params%3Dto%26screw%3Dthis%26all%3Dup&other=param&query=string"
669 if result != expectation {
670 t.Errorf(`Expected result string to be "%s", was "%s"`, expectation, result)
671 }
672 }