atagen

jelq

public

an http based roguelike rpg about jelqing

Star0
Watch0
ea51d9fda49d82c38cf3920e34d6be05d8398cae
1
ssh://brewery@pls.lobotomise.me:33/atagen/jelq.git

Blame: src/tests/body.rs

8b8e83329a1datagen2026-10-12 12:01 UTC1
use super::*;
8b8e83329a1datagen2026-10-12 12:01 UTC2
8b8e83329a1datagen2026-10-12 12:01 UTC3
#[tokio::test]
8b8e83329a1datagen2026-10-12 12:01 UTC4
async fn deleting_the_foreskin_requires_certainty() {
8b8e83329a1datagen2026-10-12 12:01 UTC5
    let Harness { app, .. } = harness(Floor::Bedroom, SEED, |_| {});
8b8e83329a1datagen2026-10-12 12:01 UTC6
    let response = send(&app, "DELETE", "/runs/cafebabe/foreskin", &[], "").await;
8b8e83329a1datagen2026-10-12 12:01 UTC7
    assert_eq!(response.status(), StatusCode::PRECONDITION_REQUIRED);
8b8e83329a1datagen2026-10-12 12:01 UTC8
    let look = send(&app, "GET", "/runs/cafebabe/foreskin", &[], "").await;
8b8e83329a1datagen2026-10-12 12:01 UTC9
    assert_eq!(look.status(), StatusCode::OK);
8b8e83329a1datagen2026-10-12 12:01 UTC10
    let etag = header(&look, "etag").to_owned();
8b8e83329a1datagen2026-10-12 12:01 UTC11
    assert!(text(look).await.contains('~'));
8b8e83329a1datagen2026-10-12 12:01 UTC12
    let response = send(
8b8e83329a1datagen2026-10-12 12:01 UTC13
        &app,
8b8e83329a1datagen2026-10-12 12:01 UTC14
        "DELETE",
8b8e83329a1datagen2026-10-12 12:01 UTC15
        "/runs/cafebabe/foreskin",
8b8e83329a1datagen2026-10-12 12:01 UTC16
        &[("if-match", &etag)],
8b8e83329a1datagen2026-10-12 12:01 UTC17
        "",
8b8e83329a1datagen2026-10-12 12:01 UTC18
    )
8b8e83329a1datagen2026-10-12 12:01 UTC19
    .await;
8b8e83329a1datagen2026-10-12 12:01 UTC20
    assert_eq!(response.status(), StatusCode::NO_CONTENT);
8b8e83329a1datagen2026-10-12 12:01 UTC21
    let response = send(&app, "GET", "/runs/cafebabe/foreskin", &[], "").await;
8b8e83329a1datagen2026-10-12 12:01 UTC22
    assert_eq!(response.status(), StatusCode::GONE);
8b8e83329a1datagen2026-10-12 12:01 UTC23
}
8b8e83329a1datagen2026-10-12 12:01 UTC24
8b8e83329a1datagen2026-10-12 12:01 UTC25
#[tokio::test]
8b8e83329a1datagen2026-10-12 12:01 UTC26
async fn length_supports_inch_ranges() {
8b8e83329a1datagen2026-10-12 12:01 UTC27
    let Harness { app, .. } = harness(Floor::Bedroom, SEED, |_| {});
8b8e83329a1datagen2026-10-12 12:01 UTC28
    let response = send(
8b8e83329a1datagen2026-10-12 12:01 UTC29
        &app,
8b8e83329a1datagen2026-10-12 12:01 UTC30
        "GET",
8b8e83329a1datagen2026-10-12 12:01 UTC31
        "/runs/cafebabe/length",
8b8e83329a1datagen2026-10-12 12:01 UTC32
        &[("range", "inches=0-1")],
8b8e83329a1datagen2026-10-12 12:01 UTC33
        "",
8b8e83329a1datagen2026-10-12 12:01 UTC34
    )
8b8e83329a1datagen2026-10-12 12:01 UTC35
    .await;
8b8e83329a1datagen2026-10-12 12:01 UTC36
    assert_eq!(response.status(), StatusCode::PARTIAL_CONTENT);
8b8e83329a1datagen2026-10-12 12:01 UTC37
    assert!(header(&response, "content-range").starts_with("inches 0.00-1.00/"));
8b8e83329a1datagen2026-10-12 12:01 UTC38
    assert_eq!(text(response).await.len(), 25);
8b8e83329a1datagen2026-10-12 12:01 UTC39
    let absurd = send(
8b8e83329a1datagen2026-10-12 12:01 UTC40
        &app,
8b8e83329a1datagen2026-10-12 12:01 UTC41
        "GET",
8b8e83329a1datagen2026-10-12 12:01 UTC42
        "/runs/cafebabe/length",
8b8e83329a1datagen2026-10-12 12:01 UTC43
        &[("range", "inches=40-50")],
8b8e83329a1datagen2026-10-12 12:01 UTC44
        "",
8b8e83329a1datagen2026-10-12 12:01 UTC45
    )
8b8e83329a1datagen2026-10-12 12:01 UTC46
    .await;
8b8e83329a1datagen2026-10-12 12:01 UTC47
    assert_eq!(absurd.status(), StatusCode::RANGE_NOT_SATISFIABLE);
8b8e83329a1datagen2026-10-12 12:01 UTC48
    let malformed = send(
8b8e83329a1datagen2026-10-12 12:01 UTC49
        &app,
8b8e83329a1datagen2026-10-12 12:01 UTC50
        "GET",
8b8e83329a1datagen2026-10-12 12:01 UTC51
        "/runs/cafebabe/length",
8b8e83329a1datagen2026-10-12 12:01 UTC52
        &[("range", "inches=6.17")],
8b8e83329a1datagen2026-10-12 12:01 UTC53
        "",
8b8e83329a1datagen2026-10-12 12:01 UTC54
    )
8b8e83329a1datagen2026-10-12 12:01 UTC55
    .await;
8b8e83329a1datagen2026-10-12 12:01 UTC56
    assert_eq!(malformed.status(), StatusCode::OK);
8b8e83329a1datagen2026-10-12 12:01 UTC57
    let suffix = send(
8b8e83329a1datagen2026-10-12 12:01 UTC58
        &app,
8b8e83329a1datagen2026-10-12 12:01 UTC59
        "GET",
8b8e83329a1datagen2026-10-12 12:01 UTC60
        "/runs/cafebabe/length",
8b8e83329a1datagen2026-10-12 12:01 UTC61
        &[("range", "inches=-1")],
8b8e83329a1datagen2026-10-12 12:01 UTC62
        "",
8b8e83329a1datagen2026-10-12 12:01 UTC63
    )
8b8e83329a1datagen2026-10-12 12:01 UTC64
    .await;
8b8e83329a1datagen2026-10-12 12:01 UTC65
    assert_eq!(suffix.status(), StatusCode::PARTIAL_CONTENT);
8b8e83329a1datagen2026-10-12 12:01 UTC66
    assert!(text(suffix).await.ends_with('D'));
8b8e83329a1datagen2026-10-12 12:01 UTC67
    for (range, status) in [
8b8e83329a1datagen2026-10-12 12:01 UTC68
        ("inches=1-", StatusCode::PARTIAL_CONTENT),
8b8e83329a1datagen2026-10-12 12:01 UTC69
        ("inches=40-", StatusCode::RANGE_NOT_SATISFIABLE),
8b8e83329a1datagen2026-10-12 12:01 UTC70
        ("inches=-0", StatusCode::RANGE_NOT_SATISFIABLE),
8b8e83329a1datagen2026-10-12 12:01 UTC71
        ("inches=2-1", StatusCode::OK),
8b8e83329a1datagen2026-10-12 12:01 UTC72
        ("inches=NaN-2", StatusCode::OK),
8b8e83329a1datagen2026-10-12 12:01 UTC73
        ("inches=0-inf", StatusCode::OK),
8b8e83329a1datagen2026-10-12 12:01 UTC74
    ] {
8b8e83329a1datagen2026-10-12 12:01 UTC75
        let response = send(
8b8e83329a1datagen2026-10-12 12:01 UTC76
            &app,
8b8e83329a1datagen2026-10-12 12:01 UTC77
            "GET",
8b8e83329a1datagen2026-10-12 12:01 UTC78
            "/runs/cafebabe/length",
8b8e83329a1datagen2026-10-12 12:01 UTC79
            &[("range", range)],
8b8e83329a1datagen2026-10-12 12:01 UTC80
            "",
8b8e83329a1datagen2026-10-12 12:01 UTC81
        )
8b8e83329a1datagen2026-10-12 12:01 UTC82
        .await;
8b8e83329a1datagen2026-10-12 12:01 UTC83
        assert_eq!(response.status(), status, "{range}");
8b8e83329a1datagen2026-10-12 12:01 UTC84
    }
8b8e83329a1datagen2026-10-12 12:01 UTC85
}
8b8e83329a1datagen2026-10-12 12:01 UTC86
8b8e83329a1datagen2026-10-12 12:01 UTC87
#[tokio::test]
8b8e83329a1datagen2026-10-12 12:01 UTC88
async fn the_displayed_length_is_accepted_as_honest() {
8b8e83329a1datagen2026-10-12 12:01 UTC89
    let app = seeded_with(Floor::Forum, |run| run.body.length = 5.2496);
8b8e83329a1datagen2026-10-12 12:01 UTC90
    let response = send(
8b8e83329a1datagen2026-10-12 12:01 UTC91
        &app,
8b8e83329a1datagen2026-10-12 12:01 UTC92
        "GET",
8b8e83329a1datagen2026-10-12 12:01 UTC93
        "/runs/cafebabe/elder",
8b8e83329a1datagen2026-10-12 12:01 UTC94
        &[("proxy-authorization", "Ruler 5.25")],
8b8e83329a1datagen2026-10-12 12:01 UTC95
        "",
8b8e83329a1datagen2026-10-12 12:01 UTC96
    )
8b8e83329a1datagen2026-10-12 12:01 UTC97
    .await;
8b8e83329a1datagen2026-10-12 12:01 UTC98
    assert_eq!(response.status(), StatusCode::OK);
8b8e83329a1datagen2026-10-12 12:01 UTC99
}
8b8e83329a1datagen2026-10-12 12:01 UTC100
8b8e83329a1datagen2026-10-12 12:01 UTC101
#[tokio::test]
8b8e83329a1datagen2026-10-12 12:01 UTC102
async fn dry_jelqs_build_friction_and_real_lube_does_not() {
bf5f8cb6f60fatagen2026-10-13 12:10 UTC103
    let app = seeded_with(Floor::ChangingRoom, |run| {
bf5f8cb6f60fatagen2026-10-13 12:10 UTC104
        run.body.eq = 60;
880b29a0c11eatagen2026-10-13 09:27 UTC105
        run.items.lubes.baby_oil = 1;
8b8e83329a1datagen2026-10-12 12:01 UTC106
        run.body.foreskin = Some(0.9);
8b8e83329a1datagen2026-10-12 12:01 UTC107
    });
8b8e83329a1datagen2026-10-12 12:01 UTC108
    let dry = send(&app, "JELQ", "/runs/cafebabe?technique=dry", &[], "").await;
8b8e83329a1datagen2026-10-12 12:01 UTC109
    assert_eq!(header(&dry, "x-friction"), "2/10");
8b8e83329a1datagen2026-10-12 12:01 UTC110
    let lube = [("content-type", "application/baby-oil")];
8b8e83329a1datagen2026-10-12 12:01 UTC111
    let wet = send(
8b8e83329a1datagen2026-10-12 12:01 UTC112
        &app,
8b8e83329a1datagen2026-10-12 12:01 UTC113
        "JELQ",
8b8e83329a1datagen2026-10-12 12:01 UTC114
        "/runs/cafebabe?technique=wet&intensity=5",
8b8e83329a1datagen2026-10-12 12:01 UTC115
        &lube,
8b8e83329a1datagen2026-10-12 12:01 UTC116
        "",
8b8e83329a1datagen2026-10-12 12:01 UTC117
    )
8b8e83329a1datagen2026-10-12 12:01 UTC118
    .await;
8b8e83329a1datagen2026-10-12 12:01 UTC119
    assert_eq!(header(&wet, "x-friction"), "2/10");
8b8e83329a1datagen2026-10-12 12:01 UTC120
    let spit = [("content-type", "text/plain; charset=spit")];
8b8e83329a1datagen2026-10-12 12:01 UTC121
    let glisten = send(
8b8e83329a1datagen2026-10-12 12:01 UTC122
        &app,
8b8e83329a1datagen2026-10-12 12:01 UTC123
        "JELQ",
8b8e83329a1datagen2026-10-12 12:01 UTC124
        "/runs/cafebabe?technique=wet&intensity=5",
8b8e83329a1datagen2026-10-12 12:01 UTC125
        &spit,
8b8e83329a1datagen2026-10-12 12:01 UTC126
        "",
8b8e83329a1datagen2026-10-12 12:01 UTC127
    )
8b8e83329a1datagen2026-10-12 12:01 UTC128
    .await;
8b8e83329a1datagen2026-10-12 12:01 UTC129
    assert_eq!(header(&glisten, "x-friction"), "3/10");
8b8e83329a1datagen2026-10-12 12:01 UTC130
}
8b8e83329a1datagen2026-10-12 12:01 UTC131
8b8e83329a1datagen2026-10-12 12:01 UTC132
#[tokio::test]
8b8e83329a1datagen2026-10-12 12:01 UTC133
async fn switching_grips_makes_the_friction_cheaper() {
bf5f8cb6f60fatagen2026-10-13 12:10 UTC134
    let app = seeded_with(Floor::ChangingRoom, |run| {
bf5f8cb6f60fatagen2026-10-13 12:10 UTC135
        run.body.eq = 60;
8b8e83329a1datagen2026-10-12 12:01 UTC136
        run.body.foreskin = Some(0.9);
8b8e83329a1datagen2026-10-12 12:01 UTC137
    });
8b8e83329a1datagen2026-10-12 12:01 UTC138
    let dry = send(&app, "JELQ", "/runs/cafebabe?technique=dry", &[], "").await;
8b8e83329a1datagen2026-10-12 12:01 UTC139
    assert_eq!(header(&dry, "x-friction"), "2/10");
8b8e83329a1datagen2026-10-12 12:01 UTC140
    send(
8b8e83329a1datagen2026-10-12 12:01 UTC141
        &app,
8b8e83329a1datagen2026-10-12 12:01 UTC142
        "GET",
8b8e83329a1datagen2026-10-12 12:01 UTC143
        "/runs/cafebabe/grip",
8b8e83329a1datagen2026-10-12 12:01 UTC144
        &[("upgrade", "underhand"), ("connection", "upgrade")],
8b8e83329a1datagen2026-10-12 12:01 UTC145
        "",
8b8e83329a1datagen2026-10-12 12:01 UTC146
    )
8b8e83329a1datagen2026-10-12 12:01 UTC147
    .await;
8b8e83329a1datagen2026-10-12 12:01 UTC148
    let switched = send(&app, "JELQ", "/runs/cafebabe?technique=dry", &[], "").await;
8b8e83329a1datagen2026-10-12 12:01 UTC149
    assert_eq!(header(&switched, "x-friction"), "3/10");
8b8e83329a1datagen2026-10-12 12:01 UTC150
    let settled = send(&app, "JELQ", "/runs/cafebabe?technique=dry", &[], "").await;
8b8e83329a1datagen2026-10-12 12:01 UTC151
    assert_eq!(header(&settled, "x-friction"), "5/10");
8b8e83329a1datagen2026-10-12 12:01 UTC152
}
8b8e83329a1datagen2026-10-12 12:01 UTC153
8b8e83329a1datagen2026-10-12 12:01 UTC154
#[tokio::test]
8b8e83329a1datagen2026-10-12 12:01 UTC155
async fn holding_and_meditating_shed_friction() {
8b8e83329a1datagen2026-10-12 12:01 UTC156
    let app = seeded_with(Floor::Bedroom, |run| {
8b8e83329a1datagen2026-10-12 12:01 UTC157
        run.body.eq = 80;
8b8e83329a1datagen2026-10-12 12:01 UTC158
        run.body.friction = 5;
8b8e83329a1datagen2026-10-12 12:01 UTC159
    });
8b8e83329a1datagen2026-10-12 12:01 UTC160
    let held = send(&app, "HOLD", "/runs/cafebabe", &[], "").await;
8b8e83329a1datagen2026-10-12 12:01 UTC161
    assert_eq!(held.status(), StatusCode::NO_CONTENT);
8b8e83329a1datagen2026-10-12 12:01 UTC162
    let view = send(&app, "GET", "/runs/cafebabe", &[], "").await;
8b8e83329a1datagen2026-10-12 12:01 UTC163
    assert_eq!(header(&view, "x-friction"), "3/10");
8b8e83329a1datagen2026-10-12 12:01 UTC164
8b8e83329a1datagen2026-10-12 12:01 UTC165
    let taught = seeded_with(Floor::Bedroom, |run| {
8b8e83329a1datagen2026-10-12 12:01 UTC166
        run.body.friction = 5;
880b29a0c11eatagen2026-10-13 09:27 UTC167
        run.online.meditation_taught = true;
8b8e83329a1datagen2026-10-12 12:01 UTC168
    });
8b8e83329a1datagen2026-10-12 12:01 UTC169
    let calmed = send(
8b8e83329a1datagen2026-10-12 12:01 UTC170
        &taught,
8b8e83329a1datagen2026-10-12 12:01 UTC171
        "JELQ",
8b8e83329a1datagen2026-10-12 12:01 UTC172
        "/runs/cafebabe?technique=meditate",
8b8e83329a1datagen2026-10-12 12:01 UTC173
        &[],
8b8e83329a1datagen2026-10-12 12:01 UTC174
        "",
8b8e83329a1datagen2026-10-12 12:01 UTC175
    )
8b8e83329a1datagen2026-10-12 12:01 UTC176
    .await;
8b8e83329a1datagen2026-10-12 12:01 UTC177
    assert_eq!(calmed.status(), StatusCode::OK);
8b8e83329a1datagen2026-10-12 12:01 UTC178
    let view = send(&taught, "GET", "/runs/cafebabe", &[], "").await;
8b8e83329a1datagen2026-10-12 12:01 UTC179
    assert_eq!(header(&view, "x-friction"), "3/10");
8b8e83329a1datagen2026-10-12 12:01 UTC180
}
8b8e83329a1datagen2026-10-12 12:01 UTC181
8b8e83329a1datagen2026-10-12 12:01 UTC182
#[tokio::test]
8b8e83329a1datagen2026-10-12 12:01 UTC183
async fn a_friction_burn_halves_the_gains() {
8b8e83329a1datagen2026-10-12 12:01 UTC184
    let lube = [("content-type", "application/baby-oil")];
8b8e83329a1datagen2026-10-12 12:01 UTC185
    let path = "/runs/cafebabe?technique=wet&intensity=5";
8b8e83329a1datagen2026-10-12 12:01 UTC186
    for seed in 0..60u64 {
8b8e83329a1datagen2026-10-12 12:01 UTC187
        let steady = app_with(Floor::Bedroom, seed, |run| {
8b8e83329a1datagen2026-10-12 12:01 UTC188
            run.body.eq = 60;
8b8e83329a1datagen2026-10-12 12:01 UTC189
            run.items.lubes.baby_oil = 9;
8b8e83329a1datagen2026-10-12 12:01 UTC190
        });
8b8e83329a1datagen2026-10-12 12:01 UTC191
        let hurt_app = app_with(Floor::Bedroom, seed, |run| {
8b8e83329a1datagen2026-10-12 12:01 UTC192
            run.body.eq = 60;
8b8e83329a1datagen2026-10-12 12:01 UTC193
            run.items.lubes.baby_oil = 9;
8b8e83329a1datagen2026-10-12 12:01 UTC194
            run.body.conditions.friction_burn = true;
8b8e83329a1datagen2026-10-12 12:01 UTC195
        });
8b8e83329a1datagen2026-10-12 12:01 UTC196
        let calm = send(&steady, "JELQ", path, &lube, "").await;
8b8e83329a1datagen2026-10-12 12:01 UTC197
        let hurt = send(&hurt_app, "JELQ", path, &lube, "").await;
8b8e83329a1datagen2026-10-12 12:01 UTC198
        if calm.status() != StatusCode::OK || hurt.status() != StatusCode::OK {
8b8e83329a1datagen2026-10-12 12:01 UTC199
            continue;
8b8e83329a1datagen2026-10-12 12:01 UTC200
        }
8b8e83329a1datagen2026-10-12 12:01 UTC201
        let calm_gain = json(calm).await["length"].as_f64().unwrap();
8b8e83329a1datagen2026-10-12 12:01 UTC202
        let hurt_gain = json(hurt).await["length"].as_f64().unwrap();
8b8e83329a1datagen2026-10-12 12:01 UTC203
        assert!(
8b8e83329a1datagen2026-10-12 12:01 UTC204
            (hurt_gain * 2.0 - calm_gain).abs() < 1e-9,
8b8e83329a1datagen2026-10-12 12:01 UTC205
            "seed {seed} burned {hurt_gain} against {calm_gain}"
8b8e83329a1datagen2026-10-12 12:01 UTC206
        );
8b8e83329a1datagen2026-10-12 12:01 UTC207
        return;
8b8e83329a1datagen2026-10-12 12:01 UTC208
    }
8b8e83329a1datagen2026-10-12 12:01 UTC209
    panic!("no seed in 60 produced two clean rolls to compare");
8b8e83329a1datagen2026-10-12 12:01 UTC210
}
8b8e83329a1datagen2026-10-12 12:01 UTC211
8b8e83329a1datagen2026-10-12 12:01 UTC212
#[tokio::test]
8b8e83329a1datagen2026-10-12 12:01 UTC213
async fn the_burn_warns_and_the_ice_treats_it() {
8b8e83329a1datagen2026-10-12 12:01 UTC214
    let app = seeded_with(Floor::Bedroom, |run| {
8b8e83329a1datagen2026-10-12 12:01 UTC215
        run.body.eq = 60;
8b8e83329a1datagen2026-10-12 12:01 UTC216
        run.items.lubes.baby_oil = 9;
8b8e83329a1datagen2026-10-12 12:01 UTC217
        run.body.friction = 7;
8b8e83329a1datagen2026-10-12 12:01 UTC218
        run.body.conditions.friction_burn = true;
8b8e83329a1datagen2026-10-12 12:01 UTC219
    });
8b8e83329a1datagen2026-10-12 12:01 UTC220
    let lube = [("content-type", "application/baby-oil")];
8b8e83329a1datagen2026-10-12 12:01 UTC221
    let jelq = send(
8b8e83329a1datagen2026-10-12 12:01 UTC222
        &app,
8b8e83329a1datagen2026-10-12 12:01 UTC223
        "JELQ",
8b8e83329a1datagen2026-10-12 12:01 UTC224
        "/runs/cafebabe?technique=wet&intensity=5",
8b8e83329a1datagen2026-10-12 12:01 UTC225
        &lube,
8b8e83329a1datagen2026-10-12 12:01 UTC226
        "",
8b8e83329a1datagen2026-10-12 12:01 UTC227
    )
8b8e83329a1datagen2026-10-12 12:01 UTC228
    .await;
8b8e83329a1datagen2026-10-12 12:01 UTC229
    let warnings: Vec<_> = jelq
8b8e83329a1datagen2026-10-12 12:01 UTC230
        .headers()
8b8e83329a1datagen2026-10-12 12:01 UTC231
        .get_all("warning")
8b8e83329a1datagen2026-10-12 12:01 UTC232
        .iter()
8b8e83329a1datagen2026-10-12 12:01 UTC233
        .filter_map(|w| w.to_str().ok())
8b8e83329a1datagen2026-10-12 12:01 UTC234
        .collect();
8b8e83329a1datagen2026-10-12 12:01 UTC235
    assert!(warnings.contains(&"199 - \"friction burn\""));
8b8e83329a1datagen2026-10-12 12:01 UTC236
    send(&app, "JELQ", "/runs/cafebabe?technique=ice", &[], "").await;
8b8e83329a1datagen2026-10-12 12:01 UTC237
    let view = send(&app, "GET", "/runs/cafebabe", &[], "").await;
8b8e83329a1datagen2026-10-12 12:01 UTC238
    let warnings: Vec<_> = view
8b8e83329a1datagen2026-10-12 12:01 UTC239
        .headers()
8b8e83329a1datagen2026-10-12 12:01 UTC240
        .get_all("warning")
8b8e83329a1datagen2026-10-12 12:01 UTC241
        .iter()
8b8e83329a1datagen2026-10-12 12:01 UTC242
        .filter_map(|w| w.to_str().ok())
8b8e83329a1datagen2026-10-12 12:01 UTC243
        .collect();
8b8e83329a1datagen2026-10-12 12:01 UTC244
    assert!(!warnings.contains(&"199 - \"friction burn\""));
8b8e83329a1datagen2026-10-12 12:01 UTC245
}
8b8e83329a1datagen2026-10-12 12:01 UTC246
8b8e83329a1datagen2026-10-12 12:01 UTC247
#[tokio::test]
8b8e83329a1datagen2026-10-12 12:01 UTC248
async fn friction_rises_with_vigour_and_edging_chafes() {
8b8e83329a1datagen2026-10-12 12:01 UTC249
    let spit = [("content-type", "text/plain; charset=spit")];
8b8e83329a1datagen2026-10-12 12:01 UTC250
    for (intensity, friction) in [(5, "1/10"), (7, "2/10"), (10, "3/10")] {
8b8e83329a1datagen2026-10-12 12:01 UTC251
        let app = seeded_with(Floor::Hotel, |run| {
8b8e83329a1datagen2026-10-12 12:01 UTC252
            run.here.wifi_accepted = true;
8b8e83329a1datagen2026-10-12 12:01 UTC253
            run.body.eq = 55;
8b8e83329a1datagen2026-10-12 12:01 UTC254
            run.body.foreskin = Some(0.5);
8b8e83329a1datagen2026-10-12 12:01 UTC255
        });
8b8e83329a1datagen2026-10-12 12:01 UTC256
        let uri = format!("/runs/cafebabe?technique=wet&intensity={intensity}");
8b8e83329a1datagen2026-10-12 12:01 UTC257
        let jelq = send(&app, "JELQ", &uri, &spit, "").await;
8b8e83329a1datagen2026-10-12 12:01 UTC258
        assert_eq!(
8b8e83329a1datagen2026-10-12 12:01 UTC259
            header(&jelq, "x-friction"),
8b8e83329a1datagen2026-10-12 12:01 UTC260
            friction,
8b8e83329a1datagen2026-10-12 12:01 UTC261
            "intensity {intensity}"
8b8e83329a1datagen2026-10-12 12:01 UTC262
        );
8b8e83329a1datagen2026-10-12 12:01 UTC263
    }
8b8e83329a1datagen2026-10-12 12:01 UTC264
    let app = seeded_with(Floor::Hotel, |run| {
8b8e83329a1datagen2026-10-12 12:01 UTC265
        run.here.wifi_accepted = true;
8b8e83329a1datagen2026-10-12 12:01 UTC266
        run.body.eq = 55;
8b8e83329a1datagen2026-10-12 12:01 UTC267
        run.body.foreskin = Some(0.5);
8b8e83329a1datagen2026-10-12 12:01 UTC268
    });
8b8e83329a1datagen2026-10-12 12:01 UTC269
    let edge = send(&app, "JELQ", "/runs/cafebabe?technique=edge", &[], "").await;
8b8e83329a1datagen2026-10-12 12:01 UTC270
    assert_eq!(header(&edge, "x-friction"), "2/10");
bad9f5b8202fatagen2026-10-14 09:23 UTC271
bad9f5b8202fatagen2026-10-14 09:23 UTC272
    let oiled = seeded_with(Floor::Hotel, |run| {
bad9f5b8202fatagen2026-10-14 09:23 UTC273
        run.here.wifi_accepted = true;
bad9f5b8202fatagen2026-10-14 09:23 UTC274
        run.body.eq = 55;
bad9f5b8202fatagen2026-10-14 09:23 UTC275
        run.body.foreskin = Some(0.5);
bad9f5b8202fatagen2026-10-14 09:23 UTC276
        run.items.lubes.baby_oil = 1;
bad9f5b8202fatagen2026-10-14 09:23 UTC277
    });
bad9f5b8202fatagen2026-10-14 09:23 UTC278
    let oil = [("content-type", "application/baby-oil")];
bad9f5b8202fatagen2026-10-14 09:23 UTC279
    let edge = send(&oiled, "JELQ", "/runs/cafebabe?technique=edge", &oil, "").await;
bad9f5b8202fatagen2026-10-14 09:23 UTC280
    assert_eq!(header(&edge, "x-friction"), "0/10");
bad9f5b8202fatagen2026-10-14 09:23 UTC281
    let empty = send(&oiled, "JELQ", "/runs/cafebabe?technique=edge", &oil, "").await;
bad9f5b8202fatagen2026-10-14 09:23 UTC282
    assert_eq!(empty.status(), StatusCode::UNSUPPORTED_MEDIA_TYPE);
8b8e83329a1datagen2026-10-12 12:01 UTC283
}
8b8e83329a1datagen2026-10-12 12:01 UTC284
8b8e83329a1datagen2026-10-12 12:01 UTC285
#[tokio::test]
8b8e83329a1datagen2026-10-12 12:01 UTC286
async fn the_body_changes_how_the_skin_takes_it() {
8b8e83329a1datagen2026-10-12 12:01 UTC287
    let spit = [("content-type", "text/plain; charset=spit")];
8b8e83329a1datagen2026-10-12 12:01 UTC288
    for (foreskin, girth, friction) in [(0.5, 4.5, "1/10"), (1.2, 4.5, "0/10"), (0.5, 5.8, "2/10")]
8b8e83329a1datagen2026-10-12 12:01 UTC289
    {
8b8e83329a1datagen2026-10-12 12:01 UTC290
        let app = seeded_with(Floor::Hotel, |run| {
8b8e83329a1datagen2026-10-12 12:01 UTC291
            run.here.wifi_accepted = true;
8b8e83329a1datagen2026-10-12 12:01 UTC292
            run.body.eq = 55;
8b8e83329a1datagen2026-10-12 12:01 UTC293
            run.body.foreskin = Some(foreskin);
8b8e83329a1datagen2026-10-12 12:01 UTC294
            run.body.girth = girth;
8b8e83329a1datagen2026-10-12 12:01 UTC295
        });
8b8e83329a1datagen2026-10-12 12:01 UTC296
        let jelq = send(&app, "JELQ", "/runs/cafebabe?technique=wet", &spit, "").await;
8b8e83329a1datagen2026-10-12 12:01 UTC297
        assert_eq!(header(&jelq, "x-friction"), friction, "{foreskin} {girth}");
8b8e83329a1datagen2026-10-12 12:01 UTC298
    }
8b8e83329a1datagen2026-10-12 12:01 UTC299
}
8b8e83329a1datagen2026-10-12 12:01 UTC300
8b8e83329a1datagen2026-10-12 12:01 UTC301
#[tokio::test]
8b8e83329a1datagen2026-10-12 12:01 UTC302
async fn a_thick_girth_outgrows_the_underhand_grip() {
8b8e83329a1datagen2026-10-12 12:01 UTC303
    let app = seeded_with(Floor::Bedroom, |run| run.body.girth = 5.8);
8b8e83329a1datagen2026-10-12 12:01 UTC304
    let grip = send(
8b8e83329a1datagen2026-10-12 12:01 UTC305
        &app,
8b8e83329a1datagen2026-10-12 12:01 UTC306
        "GET",
8b8e83329a1datagen2026-10-12 12:01 UTC307
        "/runs/cafebabe/grip",
8b8e83329a1datagen2026-10-12 12:01 UTC308
        &[("upgrade", "underhand"), ("connection", "upgrade")],
8b8e83329a1datagen2026-10-12 12:01 UTC309
        "",
8b8e83329a1datagen2026-10-12 12:01 UTC310
    )
8b8e83329a1datagen2026-10-12 12:01 UTC311
    .await;
8b8e83329a1datagen2026-10-12 12:01 UTC312
    assert_eq!(grip.status(), StatusCode::PAYLOAD_TOO_LARGE);
8b8e83329a1datagen2026-10-12 12:01 UTC313
}
8b8e83329a1datagen2026-10-12 12:01 UTC314
8b8e83329a1datagen2026-10-12 12:01 UTC315
#[tokio::test]
8b8e83329a1datagen2026-10-12 12:01 UTC316
async fn a_long_run_fills_the_jeans_before_a_thick_one() {
8b8e83329a1datagen2026-10-12 12:01 UTC317
    let app = seeded_with(Floor::Bedroom, |run| {
8b8e83329a1datagen2026-10-12 12:01 UTC318
        run.items.kit.extender = true;
8b8e83329a1datagen2026-10-12 12:01 UTC319
        run.body.girth = 4.8;
8b8e83329a1datagen2026-10-12 12:01 UTC320
        run.body.length = 7.5;
8b8e83329a1datagen2026-10-12 12:01 UTC321
    });
8b8e83329a1datagen2026-10-12 12:01 UTC322
    let lube = [("content-type", "application/baby-oil")];
8b8e83329a1datagen2026-10-12 12:01 UTC323
    let full = send(&app, "JELQ", "/runs/cafebabe?technique=wet", &lube, "").await;
8b8e83329a1datagen2026-10-12 12:01 UTC324
    assert_eq!(full.status(), StatusCode::INSUFFICIENT_STORAGE);
8b8e83329a1datagen2026-10-12 12:01 UTC325
}
8b8e83329a1datagen2026-10-12 12:01 UTC326
8b8e83329a1datagen2026-10-12 12:01 UTC327
#[tokio::test]
8b8e83329a1datagen2026-10-12 12:01 UTC328
async fn a_refused_jelq_leaves_the_lube_alone() {
8b8e83329a1datagen2026-10-12 12:01 UTC329
    let app = seeded_with(Floor::Bedroom, |run| {
8b8e83329a1datagen2026-10-12 12:01 UTC330
        run.items.kit.extender = true;
9adb509e6685atagen2026-10-14 08:54 UTC331
        run.items.lubes.baby_oil = 1;
8b8e83329a1datagen2026-10-12 12:01 UTC332
        run.body.girth = 6.0;
8b8e83329a1datagen2026-10-12 12:01 UTC333
        run.body.length = 6.0;
8b8e83329a1datagen2026-10-12 12:01 UTC334
    });
8b8e83329a1datagen2026-10-12 12:01 UTC335
    let lube = [("content-type", "application/baby-oil")];
8b8e83329a1datagen2026-10-12 12:01 UTC336
    let full = send(
8b8e83329a1datagen2026-10-12 12:01 UTC337
        &app,
8b8e83329a1datagen2026-10-12 12:01 UTC338
        "JELQ",
8b8e83329a1datagen2026-10-12 12:01 UTC339
        "/runs/cafebabe?technique=wet&intensity=5",
8b8e83329a1datagen2026-10-12 12:01 UTC340
        &lube,
8b8e83329a1datagen2026-10-12 12:01 UTC341
        "",
8b8e83329a1datagen2026-10-12 12:01 UTC342
    )
8b8e83329a1datagen2026-10-12 12:01 UTC343
    .await;
8b8e83329a1datagen2026-10-12 12:01 UTC344
    assert_eq!(full.status(), StatusCode::INSUFFICIENT_STORAGE);
8b8e83329a1datagen2026-10-12 12:01 UTC345
    let inv = send(&app, "GET", "/runs/cafebabe/inventory", &[], "").await;
8b8e83329a1datagen2026-10-12 12:01 UTC346
    let body = json(inv).await;
8b8e83329a1datagen2026-10-12 12:01 UTC347
    let held = body["items"].as_array().unwrap();
8b8e83329a1datagen2026-10-12 12:01 UTC348
    assert!(held.iter().any(|i| i["name"] == "baby-oil"));
8b8e83329a1datagen2026-10-12 12:01 UTC349
}
8b8e83329a1datagen2026-10-12 12:01 UTC350
8b8e83329a1datagen2026-10-12 12:01 UTC351
#[tokio::test]
8b8e83329a1datagen2026-10-12 12:01 UTC352
async fn lubes_run_out_but_spit_never_does() {
8b8e83329a1datagen2026-10-12 12:01 UTC353
    let app = seeded_with(Floor::Bedroom, |run| {
8b8e83329a1datagen2026-10-12 12:01 UTC354
        run.items.lubes.baby_oil = 1;
8b8e83329a1datagen2026-10-12 12:01 UTC355
    });
8b8e83329a1datagen2026-10-12 12:01 UTC356
    let lube = [("content-type", "application/baby-oil")];
8b8e83329a1datagen2026-10-12 12:01 UTC357
    send(&app, "JELQ", "/runs/cafebabe?technique=wet", &lube, "").await;
8b8e83329a1datagen2026-10-12 12:01 UTC358
    let second = send(&app, "JELQ", "/runs/cafebabe?technique=wet", &lube, "").await;
8b8e83329a1datagen2026-10-12 12:01 UTC359
    assert_eq!(second.status(), StatusCode::UNSUPPORTED_MEDIA_TYPE);
8b8e83329a1datagen2026-10-12 12:01 UTC360
8b8e83329a1datagen2026-10-12 12:01 UTC361
    let spit = [("content-type", "text/plain; charset=spit")];
8b8e83329a1datagen2026-10-12 12:01 UTC362
    for _ in 0..3 {
8b8e83329a1datagen2026-10-12 12:01 UTC363
        let wet = send(&app, "JELQ", "/runs/cafebabe?technique=wet", &spit, "").await;
8b8e83329a1datagen2026-10-12 12:01 UTC364
        assert_ne!(
8b8e83329a1datagen2026-10-12 12:01 UTC365
            wet.status(),
8b8e83329a1datagen2026-10-12 12:01 UTC366
            StatusCode::UNSUPPORTED_MEDIA_TYPE,
8b8e83329a1datagen2026-10-12 12:01 UTC367
            "spit never runs out"
8b8e83329a1datagen2026-10-12 12:01 UTC368
        );
8b8e83329a1datagen2026-10-12 12:01 UTC369
    }
8b8e83329a1datagen2026-10-12 12:01 UTC370
}
8b8e83329a1datagen2026-10-12 12:01 UTC371
8b8e83329a1datagen2026-10-12 12:01 UTC372
#[tokio::test]
8b8e83329a1datagen2026-10-12 12:01 UTC373
async fn a_combo_chafes_twice_and_costs_two_stamina() {
8b8e83329a1datagen2026-10-12 12:01 UTC374
    let app = seeded_with(Floor::Hotel, |run| {
8b8e83329a1datagen2026-10-12 12:01 UTC375
        run.here.wifi_accepted = true;
8b8e83329a1datagen2026-10-12 12:01 UTC376
        run.body.eq = 55;
8b8e83329a1datagen2026-10-12 12:01 UTC377
        run.body.foreskin = Some(0.5);
8b8e83329a1datagen2026-10-12 12:01 UTC378
    });
8b8e83329a1datagen2026-10-12 12:01 UTC379
    let spit = [("content-type", "text/plain; charset=spit")];
8b8e83329a1datagen2026-10-12 12:01 UTC380
    let combo = send(
8b8e83329a1datagen2026-10-12 12:01 UTC381
        &app,
8b8e83329a1datagen2026-10-12 12:01 UTC382
        "JELQ",
8b8e83329a1datagen2026-10-12 12:01 UTC383
        "/runs/cafebabe?technique=wet,stretch",
8b8e83329a1datagen2026-10-12 12:01 UTC384
        &spit,
8b8e83329a1datagen2026-10-12 12:01 UTC385
        "",
8b8e83329a1datagen2026-10-12 12:01 UTC386
    )
8b8e83329a1datagen2026-10-12 12:01 UTC387
    .await;
8b8e83329a1datagen2026-10-12 12:01 UTC388
    assert_eq!(header(&combo, "x-friction"), "2/10");
8b8e83329a1datagen2026-10-12 12:01 UTC389
    assert_eq!(header(&combo, "x-stamina"), "8/10");
8b8e83329a1datagen2026-10-12 12:01 UTC390
}
880b29a0c11eatagen2026-10-13 09:27 UTC391
98432b6057aaatagen2026-10-14 09:03 UTC392
#[tokio::test]
98432b6057aaatagen2026-10-14 09:03 UTC393
async fn a_combo_tells_each_technique_in_full() {
98432b6057aaatagen2026-10-14 09:03 UTC394
    let mut told = 0;
98432b6057aaatagen2026-10-14 09:03 UTC395
    for seed in 0..40 {
98432b6057aaatagen2026-10-14 09:03 UTC396
        let app = app_with(Floor::Bedroom, seed, |run| run.body.eq = 60);
98432b6057aaatagen2026-10-14 09:03 UTC397
        let lube = [("content-type", "application/baby-oil")];
98432b6057aaatagen2026-10-14 09:03 UTC398
        let combo = send(
98432b6057aaatagen2026-10-14 09:03 UTC399
            &app,
98432b6057aaatagen2026-10-14 09:03 UTC400
            "JELQ",
98432b6057aaatagen2026-10-14 09:03 UTC401
            "/runs/cafebabe?technique=wet,stretch",
98432b6057aaatagen2026-10-14 09:03 UTC402
            &lube,
98432b6057aaatagen2026-10-14 09:03 UTC403
            "",
98432b6057aaatagen2026-10-14 09:03 UTC404
        )
98432b6057aaatagen2026-10-14 09:03 UTC405
        .await;
98432b6057aaatagen2026-10-14 09:03 UTC406
        if combo.status() != StatusCode::MULTI_STATUS {
98432b6057aaatagen2026-10-14 09:03 UTC407
            continue;
98432b6057aaatagen2026-10-14 09:03 UTC408
        }
98432b6057aaatagen2026-10-14 09:03 UTC409
        for report in text(combo).await.split("<D:response>").skip(1) {
98432b6057aaatagen2026-10-14 09:03 UTC410
            if report.contains("HTTP/1.1 204") {
98432b6057aaatagen2026-10-14 09:03 UTC411
                continue;
98432b6057aaatagen2026-10-14 09:03 UTC412
            }
98432b6057aaatagen2026-10-14 09:03 UTC413
            assert!(
98432b6057aaatagen2026-10-14 09:03 UTC414
                report.contains("<D:responsedescription>{\"") && report.contains("\"detail\""),
98432b6057aaatagen2026-10-14 09:03 UTC415
                "{report}"
98432b6057aaatagen2026-10-14 09:03 UTC416
            );
98432b6057aaatagen2026-10-14 09:03 UTC417
            told += 1;
98432b6057aaatagen2026-10-14 09:03 UTC418
        }
98432b6057aaatagen2026-10-14 09:03 UTC419
    }
98432b6057aaatagen2026-10-14 09:03 UTC420
    assert!(told > 0, "no combo told anything in forty runs");
98432b6057aaatagen2026-10-14 09:03 UTC421
}
98432b6057aaatagen2026-10-14 09:03 UTC422
880b29a0c11eatagen2026-10-13 09:27 UTC423
#[test]
880b29a0c11eatagen2026-10-13 09:27 UTC424
fn the_drawer_starts_with_at_most_one_bottle() {
880b29a0c11eatagen2026-10-13 09:27 UTC425
    let mut stocked = 0;
880b29a0c11eatagen2026-10-13 09:27 UTC426
    for seed in 0..400u64 {
880b29a0c11eatagen2026-10-13 09:27 UTC427
        let lubes = &Run::new("cafebabe".into(), "cafe".into(), seed).items.lubes;
880b29a0c11eatagen2026-10-13 09:27 UTC428
        let bottles = lubes.baby_oil + lubes.lotion;
880b29a0c11eatagen2026-10-13 09:27 UTC429
        assert!(bottles <= 1 && lubes.coconut_oil + lubes.vaseline == 0);
880b29a0c11eatagen2026-10-13 09:27 UTC430
        stocked += bottles;
880b29a0c11eatagen2026-10-13 09:27 UTC431
    }
880b29a0c11eatagen2026-10-13 09:27 UTC432
    assert!(
880b29a0c11eatagen2026-10-13 09:27 UTC433
        (160..=240).contains(&stocked),
880b29a0c11eatagen2026-10-13 09:27 UTC434
        "{stocked}/400 started with lube"
880b29a0c11eatagen2026-10-13 09:27 UTC435
    );
880b29a0c11eatagen2026-10-13 09:27 UTC436
}
97c05b741d8fatagen2026-10-13 11:27 UTC437
97c05b741d8fatagen2026-10-13 11:27 UTC438
#[tokio::test]
97c05b741d8fatagen2026-10-13 11:27 UTC439
async fn stretching_reports_the_foreskin_it_grew() {
97c05b741d8fatagen2026-10-13 11:27 UTC440
    let mut landed = 0;
97c05b741d8fatagen2026-10-13 11:27 UTC441
    for seed in 0..40u64 {
97c05b741d8fatagen2026-10-13 11:27 UTC442
        let app = app_with(Floor::Bedroom, seed, |run| {
97c05b741d8fatagen2026-10-13 11:27 UTC443
            run.body.eq = 60;
97c05b741d8fatagen2026-10-13 11:27 UTC444
            run.body.foreskin = Some(1.0);
97c05b741d8fatagen2026-10-13 11:27 UTC445
        });
97c05b741d8fatagen2026-10-13 11:27 UTC446
        let stretch = send(&app, "JELQ", "/runs/cafebabe?technique=stretch", &[], "").await;
97c05b741d8fatagen2026-10-13 11:27 UTC447
        if stretch.status() == StatusCode::OK {
97c05b741d8fatagen2026-10-13 11:27 UTC448
            assert_eq!(header(&stretch, "x-foreskin"), "1.03");
97c05b741d8fatagen2026-10-13 11:27 UTC449
            assert_eq!(json(stretch).await["foreskin"], 0.03);
97c05b741d8fatagen2026-10-13 11:27 UTC450
            landed += 1;
97c05b741d8fatagen2026-10-13 11:27 UTC451
        }
97c05b741d8fatagen2026-10-13 11:27 UTC452
    }
97c05b741d8fatagen2026-10-13 11:27 UTC453
    assert!(landed > 0, "forty stretches and none landed clean");
97c05b741d8fatagen2026-10-13 11:27 UTC454
}
2d9074e0b4deatagen2026-10-13 12:35 UTC455
2d9074e0b4deatagen2026-10-13 12:35 UTC456
#[tokio::test]
61fe020d61e0atagen2026-10-14 09:19 UTC457
async fn lotion_sheds_friction_at_a_gentle_pace() {
61fe020d61e0atagen2026-10-14 09:19 UTC458
    let app = seeded_with(Floor::Bedroom, |run| {
61fe020d61e0atagen2026-10-14 09:19 UTC459
        run.body.eq = 55;
61fe020d61e0atagen2026-10-14 09:19 UTC460
        run.body.friction = 4;
61fe020d61e0atagen2026-10-14 09:19 UTC461
        run.items.lubes.lotion = 1;
61fe020d61e0atagen2026-10-14 09:19 UTC462
    });
61fe020d61e0atagen2026-10-14 09:19 UTC463
    let stroke = send(
61fe020d61e0atagen2026-10-14 09:19 UTC464
        &app,
61fe020d61e0atagen2026-10-14 09:19 UTC465
        "JELQ",
61fe020d61e0atagen2026-10-14 09:19 UTC466
        "/runs/cafebabe?technique=wet&intensity=5",
61fe020d61e0atagen2026-10-14 09:19 UTC467
        &[("content-type", "application/lotion")],
61fe020d61e0atagen2026-10-14 09:19 UTC468
        "",
61fe020d61e0atagen2026-10-14 09:19 UTC469
    )
61fe020d61e0atagen2026-10-14 09:19 UTC470
    .await;
61fe020d61e0atagen2026-10-14 09:19 UTC471
    assert_eq!(header(&stroke, "x-friction"), "3/10");
61fe020d61e0atagen2026-10-14 09:19 UTC472
}
61fe020d61e0atagen2026-10-14 09:19 UTC473
61fe020d61e0atagen2026-10-14 09:19 UTC474
#[tokio::test]
2d9074e0b4deatagen2026-10-13 12:35 UTC475
async fn lotion_soothes_a_friction_burn() {
2d9074e0b4deatagen2026-10-13 12:35 UTC476
    let app = seeded_with(Floor::ChangingRoom, |run| {
2d9074e0b4deatagen2026-10-13 12:35 UTC477
        run.body.eq = 60;
2d9074e0b4deatagen2026-10-13 12:35 UTC478
        run.body.friction = 7;
2d9074e0b4deatagen2026-10-13 12:35 UTC479
        run.body.conditions.friction_burn = true;
2d9074e0b4deatagen2026-10-13 12:35 UTC480
        run.items.lubes.lotion = 1;
2d9074e0b4deatagen2026-10-13 12:35 UTC481
    });
2d9074e0b4deatagen2026-10-13 12:35 UTC482
    let soothed = send(
2d9074e0b4deatagen2026-10-13 12:35 UTC483
        &app,
2d9074e0b4deatagen2026-10-13 12:35 UTC484
        "JELQ",
2d9074e0b4deatagen2026-10-13 12:35 UTC485
        "/runs/cafebabe?technique=wet",
2d9074e0b4deatagen2026-10-13 12:35 UTC486
        &[("content-type", "application/lotion")],
2d9074e0b4deatagen2026-10-13 12:35 UTC487
        "",
2d9074e0b4deatagen2026-10-13 12:35 UTC488
    )
2d9074e0b4deatagen2026-10-13 12:35 UTC489
    .await;
2d9074e0b4deatagen2026-10-13 12:35 UTC490
    let warned = warnings(&soothed);
2d9074e0b4deatagen2026-10-13 12:35 UTC491
    assert!(warned.iter().any(|w| w.contains("soothed")));
2d9074e0b4deatagen2026-10-13 12:35 UTC492
    assert!(warned.iter().all(|w| w != "199 - \"friction burn\""));
2d9074e0b4deatagen2026-10-13 12:35 UTC493
}