diff options
Diffstat (limited to 'tests')
-rw-r--r-- | tests/route_tests.rs | 371 |
1 files changed, 371 insertions, 0 deletions
diff --git a/tests/route_tests.rs b/tests/route_tests.rs new file mode 100644 index 0000000..ba3ecf3 --- /dev/null +++ b/tests/route_tests.rs | |||
@@ -0,0 +1,371 @@ | |||
1 | #[cfg(test)] | ||
2 | mod tests { | ||
3 | use gradecoin::schema::{ | ||
4 | create_database, AuthRequest, Block, Claims, Db, MetuId, Transaction, User, | ||
5 | }; | ||
6 | |||
7 | use gradecoin::routes::consensus_routes; | ||
8 | use warp::http::StatusCode; | ||
9 | |||
10 | use jsonwebtoken::{encode, Algorithm, EncodingKey, Header}; | ||
11 | const PRIVATE_KEY_PEM: &str = "-----BEGIN RSA PRIVATE KEY----- | ||
12 | MIIEpAIBAAKCAQEA4nU0G4WjkmcQUx0hq6LQuV5Q+ACmUFL/OjoYMDwC/O/6pCd1 | ||
13 | UZgCfgHN2xEffDPznzcTn8OiFRxr4oWyBinyrUpnY4mhy0SQUwoeCw7YkcHAyhCj | ||
14 | NT74aR/ohX0MCj0qRRdbt5ZQXM/GC3HJuXE1ptSuhFgQxziItamn8maoJ6JUSVEX | ||
15 | VO1NOrrjoM3r7Q+BK2B+sX4/bLZ+VG5g1q2nEbFdTHS6pHqtZNHQndTmEKwRfh0R | ||
16 | YtzEzOXuO6e1gQY42Tujkof40dhGCIU7TeIGGHwdFxy1niLkXwtHNjV7lnIOkTbx | ||
17 | 6+sSPamRfQAlZqUWM2Lf5o+7h3qWP3ENB138sQIDAQABAoIBAD23nYTmrganag6M | ||
18 | wPFrBSGP79c3Lhx0EjUHQjJbGKFgsdltG48qM3ut+DF9ACy0Z+/7bbC7+39vaIOq | ||
19 | 1jLR2d6aiYTaLKseO4s2FawD1sgamvU3BZPsXn0gAhnnU5Gyy8Nas1dccvhoc9wI | ||
20 | neaZUPrvucQ90AzLfo6r9yacDbYHB1lOyomApUvpJxOgHISGEtc9qGPDrdH19aF0 | ||
21 | 8fCv2bbQRh+TChgN3IB0o5w0wXaI7YAyAouAv/AzHCoEMpt7OGjFTkjh/ujlPL9O | ||
22 | +FLuJNsQRHDN0gJo2pcvwGwDCsioMixQ9bZ7ZrUu2BNpEQygyeSbj9ZI1iRvhosO | ||
23 | JU3rwEECgYEA9MppTYA6A9WQbCCwPH1QMpUAmPNVSWVhUVag4lGOEhdCDRcz9ook | ||
24 | DohQMKctiEB1luKuvDokxo0uMOfMO9/YwjsRB7qjQip7Th1zMJIjD+A+juLzHK4r | ||
25 | /RiRtWYGAnF8mptDvE+93JsPb3C/lQLvIhio5GQYWBqPJu6SpeosIskCgYEA7NPi | ||
26 | Gbffzr2UQhW8BNKmctEEh8yFRVojFo3wwwWxSNUVXGSmSm31CL+Q8h817R+2OkPV | ||
27 | 1ZMUOBU4UJiqFt28kIvTDFqbAJlJQGCpY2mY7OLQiD2A+TVLcFrHmoCaPfCAK1Qd | ||
28 | hQ0PmFK7Mf8qClpA3E5chop/WfKQfiu46sZv1qkCgYAhGdXPcw1lQ1W6KVlrdI6J | ||
29 | qHhiNlVMDXdxZkNvFxQdAiQeXQrbxaZGiMw/J/wSNpUwCAsUzM/4QVMDrfSCDCzl | ||
30 | ZtNQtj4pTlFKKNVQthIjrXEIJUw2jp7IJLBfVSJu5iWxSlmId0f3MsiNizN81N69 | ||
31 | P5Rm/doE3+KHoy8VXGsHcQKBgQCkNh62enqjHWypjex6450qS6f6iWN3PRLLVsw0 | ||
32 | TcQpniZblCaBwVCAKmRUnjOEIdL2/4ZLutnwMTaFG/YEOOfAylMiY8jKV38lNmD9 | ||
33 | X4D78CFr9klxgvS2CRwSE03f2NzmLkLxuKaxldvaxPTfjMkgeO1LFMlNExYBhkuH | ||
34 | 7uQpUQKBgQCKX6qMNh2gSdgG7qyxfTFZ4y5EGOBoKe/dE+IcVF3Vnh6DZVbCAbBL | ||
35 | 5EdFWZSrCnDjA4xiKW55mwp95Ud9EZsZAb13L8V9t82eK+UDBoWlb7VRNYpda/x1 | ||
36 | 5/i4qQJ28x2UNJDStpYFpnp4Ba1lvXjKngIbDPkjU+hbBJ+BNGAIeg== | ||
37 | -----END RSA PRIVATE KEY-----"; | ||
38 | |||
39 | /// Create a mock database to be used in tests | ||
40 | fn mocked_db() -> Db { | ||
41 | let db = create_database(); | ||
42 | |||
43 | db.users.write().insert( | ||
44 | "mock_transaction_source".to_owned(), | ||
45 | User { | ||
46 | user_id: MetuId::new("e254275".to_owned()).unwrap(), | ||
47 | public_key: "-----BEGIN PUBLIC KEY----- | ||
48 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4nU0G4WjkmcQUx0hq6LQ | ||
49 | uV5Q+ACmUFL/OjoYMDwC/O/6pCd1UZgCfgHN2xEffDPznzcTn8OiFRxr4oWyBiny | ||
50 | rUpnY4mhy0SQUwoeCw7YkcHAyhCjNT74aR/ohX0MCj0qRRdbt5ZQXM/GC3HJuXE1 | ||
51 | ptSuhFgQxziItamn8maoJ6JUSVEXVO1NOrrjoM3r7Q+BK2B+sX4/bLZ+VG5g1q2n | ||
52 | EbFdTHS6pHqtZNHQndTmEKwRfh0RYtzEzOXuO6e1gQY42Tujkof40dhGCIU7TeIG | ||
53 | GHwdFxy1niLkXwtHNjV7lnIOkTbx6+sSPamRfQAlZqUWM2Lf5o+7h3qWP3ENB138 | ||
54 | sQIDAQAB | ||
55 | -----END PUBLIC KEY-----" | ||
56 | .to_owned(), | ||
57 | balance: 0, | ||
58 | }, | ||
59 | ); | ||
60 | |||
61 | db.pending_transactions.write().insert( | ||
62 | "hash_value".to_owned(), | ||
63 | Transaction { | ||
64 | by: "source_account".to_owned(), | ||
65 | source: "source_account".to_owned(), | ||
66 | target: "target_account".to_owned(), | ||
67 | amount: 20, | ||
68 | timestamp: chrono::NaiveDate::from_ymd(2021, 04, 09).and_hms(1, 30, 30), | ||
69 | }, | ||
70 | ); | ||
71 | |||
72 | *db.blockchain.write() = Block { | ||
73 | transaction_list: vec![ | ||
74 | "old_transaction_hash_1".to_owned(), | ||
75 | "old_transaction_hash_2".to_owned(), | ||
76 | "old_transaction_hash_3".to_owned(), | ||
77 | ], | ||
78 | nonce: 0, | ||
79 | timestamp: chrono::NaiveDate::from_ymd(2021, 04, 08).and_hms(12, 30, 30), | ||
80 | hash: "not_a_thing_yet".to_owned(), | ||
81 | }; | ||
82 | |||
83 | db | ||
84 | } | ||
85 | |||
86 | fn mocked_jwt() -> String { | ||
87 | let claims = Claims { | ||
88 | tha: "6692e774eba7fb92dc0fe6cf7347591e".to_owned(), | ||
89 | iat: 1618275851, | ||
90 | exp: 1648275851, | ||
91 | }; | ||
92 | let header = Header::new(Algorithm::RS256); | ||
93 | encode( | ||
94 | &header, | ||
95 | &claims, | ||
96 | &EncodingKey::from_rsa_pem(PRIVATE_KEY_PEM.as_bytes()).unwrap(), | ||
97 | ) | ||
98 | .unwrap() | ||
99 | } | ||
100 | |||
101 | /// Create a mock user that is allowed to be in gradecoin to be used in tests | ||
102 | fn priviliged_mocked_user() -> AuthRequest { | ||
103 | AuthRequest { | ||
104 | student_id: String::from("e254275"), | ||
105 | public_key: "NOT IMPLEMENTED".to_owned(), | ||
106 | } | ||
107 | } | ||
108 | |||
109 | /// Create a mock user that is NOT allowed to be in gradecoin to be used in tests | ||
110 | fn unpriviliged_mocked_user() -> AuthRequest { | ||
111 | AuthRequest { | ||
112 | student_id: String::from("foobarbaz"), | ||
113 | public_key: "NOT IMPLEMENTED".to_owned(), | ||
114 | } | ||
115 | } | ||
116 | |||
117 | /// Create a mock transaction to be used in tests | ||
118 | fn mocked_transaction() -> Transaction { | ||
119 | Transaction { | ||
120 | by: "mock_transaction_source".to_owned(), | ||
121 | source: "mock_transaction_source".to_owned(), | ||
122 | target: "mock_transaction_target".to_owned(), | ||
123 | amount: 25, | ||
124 | timestamp: chrono::NaiveDate::from_ymd(2021, 04, 09).and_hms(14, 30, 00), | ||
125 | } | ||
126 | } | ||
127 | |||
128 | /// Create a mock block with a correct mined hash to be used in tests | ||
129 | fn mocked_block() -> Block { | ||
130 | Block { | ||
131 | transaction_list: vec!["hash_value".to_owned()], | ||
132 | nonce: 3831993, | ||
133 | timestamp: chrono::NaiveDate::from_ymd(2021, 04, 08).and_hms(12, 30, 30), | ||
134 | hash: "2b648ffab5d9af1d5d5fc052fc9e51b882fc4fb0c998608c99232f9282000000".to_owned(), | ||
135 | } | ||
136 | } | ||
137 | |||
138 | /// Create a mock block with a wrong hash and nonce | ||
139 | fn mocked_wrong_block() -> Block { | ||
140 | Block { | ||
141 | transaction_list: vec!["foobarbaz".to_owned(), "dazsaz".to_owned()], | ||
142 | nonce: 1000, // can you imagine | ||
143 | timestamp: chrono::NaiveDate::from_ymd(2021, 04, 12).and_hms(05, 29, 30), | ||
144 | hash: "tnarstnarsuthnarsthlarjstk".to_owned(), | ||
145 | } | ||
146 | } | ||
147 | |||
148 | /// Test simple GET request to /transaction, an endpoint that exists | ||
149 | /// https://tools.ietf.org/html/rfc7231#section-6.3.1 | ||
150 | /// We should get the only pending transaction available in the database as json | ||
151 | #[tokio::test] | ||
152 | async fn get_pending_transactions() { | ||
153 | let db = mocked_db(); | ||
154 | |||
155 | let reply = consensus_routes(db); | ||
156 | |||
157 | let res = warp::test::request() | ||
158 | .method("GET") | ||
159 | .path("/transaction") | ||
160 | .reply(&reply) | ||
161 | .await; | ||
162 | |||
163 | assert_eq!(res.status(), StatusCode::OK); | ||
164 | |||
165 | let expected_json_body = r#"[{"by":"source_account","source":"source_account","target":"target_account","amount":20,"timestamp":"2021-04-09T01:30:30"}]"#; | ||
166 | |||
167 | assert_eq!(res.body(), expected_json_body); | ||
168 | } | ||
169 | |||
170 | /// Test simple GET request to /block, an enpoint that exists | ||
171 | /// | ||
172 | /// https://tools.ietf.org/html/rfc7231#section-6.3.1 | ||
173 | /// | ||
174 | /// Should return the single block available in the database as json | ||
175 | #[tokio::test] | ||
176 | async fn get_blockchain() { | ||
177 | let db = mocked_db(); | ||
178 | let filter = consensus_routes(db); | ||
179 | |||
180 | let res = warp::test::request() | ||
181 | .method("GET") | ||
182 | .path("/block") | ||
183 | .reply(&filter) | ||
184 | .await; | ||
185 | |||
186 | assert_eq!(res.status(), StatusCode::OK); | ||
187 | |||
188 | let expected_json_body = r#"{"transaction_list":["old_transaction_hash_1","old_transaction_hash_2","old_transaction_hash_3"],"nonce":0,"timestamp":"2021-04-08T12:30:30","hash":"not_a_thing_yet"}"#; | ||
189 | assert_eq!(res.body(), expected_json_body); | ||
190 | } | ||
191 | |||
192 | /// Test a simple GET request to a nonexisting path | ||
193 | /// https://tools.ietf.org/html/rfc7231#section-6.5.4 | ||
194 | /// Should respond with 404 and stop | ||
195 | #[tokio::test] | ||
196 | async fn get_nonexisting_path_404() { | ||
197 | let db = mocked_db(); | ||
198 | let filter = consensus_routes(db); | ||
199 | |||
200 | let res = warp::test::request() | ||
201 | .method("GET") | ||
202 | .path("/this_path_does_not_exist") | ||
203 | .reply(&filter) | ||
204 | .await; | ||
205 | |||
206 | assert_eq!(res.status(), StatusCode::NOT_FOUND); | ||
207 | } | ||
208 | |||
209 | /// Test a POST request to /transaction, an endpoint that exists | ||
210 | /// | ||
211 | /// https://tools.ietf.org/html/rfc7231#section-6.3.2 | ||
212 | /// | ||
213 | /// Should accept the json request, create | ||
214 | /// the transaction and add it to pending transactions in the db | ||
215 | #[tokio::test] | ||
216 | async fn post_auth_json_201() { | ||
217 | let db = mocked_db(); | ||
218 | let filter = consensus_routes(db.clone()); | ||
219 | |||
220 | let res = warp::test::request() | ||
221 | .method("POST") | ||
222 | .json(&mocked_transaction()) | ||
223 | .header("Authorization", "Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0aGEiOiI2NjkyZTc3NGViYTdmYjkyZGMwZmU2Y2Y3MzQ3NTkxZSIsImlhdCI6MTYxODI2MDY0MSwiZXhwIjoxNzE4MjYwNjQxfQ.M_FVVE5F_aYcDsprkcqV8n2DAhnM6jImAUEXChI9qYn55meE_0Pmp6AaJlTzclYUT1ZUQfFuehYTYu5UkigQ_AimDhqM5VWxPdnyfTQscV916arbNn4qXW6-3oHGUR93xK7-mX6mxeXyDZLxr1SD_JEvVzGWTU4Xo9SMYSIcaHjROAg_ChxJdD4WLe5T4He7O443jpXdAeeVVYfKoJyBfINx_bxiF58-ni1vur9q6-nrjnMw6sMMbtWD3qvzKZHN7HzfwNXM-90D-9VX1KiaJN05jIxLzCYacLeBUH595I4--XfgpLmqrV_P3Sucmny0yvagbZtjYjswmf0DjR99ug") | ||
224 | .path("/transaction") | ||
225 | .reply(&filter) | ||
226 | .await; | ||
227 | |||
228 | println!("{:?}", res.body()); | ||
229 | assert_eq!(res.status(), StatusCode::CREATED); | ||
230 | assert_eq!(db.pending_transactions.read().len(), 2); | ||
231 | } | ||
232 | |||
233 | /// Test a POST request to /transaction, an endpoint that exists with an incorrect JWT in the | ||
234 | /// Authorization header | ||
235 | /// | ||
236 | /// https://tools.ietf.org/html/rfc7231#section-6.3.2 | ||
237 | /// | ||
238 | /// Should reject the request | ||
239 | #[tokio::test] | ||
240 | async fn post_auth_json_400() { | ||
241 | let db = mocked_db(); | ||
242 | let filter = consensus_routes(db.clone()); | ||
243 | |||
244 | let res = warp::test::request() | ||
245 | .method("POST") | ||
246 | .json(&mocked_transaction()) | ||
247 | .header( | ||
248 | "Authorization", | ||
249 | "Bearer aaaaaaaasdlkjaldkasljdaskjlaaaaaaaaaaaaaa", | ||
250 | ) | ||
251 | .path("/transaction") | ||
252 | .reply(&filter) | ||
253 | .await; | ||
254 | |||
255 | assert_eq!(res.status(), StatusCode::BAD_REQUEST); | ||
256 | assert_eq!(db.pending_transactions.read().len(), 1); | ||
257 | } | ||
258 | |||
259 | /// Test a POST request to /block, an endpoint that exists | ||
260 | /// | ||
261 | /// https://tools.ietf.org/html/rfc7231#section-6.3.2 | ||
262 | /// | ||
263 | /// Should accept the json request, create | ||
264 | /// the block | ||
265 | #[tokio::test] | ||
266 | async fn post_block_201() { | ||
267 | let db = mocked_db(); | ||
268 | let filter = consensus_routes(db.clone()); | ||
269 | |||
270 | let res = warp::test::request() | ||
271 | .method("POST") | ||
272 | .json(&mocked_block()) | ||
273 | .path("/block") | ||
274 | .reply(&filter) | ||
275 | .await; | ||
276 | |||
277 | assert_eq!(res.status(), StatusCode::CREATED); | ||
278 | assert_eq!( | ||
279 | *db.blockchain.read().hash, | ||
280 | "2b648ffab5d9af1d5d5fc052fc9e51b882fc4fb0c998608c99232f9282000000".to_owned() | ||
281 | ); | ||
282 | } | ||
283 | |||
284 | /// Test a POST request to /block, an endpoint that exists | ||
285 | /// | ||
286 | /// https://tools.ietf.org/html/rfc7231#section-6.3.2 | ||
287 | /// | ||
288 | /// Should reject the block because of the wrong hash/nonce | ||
289 | /// // TODO: split this into two tests | ||
290 | #[tokio::test] | ||
291 | async fn post_block_wrong_hash() { | ||
292 | let db = mocked_db(); | ||
293 | let filter = consensus_routes(db.clone()); | ||
294 | |||
295 | let res = warp::test::request() | ||
296 | .method("POST") | ||
297 | .json(&mocked_wrong_block()) | ||
298 | .path("/block") | ||
299 | .reply(&filter) | ||
300 | .await; | ||
301 | |||
302 | assert_eq!(res.status(), StatusCode::BAD_REQUEST); | ||
303 | } | ||
304 | |||
305 | /// Test a POST request to /register, an endpoint that exists | ||
306 | /// | ||
307 | /// https://tools.ietf.org/html/rfc7231#section-6.3.2 | ||
308 | /// | ||
309 | /// Should accept the json request, create a new user and | ||
310 | /// add it to the user hashmap in the db | ||
311 | #[tokio::test] | ||
312 | async fn post_register_priviliged_user() { | ||
313 | let db = mocked_db(); | ||
314 | let filter = consensus_routes(db.clone()); | ||
315 | |||
316 | let res = warp::test::request() | ||
317 | .method("POST") | ||
318 | .json(&priviliged_mocked_user()) | ||
319 | .path("/register") | ||
320 | .reply(&filter) | ||
321 | .await; | ||
322 | |||
323 | println!("{:?}", res.body()); | ||
324 | assert_eq!(res.status(), StatusCode::CREATED); | ||
325 | assert_eq!(db.users.read().len(), 2); | ||
326 | } | ||
327 | |||
328 | /// Test a POST request to /transaction, an endpoint that exists | ||
329 | /// https://tools.ietf.org/html/rfc7231#section-6.3.2 | ||
330 | /// Should NOT accept the json request as the user is unpriviliged | ||
331 | #[tokio::test] | ||
332 | async fn post_register_unpriviliged_user() { | ||
333 | let db = mocked_db(); | ||
334 | let filter = consensus_routes(db.clone()); | ||
335 | |||
336 | let res = warp::test::request() | ||
337 | .method("POST") | ||
338 | .json(&unpriviliged_mocked_user()) | ||
339 | .path("/register") | ||
340 | .reply(&filter) | ||
341 | .await; | ||
342 | |||
343 | println!("{:?}", res.body()); | ||
344 | assert_eq!(res.status(), StatusCode::BAD_REQUEST); | ||
345 | assert_eq!(db.users.read().len(), 1); | ||
346 | } | ||
347 | |||
348 | /// Test a POST request to /transaction, an endpoint that exists with a longer than expected | ||
349 | /// payload | ||
350 | /// | ||
351 | /// https://tools.ietf.org/html/rfc7231#section-6.5.11 | ||
352 | /// | ||
353 | /// Should return 413 to user | ||
354 | #[tokio::test] | ||
355 | async fn post_too_long_content_413() { | ||
356 | let db = mocked_db(); | ||
357 | let filter = consensus_routes(db); | ||
358 | |||
359 | let res = warp::test::request() | ||
360 | .method("POST") | ||
361 | .header("content-length", 1024 * 36) | ||
362 | .path("/transaction") | ||
363 | .reply(&filter) | ||
364 | .await; | ||
365 | |||
366 | assert_eq!(res.status(), StatusCode::PAYLOAD_TOO_LARGE); | ||
367 | } | ||
368 | } | ||
369 | |||
370 | // TODO: POST block without correct transactions test <09-04-21, yigit> // | ||
371 | // TODO: POST transaction while that source has pending transaction test <09-04-21, yigit> // | ||