forked from rwf2/Rocket
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtests.rs
193 lines (160 loc) · 6.51 KB
/
tests.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
use super::task::Task;
use rand::{Rng, thread_rng, distributions::Alphanumeric};
use rocket::local::asynchronous::Client;
use rocket::http::{Status, ContentType, Cookie};
// We use a lock to synchronize between tests so DB operations don't collide.
// For now. In the future, we'll have a nice way to run each test in a DB
// transaction so we can regain concurrency.
static DB_LOCK: parking_lot::Mutex<()> = parking_lot::const_mutex(());
macro_rules! run_test {
(|$client:ident, $conn:ident| $block:expr) => ({
let _lock = DB_LOCK.lock();
rocket::async_test(async move {
let $client = Client::tracked(super::rocket()).await.expect("Rocket client");
let db = super::DbConn::get_one($client.rocket()).await;
let $conn = db.expect("failed to get database connection for testing");
Task::delete_all(&$conn).await.expect("failed to delete all tasks for testing");
$block
})
})
}
#[test]
fn test_index() {
use rocket::local::blocking::Client;
let _lock = DB_LOCK.lock();
let client = Client::tracked(super::rocket()).unwrap();
let response = client.get("/").dispatch();
assert_eq!(response.status(), Status::Ok);
}
#[test]
fn test_insertion_deletion() {
run_test!(|client, conn| {
// Get the tasks before making changes.
let init_tasks = Task::all(&conn).await.unwrap();
// Issue a request to insert a new task.
client.post("/todo")
.header(ContentType::Form)
.body("description=My+first+task")
.dispatch()
.await;
// Ensure we have one more task in the database.
let new_tasks = Task::all(&conn).await.unwrap();
assert_eq!(new_tasks.len(), init_tasks.len() + 1);
// Ensure the task is what we expect.
assert_eq!(new_tasks[0].description, "My first task");
assert_eq!(new_tasks[0].completed, false);
// Issue a request to delete the task.
let id = new_tasks[0].id.unwrap();
client.delete(format!("/todo/{}", id)).dispatch().await;
// Ensure it's gone.
let final_tasks = Task::all(&conn).await.unwrap();
assert_eq!(final_tasks.len(), init_tasks.len());
if final_tasks.len() > 0 {
assert_ne!(final_tasks[0].description, "My first task");
}
})
}
#[test]
fn test_toggle() {
run_test!(|client, conn| {
// Issue a request to insert a new task; ensure it's not yet completed.
client.post("/todo")
.header(ContentType::Form)
.body("description=test_for_completion")
.dispatch()
.await;
let task = Task::all(&conn).await.unwrap()[0].clone();
assert_eq!(task.completed, false);
// Issue a request to toggle the task; ensure it is completed.
client.put(format!("/todo/{}", task.id.unwrap())).dispatch().await;
assert_eq!(Task::all(&conn).await.unwrap()[0].completed, true);
// Issue a request to toggle the task; ensure it's not completed again.
client.put(format!("/todo/{}", task.id.unwrap())).dispatch().await;
assert_eq!(Task::all(&conn).await.unwrap()[0].completed, false);
})
}
#[test]
fn test_many_insertions() {
const ITER: usize = 100;
run_test!(|client, conn| {
// Get the number of tasks initially.
let init_num = Task::all(&conn).await.unwrap().len();
let mut descs = Vec::new();
for i in 0..ITER {
// Issue a request to insert a new task with a random description.
let desc: String = thread_rng()
.sample_iter(&Alphanumeric)
.take(12)
.map(char::from)
.collect();
client.post("/todo")
.header(ContentType::Form)
.body(format!("description={}", desc))
.dispatch()
.await;
// Record the description we choose for this iteration.
descs.insert(0, desc);
// Ensure the task was inserted properly and all other tasks remain.
let tasks = Task::all(&conn).await.unwrap();
assert_eq!(tasks.len(), init_num + i + 1);
for j in 0..i {
assert_eq!(descs[j], tasks[j].description);
}
}
})
}
#[test]
fn test_bad_form_submissions() {
run_test!(|client, _conn| {
// Submit an empty form. We should get a 422 but no flash error.
let res = client.post("/todo")
.header(ContentType::Form)
.dispatch()
.await;
let mut cookies = res.headers().get("Set-Cookie");
assert_eq!(res.status(), Status::UnprocessableEntity);
assert!(!cookies.any(|value| value.contains("error")));
// Submit a form with an empty description. We look for 'error' in the
// cookies which corresponds to flash message being set as an error.
let res = client.post("/todo")
.header(ContentType::Form)
.body("description=")
.dispatch()
.await;
let mut cookies = res.headers().get("Set-Cookie");
let cookie = cookies
.find(|value| value.contains("error"))
.map(|value| Cookie::parse_encoded(value).expect("cookie"))
.expect("cookie");
assert_eq!(cookie.name(), "_flash"); // private const
// Check that the index page now contains the flash message that we were expecting.
// A client supporting cookies will set the cookie by the receiving _Set-Cookie_ header.
let body = client
.get("/")
.cookie(cookie) // set cookie
.dispatch()
.await
.into_string()
.await
.expect("body");
assert!(body.contains("Description cannot be empty."));
// Check that the flash message was cleared upon another visit to the index page.
let body = client
.get("/")
.dispatch()
.await
.into_string()
.await
.expect("body");
assert!(!body.contains("Description cannot be empty."));
// Submit a form without a description. Expect a 422 but no flash error.
let res = client.post("/todo")
.header(ContentType::Form)
.body("evil=smile")
.dispatch()
.await;
let mut cookies = res.headers().get("Set-Cookie");
assert_eq!(res.status(), Status::UnprocessableEntity);
assert!(!cookies.any(|value| value.contains("error")));
})
}