A simple to-do app focused on tasks that can be completed within a specific time span.
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

todo insertion tests

tobinio e083205e afba7f2e

+569 -22
+105
Cargo.lock
··· 47 47 dependencies = [ 48 48 "dotenvy", 49 49 "reqwest 0.13.2", 50 + "serde", 51 + "serde_json", 52 + "serial_test", 50 53 "tokio", 51 54 "types", 55 + "uuid", 52 56 ] 53 57 54 58 [[package]] ··· 962 966 checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" 963 967 964 968 [[package]] 969 + name = "lock_api" 970 + version = "0.4.14" 971 + source = "registry+https://github.com/rust-lang/crates.io-index" 972 + checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" 973 + dependencies = [ 974 + "scopeguard", 975 + ] 976 + 977 + [[package]] 965 978 name = "log" 966 979 version = "0.4.29" 967 980 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1113 1126 checksum = "d211803b9b6b570f68772237e415a029d5a50c65d382910b879fb19d3271f94d" 1114 1127 1115 1128 [[package]] 1129 + name = "parking_lot" 1130 + version = "0.12.5" 1131 + source = "registry+https://github.com/rust-lang/crates.io-index" 1132 + checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" 1133 + dependencies = [ 1134 + "lock_api", 1135 + "parking_lot_core", 1136 + ] 1137 + 1138 + [[package]] 1139 + name = "parking_lot_core" 1140 + version = "0.9.12" 1141 + source = "registry+https://github.com/rust-lang/crates.io-index" 1142 + checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" 1143 + dependencies = [ 1144 + "cfg-if", 1145 + "libc", 1146 + "redox_syscall", 1147 + "smallvec", 1148 + "windows-link", 1149 + ] 1150 + 1151 + [[package]] 1116 1152 name = "percent-encoding" 1117 1153 version = "2.3.2" 1118 1154 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1277 1313 checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" 1278 1314 dependencies = [ 1279 1315 "getrandom 0.3.4", 1316 + ] 1317 + 1318 + [[package]] 1319 + name = "redox_syscall" 1320 + version = "0.5.18" 1321 + source = "registry+https://github.com/rust-lang/crates.io-index" 1322 + checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" 1323 + dependencies = [ 1324 + "bitflags", 1280 1325 ] 1281 1326 1282 1327 [[package]] ··· 1566 1611 ] 1567 1612 1568 1613 [[package]] 1614 + name = "scc" 1615 + version = "2.4.0" 1616 + source = "registry+https://github.com/rust-lang/crates.io-index" 1617 + checksum = "46e6f046b7fef48e2660c57ed794263155d713de679057f2d0c169bfc6e756cc" 1618 + dependencies = [ 1619 + "sdd", 1620 + ] 1621 + 1622 + [[package]] 1569 1623 name = "schannel" 1570 1624 version = "0.1.29" 1571 1625 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1573 1627 dependencies = [ 1574 1628 "windows-sys 0.61.2", 1575 1629 ] 1630 + 1631 + [[package]] 1632 + name = "scopeguard" 1633 + version = "1.2.0" 1634 + source = "registry+https://github.com/rust-lang/crates.io-index" 1635 + checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 1636 + 1637 + [[package]] 1638 + name = "sdd" 1639 + version = "3.0.10" 1640 + source = "registry+https://github.com/rust-lang/crates.io-index" 1641 + checksum = "490dcfcbfef26be6800d11870ff2df8774fa6e86d047e3e8c8a76b25655e41ca" 1576 1642 1577 1643 [[package]] 1578 1644 name = "security-framework" ··· 1656 1722 "itoa", 1657 1723 "ryu", 1658 1724 "serde", 1725 + ] 1726 + 1727 + [[package]] 1728 + name = "serial_test" 1729 + version = "3.4.0" 1730 + source = "registry+https://github.com/rust-lang/crates.io-index" 1731 + checksum = "911bd979bf1070a3f3aa7b691a3b3e9968f339ceeec89e08c280a8a22207a32f" 1732 + dependencies = [ 1733 + "futures-executor", 1734 + "futures-util", 1735 + "log", 1736 + "once_cell", 1737 + "parking_lot", 1738 + "scc", 1739 + "serial_test_derive", 1740 + ] 1741 + 1742 + [[package]] 1743 + name = "serial_test_derive" 1744 + version = "3.4.0" 1745 + source = "registry+https://github.com/rust-lang/crates.io-index" 1746 + checksum = "0a7d91949b85b0d2fb687445e448b40d322b6b3e4af6b44a29b21d9a5f33e6d9" 1747 + dependencies = [ 1748 + "proc-macro2", 1749 + "quote", 1750 + "syn 2.0.117", 1659 1751 ] 1660 1752 1661 1753 [[package]] ··· 2024 2116 "chrono", 2025 2117 "serde", 2026 2118 "serde_json", 2119 + "uuid", 2027 2120 ] 2028 2121 2029 2122 [[package]] ··· 2088 2181 version = "1.0.4" 2089 2182 source = "registry+https://github.com/rust-lang/crates.io-index" 2090 2183 checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" 2184 + 2185 + [[package]] 2186 + name = "uuid" 2187 + version = "1.23.1" 2188 + source = "registry+https://github.com/rust-lang/crates.io-index" 2189 + checksum = "ddd74a9687298c6858e9b88ec8935ec45d22e8fd5e6394fa1bd4e99a87789c76" 2190 + dependencies = [ 2191 + "getrandom 0.4.2", 2192 + "js-sys", 2193 + "serde_core", 2194 + "wasm-bindgen", 2195 + ] 2091 2196 2092 2197 [[package]] 2093 2198 name = "valuable"
+8 -2
api/Cargo.toml
··· 6 6 [dependencies] 7 7 types = { path = "../libs/types" } 8 8 9 - dotenvy = "0.15.7" 10 - reqwest = { version = "0.13.2", features = ["json"] } 11 9 tokio = { version = "1.52.1", features = ["fs", "macros", "rt-multi-thread"] } 10 + uuid = { version = "1.23.1", features = ["v4"] } 11 + 12 + serde = { version = "1.0.228", features = ["derive"] } 13 + serde_json = "1.0.149" 14 + 15 + serial_test = "3.4.0" 16 + reqwest = { version = "0.13.2", features = ["json"] } 17 + dotenvy = "0.15.7"
+44 -3
api/tests/common/client.rs
··· 1 - use reqwest::header; 1 + use reqwest::{Response, header}; 2 + use types::Todo; 3 + 4 + pub struct Client { 5 + pub api: reqwest::Client, 6 + pub base_url: String, 7 + } 8 + 9 + impl Client { 10 + pub async fn get_todos(&self) -> Response { 11 + self.api 12 + .get(&format!("{}/api/todos", self.base_url)) 13 + .send() 14 + .await 15 + .unwrap() 16 + } 17 + 18 + pub async fn get_todos_json(&self) -> Vec<Todo> { 19 + let response = self.get_todos().await; 20 + 21 + assert_eq!(response.status().as_str(), "200"); 22 + response.json().await.unwrap() 23 + } 24 + 25 + pub async fn add_todo(&self, todo: serde_json::Value) -> Response { 26 + self.api 27 + .post(&format!("{}/api/todos", self.base_url)) 28 + .json(&todo) 29 + .send() 30 + .await 31 + .unwrap() 32 + } 33 + 34 + pub async fn add_todo_json(&self, todo: serde_json::Value) -> Todo { 35 + let response = self.add_todo(todo).await; 36 + assert_eq!(response.status().as_str(), "200"); 37 + response.json().await.unwrap() 38 + } 39 + } 2 40 3 - pub fn get_client() -> (reqwest::Client, String) { 41 + pub fn get_client() -> Client { 4 42 dotenvy::dotenv().unwrap(); 5 43 let token = std::env::var("API_TOKEN").expect("no API_TOKEN set"); 6 44 let base_url = std::env::var("BASE_URL").expect("no BASE_URL set"); ··· 16 54 .build() 17 55 .unwrap(); 18 56 19 - (client, base_url) 57 + Client { 58 + api: client, 59 + base_url, 60 + } 20 61 }
+403 -8
api/tests/todos.rs
··· 1 1 use crate::common::db::clear_db; 2 2 use common::client::get_client; 3 + use serde_json::json; 4 + use serial_test::serial; 3 5 use types::Todo; 6 + use uuid::Uuid; 4 7 5 8 mod common; 6 9 7 10 #[tokio::test] 8 - async fn get_projects() { 9 - let (client, base_url) = get_client(); 11 + #[serial] 12 + async fn get_empty_todos() { 13 + let client = get_client(); 10 14 clear_db().await; 11 15 16 + let response = client.get_todos().await; 17 + assert_eq!(response.status().as_str(), "200"); 18 + let json = response.json::<Vec<Todo>>().await.unwrap(); 19 + 20 + assert_eq!(json.len(), 0); 21 + } 22 + 23 + #[tokio::test] 24 + #[serial] 25 + async fn add_todo() { 26 + let client = get_client(); 27 + clear_db().await; 28 + 29 + let todo_uuid = Uuid::new_v4(); 30 + let todo = Todo { 31 + uuid: todo_uuid, 32 + title: "Test Todo".to_string(), 33 + note: "Test Note".to_string(), 34 + time: None, 35 + tags: vec![], 36 + category: None, 37 + checks: vec![], 38 + }; 39 + 12 40 let response = client 13 - .get(&format!("{}/api/todos", base_url)) 14 - .send() 15 - .await 16 - .unwrap(); 41 + .add_todo(json!( 42 + { 43 + "data": { 44 + "uuid": todo_uuid, 45 + "title": "Test Todo", 46 + "note": "Test Note", 47 + "tags": [], 48 + "checks": [], 49 + } 50 + })) 51 + .await; 17 52 18 53 assert_eq!(response.status().as_str(), "200"); 19 54 20 - let json = response.json::<Vec<Todo>>().await.unwrap(); 21 - assert_eq!(json.len(), 0); 55 + let json = response.json::<Todo>().await.unwrap(); 56 + assert_eq!(json, todo); 57 + 58 + let todos = client.get_todos_json().await; 59 + assert_eq!(todos.len(), 1); 60 + assert_eq!(todos.first().unwrap(), &todo); 61 + } 62 + 63 + #[tokio::test] 64 + #[serial] 65 + async fn add_todo_with_labels() { 66 + let client = get_client(); 67 + clear_db().await; 68 + 69 + let todo_uuid = Uuid::new_v4(); 70 + let tag_uuid = Uuid::new_v4(); 71 + let category_uuid = Uuid::new_v4(); 72 + 73 + let todo = Todo { 74 + uuid: todo_uuid, 75 + title: "Test Todo".to_string(), 76 + note: "Test Note".to_string(), 77 + time: None, 78 + tags: vec![tag_uuid], 79 + category: Some(category_uuid), 80 + checks: vec![], 81 + }; 82 + 83 + let json = client 84 + .add_todo_json(json!( 85 + { 86 + "data": { 87 + "uuid": todo_uuid, 88 + "title": "Test Todo", 89 + "note": "Test Note", 90 + "tags": [tag_uuid], 91 + "category": category_uuid, 92 + "checks": [], 93 + } 94 + } 95 + )) 96 + .await; 97 + 98 + assert_eq!(json, todo); 99 + } 100 + 101 + #[tokio::test] 102 + #[serial] 103 + async fn add_mutliple_todos() { 104 + let client = get_client(); 105 + clear_db().await; 106 + 107 + let todo1_uuid = Uuid::new_v4(); 108 + let todo2_uuid = Uuid::new_v4(); 109 + let todo3_uuid = Uuid::new_v4(); 110 + 111 + client 112 + .add_todo_json(json!( 113 + { 114 + "data": { 115 + "uuid": todo1_uuid, 116 + "title": "Test Todo", 117 + "note": "Test Note", 118 + "tags": [], 119 + "checks": [], 120 + } 121 + } 122 + )) 123 + .await; 124 + client 125 + .add_todo_json(json!( 126 + { 127 + "data": { 128 + "uuid": todo2_uuid, 129 + "title": "Test Todo", 130 + "note": "Test Note", 131 + "tags": [], 132 + "checks": [], 133 + } 134 + } 135 + )) 136 + .await; 137 + client 138 + .add_todo_json(json!( 139 + { 140 + "data": { 141 + "uuid": todo3_uuid, 142 + "title": "Test Todo", 143 + "note": "Test Note", 144 + "tags": [], 145 + "checks": [], 146 + } 147 + } 148 + )) 149 + .await; 150 + 151 + let todos = client.get_todos_json().await; 152 + 153 + assert_eq!(todos.len(), 3); 154 + assert_eq!(todos[2].uuid, todo1_uuid); 155 + assert_eq!(todos[1].uuid, todo2_uuid); 156 + assert_eq!(todos[0].uuid, todo3_uuid); 157 + } 158 + 159 + #[tokio::test] 160 + #[serial] 161 + async fn add_mutliple_bottom_todos() { 162 + let client = get_client(); 163 + clear_db().await; 164 + 165 + let todo1_uuid = Uuid::new_v4(); 166 + let todo2_uuid = Uuid::new_v4(); 167 + let todo3_uuid = Uuid::new_v4(); 168 + 169 + client 170 + .add_todo_json(json!( 171 + { 172 + "data": { 173 + "uuid": todo1_uuid, 174 + "title": "Test Todo", 175 + "note": "Test Note", 176 + "tags": [], 177 + "checks": [], 178 + }, 179 + "position": "bottom" 180 + } 181 + )) 182 + .await; 183 + client 184 + .add_todo_json(json!( 185 + { 186 + "data": { 187 + "uuid": todo2_uuid, 188 + "title": "Test Todo", 189 + "note": "Test Note", 190 + "tags": [], 191 + "checks": [], 192 + }, 193 + "position": "bottom" 194 + } 195 + )) 196 + .await; 197 + client 198 + .add_todo_json(json!( 199 + { 200 + "data": { 201 + "uuid": todo3_uuid, 202 + "title": "Test Todo", 203 + "note": "Test Note", 204 + "tags": [], 205 + "checks": [], 206 + }, 207 + "position": "bottom" 208 + } 209 + )) 210 + .await; 211 + 212 + let todos = client.get_todos_json().await; 213 + 214 + assert_eq!(todos.len(), 3); 215 + assert_eq!(todos[0].uuid, todo1_uuid); 216 + assert_eq!(todos[1].uuid, todo2_uuid); 217 + assert_eq!(todos[2].uuid, todo3_uuid); 218 + } 219 + 220 + #[tokio::test] 221 + #[serial] 222 + async fn add_mutliple_top_todos() { 223 + let client = get_client(); 224 + clear_db().await; 225 + 226 + let todo1_uuid = Uuid::new_v4(); 227 + let todo2_uuid = Uuid::new_v4(); 228 + let todo3_uuid = Uuid::new_v4(); 229 + 230 + client 231 + .add_todo_json(json!( 232 + { 233 + "data": { 234 + "uuid": todo1_uuid, 235 + "title": "Test Todo", 236 + "note": "Test Note", 237 + "tags": [], 238 + "checks": [], 239 + }, 240 + "position": "top" 241 + } 242 + )) 243 + .await; 244 + client 245 + .add_todo_json(json!( 246 + { 247 + "data": { 248 + "uuid": todo2_uuid, 249 + "title": "Test Todo", 250 + "note": "Test Note", 251 + "tags": [], 252 + "checks": [], 253 + }, 254 + "position": "top" 255 + } 256 + )) 257 + .await; 258 + client 259 + .add_todo_json(json!( 260 + { 261 + "data": { 262 + "uuid": todo3_uuid, 263 + "title": "Test Todo", 264 + "note": "Test Note", 265 + "tags": [], 266 + "checks": [], 267 + }, 268 + "position": "top" 269 + } 270 + )) 271 + .await; 272 + 273 + let todos = client.get_todos_json().await; 274 + 275 + assert_eq!(todos.len(), 3); 276 + assert_eq!(todos[2].uuid, todo1_uuid); 277 + assert_eq!(todos[1].uuid, todo2_uuid); 278 + assert_eq!(todos[0].uuid, todo3_uuid); 279 + } 280 + 281 + #[tokio::test] 282 + #[serial] 283 + async fn add_mutliple_mixed_todos() { 284 + let client = get_client(); 285 + clear_db().await; 286 + 287 + let todo1_uuid = Uuid::new_v4(); 288 + let todo2_uuid = Uuid::new_v4(); 289 + let todo3_uuid = Uuid::new_v4(); 290 + 291 + client 292 + .add_todo_json(json!( 293 + { 294 + "data": { 295 + "uuid": todo1_uuid, 296 + "title": "Test Todo", 297 + "note": "Test Note", 298 + "tags": [], 299 + "checks": [], 300 + } 301 + } 302 + )) 303 + .await; 304 + client 305 + .add_todo_json(json!( 306 + { 307 + "data": { 308 + "uuid": todo2_uuid, 309 + "title": "Test Todo", 310 + "note": "Test Note", 311 + "tags": [], 312 + "checks": [], 313 + }, 314 + "position": "bottom" 315 + } 316 + )) 317 + .await; 318 + client 319 + .add_todo_json(json!( 320 + { 321 + "data": { 322 + "uuid": todo3_uuid, 323 + "title": "Test Todo", 324 + "note": "Test Note", 325 + "tags": [], 326 + "checks": [], 327 + }, 328 + "position": "top" 329 + } 330 + )) 331 + .await; 332 + 333 + let todos = client.get_todos_json().await; 334 + 335 + assert_eq!(todos.len(), 3); 336 + assert_eq!(todos[0].uuid, todo3_uuid); 337 + assert_eq!(todos[1].uuid, todo1_uuid); 338 + assert_eq!(todos[2].uuid, todo2_uuid); 339 + } 340 + 341 + #[tokio::test] 342 + #[serial] 343 + async fn add_mutliple_reverencing_todos() { 344 + let client = get_client(); 345 + clear_db().await; 346 + 347 + let todo1_uuid = Uuid::new_v4(); 348 + let todo2_uuid = Uuid::new_v4(); 349 + let todo3_uuid = Uuid::new_v4(); 350 + let todo4_uuid = Uuid::new_v4(); 351 + 352 + client 353 + .add_todo_json(json!( 354 + { 355 + "data": { 356 + "uuid": todo1_uuid, 357 + "title": "Test Todo", 358 + "note": "Test Note", 359 + "tags": [], 360 + "checks": [], 361 + } 362 + } 363 + )) 364 + .await; 365 + client 366 + .add_todo_json(json!( 367 + { 368 + "data": { 369 + "uuid": todo2_uuid, 370 + "title": "Test Todo", 371 + "note": "Test Note", 372 + "tags": [], 373 + "checks": [], 374 + "previousId": todo1_uuid 375 + } 376 + } 377 + )) 378 + .await; 379 + client 380 + .add_todo_json(json!( 381 + { 382 + "data": { 383 + "uuid": todo3_uuid, 384 + "title": "Test Todo", 385 + "note": "Test Note", 386 + "tags": [], 387 + "checks": [], 388 + }, 389 + "position": "top", 390 + "previousId": todo1_uuid 391 + } 392 + )) 393 + .await; 394 + client 395 + .add_todo_json(json!( 396 + { 397 + "data": { 398 + "uuid": todo4_uuid, 399 + "title": "Test Todo", 400 + "note": "Test Note", 401 + "tags": [], 402 + "checks": [], 403 + }, 404 + "position": "bottom", 405 + "previousId": todo3_uuid 406 + } 407 + )) 408 + .await; 409 + 410 + let todos = client.get_todos_json().await; 411 + 412 + assert_eq!(todos.len(), 4); 413 + assert_eq!(todos[0].uuid, todo2_uuid); 414 + assert_eq!(todos[1].uuid, todo3_uuid); 415 + assert_eq!(todos[2].uuid, todo4_uuid); 416 + assert_eq!(todos[3].uuid, todo1_uuid); 22 417 }
+1
libs/types/Cargo.toml
··· 8 8 serde_json = "1.0.145" 9 9 10 10 chrono = { version = "0.4.42", features = ["serde"] } 11 + uuid = { version = "1.23.1", features = ["serde", "v4"] }
+8 -9
libs/types/src/lib.rs
··· 2 2 3 3 use chrono::{Days, NaiveDate}; 4 4 use serde::{Deserialize, Serialize}; 5 + use uuid::Uuid; 5 6 6 - pub type UUID = String; 7 - 8 - #[derive(Debug, Serialize, Deserialize)] 7 + #[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] 9 8 pub struct Todo { 10 - pub uuid: UUID, 9 + pub uuid: Uuid, 11 10 pub title: String, 12 11 pub note: String, 13 12 pub time: Option<Time>, 14 - pub tags: Vec<UUID>, 15 - pub category: Option<UUID>, 13 + pub tags: Vec<Uuid>, 14 + pub category: Option<Uuid>, 16 15 pub checks: Vec<NaiveDate>, 17 16 } 18 17 ··· 39 38 } 40 39 } 41 40 42 - #[derive(Debug, Clone, Serialize, Deserialize)] 41 + #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] 43 42 pub struct TimeRange { 44 43 pub start: NaiveDate, 45 44 pub end: NaiveDate, ··· 53 52 0 54 53 } 55 54 56 - #[derive(Debug, Serialize, Deserialize)] 55 + #[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] 57 56 #[serde(tag = "mode")] 58 57 pub enum TimeRecurring { 59 58 #[serde(rename = "daily")] ··· 68 67 }, 69 68 } 70 69 71 - #[derive(Debug, Serialize, Deserialize)] 70 + #[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] 72 71 #[serde(tag = "type")] 73 72 pub enum Time { 74 73 #[serde(rename = "range")]