1 use super::task::Task;
2
3 use parking_lot::Mutex;
4 use rand::{Rng, thread_rng, distributions::Alphanumeric};
5
6 use rocket::local::Client;
7 use rocket::http::{Status, ContentType};
8
9 // We use a lock to synchronize between tests so DB operations don't collide.
10 // For now. In the future, we'll have a nice way to run each test in a DB
11 // transaction so we can regain concurrency.
12 static DB_LOCK: Mutex<()> = Mutex::new(());
13
14 macro_rules! run_test {
15 (|$client:ident, $conn:ident| $block:expr) => ({
16 let _lock = DB_LOCK.lock();
17 let rocket = super::rocket();
18 let db = super::DbConn::get_one(&rocket);
19 let $client = Client::new(rocket).expect("Rocket client");
20 let $conn = db.expect("failed to get database connection for testing");
21 Task::delete_all(&$conn).expect("failed to delete all tasks for testing");
22
23 $block
24 })
25 }
26
27 #[test]
test_insertion_deletion()28 fn test_insertion_deletion() {
29 run_test!(|client, conn| {
30 // Get the tasks before making changes.
31 let init_tasks = Task::all(&conn).unwrap();
32
33 // Issue a request to insert a new task.
34 client.post("/todo")
35 .header(ContentType::Form)
36 .body("description=My+first+task")
37 .dispatch();
38
39 // Ensure we have one more task in the database.
40 let new_tasks = Task::all(&conn).unwrap();
41 assert_eq!(new_tasks.len(), init_tasks.len() + 1);
42
43 // Ensure the task is what we expect.
44 assert_eq!(new_tasks[0].description, "My first task");
45 assert_eq!(new_tasks[0].completed, false);
46
47 // Issue a request to delete the task.
48 let id = new_tasks[0].id.unwrap();
49 client.delete(format!("/todo/{}", id)).dispatch();
50
51 // Ensure it's gone.
52 let final_tasks = Task::all(&conn).unwrap();
53 assert_eq!(final_tasks.len(), init_tasks.len());
54 if final_tasks.len() > 0 {
55 assert_ne!(final_tasks[0].description, "My first task");
56 }
57 })
58 }
59
60 #[test]
test_toggle()61 fn test_toggle() {
62 run_test!(|client, conn| {
63 // Issue a request to insert a new task; ensure it's not yet completed.
64 client.post("/todo")
65 .header(ContentType::Form)
66 .body("description=test_for_completion")
67 .dispatch();
68
69 let task = Task::all(&conn).unwrap()[0].clone();
70 assert_eq!(task.completed, false);
71
72 // Issue a request to toggle the task; ensure it is completed.
73 client.put(format!("/todo/{}", task.id.unwrap())).dispatch();
74 assert_eq!(Task::all(&conn).unwrap()[0].completed, true);
75
76 // Issue a request to toggle the task; ensure it's not completed again.
77 client.put(format!("/todo/{}", task.id.unwrap())).dispatch();
78 assert_eq!(Task::all(&conn).unwrap()[0].completed, false);
79 })
80 }
81
82 #[test]
test_many_insertions()83 fn test_many_insertions() {
84 const ITER: usize = 100;
85
86 let rng = thread_rng();
87 run_test!(|client, conn| {
88 // Get the number of tasks initially.
89 let init_num = Task::all(&conn).unwrap().len();
90 let mut descs = Vec::new();
91
92 for i in 0..ITER {
93 // Issue a request to insert a new task with a random description.
94 let desc: String = rng.sample_iter(&Alphanumeric).take(12).collect();
95 client.post("/todo")
96 .header(ContentType::Form)
97 .body(format!("description={}", desc))
98 .dispatch();
99
100 // Record the description we choose for this iteration.
101 descs.insert(0, desc);
102
103 // Ensure the task was inserted properly and all other tasks remain.
104 let tasks = Task::all(&conn).unwrap();
105 assert_eq!(tasks.len(), init_num + i + 1);
106
107 for j in 0..i {
108 assert_eq!(descs[j], tasks[j].description);
109 }
110 }
111 })
112 }
113
114 #[test]
test_bad_form_submissions()115 fn test_bad_form_submissions() {
116 run_test!(|client, _conn| {
117 // Submit an empty form. We should get a 422 but no flash error.
118 let res = client.post("/todo")
119 .header(ContentType::Form)
120 .dispatch();
121
122 let mut cookies = res.headers().get("Set-Cookie");
123 assert_eq!(res.status(), Status::UnprocessableEntity);
124 assert!(!cookies.any(|value| value.contains("error")));
125
126 // Submit a form with an empty description. We look for 'error' in the
127 // cookies which corresponds to flash message being set as an error.
128 let res = client.post("/todo")
129 .header(ContentType::Form)
130 .body("description=")
131 .dispatch();
132
133 let mut cookies = res.headers().get("Set-Cookie");
134 assert!(cookies.any(|value| value.contains("error")));
135
136 // Submit a form without a description. Expect a 422 but no flash error.
137 let res = client.post("/todo")
138 .header(ContentType::Form)
139 .body("evil=smile")
140 .dispatch();
141
142 let mut cookies = res.headers().get("Set-Cookie");
143 assert_eq!(res.status(), Status::UnprocessableEntity);
144 assert!(!cookies.any(|value| value.contains("error")));
145 })
146 }
147