View raw

1 open Hito_app 2 open Support 3 4 let suite = 5 [ 6 ( "web.profile", 7 [ 8 ( "changing the username updates the stored account", 9 `Quick, 10 fun () -> 11 let c, repo = client_with_repo () in 12 let _ = register c ~username:"lifter" ~password:"heavyduty1" in 13 let trainee = the_trainee repo ~username:"lifter" in 14 let token = Option.get (csrf_token (body (get c "/profile"))) in 15 let changed = 16 post c "/profile/username" 17 [ ("dream.csrf", token); ("username", "athlete") ] 18 in 19 Alcotest.(check int) 20 "redirects after the change" 303 (status changed); 21 Alcotest.(check bool) 22 "redirects back to the profile with a notice" true 23 (List.mem "/profile?changed=username" 24 (Dream.headers changed "Location")); 25 (* The account now answers to the new name, and no longer to the 26 old one. *) 27 let renamed = 28 Lwt_main.run (Test_repo.find_trainee repo trainee.Trainee.id) 29 in 30 Alcotest.(check (option string)) 31 "stored under the new username" (Some "athlete") 32 (Option.map 33 (fun t -> Trainee.username_to_string t.Trainee.username) 34 renamed); 35 Alcotest.(check bool) 36 "old name no longer resolves" true 37 (Option.is_none 38 (Lwt_main.run 39 (match Trainee.username "lifter" with 40 | Ok u -> Test_repo.find_trainee_by_username repo u 41 | Error _ -> Lwt.return None))) ); 42 ( "a taken username is refused and the account keeps its name", 43 `Quick, 44 fun () -> 45 let c, repo = client_with_repo () in 46 let _ = register c ~username:"lifter" ~password:"heavyduty1" in 47 let trainee = the_trainee repo ~username:"lifter" in 48 (* Register a second account in the same store. *) 49 let other = { c with jar = [] } in 50 let _ = register other ~username:"taken" ~password:"heavyduty1" in 51 let token = Option.get (csrf_token (body (get c "/profile"))) in 52 let refused = 53 post c "/profile/username" 54 [ ("dream.csrf", token); ("username", "taken") ] 55 in 56 Alcotest.(check int) 57 "renders an invalid response" 400 (status refused); 58 (* The account still holds its original name. *) 59 Alcotest.(check (option string)) 60 "name unchanged after refusal" (Some "lifter") 61 (Option.map 62 (fun t -> Trainee.username_to_string t.Trainee.username) 63 (Lwt_main.run (Test_repo.find_trainee repo trainee.Trainee.id))) 64 ); 65 ( "changing the password requires the current one", 66 `Quick, 67 fun () -> 68 let c = client () in 69 let _ = sign_in_new c in 70 let token = Option.get (csrf_token (body (get c "/profile"))) in 71 let refused = 72 post c "/profile/password" 73 [ 74 ("dream.csrf", token); 75 ("current", "wrongpass"); 76 ("next", "newsecret1"); 77 ] 78 in 79 Alcotest.(check int) 80 "renders an invalid response" 400 (status refused); 81 (* Now change it with the correct current password. *) 82 let token = Option.get (csrf_token (body (get c "/profile"))) in 83 let changed = 84 post c "/profile/password" 85 [ 86 ("dream.csrf", token); 87 ("current", "heavyduty1"); 88 ("next", "newsecret1"); 89 ] 90 in 91 Alcotest.(check int) 92 "redirects after the change" 303 (status changed); 93 Alcotest.(check bool) 94 "redirects back to the profile with a notice" true 95 (List.mem "/profile?changed=password" 96 (Dream.headers changed "Location")); 97 (* The new password now authenticates and the old one no longer 98 does. Sign out, then prove the change through the sign-in flow. *) 99 let token = Option.get (csrf_token (body (get c "/profile"))) in 100 let _ = post c "/logout" [ ("dream.csrf", token) ] in 101 let old_pw = sign_in c ~username:"lifter" ~password:"heavyduty1" in 102 Alcotest.(check int) 103 "the old password no longer works" 401 (status old_pw); 104 let new_pw = sign_in c ~username:"lifter" ~password:"newsecret1" in 105 Alcotest.(check int) 106 "the new password authenticates" 303 (status new_pw) ); 107 ( "the settings page is separate from the profile page", 108 `Quick, 109 fun () -> 110 let c = client () in 111 let _ = sign_in_new c in 112 Alcotest.(check int) 113 "settings served" 200 114 (status (get c "/settings")); 115 Alcotest.(check int) 116 "profile served separately" 200 117 (status (get c "/profile")) ); 118 ( "the initial release hides the import", 119 `Quick, 120 fun () -> 121 let c = client ~import_open:false () in 122 let _ = sign_in_new c in 123 let profile = body (get c "/profile") in 124 Alcotest.(check bool) 125 "profile offers no upload" true 126 (Option.is_none (index_from ~needle:"Upload import" profile 0)); 127 Alcotest.(check int) 128 "import route is not served" 404 129 (status (post c "/profile/import" [])) ); 130 ( "the settings theme form redirects after a valid update", 131 `Quick, 132 fun () -> 133 let c = client () in 134 let _ = sign_in_new c in 135 let token = Option.get (csrf_token (body (get c "/settings"))) in 136 let updated = 137 post c "/settings/theme" 138 [ ("dream.csrf", token); ("theme", "dark") ] 139 in 140 Alcotest.(check int) "theme update redirects" 303 (status updated); 141 Alcotest.(check string) 142 "returns to settings" "/settings" 143 (List.hd (Dream.headers updated "Location")) ); 144 ] ); 145 ] 146