max / makenotwork
- Co-Authored-By
- Claude Opus 5 (1M context) <noreply@anthropic.com>
4 files changed,
+55 insertions,
-49 deletions
| @@ -18,7 +18,7 @@ | |||
| 18 | 18 | use std::fs; | |
| 19 | 19 | use std::path::{Path, PathBuf}; | |
| 20 | 20 | ||
| 21 | - | /// Loose status assertions in `tests/workflows/`: 6 on 2026-08-06, down from 830. | |
| 21 | + | /// Loose status assertions across `tests/`: 6 on 2026-08-06, down from 830. | |
| 22 | 22 | /// | |
| 23 | 23 | /// `assert!(resp.status.is_success())` passes on a 200 when the handler promised | |
| 24 | 24 | /// 201, and `is_client_error()` passes on the 404 you get when a route silently | |
| @@ -43,6 +43,21 @@ | |||
| 43 | 43 | /// cases that answer with different codes, and two are login paths that render a | |
| 44 | 44 | /// 200 page with the error in the body, where the status is not where the | |
| 45 | 45 | /// contract lives. | |
| 46 | + | /// | |
| 47 | + | /// The count reads all of `tests/`, not just `tests/workflows/`, since 2026-08-06. | |
| 48 | + | /// Scoping it to the workflow modules left the two files every workflow test | |
| 49 | + | /// depends on unsealed, and both had kept the loose form: `harness/mod.rs` and | |
| 50 | + | /// `load/runner.rs` took `is_success() || is_redirection()` on signup, login, | |
| 51 | + | /// create-project and create-item, so a route changing shape underneath the whole | |
| 52 | + | /// suite would have gone unnoticed in the one place it is least visible. Measured | |
| 53 | + | /// the same way as the 824: signup answers 200, login 303, both creates 200, over | |
| 54 | + | /// 2,700 observations with no variance. | |
| 55 | + | /// | |
| 56 | + | /// `tests/load/scenarios.rs` is left alone deliberately. Its `is_success()` calls | |
| 57 | + | /// are a virtual user deciding whether to continue a cycle, not assertions, and | |
| 58 | + | /// they do not match the pattern below anyway (the receiver is a local, not | |
| 59 | + | /// `.status`). This file excludes itself for the obvious reason: the strings it | |
| 60 | + | /// searches for are in its own source. | |
| 46 | 61 | const LOOSE_STATUS_HIGH_WATER: usize = 6; | |
| 47 | 62 | ||
| 48 | 63 | /// `test_`-prefixed test functions across `src/` and `tests/`: 176 on 2026-08-03. | |
| @@ -67,11 +82,18 @@ | |||
| 67 | 82 | ||
| 68 | 83 | const WORKFLOWS_DIR: &str = "tests/workflows"; | |
| 69 | 84 | ||
| 85 | + | /// The whole test tree: the harness and the load runner are as much a part of the | |
| 86 | + | /// suite's contract as the workflow modules are. | |
| 87 | + | const TESTS_DIR: &str = "tests"; | |
| 88 | + | ||
| 70 | 89 | #[test] | |
| 71 | 90 | fn loose_status_assertions_do_not_increase() { | |
| 72 | 91 | let mut per_file: Vec<(String, usize)> = Vec::new(); | |
| 73 | - | for path in rs_files(Path::new(WORKFLOWS_DIR)) { | |
| 74 | - | let text = fs::read_to_string(&path).expect("read workflow module"); | |
| 92 | + | for path in rs_files(Path::new(TESTS_DIR)) { | |
| 93 | + | if file_name(&path) == "test_hygiene.rs" { | |
| 94 | + | continue; | |
| 95 | + | } | |
| 96 | + | let text = fs::read_to_string(&path).expect("read test module"); | |
| 75 | 97 | let n = text.matches(".status.is_success()").count() | |
| 76 | 98 | + text.matches(".status.is_client_error()").count(); | |
| 77 | 99 | if n > 0 { |
| @@ -583,11 +583,10 @@ | |||
| 583 | 583 | ); | |
| 584 | 584 | ||
| 585 | 585 | let resp = self.client.post_form("/join/step/account", &body).await; | |
| 586 | - | assert!( | |
| 587 | - | resp.status.is_success() || resp.status.is_redirection(), | |
| 586 | + | assert_eq!( | |
| 587 | + | resp.status, 200, | |
| 588 | 588 | "Signup failed with status {}: {}", | |
| 589 | - | resp.status, | |
| 590 | - | resp.text | |
| 589 | + | resp.status, resp.text | |
| 591 | 590 | ); | |
| 592 | 591 | ||
| 593 | 592 | // Login rotates the CSRF token, fetch the new one | |
| @@ -755,11 +754,10 @@ | |||
| 755 | 754 | ); | |
| 756 | 755 | ||
| 757 | 756 | let resp = self.client.post_form("/login", &body).await; | |
| 758 | - | assert!( | |
| 759 | - | resp.status.is_success() || resp.status.is_redirection(), | |
| 757 | + | assert_eq!( | |
| 758 | + | resp.status, 303, | |
| 760 | 759 | "Login failed with status {}: {}", | |
| 761 | - | resp.status, | |
| 762 | - | resp.text | |
| 760 | + | resp.status, resp.text | |
| 763 | 761 | ); | |
| 764 | 762 | ||
| 765 | 763 | // Login rotates the CSRF token, fetch the new one | |
| @@ -796,11 +794,7 @@ | |||
| 796 | 794 | .client | |
| 797 | 795 | .post_form("/api/projects", &format!("slug={slug}&title=Test+Project")) | |
| 798 | 796 | .await; | |
| 799 | - | assert!( | |
| 800 | - | resp.status.is_success(), | |
| 801 | - | "Create project failed: {}", | |
| 802 | - | resp.text | |
| 803 | - | ); | |
| 797 | + | assert_eq!(resp.status, 200, "Create project failed: {}", resp.text); | |
| 804 | 798 | let project: serde_json::Value = resp.json(); | |
| 805 | 799 | let project_id = project["id"].as_str().unwrap().to_string(); | |
| 806 | 800 | ||
| @@ -811,11 +805,7 @@ | |||
| 811 | 805 | &format!("title=Test+Item&item_type={item_type}&price_cents={price_cents}"), | |
| 812 | 806 | ) | |
| 813 | 807 | .await; | |
| 814 | - | assert!( | |
| 815 | - | resp.status.is_success(), | |
| 816 | - | "Create item failed: {}", | |
| 817 | - | resp.text | |
| 818 | - | ); | |
| 808 | + | assert_eq!(resp.status, 200, "Create item failed: {}", resp.text); | |
| 819 | 809 | let item: serde_json::Value = resp.json(); | |
| 820 | 810 | let item_id = item["id"].as_str().unwrap().to_string(); | |
| 821 | 811 |
| @@ -265,12 +265,10 @@ | |||
| 265 | 265 | let body = | |
| 266 | 266 | format!("username={username}&email={username}%40seed.local&password=seedpass123"); | |
| 267 | 267 | let resp = client.post_form("/join/step/account", &body).await; | |
| 268 | - | assert!( | |
| 269 | - | resp.status.is_success() || resp.status.is_redirection(), | |
| 268 | + | assert_eq!( | |
| 269 | + | resp.status, 200, | |
| 270 | 270 | "Seed signup failed for {}: {} {}", | |
| 271 | - | username, | |
| 272 | - | resp.status, | |
| 273 | - | resp.text | |
| 271 | + | username, resp.status, resp.text | |
| 274 | 272 | ); | |
| 275 | 273 | ||
| 276 | 274 | // Grant creator via SQL | |
| @@ -291,12 +289,10 @@ | |||
| 291 | 289 | client.fetch_csrf_token().await; | |
| 292 | 290 | let body = format!("login={username}&password=seedpass123"); | |
| 293 | 291 | let resp = client.post_form("/login", &body).await; | |
| 294 | - | assert!( | |
| 295 | - | resp.status.is_success() || resp.status.is_redirection(), | |
| 292 | + | assert_eq!( | |
| 293 | + | resp.status, 303, | |
| 296 | 294 | "Seed login failed for {}: {} {}", | |
| 297 | - | username, | |
| 298 | - | resp.status, | |
| 299 | - | resp.text | |
| 295 | + | username, resp.status, resp.text | |
| 300 | 296 | ); | |
| 301 | 297 | ||
| 302 | 298 | // Create project | |
| @@ -306,11 +302,10 @@ | |||
| 306 | 302 | i | |
| 307 | 303 | ); | |
| 308 | 304 | let resp = client.post_form("/api/projects", &body).await; | |
| 309 | - | assert!( | |
| 310 | - | resp.status.is_success(), | |
| 305 | + | assert_eq!( | |
| 306 | + | resp.status, 200, | |
| 311 | 307 | "Seed create project failed: {} {}", | |
| 312 | - | resp.status, | |
| 313 | - | resp.text | |
| 308 | + | resp.status, resp.text | |
| 314 | 309 | ); | |
| 315 | 310 | let project: serde_json::Value = resp.json(); | |
| 316 | 311 | let project_id = project["id"].as_str().expect("project should have id"); | |
| @@ -329,11 +324,10 @@ | |||
| 329 | 324 | let resp = client | |
| 330 | 325 | .post_form(&format!("/api/projects/{project_id}/items"), &item_body) | |
| 331 | 326 | .await; | |
| 332 | - | assert!( | |
| 333 | - | resp.status.is_success(), | |
| 327 | + | assert_eq!( | |
| 328 | + | resp.status, 200, | |
| 334 | 329 | "Seed create item failed: {} {}", | |
| 335 | - | resp.status, | |
| 336 | - | resp.text | |
| 330 | + | resp.status, resp.text | |
| 337 | 331 | ); | |
| 338 | 332 | let item: serde_json::Value = resp.json(); | |
| 339 | 333 | let item_id = item["id"].as_str().expect("item should have id"); |
| @@ -912,7 +912,7 @@ | |||
| 912 | 912 | /// A stand-in recipient id for the Optional senders. `client_with_capture` | |
| 913 | 913 | /// builds a pool-less client, so `dispatch` sends without consulting a | |
| 914 | 914 | /// preference and these tests stay about the composed message. | |
| 915 | - | fn test_user_id() -> crate::db::UserId { | |
| 915 | + | fn recipient_id() -> crate::db::UserId { | |
| 916 | 916 | crate::db::UserId::from(uuid::Uuid::nil()) | |
| 917 | 917 | } | |
| 918 | 918 | ||
| @@ -929,7 +929,7 @@ | |||
| 929 | 929 | let (client, captured) = client_with_capture(); | |
| 930 | 930 | client | |
| 931 | 931 | .send_sale_notification( | |
| 932 | - | test_user_id(), | |
| 932 | + | recipient_id(), | |
| 933 | 933 | "seller@example.com", | |
| 934 | 934 | Some("Sasha"), | |
| 935 | 935 | "buyer42", | |
| @@ -955,7 +955,7 @@ | |||
| 955 | 955 | // greeting(None) → empty; body should still build coherently. | |
| 956 | 956 | let (client, captured) = client_with_capture(); | |
| 957 | 957 | client | |
| 958 | - | .send_sale_notification(test_user_id(), "s@x", None, "buyer", "Item", "$5", None) | |
| 958 | + | .send_sale_notification(recipient_id(), "s@x", None, "buyer", "Item", "$5", None) | |
| 959 | 959 | .await | |
| 960 | 960 | .unwrap(); | |
| 961 | 961 | let (_, _, body, unsub) = captured.last(); | |
| @@ -971,7 +971,7 @@ | |||
| 971 | 971 | let (client, captured) = client_with_capture(); | |
| 972 | 972 | client | |
| 973 | 973 | .send_tip_notification( | |
| 974 | - | test_user_id(), | |
| 974 | + | recipient_id(), | |
| 975 | 975 | "c@x", | |
| 976 | 976 | None, | |
| 977 | 977 | "Alex", | |
| @@ -993,7 +993,7 @@ | |||
| 993 | 993 | // without-message branch must NOT include the "with a message:" preamble. | |
| 994 | 994 | let (client, captured) = client_with_capture(); | |
| 995 | 995 | client | |
| 996 | - | .send_tip_notification(test_user_id(), "c@x", None, "Alex", "$3", None, None) | |
| 996 | + | .send_tip_notification(recipient_id(), "c@x", None, "Alex", "$3", None, None) | |
| 997 | 997 | .await | |
| 998 | 998 | .unwrap(); | |
| 999 | 999 | let (_, _, body, _) = captured.last(); | |
| @@ -1146,7 +1146,7 @@ | |||
| 1146 | 1146 | let (client, captured) = client_with_capture(); | |
| 1147 | 1147 | client | |
| 1148 | 1148 | .send_new_issue_notification( | |
| 1149 | - | test_user_id(), | |
| 1149 | + | recipient_id(), | |
| 1150 | 1150 | "owner@x", | |
| 1151 | 1151 | Some("Jordan"), | |
| 1152 | 1152 | "alex", | |
| @@ -1177,7 +1177,7 @@ | |||
| 1177 | 1177 | let (client, captured) = client_with_capture(); | |
| 1178 | 1178 | client | |
| 1179 | 1179 | .send_issue_comment_notification( | |
| 1180 | - | test_user_id(), | |
| 1180 | + | recipient_id(), | |
| 1181 | 1181 | "owner@x", | |
| 1182 | 1182 | None, | |
| 1183 | 1183 | "alex", | |
| @@ -1210,7 +1210,7 @@ | |||
| 1210 | 1210 | let (client, captured) = client_with_capture(); | |
| 1211 | 1211 | client | |
| 1212 | 1212 | .send_status_notification( | |
| 1213 | - | test_user_id(), | |
| 1213 | + | recipient_id(), | |
| 1214 | 1214 | "u@x", | |
| 1215 | 1215 | None, | |
| 1216 | 1216 | "operational", | |
| @@ -1229,7 +1229,7 @@ | |||
| 1229 | 1229 | let (client, captured) = client_with_capture(); | |
| 1230 | 1230 | client | |
| 1231 | 1231 | .send_status_notification( | |
| 1232 | - | test_user_id(), | |
| 1232 | + | recipient_id(), | |
| 1233 | 1233 | "u@x", | |
| 1234 | 1234 | None, | |
| 1235 | 1235 | "degraded", | |
| @@ -1248,7 +1248,7 @@ | |||
| 1248 | 1248 | let (client, captured) = client_with_capture(); | |
| 1249 | 1249 | client | |
| 1250 | 1250 | .send_status_notification( | |
| 1251 | - | test_user_id(), | |
| 1251 | + | recipient_id(), | |
| 1252 | 1252 | "u@x", | |
| 1253 | 1253 | None, | |
| 1254 | 1254 | "outage", |