[OCaml] High Intensity Training Online
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