diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/routes.rs | 148 |
1 files changed, 148 insertions, 0 deletions
diff --git a/src/routes.rs b/src/routes.rs index 23fcd29..1ab6eff 100644 --- a/src/routes.rs +++ b/src/routes.rs | |||
@@ -46,3 +46,151 @@ pub fn block_propose(db: Db) -> impl Filter<Extract = impl Reply, Error = Reject | |||
46 | .and_then(handlers::propose_block) | 46 | .and_then(handlers::propose_block) |
47 | } | 47 | } |
48 | 48 | ||
49 | #[cfg(test)] | ||
50 | mod tests { | ||
51 | use super::*; | ||
52 | |||
53 | use chrono::prelude::*; | ||
54 | use parking_lot::RwLock; | ||
55 | use std::sync::Arc; | ||
56 | use warp::http::StatusCode; | ||
57 | |||
58 | use crate::schema; | ||
59 | use crate::schema::{Block, Transaction}; | ||
60 | |||
61 | /// Create a mock database to be used in tests | ||
62 | fn mocked_db() -> Db { | ||
63 | let db = schema::create_database(); | ||
64 | |||
65 | db.pending_transactions.write().insert( | ||
66 | "hash_value".to_owned(), | ||
67 | Transaction { | ||
68 | source: "source_account".to_owned(), | ||
69 | target: "target_account".to_owned(), | ||
70 | amount: 20, | ||
71 | timestamp: chrono::NaiveDate::from_ymd(2021, 04, 09).and_hms(1, 30, 30), | ||
72 | }, | ||
73 | ); | ||
74 | |||
75 | db.blockchain.write().push(Block { | ||
76 | transaction_list: vec![ | ||
77 | "old_transaction_hash_1".to_owned(), | ||
78 | "old_transaction_hash_2".to_owned(), | ||
79 | "old_transaction_hash_3".to_owned(), | ||
80 | ], | ||
81 | nonce: "not_a_thing_yet".to_owned(), | ||
82 | timestamp: chrono::NaiveDate::from_ymd(2021, 04, 08).and_hms(12, 30, 30), | ||
83 | hash: "not_a_thing_yet".to_owned(), | ||
84 | }); | ||
85 | |||
86 | db | ||
87 | } | ||
88 | |||
89 | /// Create a mock transaction to be used in tests | ||
90 | fn mocked_transaction() -> Transaction { | ||
91 | Transaction { | ||
92 | source: "mock_transaction_source".to_owned(), | ||
93 | target: "mock_transaction_target".to_owned(), | ||
94 | amount: 25, | ||
95 | timestamp: chrono::NaiveDate::from_ymd(2021, 04, 09).and_hms(14, 30, 00), | ||
96 | } | ||
97 | } | ||
98 | |||
99 | /// Test simple GET request to /transaction, resource that exists | ||
100 | /// https://tools.ietf.org/html/rfc7231#section-6.3.1 | ||
101 | /// We should get the only pending transaction available in the database as json | ||
102 | #[tokio::test] | ||
103 | async fn get_pending_transactions() { | ||
104 | let db = mocked_db(); | ||
105 | |||
106 | let reply = consensus_routes(db); | ||
107 | |||
108 | let res = warp::test::request() | ||
109 | .method("GET") | ||
110 | .path("/transaction") | ||
111 | .reply(&reply) | ||
112 | .await; | ||
113 | |||
114 | assert_eq!(res.status(), StatusCode::OK); | ||
115 | |||
116 | let expected_json_body = r#"[{"source":"source_account","target":"target_account","amount":20,"timestamp":"2021-04-09T01:30:30"}]"#; | ||
117 | |||
118 | assert_eq!(res.body(), expected_json_body); | ||
119 | } | ||
120 | |||
121 | /// Test simple GET request to /block, resource that exists | ||
122 | /// https://tools.ietf.org/html/rfc7231#section-6.3.1 | ||
123 | /// Should return the single block available in the database as json | ||
124 | #[tokio::test] | ||
125 | async fn get_blockchain() { | ||
126 | let db = mocked_db(); | ||
127 | let filter = consensus_routes(db); | ||
128 | |||
129 | let res = warp::test::request() | ||
130 | .method("GET") | ||
131 | .path("/block") | ||
132 | .reply(&filter) | ||
133 | .await; | ||
134 | |||
135 | assert_eq!(res.status(), StatusCode::OK); | ||
136 | |||
137 | let expected_json_body = r#"[{"transaction_list":["old_transaction_hash_1","old_transaction_hash_2","old_transaction_hash_3"],"nonce":"not_a_thing_yet","timestamp":"2021-04-08T12:30:30","hash":"not_a_thing_yet"}]"#; | ||
138 | assert_eq!(res.body(), expected_json_body); | ||
139 | } | ||
140 | |||
141 | /// Test a simple GET request to a nonexisting path | ||
142 | /// https://tools.ietf.org/html/rfc7231#section-6.5.4 | ||
143 | /// Should respond with 404 and stop | ||
144 | #[tokio::test] | ||
145 | async fn get_nonexisting_path_404() { | ||
146 | let db = mocked_db(); | ||
147 | let filter = consensus_routes(db); | ||
148 | |||
149 | let res = warp::test::request() | ||
150 | .method("GET") | ||
151 | .path("/this_path_does_not_exist") | ||
152 | .reply(&filter) | ||
153 | .await; | ||
154 | |||
155 | assert_eq!(res.status(), StatusCode::NOT_FOUND); | ||
156 | } | ||
157 | |||
158 | /// Test a POST request to /transaction, a resource that exists | ||
159 | /// https://tools.ietf.org/html/rfc7231#section-6.3.2 | ||
160 | /// Should accept the json request, create | ||
161 | /// the transaction and add it to pending transactions in the db | ||
162 | #[tokio::test] | ||
163 | async fn post_json_201() { | ||
164 | let db = mocked_db(); | ||
165 | let filter = consensus_routes(db.clone()); | ||
166 | |||
167 | let res = warp::test::request() | ||
168 | .method("POST") | ||
169 | .json(&mocked_transaction()) | ||
170 | .path("/transaction") | ||
171 | .reply(&filter) | ||
172 | .await; | ||
173 | |||
174 | assert_eq!(res.status(), StatusCode::CREATED); | ||
175 | assert_eq!(db.pending_transactions.read().len(), 2); | ||
176 | } | ||
177 | |||
178 | /// Test a POST request to /transaction, a resource that exists with a longer than expected | ||
179 | /// payload | ||
180 | /// https://tools.ietf.org/html/rfc7231#section-6.5.11 | ||
181 | /// Should return 413 to user | ||
182 | #[tokio::test] | ||
183 | async fn post_too_long_content_413() { | ||
184 | let db = mocked_db(); | ||
185 | let filter = consensus_routes(db); | ||
186 | |||
187 | let res = warp::test::request() | ||
188 | .method("POST") | ||
189 | .header("content-length", 1024 * 36) | ||
190 | .path("/transaction") | ||
191 | .reply(&filter) | ||
192 | .await; | ||
193 | |||
194 | assert_eq!(res.status(), StatusCode::PAYLOAD_TOO_LARGE); | ||
195 | } | ||
196 | } | ||