github.com/decred/politeia@v1.4.0/politeiawww/legacy/wwwuser_test.go (about) 1 // Copyright (c) 2017-2020 The Decred developers 2 // Use of this source code is governed by an ISC 3 // license that can be found in the LICENSE file. 4 5 package legacy 6 7 import ( 8 "bytes" 9 "encoding/hex" 10 "encoding/json" 11 "io" 12 "net/http" 13 "net/http/httptest" 14 "testing" 15 "time" 16 17 "github.com/davecgh/go-spew/spew" 18 "github.com/decred/politeia/politeiad/api/v1/identity" 19 www "github.com/decred/politeia/politeiawww/api/www/v1" 20 "github.com/go-test/deep" 21 "github.com/gorilla/mux" 22 "github.com/gorilla/sessions" 23 ) 24 25 func TestHandleNewUser(t *testing.T) { 26 p, cleanup := newTestPoliteiawww(t) 27 defer cleanup() 28 29 id, err := identity.New() 30 if err != nil { 31 t.Fatalf("%v", err) 32 } 33 34 // Setup tests 35 var tests = []struct { 36 name string 37 reqBody interface{} 38 wantStatus int 39 wantError error 40 }{ 41 { 42 "invalid request body", 43 "", 44 http.StatusBadRequest, 45 www.UserError{ 46 ErrorCode: www.ErrorStatusInvalidInput, 47 }, 48 }, 49 { 50 "processNewUser error", 51 www.NewUser{}, 52 http.StatusBadRequest, 53 www.UserError{ 54 ErrorCode: www.ErrorStatusMalformedEmail, 55 }, 56 }, 57 { 58 "success", 59 www.NewUser{ 60 Email: "user@example.com", 61 Password: "password", 62 PublicKey: hex.EncodeToString(id.Public.Key[:]), 63 Username: "user", 64 }, 65 http.StatusOK, 66 nil, 67 }, 68 } 69 70 // Run tests 71 for _, v := range tests { 72 t.Run(v.name, func(t *testing.T) { 73 // Setup request 74 r := newPostReq(t, www.RouteNewUser, v.reqBody) 75 w := httptest.NewRecorder() 76 77 // Run test case 78 p.handleNewUser(w, r) 79 res := w.Result() 80 body, _ := io.ReadAll(res.Body) 81 res.Body.Close() 82 83 // Validate response 84 if res.StatusCode != v.wantStatus { 85 t.Errorf("got status code %v, want %v", 86 res.StatusCode, v.wantStatus) 87 } 88 89 if res.StatusCode == http.StatusOK { 90 // Test case passes; next case 91 return 92 } 93 94 var ue www.UserError 95 err = json.Unmarshal(body, &ue) 96 if err != nil { 97 t.Errorf("unmarshal UserError: %v", err) 98 } 99 100 got := errToStr(ue) 101 want := errToStr(v.wantError) 102 if got != want { 103 t.Errorf("got error %v, want %v", got, want) 104 } 105 }) 106 } 107 } 108 109 func TestHandleVerifyNewUser(t *testing.T) { 110 p, cleanup := newTestPoliteiawww(t) 111 defer cleanup() 112 113 // Create an unverified user to test against 114 usr, id := newUser(t, p, false, false) 115 token := hex.EncodeToString(usr.NewUserVerificationToken) 116 s := id.SignMessage([]byte(token)) 117 sig := hex.EncodeToString(s[:]) 118 119 // Test invalid query param. We have to run it individually 120 // so that we can set the wrong query param. 121 t.Run("invalid query params", func(t *testing.T) { 122 // Setup request 123 r := httptest.NewRequest(http.MethodGet, www.RouteVerifyNewUser, nil) 124 w := httptest.NewRecorder() 125 126 q := r.URL.Query() 127 q.Add("hello", "world") 128 r.URL.RawQuery = q.Encode() 129 130 // Run test case 131 p.handleVerifyNewUser(w, r) 132 res := w.Result() 133 body, _ := io.ReadAll(res.Body) 134 res.Body.Close() 135 136 // Validate response 137 if res.StatusCode != http.StatusBadRequest { 138 t.Errorf("got status code %v, want %v", 139 res.StatusCode, http.StatusBadRequest) 140 } 141 142 var ue www.UserError 143 err := json.Unmarshal(body, &ue) 144 if err != nil { 145 t.Errorf("unmarshal UserError: %v", err) 146 } 147 148 got := errToStr(ue) 149 want := www.ErrorStatus[www.ErrorStatusInvalidInput] 150 if got != want { 151 t.Errorf("got error %v, want %v", got, want) 152 } 153 }) 154 155 // Setup remaining tests 156 var tests = []struct { 157 name string 158 params www.VerifyNewUser 159 wantStatus int 160 wantError error 161 }{ 162 { 163 "processVerifyNewUser error", 164 www.VerifyNewUser{}, http.StatusBadRequest, 165 www.UserError{ 166 ErrorCode: www.ErrorStatusVerificationTokenInvalid, 167 }, 168 }, 169 { 170 "success", 171 www.VerifyNewUser{ 172 Email: usr.Email, 173 VerificationToken: token, 174 Signature: sig, 175 }, 176 http.StatusOK, 177 nil, 178 }, 179 } 180 181 // Run tests 182 for _, v := range tests { 183 t.Run(v.name, func(t *testing.T) { 184 // Setup request 185 r := httptest.NewRequest(http.MethodGet, www.RouteVerifyNewUser, nil) 186 w := httptest.NewRecorder() 187 188 q := r.URL.Query() 189 q.Add("email", v.params.Email) 190 q.Add("verificationtoken", v.params.VerificationToken) 191 q.Add("signature", v.params.Signature) 192 r.URL.RawQuery = q.Encode() 193 194 // Run test case 195 p.handleVerifyNewUser(w, r) 196 res := w.Result() 197 body, _ := io.ReadAll(res.Body) 198 res.Body.Close() 199 200 // Validate response 201 if res.StatusCode != v.wantStatus { 202 t.Errorf("got status code %v, want %v", 203 res.StatusCode, v.wantStatus) 204 } 205 206 if res.StatusCode == http.StatusOK { 207 // Test case passes; next case 208 return 209 } 210 211 var ue www.UserError 212 err := json.Unmarshal(body, &ue) 213 if err != nil { 214 t.Errorf("unmarshal UserError: %v", err) 215 } 216 217 got := errToStr(ue) 218 want := errToStr(v.wantError) 219 if got != want { 220 t.Errorf("got error %v, want %v", got, want) 221 } 222 }) 223 } 224 } 225 226 func TestHandleResendVerification(t *testing.T) { 227 p, cleanup := newTestPoliteiawww(t) 228 defer cleanup() 229 230 // Create a verified user 231 usrVerified, _ := newUser(t, p, true, false) 232 233 // Create an unverified user that has already had the 234 // verification email resent. 235 usrResent, _ := newUser(t, p, false, false) 236 usrResent.ResendNewUserVerificationExpiry = time.Now().Unix() + 100 237 err := p.db.UserUpdate(*usrResent) 238 if err != nil { 239 t.Fatalf("%v", err) 240 } 241 242 // Create a user for the success case 243 usr, id := newUser(t, p, false, false) 244 usrPubkey := id.Public.String() 245 246 // Setup tests 247 var tests = []struct { 248 name string 249 reqBody interface{} 250 wantStatus int 251 wantError error 252 }{ 253 { 254 "invalid request body", 255 "", 256 http.StatusBadRequest, 257 www.UserError{ 258 ErrorCode: www.ErrorStatusInvalidInput, 259 }, 260 }, 261 { 262 "user not found", 263 www.ResendVerification{ 264 Email: "", 265 PublicKey: usrPubkey, 266 }, 267 http.StatusOK, 268 nil, 269 }, 270 { 271 "user already verified", 272 www.ResendVerification{ 273 Email: usrVerified.Email, 274 }, 275 http.StatusOK, 276 nil, 277 }, 278 { 279 "verification already resent", 280 www.ResendVerification{ 281 Email: usrResent.Email, 282 }, 283 http.StatusOK, 284 nil, 285 }, 286 { 287 "processResendVerification error", 288 www.ResendVerification{ 289 Email: usr.Email, 290 PublicKey: "abc", 291 }, 292 http.StatusBadRequest, 293 www.UserError{ 294 ErrorCode: www.ErrorStatusInvalidPublicKey, 295 }, 296 }, 297 { 298 "success", 299 www.ResendVerification{ 300 Email: usr.Email, 301 PublicKey: usrPubkey, 302 }, 303 http.StatusOK, 304 nil, 305 }, 306 } 307 308 // Run tests 309 for _, v := range tests { 310 t.Run(v.name, func(t *testing.T) { 311 r := newPostReq(t, www.RouteResendVerification, v.reqBody) 312 w := httptest.NewRecorder() 313 p.handleResendVerification(w, r) 314 res := w.Result() 315 body, _ := io.ReadAll(res.Body) 316 res.Body.Close() 317 318 // Validate response 319 if res.StatusCode != v.wantStatus { 320 t.Errorf("got status code %v, want %v", 321 res.StatusCode, v.wantStatus) 322 } 323 324 if res.StatusCode == http.StatusOK { 325 // Test case passes; next case 326 return 327 } 328 329 var ue www.UserError 330 err := json.Unmarshal(body, &ue) 331 if err != nil { 332 t.Errorf("unmarshal UserError: %v", err) 333 } 334 335 got := errToStr(ue) 336 want := errToStr(v.wantError) 337 if got != want { 338 t.Errorf("got error %v, want %v", got, want) 339 } 340 }) 341 } 342 } 343 344 func TestHandleLogin(t *testing.T) { 345 p, cleanup := newTestPoliteiawww(t) 346 defer cleanup() 347 348 // loginMinWaitTime is a global variable used to prevent 349 // timing attacks. We're not testing it here so we 350 // temporarily zero it out to make the tests run faster. 351 m := loginMinWaitTime 352 loginMinWaitTime = 0 353 defer func() { 354 loginMinWaitTime = m 355 }() 356 357 // Create a user to test against. newUser() sets the 358 // password to be the same as the username. 359 u, _ := newUser(t, p, true, false) 360 password := u.Username 361 successReply, err := p.createLoginReply(u, u.LastLoginTime) 362 if err != nil { 363 t.Fatalf("%v", err) 364 } 365 successReply.SessionMaxAge = testSessionMaxAge 366 367 // Setup tests 368 var tests = []struct { 369 name string 370 reqBody interface{} 371 wantStatus int 372 wantReply *www.LoginReply 373 wantError error 374 }{ 375 { 376 "invalid request body", 377 "", 378 http.StatusBadRequest, 379 nil, 380 www.UserError{ 381 ErrorCode: www.ErrorStatusInvalidInput, 382 }, 383 }, 384 { 385 "processLogin error", 386 www.Login{}, 387 http.StatusUnauthorized, 388 nil, 389 www.UserError{ 390 ErrorCode: www.ErrorStatusInvalidLogin, 391 }, 392 }, 393 { 394 "success", 395 www.Login{ 396 Email: u.Email, 397 Password: password, 398 }, 399 http.StatusOK, 400 successReply, 401 nil, 402 }, 403 } 404 405 // Run tests 406 for _, v := range tests { 407 t.Run(v.name, func(t *testing.T) { 408 // Setup request 409 r := newPostReq(t, www.RouteLogin, v.reqBody) 410 w := httptest.NewRecorder() 411 412 // Run test case 413 p.handleLogin(w, r) 414 res := w.Result() 415 body, _ := io.ReadAll(res.Body) 416 res.Body.Close() 417 418 // Validate response 419 if res.StatusCode != v.wantStatus { 420 t.Errorf("got status code %v, want %v", 421 res.StatusCode, v.wantStatus) 422 } 423 424 if res.StatusCode == http.StatusOK { 425 // A user session should have been added to the response 426 // cookie. 427 var sessionID string 428 for _, v := range res.Cookies() { 429 if v.Name == www.CookieSession && v.Value != "" { 430 sessionID = v.Value 431 } 432 } 433 if sessionID == "" { 434 t.Errorf("no session cookie in response") 435 } 436 437 // A user session should have been added to the session 438 // store. The best way to check this is to add a session 439 // cookie onto a request and use the getSession() method. 440 req := httptest.NewRequest(http.MethodGet, "/", 441 bytes.NewReader([]byte{})) 442 opts := newSessionOptions() 443 c := sessions.NewCookie(www.CookieSession, sessionID, opts) 444 req.AddCookie(c) 445 s, err := p.sessions.GetSession(req) 446 if err != nil { 447 t.Error(err) 448 } 449 if s.IsNew { 450 t.Errorf("session not saved to session store") 451 } 452 453 // Check response body 454 var lr www.LoginReply 455 err = json.Unmarshal(body, &lr) 456 if err != nil { 457 t.Errorf("unmarshal LoginReply: %v", err) 458 } 459 460 diff := deep.Equal(lr, *v.wantReply) 461 if diff != nil { 462 t.Errorf("LoginReply got/want diff:\n%v", 463 spew.Sdump(diff)) 464 } 465 466 // Test case passes; next case 467 return 468 } 469 470 var ue www.UserError 471 err := json.Unmarshal(body, &ue) 472 if err != nil { 473 t.Errorf("unmarshal UserError: %v", err) 474 } 475 476 got := errToStr(ue) 477 want := errToStr(v.wantError) 478 if got != want { 479 t.Errorf("got error %v, want %v", got, want) 480 } 481 }) 482 } 483 } 484 485 func TestHandleChangePassword(t *testing.T) { 486 p, cleanup := newTestPoliteiawww(t) 487 defer cleanup() 488 489 // Create a user to test against. newUser() 490 // sets the password to be the username. 491 usr, _ := newUser(t, p, true, false) 492 currPass := usr.Username 493 newPass := currPass + "aaa" 494 495 var tests = []struct { 496 name string 497 reqBody interface{} 498 wantStatus int 499 wantError error 500 }{ 501 // Middleware will catch any invalid user sessions. 502 // We can assume that the request contains a valid 503 // user session. 504 505 { 506 "invalid request body", 507 "", 508 http.StatusBadRequest, 509 www.UserError{ 510 ErrorCode: www.ErrorStatusInvalidInput, 511 }, 512 }, 513 { 514 "processChangePassword error", 515 www.ChangePassword{ 516 CurrentPassword: "", 517 NewPassword: newPass, 518 }, 519 http.StatusBadRequest, 520 www.UserError{ 521 ErrorCode: www.ErrorStatusInvalidPassword, 522 }, 523 }, 524 { 525 "success", 526 www.ChangePassword{ 527 CurrentPassword: currPass, 528 NewPassword: newPass, 529 }, 530 http.StatusOK, 531 nil, 532 }, 533 } 534 535 // Run tests 536 for _, v := range tests { 537 t.Run(v.name, func(t *testing.T) { 538 // Setup request 539 r := newPostReq(t, www.RouteChangePassword, v.reqBody) 540 addSessionToReq(t, p, r, usr.ID.String()) 541 w := httptest.NewRecorder() 542 543 // Run test case 544 p.handleChangePassword(w, r) 545 res := w.Result() 546 body, _ := io.ReadAll(res.Body) 547 res.Body.Close() 548 549 // Validate response 550 if res.StatusCode != v.wantStatus { 551 t.Errorf("got status code %v, want %v", 552 res.StatusCode, v.wantStatus) 553 } 554 555 if res.StatusCode == http.StatusOK { 556 // Test case passes; next case 557 return 558 } 559 560 var ue www.UserError 561 err := json.Unmarshal(body, &ue) 562 if err != nil { 563 t.Errorf("unmarshal UserError: %v", err) 564 } 565 566 got := errToStr(ue) 567 want := errToStr(v.wantError) 568 if got != want { 569 t.Errorf("got error %v, want %v", got, want) 570 } 571 }) 572 } 573 } 574 575 func TestHandleResetPassword(t *testing.T) { 576 p, cleanup := newTestPoliteiawww(t) 577 defer cleanup() 578 579 // Create a test user 580 usr, _ := newUser(t, p, true, false) 581 582 // Remove the min wait time requirement so that the tests 583 // aren't slow. 584 wt := resetPasswordMinWaitTime 585 resetPasswordMinWaitTime = 0 * time.Millisecond 586 defer func() { 587 resetPasswordMinWaitTime = wt 588 }() 589 590 // Setup tests 591 var tests = []struct { 592 name string 593 reqBody interface{} 594 wantStatus int // HTTP status code 595 wantError error 596 }{ 597 { 598 "invalid request body", 599 "", 600 http.StatusBadRequest, 601 www.UserError{ 602 ErrorCode: www.ErrorStatusInvalidInput, 603 }, 604 }, 605 { 606 "processResetPassword error", 607 www.ResetPassword{ 608 Username: "wrongusername", 609 }, 610 http.StatusBadRequest, 611 www.UserError{ 612 ErrorCode: www.ErrorStatusUserNotFound, 613 }, 614 }, 615 { 616 "success", 617 www.ResetPassword{ 618 Username: usr.Username, 619 Email: usr.Email, 620 }, 621 http.StatusOK, 622 nil, 623 }, 624 } 625 626 // Run tests 627 for _, v := range tests { 628 t.Run(v.name, func(t *testing.T) { 629 // Setup request 630 r := newPostReq(t, www.RouteResetPassword, v.reqBody) 631 w := httptest.NewRecorder() 632 633 // Run test case 634 p.handleResetPassword(w, r) 635 res := w.Result() 636 body, _ := io.ReadAll(res.Body) 637 res.Body.Close() 638 639 // Validate response 640 if res.StatusCode != v.wantStatus { 641 t.Errorf("got status code %v, want %v", 642 res.StatusCode, v.wantStatus) 643 } 644 645 if res.StatusCode == http.StatusOK { 646 // Test case passes; next case 647 return 648 } 649 650 var ue www.UserError 651 err := json.Unmarshal(body, &ue) 652 if err != nil { 653 t.Errorf("unmarshal UserError: %v", err) 654 } 655 656 got := errToStr(ue) 657 want := errToStr(v.wantError) 658 if got != want { 659 t.Errorf("got error %v, want %v", got, want) 660 } 661 }) 662 } 663 } 664 665 func TestHandleVerifyResetPassword(t *testing.T) { 666 p, cleanup := newTestPoliteiawww(t) 667 defer cleanup() 668 669 // Create a user that has already been assigned a reset 670 // password verification token. 671 usr, _ := newUser(t, p, true, false) 672 token, expiry, err := newVerificationTokenAndExpiry() 673 if err != nil { 674 t.Fatal(err) 675 } 676 usr.ResetPasswordVerificationToken = token 677 usr.ResetPasswordVerificationExpiry = expiry 678 err = p.db.UserUpdate(*usr) 679 if err != nil { 680 t.Fatal(err) 681 } 682 verificationToken := hex.EncodeToString(token) 683 684 // Setup tests 685 var tests = []struct { 686 name string 687 reqBody interface{} 688 wantStatus int // HTTP status code 689 wantErr error 690 }{ 691 { 692 "invalid request body", 693 "", 694 http.StatusBadRequest, 695 www.UserError{ 696 ErrorCode: www.ErrorStatusInvalidInput, 697 }, 698 }, 699 { 700 "processVerifyResetPassword error", 701 www.VerifyResetPassword{ 702 Username: "wrongusername", 703 }, 704 http.StatusBadRequest, 705 www.UserError{ 706 ErrorCode: www.ErrorStatusUserNotFound, 707 }, 708 }, 709 { 710 "success", 711 www.VerifyResetPassword{ 712 Username: usr.Username, 713 VerificationToken: verificationToken, 714 NewPassword: "helloworld", 715 }, 716 http.StatusOK, 717 nil, 718 }, 719 } 720 721 // Run tests 722 for _, v := range tests { 723 t.Run(v.name, func(t *testing.T) { 724 // Setup request 725 r := newPostReq(t, www.RouteVerifyResetPassword, v.reqBody) 726 w := httptest.NewRecorder() 727 728 // Run test case 729 p.handleVerifyResetPassword(w, r) 730 res := w.Result() 731 body, _ := io.ReadAll(res.Body) 732 res.Body.Close() 733 734 // Check status code 735 if res.StatusCode != v.wantStatus { 736 t.Errorf("got status code %v, want %v", 737 res.StatusCode, v.wantStatus) 738 } 739 if res.StatusCode == http.StatusOK { 740 // Test case passes; next case 741 return 742 } 743 744 // Check user error 745 var ue www.UserError 746 err := json.Unmarshal(body, &ue) 747 if err != nil { 748 t.Errorf("unmarshal UserError: %v", err) 749 } 750 got := errToStr(ue) 751 want := errToStr(v.wantErr) 752 if got != want { 753 t.Errorf("got error %v, want %v", got, want) 754 } 755 }) 756 } 757 } 758 759 func TestHandleChangeUsername(t *testing.T) { 760 p, cleanup := newTestPoliteiawww(t) 761 defer cleanup() 762 763 // Create a user to test against. newUser() 764 // sets the password to be the username. 765 usr, _ := newUser(t, p, true, false) 766 pass := usr.Username 767 768 // Setup tests 769 var tests = []struct { 770 name string 771 reqBody interface{} 772 wantStatus int 773 wantError error 774 }{ 775 // Middleware will catch any invalid user sessions. 776 // We can assume that the request contains a valid 777 // user session. 778 779 { 780 "invalid request body", 781 "", 782 http.StatusBadRequest, 783 www.UserError{ 784 ErrorCode: www.ErrorStatusInvalidInput, 785 }, 786 }, 787 { 788 "processChangeUsername error", 789 www.ChangeUsername{}, 790 http.StatusBadRequest, 791 www.UserError{ 792 ErrorCode: www.ErrorStatusInvalidPassword, 793 }, 794 }, 795 { 796 "success", 797 www.ChangeUsername{ 798 Password: pass, 799 NewUsername: usr.Username + "aaa", 800 }, 801 http.StatusOK, 802 nil, 803 }, 804 } 805 806 // Run tests 807 for _, v := range tests { 808 t.Run(v.name, func(t *testing.T) { 809 // Setup request 810 r := newPostReq(t, www.RouteChangeUsername, v.reqBody) 811 addSessionToReq(t, p, r, usr.ID.String()) 812 w := httptest.NewRecorder() 813 814 // Run test case 815 p.handleChangeUsername(w, r) 816 res := w.Result() 817 body, _ := io.ReadAll(res.Body) 818 res.Body.Close() 819 820 // Validate response 821 if res.StatusCode != v.wantStatus { 822 t.Errorf("got status code %v, want %v", 823 res.StatusCode, v.wantStatus) 824 } 825 826 if res.StatusCode == http.StatusOK { 827 // Test case passes; next case 828 return 829 } 830 831 var ue www.UserError 832 err := json.Unmarshal(body, &ue) 833 if err != nil { 834 t.Errorf("unmarshal UserError: %v", err) 835 } 836 837 got := errToStr(ue) 838 want := errToStr(v.wantError) 839 if got != want { 840 t.Errorf("got error %v, want %v", got, want) 841 } 842 }) 843 } 844 } 845 846 func TestHandleUserDetails(t *testing.T) { 847 p, cleanup := newTestPoliteiawww(t) 848 defer cleanup() 849 850 // Create a user whose details we can fetch 851 usr, _ := newUser(t, p, true, false) 852 853 // Setup tests 854 var tests = []struct { 855 name string // Test name 856 uuid string // UUID for route param 857 loggedIn bool // Should request contain a user session 858 wantStatus int // Wanted response status code 859 wantError error // Wanted response error 860 }{ 861 // The UUID is a route param so an invalid length UUID will 862 // be caught by the router. A correct length UUID with an 863 // invalid format will not be caught by the router and needs 864 // to be tested for. 865 { 866 "invalid uuid format", 867 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 868 false, http.StatusBadRequest, 869 www.UserError{ 870 ErrorCode: www.ErrorStatusInvalidInput, 871 }, 872 }, 873 { 874 "process user details error", 875 "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", 876 false, http.StatusBadRequest, 877 www.UserError{ 878 ErrorCode: www.ErrorStatusUserNotFound, 879 }, 880 }, 881 { 882 "logged in user success", 883 usr.ID.String(), 884 true, 885 http.StatusOK, 886 nil, 887 }, 888 { 889 "public user success", 890 usr.ID.String(), 891 false, 892 http.StatusOK, 893 nil, 894 }, 895 } 896 897 // Run tests 898 for _, v := range tests { 899 t.Run(v.name, func(t *testing.T) { 900 // Setup request 901 r := httptest.NewRequest(http.MethodGet, www.RouteUserDetails, nil) 902 r = mux.SetURLVars(r, map[string]string{ 903 "userid": v.uuid, 904 }) 905 w := httptest.NewRecorder() 906 907 // Initialize the user session 908 if v.loggedIn { 909 err := p.sessions.NewSession(w, r, usr.ID.String()) 910 if err != nil { 911 t.Fatalf("%v", err) 912 } 913 } 914 915 // Run test case 916 p.handleUserDetails(w, r) 917 res := w.Result() 918 body, _ := io.ReadAll(res.Body) 919 res.Body.Close() 920 921 // Validate response 922 if res.StatusCode != v.wantStatus { 923 t.Errorf("got status code %v, want %v", 924 res.StatusCode, v.wantStatus) 925 } 926 927 if res.StatusCode == http.StatusOK { 928 // Test case passes; next case 929 return 930 } 931 932 var ue www.UserError 933 err := json.Unmarshal(body, &ue) 934 if err != nil { 935 t.Errorf("unmarshal UserError: %v", err) 936 } 937 938 got := errToStr(ue) 939 want := errToStr(v.wantError) 940 if got != want { 941 t.Errorf("got error %v, want %v", got, want) 942 } 943 }) 944 } 945 } 946 947 func TestHandleEditUser(t *testing.T) { 948 p, cleanup := newTestPoliteiawww(t) 949 defer cleanup() 950 951 var notif uint64 = 0x01 952 usr, _ := newUser(t, p, true, true) 953 954 var tests = []struct { 955 name string 956 reqBody interface{} 957 wantStatus int 958 wantError error 959 }{ 960 // Middleware will catch any invalid admin sessions. 961 // We can assume that the request contains a valid 962 // admin session. 963 964 { 965 "invalid request body", 966 "", 967 http.StatusBadRequest, 968 www.UserError{ 969 ErrorCode: www.ErrorStatusInvalidInput, 970 }, 971 }, 972 { 973 "success", 974 www.EditUser{ 975 EmailNotifications: ¬if, 976 }, 977 http.StatusOK, 978 nil, 979 }, 980 } 981 982 // Run tests 983 for _, v := range tests { 984 t.Run(v.name, func(t *testing.T) { 985 // Setup request 986 r := newPostReq(t, www.RouteEditUser, v.reqBody) 987 addSessionToReq(t, p, r, usr.ID.String()) 988 w := httptest.NewRecorder() 989 990 // Run test case 991 p.handleEditUser(w, r) 992 res := w.Result() 993 body, _ := io.ReadAll(res.Body) 994 res.Body.Close() 995 996 // Validate response 997 if res.StatusCode != v.wantStatus { 998 t.Errorf("got status code %v, want %v", 999 res.StatusCode, v.wantStatus) 1000 } 1001 1002 if res.StatusCode == http.StatusOK { 1003 // Test case passes; next case 1004 return 1005 } 1006 1007 var ue www.UserError 1008 err := json.Unmarshal(body, &ue) 1009 if err != nil { 1010 t.Errorf("unmarshal UserError: %v", err) 1011 } 1012 1013 got := errToStr(ue) 1014 want := errToStr(v.wantError) 1015 if got != want { 1016 t.Errorf("got error %v, want %v", got, want) 1017 } 1018 }) 1019 } 1020 } 1021 1022 const ( 1023 testSessionMaxAge = 86400 // One day 1024 ) 1025 1026 func newSessionOptions() *sessions.Options { 1027 return &sessions.Options{ 1028 Path: "/", 1029 MaxAge: testSessionMaxAge, 1030 Secure: true, 1031 HttpOnly: true, 1032 SameSite: http.SameSiteStrictMode, 1033 } 1034 } 1035 1036 // newPostReq returns an httptest post request that was created using the 1037 // passed in data. 1038 func newPostReq(t *testing.T, route string, body interface{}) *http.Request { 1039 t.Helper() 1040 1041 b, err := json.Marshal(body) 1042 if err != nil { 1043 t.Fatalf("%v", err) 1044 } 1045 1046 return httptest.NewRequest(http.MethodPost, route, bytes.NewReader(b)) 1047 } 1048 1049 // addSessionToReq initializes a user session and adds a session cookie to the 1050 // given http request. The user session is saved to the politeiawww session 1051 // store during intialization. 1052 func addSessionToReq(t *testing.T, p *Politeiawww, req *http.Request, userID string) { 1053 t.Helper() 1054 1055 // Init session adds a session cookie onto the http response. 1056 r := httptest.NewRequest(http.MethodGet, "/", bytes.NewReader([]byte{})) 1057 w := httptest.NewRecorder() 1058 err := p.sessions.NewSession(w, r, userID) 1059 if err != nil { 1060 t.Fatal(err) 1061 } 1062 res := w.Result() 1063 res.Body.Close() 1064 1065 // Grab the session cookie from the response and add it to the 1066 // request. 1067 var c *http.Cookie 1068 for _, v := range res.Cookies() { 1069 if v.Name == www.CookieSession { 1070 c = v 1071 break 1072 } 1073 } 1074 req.AddCookie(c) 1075 1076 // Verify the session was added successfully. 1077 s, err := p.sessions.GetSession(req) 1078 if err != nil { 1079 t.Fatal(err) 1080 } 1081 if s.IsNew { 1082 t.Fatal("session not found in store") 1083 } 1084 }