summaryrefslogtreecommitdiffstats
path: root/tests/route_tests.rs
diff options
context:
space:
mode:
Diffstat (limited to 'tests/route_tests.rs')
-rw-r--r--tests/route_tests.rs371
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)]
2mod 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-----
12MIIEpAIBAAKCAQEA4nU0G4WjkmcQUx0hq6LQuV5Q+ACmUFL/OjoYMDwC/O/6pCd1
13UZgCfgHN2xEffDPznzcTn8OiFRxr4oWyBinyrUpnY4mhy0SQUwoeCw7YkcHAyhCj
14NT74aR/ohX0MCj0qRRdbt5ZQXM/GC3HJuXE1ptSuhFgQxziItamn8maoJ6JUSVEX
15VO1NOrrjoM3r7Q+BK2B+sX4/bLZ+VG5g1q2nEbFdTHS6pHqtZNHQndTmEKwRfh0R
16YtzEzOXuO6e1gQY42Tujkof40dhGCIU7TeIGGHwdFxy1niLkXwtHNjV7lnIOkTbx
176+sSPamRfQAlZqUWM2Lf5o+7h3qWP3ENB138sQIDAQABAoIBAD23nYTmrganag6M
18wPFrBSGP79c3Lhx0EjUHQjJbGKFgsdltG48qM3ut+DF9ACy0Z+/7bbC7+39vaIOq
191jLR2d6aiYTaLKseO4s2FawD1sgamvU3BZPsXn0gAhnnU5Gyy8Nas1dccvhoc9wI
20neaZUPrvucQ90AzLfo6r9yacDbYHB1lOyomApUvpJxOgHISGEtc9qGPDrdH19aF0
218fCv2bbQRh+TChgN3IB0o5w0wXaI7YAyAouAv/AzHCoEMpt7OGjFTkjh/ujlPL9O
22+FLuJNsQRHDN0gJo2pcvwGwDCsioMixQ9bZ7ZrUu2BNpEQygyeSbj9ZI1iRvhosO
23JU3rwEECgYEA9MppTYA6A9WQbCCwPH1QMpUAmPNVSWVhUVag4lGOEhdCDRcz9ook
24DohQMKctiEB1luKuvDokxo0uMOfMO9/YwjsRB7qjQip7Th1zMJIjD+A+juLzHK4r
25/RiRtWYGAnF8mptDvE+93JsPb3C/lQLvIhio5GQYWBqPJu6SpeosIskCgYEA7NPi
26Gbffzr2UQhW8BNKmctEEh8yFRVojFo3wwwWxSNUVXGSmSm31CL+Q8h817R+2OkPV
271ZMUOBU4UJiqFt28kIvTDFqbAJlJQGCpY2mY7OLQiD2A+TVLcFrHmoCaPfCAK1Qd
28hQ0PmFK7Mf8qClpA3E5chop/WfKQfiu46sZv1qkCgYAhGdXPcw1lQ1W6KVlrdI6J
29qHhiNlVMDXdxZkNvFxQdAiQeXQrbxaZGiMw/J/wSNpUwCAsUzM/4QVMDrfSCDCzl
30ZtNQtj4pTlFKKNVQthIjrXEIJUw2jp7IJLBfVSJu5iWxSlmId0f3MsiNizN81N69
31P5Rm/doE3+KHoy8VXGsHcQKBgQCkNh62enqjHWypjex6450qS6f6iWN3PRLLVsw0
32TcQpniZblCaBwVCAKmRUnjOEIdL2/4ZLutnwMTaFG/YEOOfAylMiY8jKV38lNmD9
33X4D78CFr9klxgvS2CRwSE03f2NzmLkLxuKaxldvaxPTfjMkgeO1LFMlNExYBhkuH
347uQpUQKBgQCKX6qMNh2gSdgG7qyxfTFZ4y5EGOBoKe/dE+IcVF3Vnh6DZVbCAbBL
355EdFWZSrCnDjA4xiKW55mwp95Ud9EZsZAb13L8V9t82eK+UDBoWlb7VRNYpda/x1
365/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-----
48MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4nU0G4WjkmcQUx0hq6LQ
49uV5Q+ACmUFL/OjoYMDwC/O/6pCd1UZgCfgHN2xEffDPznzcTn8OiFRxr4oWyBiny
50rUpnY4mhy0SQUwoeCw7YkcHAyhCjNT74aR/ohX0MCj0qRRdbt5ZQXM/GC3HJuXE1
51ptSuhFgQxziItamn8maoJ6JUSVEXVO1NOrrjoM3r7Q+BK2B+sX4/bLZ+VG5g1q2n
52EbFdTHS6pHqtZNHQndTmEKwRfh0RYtzEzOXuO6e1gQY42Tujkof40dhGCIU7TeIG
53GHwdFxy1niLkXwtHNjV7lnIOkTbx6+sSPamRfQAlZqUWM2Lf5o+7h3qWP3ENB138
54sQIDAQAB
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> //