atagen

jelq

public

an http based roguelike rpg about jelqing

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

Blame: src/tests/turns.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 meditation_is_free_but_only_once_per_floor() {
e028a4184d00atagen2026-10-14 10:49 UTC5
    let held = harness(Floor::Bedroom, SEED, |run| {
a043b8dc91b7atagen2026-10-14 12:22 UTC6
        run.online.meditation_taught = true;
a043b8dc91b7atagen2026-10-14 12:22 UTC7
        run.standing.suspicion = 3;
e028a4184d00atagen2026-10-14 10:49 UTC8
    });
e028a4184d00atagen2026-10-14 10:49 UTC9
    let app = &held.app;
e028a4184d00atagen2026-10-14 10:49 UTC10
    let response = send(app, "JELQ", "/runs/cafebabe?technique=meditate", &[], "").await;
8b8e83329a1datagen2026-10-12 12:01 UTC11
    assert_eq!(response.status(), StatusCode::OK);
e028a4184d00atagen2026-10-14 10:49 UTC12
    assert_eq!(header(&response, "x-eq"), "Cold");
e028a4184d00atagen2026-10-14 10:49 UTC13
    assert_eq!(held.eq(), 10);
a043b8dc91b7atagen2026-10-14 12:22 UTC14
    assert_eq!(header(&response, "x-suspicion"), "1/5");
8b8e83329a1datagen2026-10-12 12:01 UTC15
    assert_eq!(header(&response, "x-stamina"), "10/10");
e028a4184d00atagen2026-10-14 10:49 UTC16
    let again = send(app, "JELQ", "/runs/cafebabe?technique=meditate", &[], "").await;
8b8e83329a1datagen2026-10-12 12:01 UTC17
    assert_eq!(again.status(), StatusCode::CONFLICT);
8b8e83329a1datagen2026-10-12 12:01 UTC18
}
8b8e83329a1datagen2026-10-12 12:01 UTC19
8b8e83329a1datagen2026-10-12 12:01 UTC20
#[tokio::test]
8b8e83329a1datagen2026-10-12 12:01 UTC21
async fn meditation_respects_the_priapism_floor() {
e028a4184d00atagen2026-10-14 10:49 UTC22
    let held = harness(Floor::Bedroom, SEED, |run| {
8b8e83329a1datagen2026-10-12 12:01 UTC23
        run.body.eq = 80;
8b8e83329a1datagen2026-10-12 12:01 UTC24
        run.body.conditions.priapism_turns = 3;
880b29a0c11eatagen2026-10-13 09:27 UTC25
        run.online.meditation_taught = true;
8b8e83329a1datagen2026-10-12 12:01 UTC26
    });
e028a4184d00atagen2026-10-14 10:49 UTC27
    let calmed = send(
e028a4184d00atagen2026-10-14 10:49 UTC28
        &held.app,
e028a4184d00atagen2026-10-14 10:49 UTC29
        "JELQ",
e028a4184d00atagen2026-10-14 10:49 UTC30
        "/runs/cafebabe?technique=meditate",
e028a4184d00atagen2026-10-14 10:49 UTC31
        &[],
e028a4184d00atagen2026-10-14 10:49 UTC32
        "",
e028a4184d00atagen2026-10-14 10:49 UTC33
    )
e028a4184d00atagen2026-10-14 10:49 UTC34
    .await;
8b8e83329a1datagen2026-10-12 12:01 UTC35
    assert_eq!(calmed.status(), StatusCode::OK);
e028a4184d00atagen2026-10-14 10:49 UTC36
    assert_eq!(header(&calmed, "x-eq"), "Priapic");
e028a4184d00atagen2026-10-14 10:49 UTC37
    assert_eq!(held.eq(), 75);
8b8e83329a1datagen2026-10-12 12:01 UTC38
}
8b8e83329a1datagen2026-10-12 12:01 UTC39
8b8e83329a1datagen2026-10-12 12:01 UTC40
#[tokio::test]
8b8e83329a1datagen2026-10-12 12:01 UTC41
async fn priapism_holds_the_floor_through_every_reset() {
e028a4184d00atagen2026-10-14 10:49 UTC42
    let exit = harness(Floor::Bedroom, SEED, |run| {
8b8e83329a1datagen2026-10-12 12:01 UTC43
        run.body.eq = 90;
8b8e83329a1datagen2026-10-12 12:01 UTC44
        run.here.turns = 99;
8b8e83329a1datagen2026-10-12 12:01 UTC45
        run.body.conditions.priapism_turns = 3;
8b8e83329a1datagen2026-10-12 12:01 UTC46
    });
e028a4184d00atagen2026-10-14 10:49 UTC47
    let out = send(&exit.app, "POST", "/runs/cafebabe/floor", &[], "").await;
8b8e83329a1datagen2026-10-12 12:01 UTC48
    assert_eq!(out.status(), StatusCode::SEE_OTHER);
e028a4184d00atagen2026-10-14 10:49 UTC49
    assert_eq!(exit.eq(), 75);
8b8e83329a1datagen2026-10-12 12:01 UTC50
e028a4184d00atagen2026-10-14 10:49 UTC51
    let melon = harness(Floor::Bedroom, SEED, |run| {
8b8e83329a1datagen2026-10-12 12:01 UTC52
        run.body.eq = 95;
8b8e83329a1datagen2026-10-12 12:01 UTC53
        run.here.kitchen.melon_offered = true;
8b8e83329a1datagen2026-10-12 12:01 UTC54
        run.body.conditions.priapism_turns = 2;
8b8e83329a1datagen2026-10-12 12:01 UTC55
    });
e028a4184d00atagen2026-10-14 10:49 UTC56
    let bite = send(
e028a4184d00atagen2026-10-14 10:49 UTC57
        &melon.app,
e028a4184d00atagen2026-10-14 10:49 UTC58
        "POST",
e028a4184d00atagen2026-10-14 10:49 UTC59
        "/runs/cafebabe/kitchen/watermelon",
e028a4184d00atagen2026-10-14 10:49 UTC60
        &[],
e028a4184d00atagen2026-10-14 10:49 UTC61
        "",
e028a4184d00atagen2026-10-14 10:49 UTC62
    )
e028a4184d00atagen2026-10-14 10:49 UTC63
    .await;
8b8e83329a1datagen2026-10-12 12:01 UTC64
    assert_eq!(bite.status(), StatusCode::OK);
e028a4184d00atagen2026-10-14 10:49 UTC65
    assert_eq!(melon.eq(), 75);
8b8e83329a1datagen2026-10-12 12:01 UTC66
}
8b8e83329a1datagen2026-10-12 12:01 UTC67
8b8e83329a1datagen2026-10-12 12:01 UTC68
#[tokio::test]
8b8e83329a1datagen2026-10-12 12:01 UTC69
async fn the_towel_warms_once_until_the_kettle_boils() {
8b8e83329a1datagen2026-10-12 12:01 UTC70
    let app = seeded_with(Floor::Bedroom, |_| {});
8b8e83329a1datagen2026-10-12 12:01 UTC71
    let warm = send(&app, "JELQ", "/runs/cafebabe?technique=warmup", &[], "").await;
8b8e83329a1datagen2026-10-12 12:01 UTC72
    assert_eq!(warm.status(), StatusCode::OK);
8b8e83329a1datagen2026-10-12 12:01 UTC73
    let again = send(&app, "JELQ", "/runs/cafebabe?technique=warmup", &[], "").await;
8b8e83329a1datagen2026-10-12 12:01 UTC74
    assert_eq!(again.status(), StatusCode::CONFLICT);
8b8e83329a1datagen2026-10-12 12:01 UTC75
8b8e83329a1datagen2026-10-12 12:01 UTC76
    let app = seeded_with(Floor::Bedroom, |run| {
4b1f5f88827datagen2026-10-13 10:18 UTC77
        run.here.towel_warmed = true;
8b8e83329a1datagen2026-10-12 12:01 UTC78
        run.here.kitchen.kettle_boiled = true;
8b8e83329a1datagen2026-10-12 12:01 UTC79
    });
8b8e83329a1datagen2026-10-12 12:01 UTC80
    let reheated = send(&app, "JELQ", "/runs/cafebabe?technique=warmup", &[], "").await;
8b8e83329a1datagen2026-10-12 12:01 UTC81
    assert_eq!(reheated.status(), StatusCode::OK);
8b8e83329a1datagen2026-10-12 12:01 UTC82
    let cold = send(&app, "JELQ", "/runs/cafebabe?technique=warmup", &[], "").await;
8b8e83329a1datagen2026-10-12 12:01 UTC83
    assert_eq!(cold.status(), StatusCode::CONFLICT);
8b8e83329a1datagen2026-10-12 12:01 UTC84
}
8b8e83329a1datagen2026-10-12 12:01 UTC85
8b8e83329a1datagen2026-10-12 12:01 UTC86
#[tokio::test]
8b8e83329a1datagen2026-10-12 12:01 UTC87
async fn the_refractory_period_is_the_next_hold() {
8b8e83329a1datagen2026-10-12 12:01 UTC88
    let app = seeded_with(Floor::Hotel, |run| {
8b8e83329a1datagen2026-10-12 12:01 UTC89
        run.here.wifi_accepted = true;
4b1f5f88827datagen2026-10-13 10:18 UTC90
        run.here.towel_warmed = true;
8b8e83329a1datagen2026-10-12 12:01 UTC91
        run.body.eq = 60;
8b8e83329a1datagen2026-10-12 12:01 UTC92
        run.body.stamina = 0;
8b8e83329a1datagen2026-10-12 12:01 UTC93
        run.body.exhausted = true;
8b8e83329a1datagen2026-10-12 12:01 UTC94
    });
8b8e83329a1datagen2026-10-12 12:01 UTC95
    let tired = send(&app, "JELQ", "/runs/cafebabe?technique=edge", &[], "").await;
8b8e83329a1datagen2026-10-12 12:01 UTC96
    assert_eq!(tired.status(), StatusCode::TOO_MANY_REQUESTS);
8b8e83329a1datagen2026-10-12 12:01 UTC97
    let hold = send(&app, "HOLD", "/runs/cafebabe", &[], "").await;
8b8e83329a1datagen2026-10-12 12:01 UTC98
    assert_eq!(hold.status(), StatusCode::OK);
f48daa8adf59atagen2026-10-13 12:51 UTC99
    assert!(["4/10", "5/10", "6/10", "10/10"].contains(&header(&hold, "x-stamina")));
4b1f5f88827datagen2026-10-13 10:18 UTC100
    let towel = send(&app, "JELQ", "/runs/cafebabe?technique=warmup", &[], "").await;
4b1f5f88827datagen2026-10-13 10:18 UTC101
    assert_eq!(towel.status(), StatusCode::CONFLICT, "the towel stays cold");
8b8e83329a1datagen2026-10-12 12:01 UTC102
}
8b8e83329a1datagen2026-10-12 12:01 UTC103
8b8e83329a1datagen2026-10-12 12:01 UTC104
#[tokio::test]
8b8e83329a1datagen2026-10-12 12:01 UTC105
async fn a_refused_jelq_does_not_break_the_edging_streak() {
8b8e83329a1datagen2026-10-12 12:01 UTC106
    let app = seeded_with(Floor::ChangingRoom, |run| {
8b8e83329a1datagen2026-10-12 12:01 UTC107
        run.body.eq = 45;
8b8e83329a1datagen2026-10-12 12:01 UTC108
        run.here.edges = 2;
8b8e83329a1datagen2026-10-12 12:01 UTC109
    });
8b8e83329a1datagen2026-10-12 12:01 UTC110
    let refused = send(
8b8e83329a1datagen2026-10-12 12:01 UTC111
        &app,
8b8e83329a1datagen2026-10-12 12:01 UTC112
        "JELQ",
8b8e83329a1datagen2026-10-12 12:01 UTC113
        "/runs/cafebabe?technique=dry",
8b8e83329a1datagen2026-10-12 12:01 UTC114
        &[("content-type", "application/baby-oil")],
8b8e83329a1datagen2026-10-12 12:01 UTC115
        "",
8b8e83329a1datagen2026-10-12 12:01 UTC116
    )
8b8e83329a1datagen2026-10-12 12:01 UTC117
    .await;
8b8e83329a1datagen2026-10-12 12:01 UTC118
    assert_eq!(refused.status(), StatusCode::UNSUPPORTED_MEDIA_TYPE);
8b8e83329a1datagen2026-10-12 12:01 UTC119
    let third = send(&app, "JELQ", "/runs/cafebabe?technique=edge", &[], "").await;
8b8e83329a1datagen2026-10-12 12:01 UTC120
    assert_eq!(third.status(), StatusCode::LOOP_DETECTED);
8b8e83329a1datagen2026-10-12 12:01 UTC121
}
8b8e83329a1datagen2026-10-12 12:01 UTC122
8b8e83329a1datagen2026-10-12 12:01 UTC123
#[tokio::test]
8b8e83329a1datagen2026-10-12 12:01 UTC124
async fn an_interrupted_edge_keeps_no_gains() {
bf5f8cb6f60fatagen2026-10-13 12:10 UTC125
    let app = seeded_with(Floor::Hotel, |run| {
bf5f8cb6f60fatagen2026-10-13 12:10 UTC126
        run.here.wifi_accepted = true;
bf5f8cb6f60fatagen2026-10-13 12:10 UTC127
        run.body.eq = 100;
8b8e83329a1datagen2026-10-12 12:01 UTC128
    });
8b8e83329a1datagen2026-10-12 12:01 UTC129
    let before = send(&app, "GET", "/runs/cafebabe", &[], "").await;
8b8e83329a1datagen2026-10-12 12:01 UTC130
    let girth = header(&before, "x-girth").to_owned();
8b8e83329a1datagen2026-10-12 12:01 UTC131
    let edge = send(&app, "JELQ", "/runs/cafebabe?technique=edge", &[], "").await;
bf5f8cb6f60fatagen2026-10-13 12:10 UTC132
    assert!(!edge.status().is_success());
8b8e83329a1datagen2026-10-12 12:01 UTC133
    assert_eq!(header(&edge, "x-girth"), girth);
8b8e83329a1datagen2026-10-12 12:01 UTC134
}
8b8e83329a1datagen2026-10-12 12:01 UTC135
8b8e83329a1datagen2026-10-12 12:01 UTC136
#[tokio::test]
8b8e83329a1datagen2026-10-12 12:01 UTC137
async fn greed_multiplies_gains_above_the_band() {
8b8e83329a1datagen2026-10-12 12:01 UTC138
    let lube = [("content-type", "application/baby-oil")];
8b8e83329a1datagen2026-10-12 12:01 UTC139
    for (eq, greed) in [(75, 1.5), (88, 2.0)] {
8b8e83329a1datagen2026-10-12 12:01 UTC140
        let mut compared = false;
8b8e83329a1datagen2026-10-12 12:01 UTC141
        for seed in 0..120u64 {
8b8e83329a1datagen2026-10-12 12:01 UTC142
            let app = app_with(Floor::Bedroom, seed, |run| {
8b8e83329a1datagen2026-10-12 12:01 UTC143
                run.body.eq = 60;
8b8e83329a1datagen2026-10-12 12:01 UTC144
            });
8b8e83329a1datagen2026-10-12 12:01 UTC145
            let in_band = send(&app, "JELQ", "/runs/cafebabe?technique=wet", &lube, "").await;
8b8e83329a1datagen2026-10-12 12:01 UTC146
            let app = app_with(Floor::Bedroom, seed, |run| {
8b8e83329a1datagen2026-10-12 12:01 UTC147
                run.body.eq = eq;
8b8e83329a1datagen2026-10-12 12:01 UTC148
            });
8b8e83329a1datagen2026-10-12 12:01 UTC149
            let greedy = send(&app, "JELQ", "/runs/cafebabe?technique=wet", &lube, "").await;
8b8e83329a1datagen2026-10-12 12:01 UTC150
            if in_band.status() != StatusCode::OK || greedy.status() != StatusCode::OK {
8b8e83329a1datagen2026-10-12 12:01 UTC151
                continue;
8b8e83329a1datagen2026-10-12 12:01 UTC152
            }
8b8e83329a1datagen2026-10-12 12:01 UTC153
            let steady = json(in_band).await["length"].as_f64().unwrap();
8b8e83329a1datagen2026-10-12 12:01 UTC154
            let eager = json(greedy).await["length"].as_f64().unwrap();
8b8e83329a1datagen2026-10-12 12:01 UTC155
            assert!(
8b8e83329a1datagen2026-10-12 12:01 UTC156
                (eager - steady * greed).abs() < 1e-9,
8b8e83329a1datagen2026-10-12 12:01 UTC157
                "seed {seed} gained {eager} at {eq} EQ against {steady} inside the band"
8b8e83329a1datagen2026-10-12 12:01 UTC158
            );
8b8e83329a1datagen2026-10-12 12:01 UTC159
            compared = true;
8b8e83329a1datagen2026-10-12 12:01 UTC160
            break;
8b8e83329a1datagen2026-10-12 12:01 UTC161
        }
8b8e83329a1datagen2026-10-12 12:01 UTC162
        assert!(compared, "no seed produced two clean rolls at {eq} EQ");
8b8e83329a1datagen2026-10-12 12:01 UTC163
    }
8b8e83329a1datagen2026-10-12 12:01 UTC164
}
8b8e83329a1datagen2026-10-12 12:01 UTC165
8b8e83329a1datagen2026-10-12 12:01 UTC166
#[tokio::test]
8b8e83329a1datagen2026-10-12 12:01 UTC167
async fn queued_gains_arrive_on_their_turn() {
8b8e83329a1datagen2026-10-12 12:01 UTC168
    let mut run = Run::new("cafebabe".into(), "cafe".into(), SEED);
8b8e83329a1datagen2026-10-12 12:01 UTC169
    run.body.eq = 75;
8b8e83329a1datagen2026-10-12 12:01 UTC170
    let before = run.body.length;
8b8e83329a1datagen2026-10-12 12:01 UTC171
    run.body.pending.push(crate::game::Pending {
8b8e83329a1datagen2026-10-12 12:01 UTC172
        due: run.turn + 1,
8b8e83329a1datagen2026-10-12 12:01 UTC173
        length: 0.5,
8b8e83329a1datagen2026-10-12 12:01 UTC174
        girth: 0.0,
8b8e83329a1datagen2026-10-12 12:01 UTC175
        foreskin: 0.0,
8b8e83329a1datagen2026-10-12 12:01 UTC176
    });
8b8e83329a1datagen2026-10-12 12:01 UTC177
    assert!(run.hold().is_ok());
8b8e83329a1datagen2026-10-12 12:01 UTC178
    assert!((run.body.length - before - 0.5).abs() < 1e-9);
8b8e83329a1datagen2026-10-12 12:01 UTC179
    assert!(run.body.pending.is_empty());
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 hold_keeps_the_shop_and_the_melon_open() {
8b8e83329a1datagen2026-10-12 12:01 UTC184
    let hotel = seeded_with(Floor::Hotel, |run| {
8b8e83329a1datagen2026-10-12 12:01 UTC185
        run.here.wifi_accepted = true;
8b8e83329a1datagen2026-10-12 12:01 UTC186
        run.body.eq = 80;
8b8e83329a1datagen2026-10-12 12:01 UTC187
    });
8b8e83329a1datagen2026-10-12 12:01 UTC188
    send(&hotel, "GET", "/runs/cafebabe/shop", &[], "").await;
8b8e83329a1datagen2026-10-12 12:01 UTC189
    let held = send(&hotel, "HOLD", "/runs/cafebabe", &[], "").await;
8b8e83329a1datagen2026-10-12 12:01 UTC190
    assert!(linked_rels(&held).contains(&"shop-item".to_owned()));
8b8e83329a1datagen2026-10-12 12:01 UTC191
8b8e83329a1datagen2026-10-12 12:01 UTC192
    let mut run = Run::new("cafebabe".into(), "cafe".into(), SEED);
8b8e83329a1datagen2026-10-12 12:01 UTC193
    run.body.eq = 80;
8b8e83329a1datagen2026-10-12 12:01 UTC194
    run.here.kitchen.melon_offered = true;
8b8e83329a1datagen2026-10-12 12:01 UTC195
    assert!(run.hold().is_ok());
8b8e83329a1datagen2026-10-12 12:01 UTC196
    assert!(run.here.kitchen.melon_offered, "the offer survives a HOLD");
8b8e83329a1datagen2026-10-12 12:01 UTC197
}
8b8e83329a1datagen2026-10-12 12:01 UTC198
8b8e83329a1datagen2026-10-12 12:01 UTC199
#[tokio::test]
8b8e83329a1datagen2026-10-12 12:01 UTC200
async fn leaving_the_band_warms_the_next_floor() {
8b8e83329a1datagen2026-10-12 12:01 UTC201
    let app = seeded_with(Floor::Bedroom, |run| {
8b8e83329a1datagen2026-10-12 12:01 UTC202
        run.body.eq = 60;
8b8e83329a1datagen2026-10-12 12:01 UTC203
        run.here.turns = 6;
8b8e83329a1datagen2026-10-12 12:01 UTC204
        run.items.lubes.baby_oil = 9;
8b8e83329a1datagen2026-10-12 12:01 UTC205
    });
8b8e83329a1datagen2026-10-12 12:01 UTC206
    let left = send(&app, "POST", "/runs/cafebabe/floor", &[], "").await;
8b8e83329a1datagen2026-10-12 12:01 UTC207
    assert_eq!(left.status(), StatusCode::SEE_OTHER);
8b8e83329a1datagen2026-10-12 12:01 UTC208
    let detail = json(left).await["detail"].as_str().unwrap().to_owned();
8b8e83329a1datagen2026-10-12 12:01 UTC209
    assert!(detail.contains("the warmth came with you"));
8b8e83329a1datagen2026-10-12 12:01 UTC210
8b8e83329a1datagen2026-10-12 12:01 UTC211
    let lube = [("content-type", "application/baby-oil")];
8b8e83329a1datagen2026-10-12 12:01 UTC212
    send(&app, "JELQ", "/runs/cafebabe?technique=warmup", &[], "").await;
8b8e83329a1datagen2026-10-12 12:01 UTC213
    // The shower is a +35 warm-up, so EQ lands in the band and the band bonus rides along.
8b8e83329a1datagen2026-10-12 12:01 UTC214
    // The arrival bonus is the one point the second jelq loses.
8b8e83329a1datagen2026-10-12 12:01 UTC215
    let first = send(&app, "JELQ", "/runs/cafebabe?technique=wet", &lube, "").await;
8b8e83329a1datagen2026-10-12 12:01 UTC216
    assert!(
8b8e83329a1datagen2026-10-12 12:01 UTC217
        header(&first, "server-timing").contains("d20+4"),
8b8e83329a1datagen2026-10-12 12:01 UTC218
        "the first jelq of a warm arrival rolls with +1, got {}",
8b8e83329a1datagen2026-10-12 12:01 UTC219
        header(&first, "server-timing")
8b8e83329a1datagen2026-10-12 12:01 UTC220
    );
8b8e83329a1datagen2026-10-12 12:01 UTC221
    let second = send(&app, "JELQ", "/runs/cafebabe?technique=wet", &lube, "").await;
8b8e83329a1datagen2026-10-12 12:01 UTC222
    assert!(header(&second, "server-timing").contains("d20+3"));
8b8e83329a1datagen2026-10-12 12:01 UTC223
}
8b8e83329a1datagen2026-10-12 12:01 UTC224
8b8e83329a1datagen2026-10-12 12:01 UTC225
#[tokio::test]
8b8e83329a1datagen2026-10-12 12:01 UTC226
async fn energy_drinks_add_three_stamina() {
8b8e83329a1datagen2026-10-12 12:01 UTC227
    let app = seeded_with(Floor::Bedroom, |run| {
8b8e83329a1datagen2026-10-12 12:01 UTC228
        run.body.stamina = 2;
8b8e83329a1datagen2026-10-12 12:01 UTC229
        run.items.stores.energy_drinks = 1;
8b8e83329a1datagen2026-10-12 12:01 UTC230
    });
8b8e83329a1datagen2026-10-12 12:01 UTC231
    let view = send(&app, "GET", "/runs/cafebabe", &[], "").await;
8b8e83329a1datagen2026-10-12 12:01 UTC232
    assert!(header(&view, "link").contains("technique=energy"));
8b8e83329a1datagen2026-10-12 12:01 UTC233
    let drink = send(&app, "JELQ", "/runs/cafebabe?technique=energy", &[], "").await;
8b8e83329a1datagen2026-10-12 12:01 UTC234
    assert_eq!(drink.status(), StatusCode::OK);
8b8e83329a1datagen2026-10-12 12:01 UTC235
    let view = send(&app, "GET", "/runs/cafebabe", &[], "").await;
8b8e83329a1datagen2026-10-12 12:01 UTC236
    assert_eq!(header(&view, "x-stamina"), "5/10");
8b8e83329a1datagen2026-10-12 12:01 UTC237
    let again = send(&app, "JELQ", "/runs/cafebabe?technique=energy", &[], "").await;
8b8e83329a1datagen2026-10-12 12:01 UTC238
    assert_eq!(again.status(), StatusCode::FAILED_DEPENDENCY);
8b8e83329a1datagen2026-10-12 12:01 UTC239
}
fcc6cf6d5edcatagen2026-10-13 11:31 UTC240
fcc6cf6d5edcatagen2026-10-13 11:31 UTC241
#[tokio::test]
fcc6cf6d5edcatagen2026-10-13 11:31 UTC242
async fn queued_gains_announce_their_arrival() {
fcc6cf6d5edcatagen2026-10-13 11:31 UTC243
    let app = seeded_with(Floor::Bedroom, |run| {
fcc6cf6d5edcatagen2026-10-13 11:31 UTC244
        run.body.eq = 75;
fcc6cf6d5edcatagen2026-10-13 11:31 UTC245
        run.body.foreskin = Some(1.0);
fcc6cf6d5edcatagen2026-10-13 11:31 UTC246
        run.body.pending.push(crate::game::Pending {
fcc6cf6d5edcatagen2026-10-13 11:31 UTC247
            due: run.turn + 1,
fcc6cf6d5edcatagen2026-10-13 11:31 UTC248
            length: 0.105,
fcc6cf6d5edcatagen2026-10-13 11:31 UTC249
            girth: 0.0,
fcc6cf6d5edcatagen2026-10-13 11:31 UTC250
            foreskin: 0.03,
fcc6cf6d5edcatagen2026-10-13 11:31 UTC251
        });
fcc6cf6d5edcatagen2026-10-13 11:31 UTC252
    });
fcc6cf6d5edcatagen2026-10-13 11:31 UTC253
    let held = send(&app, "HOLD", "/runs/cafebabe", &[], "").await;
fcc6cf6d5edcatagen2026-10-13 11:31 UTC254
    assert_eq!(
fcc6cf6d5edcatagen2026-10-13 11:31 UTC255
        header(&held, "x-gains-arrived"),
fcc6cf6d5edcatagen2026-10-13 11:31 UTC256
        "length=0.105, girth=0.000, foreskin=0.030"
fcc6cf6d5edcatagen2026-10-13 11:31 UTC257
    );
fcc6cf6d5edcatagen2026-10-13 11:31 UTC258
    assert!(
fcc6cf6d5edcatagen2026-10-13 11:31 UTC259
        warnings(&held)
fcc6cf6d5edcatagen2026-10-13 11:31 UTC260
            .iter()
fcc6cf6d5edcatagen2026-10-13 11:31 UTC261
            .any(|w| w.contains("the queued gains have arrived"))
fcc6cf6d5edcatagen2026-10-13 11:31 UTC262
    );
fcc6cf6d5edcatagen2026-10-13 11:31 UTC263
    let later = send(&app, "GET", "/runs/cafebabe", &[], "").await;
fcc6cf6d5edcatagen2026-10-13 11:31 UTC264
    assert!(later.headers().get("x-gains-arrived").is_none());
fcc6cf6d5edcatagen2026-10-13 11:31 UTC265
}
a11497af08aaatagen2026-10-13 12:04 UTC266
a11497af08aaatagen2026-10-13 12:04 UTC267
#[tokio::test]
a11497af08aaatagen2026-10-13 12:04 UTC268
async fn an_energy_drink_ends_exhaustion_without_the_refractory_period() {
a11497af08aaatagen2026-10-13 12:04 UTC269
    let app = seeded_with(Floor::Bedroom, |run| {
a11497af08aaatagen2026-10-13 12:04 UTC270
        run.body.eq = 60;
a11497af08aaatagen2026-10-13 12:04 UTC271
        run.body.stamina = 0;
a11497af08aaatagen2026-10-13 12:04 UTC272
        run.body.exhausted = true;
a11497af08aaatagen2026-10-13 12:04 UTC273
        run.items.stores.energy_drinks = 1;
a11497af08aaatagen2026-10-13 12:04 UTC274
    });
a11497af08aaatagen2026-10-13 12:04 UTC275
    let tired = send(&app, "GET", "/runs/cafebabe", &[], "").await;
a11497af08aaatagen2026-10-13 12:04 UTC276
    assert_eq!(advertised(&tired), ["energy"]);
a11497af08aaatagen2026-10-13 12:04 UTC277
    let drink = send(&app, "JELQ", "/runs/cafebabe?technique=energy", &[], "").await;
a11497af08aaatagen2026-10-13 12:04 UTC278
    assert_eq!(drink.status(), StatusCode::OK);
a11497af08aaatagen2026-10-13 12:04 UTC279
    assert_eq!(header(&drink, "x-stamina"), "3/10");
a11497af08aaatagen2026-10-13 12:04 UTC280
    assert_eq!(
a11497af08aaatagen2026-10-13 12:04 UTC281
        header(&drink, "x-eq"),
e028a4184d00atagen2026-10-14 10:49 UTC282
        "Ideal",
a11497af08aaatagen2026-10-13 12:04 UTC283
        "still warm, unlike the refractory HOLD"
a11497af08aaatagen2026-10-13 12:04 UTC284
    );
a11497af08aaatagen2026-10-13 12:04 UTC285
    let jelq = send(&app, "JELQ", "/runs/cafebabe?technique=edge", &[], "").await;
a11497af08aaatagen2026-10-13 12:04 UTC286
    assert_ne!(jelq.status(), StatusCode::TOO_MANY_REQUESTS);
a11497af08aaatagen2026-10-13 12:04 UTC287
}
bf5f8cb6f60fatagen2026-10-13 12:10 UTC288
bf5f8cb6f60fatagen2026-10-13 12:10 UTC289
#[tokio::test]
bf5f8cb6f60fatagen2026-10-13 12:10 UTC290
async fn edging_climbs_out_of_the_cold() {
bf5f8cb6f60fatagen2026-10-13 12:10 UTC291
    let app = seeded_with(Floor::ChangingRoom, |run| run.body.eq = 20);
bf5f8cb6f60fatagen2026-10-13 12:10 UTC292
    let edge = send(&app, "JELQ", "/runs/cafebabe?technique=edge", &[], "").await;
bf5f8cb6f60fatagen2026-10-13 12:10 UTC293
    assert_eq!(edge.status(), StatusCode::OK);
bf5f8cb6f60fatagen2026-10-13 12:10 UTC294
    assert!(warnings(&edge).iter().all(|w| !w.contains("Cold start")));
bf5f8cb6f60fatagen2026-10-13 12:10 UTC295
    assert_eq!(
bf5f8cb6f60fatagen2026-10-13 12:10 UTC296
        header(&edge, "x-friction"),
bf5f8cb6f60fatagen2026-10-13 12:10 UTC297
        "2/10",
bf5f8cb6f60fatagen2026-10-13 12:10 UTC298
        "edging pays no cold tax"
bf5f8cb6f60fatagen2026-10-13 12:10 UTC299
    );
bf5f8cb6f60fatagen2026-10-13 12:10 UTC300
}
3ac624339c98atagen2026-10-13 12:21 UTC301
3ac624339c98atagen2026-10-13 12:21 UTC302
#[tokio::test]
3ac624339c98atagen2026-10-13 12:21 UTC303
async fn overstaying_a_floor_is_warned_then_punished() {
3ac624339c98atagen2026-10-13 12:21 UTC304
    let mut busted = 0;
3ac624339c98atagen2026-10-13 12:21 UTC305
    let mut moved = 0;
3ac624339c98atagen2026-10-13 12:21 UTC306
    for seed in 0..60u64 {
e028a4184d00atagen2026-10-14 10:49 UTC307
        let held = harness(Floor::ChangingRoom, seed, |run| {
3ac624339c98atagen2026-10-13 12:21 UTC308
            run.body.eq = 80;
3ac624339c98atagen2026-10-13 12:21 UTC309
            run.body.conditions.priapism_turns = 9;
3ac624339c98atagen2026-10-13 12:21 UTC310
            run.here.turns = 7;
3ac624339c98atagen2026-10-13 12:21 UTC311
        });
e028a4184d00atagen2026-10-14 10:49 UTC312
        let app = &held.app;
e028a4184d00atagen2026-10-14 10:49 UTC313
        let eighth = send(app, "HOLD", "/runs/cafebabe", &[], "").await;
3ac624339c98atagen2026-10-13 12:21 UTC314
        assert!(
3ac624339c98atagen2026-10-13 12:21 UTC315
            warnings(&eighth)
3ac624339c98atagen2026-10-13 12:21 UTC316
                .iter()
3ac624339c98atagen2026-10-13 12:21 UTC317
                .any(|w| w.contains("The gym closes"))
3ac624339c98atagen2026-10-13 12:21 UTC318
        );
e028a4184d00atagen2026-10-14 10:49 UTC319
        let ninth = send(app, "HOLD", "/runs/cafebabe", &[], "").await;
3ac624339c98atagen2026-10-13 12:21 UTC320
        assert!(warnings(&ninth).iter().any(|w| w.contains("last turn")));
e028a4184d00atagen2026-10-14 10:49 UTC321
        let tenth = send(app, "HOLD", "/runs/cafebabe", &[], "").await;
3ac624339c98atagen2026-10-13 12:21 UTC322
        match tenth.status() {
3ac624339c98atagen2026-10-13 12:21 UTC323
            StatusCode::GONE => busted += 1,
3ac624339c98atagen2026-10-13 12:21 UTC324
            StatusCode::REQUEST_TIMEOUT => {
3ac624339c98atagen2026-10-13 12:21 UTC325
                assert_eq!(header(&tenth, "x-floor"), "4");
e028a4184d00atagen2026-10-14 10:49 UTC326
                assert_eq!(held.eq(), 75, "priapism holds the deflation at 75");
3ac624339c98atagen2026-10-13 12:21 UTC327
                moved += 1;
3ac624339c98atagen2026-10-13 12:21 UTC328
            }
3ac624339c98atagen2026-10-13 12:21 UTC329
            other => panic!("seed {seed} overstayed into {other}"),
3ac624339c98atagen2026-10-13 12:21 UTC330
        }
3ac624339c98atagen2026-10-13 12:21 UTC331
    }
3ac624339c98atagen2026-10-13 12:21 UTC332
    assert!(
3ac624339c98atagen2026-10-13 12:21 UTC333
        busted > 15 && moved > 15,
3ac624339c98atagen2026-10-13 12:21 UTC334
        "{busted} busted, {moved} moved along"
3ac624339c98atagen2026-10-13 12:21 UTC335
    );
3ac624339c98atagen2026-10-13 12:21 UTC336
}
3ac624339c98atagen2026-10-13 12:21 UTC337
3ac624339c98atagen2026-10-13 12:21 UTC338
#[tokio::test]
3ac624339c98atagen2026-10-13 12:21 UTC339
async fn friction_at_the_limit_tears_the_foreskin() {
3ac624339c98atagen2026-10-13 12:21 UTC340
    let app = seeded_with(Floor::ChangingRoom, |run| {
3ac624339c98atagen2026-10-13 12:21 UTC341
        run.body.eq = 60;
3ac624339c98atagen2026-10-13 12:21 UTC342
        run.body.friction = 10;
3ac624339c98atagen2026-10-13 12:21 UTC343
        run.body.foreskin = Some(1.0);
3ac624339c98atagen2026-10-13 12:21 UTC344
    });
3ac624339c98atagen2026-10-13 12:21 UTC345
    let raw = send(&app, "JELQ", "/runs/cafebabe?technique=stretch", &[], "").await;
3ac624339c98atagen2026-10-13 12:21 UTC346
    assert!(warnings(&raw).iter().any(|w| w.contains("giving way")));
3ac624339c98atagen2026-10-13 12:21 UTC347
    let left: f64 = header(&raw, "x-foreskin").parse().unwrap();
3ac624339c98atagen2026-10-13 12:21 UTC348
    assert!(left < 1.0, "the foreskin is down to {left}");
3ac624339c98atagen2026-10-13 12:21 UTC349
3ac624339c98atagen2026-10-13 12:21 UTC350
    let thin = seeded_with(Floor::ChangingRoom, |run| {
3ac624339c98atagen2026-10-13 12:21 UTC351
        run.body.eq = 60;
3ac624339c98atagen2026-10-13 12:21 UTC352
        run.body.friction = 10;
3ac624339c98atagen2026-10-13 12:21 UTC353
        run.body.foreskin = Some(0.05);
3ac624339c98atagen2026-10-13 12:21 UTC354
    });
3ac624339c98atagen2026-10-13 12:21 UTC355
    let gone = send(&thin, "JELQ", "/runs/cafebabe?technique=stretch", &[], "").await;
3ac624339c98atagen2026-10-13 12:21 UTC356
    assert_eq!(gone.status(), StatusCode::GONE);
3ac624339c98atagen2026-10-13 12:21 UTC357
    assert_eq!(header(&gone, "x-foreskin"), "gone");
3ac624339c98atagen2026-10-13 12:21 UTC358
    assert_eq!(json(gone).await["foreskin"], -0.05);
3ac624339c98atagen2026-10-13 12:21 UTC359
}
e2727527c151atagen2026-10-13 12:38 UTC360
e2727527c151atagen2026-10-13 12:38 UTC361
#[tokio::test]
e2727527c151atagen2026-10-13 12:38 UTC362
async fn ice_treats_a_burn_even_through_priapism() {
e028a4184d00atagen2026-10-14 10:49 UTC363
    let held = harness(Floor::ChangingRoom, SEED, |run| {
e2727527c151atagen2026-10-13 12:38 UTC364
        run.body.eq = 80;
e2727527c151atagen2026-10-13 12:38 UTC365
        run.body.conditions.priapism_turns = 3;
e2727527c151atagen2026-10-13 12:38 UTC366
        run.body.conditions.friction_burn = true;
d3c4718d061eatagen2026-10-14 09:03 UTC367
        run.body.friction = 9;
e2727527c151atagen2026-10-13 12:38 UTC368
        run.items.stores.ice_packs = 1;
e2727527c151atagen2026-10-13 12:38 UTC369
    });
e028a4184d00atagen2026-10-14 10:49 UTC370
    let iced = send(&held.app, "JELQ", "/runs/cafebabe?technique=ice", &[], "").await;
e028a4184d00atagen2026-10-14 10:49 UTC371
    assert_eq!(held.eq(), 80);
d3c4718d061eatagen2026-10-14 09:03 UTC372
    assert_eq!(header(&iced, "x-friction"), "6/10");
e2727527c151atagen2026-10-13 12:38 UTC373
    assert!(warnings(&iced).iter().all(|w| !w.contains("friction burn")));
e2727527c151atagen2026-10-13 12:38 UTC374
}
6033b0db186catagen2026-10-13 12:44 UTC375
6033b0db186catagen2026-10-13 12:44 UTC376
#[tokio::test]
6033b0db186catagen2026-10-13 12:44 UTC377
async fn leaving_a_floor_delivers_every_queued_gain() {
6033b0db186catagen2026-10-13 12:44 UTC378
    let app = seeded_with(Floor::ChangingRoom, |run| {
6033b0db186catagen2026-10-13 12:44 UTC379
        run.here.turns = 6;
6033b0db186catagen2026-10-13 12:44 UTC380
        run.body.pending.push(crate::game::Pending {
6033b0db186catagen2026-10-13 12:44 UTC381
            due: run.turn + 4,
6033b0db186catagen2026-10-13 12:44 UTC382
            length: 0.2,
6033b0db186catagen2026-10-13 12:44 UTC383
            girth: 0.1,
6033b0db186catagen2026-10-13 12:44 UTC384
            foreskin: 0.0,
6033b0db186catagen2026-10-13 12:44 UTC385
        });
6033b0db186catagen2026-10-13 12:44 UTC386
    });
6033b0db186catagen2026-10-13 12:44 UTC387
    let left = send(&app, "POST", "/runs/cafebabe/floor", &[], "").await;
6033b0db186catagen2026-10-13 12:44 UTC388
    assert_eq!(left.status(), StatusCode::SEE_OTHER);
6033b0db186catagen2026-10-13 12:44 UTC389
    assert_eq!(
6033b0db186catagen2026-10-13 12:44 UTC390
        header(&left, "x-gains-arrived"),
6033b0db186catagen2026-10-13 12:44 UTC391
        "length=0.200, girth=0.100"
6033b0db186catagen2026-10-13 12:44 UTC392
    );
6033b0db186catagen2026-10-13 12:44 UTC393
}
6033b0db186catagen2026-10-13 12:44 UTC394
6033b0db186catagen2026-10-13 12:44 UTC395
#[tokio::test]
6033b0db186catagen2026-10-13 12:44 UTC396
async fn an_overstayed_jelq_spends_no_lube() {
6033b0db186catagen2026-10-13 12:44 UTC397
    let app = seeded_with(Floor::ChangingRoom, |run| {
6033b0db186catagen2026-10-13 12:44 UTC398
        run.body.eq = 60;
6033b0db186catagen2026-10-13 12:44 UTC399
        run.here.turns = 9;
6033b0db186catagen2026-10-13 12:44 UTC400
        run.items.lubes.baby_oil = 1;
6033b0db186catagen2026-10-13 12:44 UTC401
    });
6033b0db186catagen2026-10-13 12:44 UTC402
    let late = send(
6033b0db186catagen2026-10-13 12:44 UTC403
        &app,
6033b0db186catagen2026-10-13 12:44 UTC404
        "JELQ",
6033b0db186catagen2026-10-13 12:44 UTC405
        "/runs/cafebabe?technique=wet",
6033b0db186catagen2026-10-13 12:44 UTC406
        &[("content-type", "application/baby-oil")],
6033b0db186catagen2026-10-13 12:44 UTC407
        "",
6033b0db186catagen2026-10-13 12:44 UTC408
    )
6033b0db186catagen2026-10-13 12:44 UTC409
    .await;
6033b0db186catagen2026-10-13 12:44 UTC410
    assert!([StatusCode::GONE, StatusCode::REQUEST_TIMEOUT].contains(&late.status()));
6033b0db186catagen2026-10-13 12:44 UTC411
    if late.status() == StatusCode::REQUEST_TIMEOUT {
6033b0db186catagen2026-10-13 12:44 UTC412
        let held = json(send(&app, "GET", "/runs/cafebabe/inventory", &[], "").await).await;
6033b0db186catagen2026-10-13 12:44 UTC413
        assert!(
6033b0db186catagen2026-10-13 12:44 UTC414
            held["items"]
6033b0db186catagen2026-10-13 12:44 UTC415
                .as_array()
6033b0db186catagen2026-10-13 12:44 UTC416
                .unwrap()
6033b0db186catagen2026-10-13 12:44 UTC417
                .iter()
6033b0db186catagen2026-10-13 12:44 UTC418
                .any(|i| i["name"] == "baby-oil")
6033b0db186catagen2026-10-13 12:44 UTC419
        );
6033b0db186catagen2026-10-13 12:44 UTC420
    }
6033b0db186catagen2026-10-13 12:44 UTC421
}
f48daa8adf59atagen2026-10-13 12:51 UTC422
f48daa8adf59atagen2026-10-13 12:51 UTC423
#[tokio::test]
f48daa8adf59atagen2026-10-13 12:51 UTC424
async fn the_refractory_period_gives_back_some_stamina_and_rarely_all() {
f48daa8adf59atagen2026-10-13 12:51 UTC425
    let mut full = 0;
f48daa8adf59atagen2026-10-13 12:51 UTC426
    for seed in 0..400u64 {
f48daa8adf59atagen2026-10-13 12:51 UTC427
        let mut run = Run::new("cafebabe".into(), "cafe".into(), seed);
f48daa8adf59atagen2026-10-13 12:51 UTC428
        run.floor = Floor::ChangingRoom;
f48daa8adf59atagen2026-10-13 12:51 UTC429
        run.body.stamina = 0;
f48daa8adf59atagen2026-10-13 12:51 UTC430
        run.body.exhausted = true;
f48daa8adf59atagen2026-10-13 12:51 UTC431
        if run.hold().is_err() {
f48daa8adf59atagen2026-10-13 12:51 UTC432
            continue;
f48daa8adf59atagen2026-10-13 12:51 UTC433
        }
f48daa8adf59atagen2026-10-13 12:51 UTC434
        match run.body.stamina {
f48daa8adf59atagen2026-10-13 12:51 UTC435
            4..=6 => {}
f48daa8adf59atagen2026-10-13 12:51 UTC436
            10 => full += 1,
f48daa8adf59atagen2026-10-13 12:51 UTC437
            other => panic!("seed {seed} recovered {other} stamina"),
f48daa8adf59atagen2026-10-13 12:51 UTC438
        }
f48daa8adf59atagen2026-10-13 12:51 UTC439
    }
f48daa8adf59atagen2026-10-13 12:51 UTC440
    assert!((8..=35).contains(&full), "{full} second winds in 400");
f48daa8adf59atagen2026-10-13 12:51 UTC441
}
e028a4184d00atagen2026-10-14 10:49 UTC442
e028a4184d00atagen2026-10-14 10:49 UTC443
#[tokio::test]
e028a4184d00atagen2026-10-14 10:49 UTC444
async fn eq_is_named_rather_than_numbered() {
e028a4184d00atagen2026-10-14 10:49 UTC445
    for (eq, setup, named) in [
e028a4184d00atagen2026-10-14 10:49 UTC446
        (0, None, "Flaccid"),
e028a4184d00atagen2026-10-14 10:49 UTC447
        (20, None, "Cold"),
e028a4184d00atagen2026-10-14 10:49 UTC448
        (40, None, "Warm"),
e028a4184d00atagen2026-10-14 10:49 UTC449
        (60, None, "Ideal"),
e028a4184d00atagen2026-10-14 10:49 UTC450
        (60, Some("burn"), "Swollen"),
e028a4184d00atagen2026-10-14 10:49 UTC451
        (80, None, "Swollen"),
e028a4184d00atagen2026-10-14 10:49 UTC452
        (88, None, "Throbbing"),
e028a4184d00atagen2026-10-14 10:49 UTC453
        (85, Some("ring"), "Straining"),
e028a4184d00atagen2026-10-14 10:49 UTC454
        (93, None, "Straining"),
e028a4184d00atagen2026-10-14 10:49 UTC455
        (97, None, "Imminent"),
e028a4184d00atagen2026-10-14 10:49 UTC456
        (40, Some("priapism"), "Priapic"),
e028a4184d00atagen2026-10-14 10:49 UTC457
    ] {
e028a4184d00atagen2026-10-14 10:49 UTC458
        let app = seeded_with(Floor::Bedroom, |run| {
e028a4184d00atagen2026-10-14 10:49 UTC459
            run.body.eq = eq;
e028a4184d00atagen2026-10-14 10:49 UTC460
            match setup {
e028a4184d00atagen2026-10-14 10:49 UTC461
                Some("burn") => run.body.conditions.friction_burn = true,
e028a4184d00atagen2026-10-14 10:49 UTC462
                Some("ring") => run.items.kit.ring = true,
e028a4184d00atagen2026-10-14 10:49 UTC463
                Some("priapism") => run.body.conditions.priapism_turns = 3,
e028a4184d00atagen2026-10-14 10:49 UTC464
                _ => {}
e028a4184d00atagen2026-10-14 10:49 UTC465
            }
e028a4184d00atagen2026-10-14 10:49 UTC466
        });
e028a4184d00atagen2026-10-14 10:49 UTC467
        let view = send(&app, "GET", "/runs/cafebabe", &[], "").await;
e028a4184d00atagen2026-10-14 10:49 UTC468
        assert_eq!(header(&view, "x-eq"), named, "{eq} with {setup:?}");
e028a4184d00atagen2026-10-14 10:49 UTC469
    }
e028a4184d00atagen2026-10-14 10:49 UTC470
}
f3a1913c1299atagen2026-10-14 11:42 UTC471
f3a1913c1299atagen2026-10-14 11:42 UTC472
#[tokio::test]
f3a1913c1299atagen2026-10-14 11:42 UTC473
async fn gauges_near_the_edge_are_flagged() {
f3a1913c1299atagen2026-10-14 11:42 UTC474
    let calm = seeded_with(Floor::Bedroom, |run| run.body.eq = 60);
f3a1913c1299atagen2026-10-14 11:42 UTC475
    let view = send(&calm, "GET", "/runs/cafebabe", &[], "").await;
f3a1913c1299atagen2026-10-14 11:42 UTC476
    assert!(view.headers().get("x-danger").is_none());
f3a1913c1299atagen2026-10-14 11:42 UTC477
f3a1913c1299atagen2026-10-14 11:42 UTC478
    let tense = seeded_with(Floor::Bedroom, |run| {
f3a1913c1299atagen2026-10-14 11:42 UTC479
        run.body.eq = 93;
f3a1913c1299atagen2026-10-14 11:42 UTC480
        run.body.friction = 6;
f3a1913c1299atagen2026-10-14 11:42 UTC481
        run.body.stamina = 2;
f3a1913c1299atagen2026-10-14 11:42 UTC482
        run.standing.suspicion = 4;
f3a1913c1299atagen2026-10-14 11:42 UTC483
    });
f3a1913c1299atagen2026-10-14 11:42 UTC484
    let view = send(&tense, "GET", "/runs/cafebabe", &[], "").await;
f3a1913c1299atagen2026-10-14 11:42 UTC485
    assert_eq!(
f3a1913c1299atagen2026-10-14 11:42 UTC486
        header(&view, "x-danger"),
f3a1913c1299atagen2026-10-14 11:42 UTC487
        "friction=dangerous, eq=critical, stamina=dangerous, suspicion=critical"
f3a1913c1299atagen2026-10-14 11:42 UTC488
    );
f3a1913c1299atagen2026-10-14 11:42 UTC489
}
624fe9c09c8eatagen2026-10-14 12:11 UTC490
624fe9c09c8eatagen2026-10-14 12:11 UTC491
#[tokio::test]
624fe9c09c8eatagen2026-10-14 12:11 UTC492
async fn a_served_floor_says_so() {
624fe9c09c8eatagen2026-10-14 12:11 UTC493
    let app = seeded_with(Floor::Bedroom, |run| run.here.turns = 6);
624fe9c09c8eatagen2026-10-14 12:11 UTC494
    let view = send(&app, "GET", "/runs/cafebabe", &[], "").await;
624fe9c09c8eatagen2026-10-14 12:11 UTC495
    assert_eq!(header(&view, "x-floor-over"), "?1");
624fe9c09c8eatagen2026-10-14 12:11 UTC496
    let forum = seeded_with(Floor::Forum, |run| run.here.turns = 6);
624fe9c09c8eatagen2026-10-14 12:11 UTC497
    let view = send(&forum, "GET", "/runs/cafebabe", &[], "").await;
624fe9c09c8eatagen2026-10-14 12:11 UTC498
    assert!(view.headers().get("x-floor-over").is_none());
624fe9c09c8eatagen2026-10-14 12:11 UTC499
}
ea51d9fda49datagen2026-10-14 12:20 UTC500
ea51d9fda49datagen2026-10-14 12:20 UTC501
#[tokio::test]
ea51d9fda49datagen2026-10-14 12:20 UTC502
async fn a_rupture_names_itself_in_x_eq() {
ea51d9fda49datagen2026-10-14 12:20 UTC503
    for seed in 0..500u64 {
ea51d9fda49datagen2026-10-14 12:20 UTC504
        let app = app_with(Floor::Bedroom, seed, |run| {
ea51d9fda49datagen2026-10-14 12:20 UTC505
            run.body.eq = 92;
ea51d9fda49datagen2026-10-14 12:20 UTC506
            run.body.conditions.friction_burn = true;
ea51d9fda49datagen2026-10-14 12:20 UTC507
        });
ea51d9fda49datagen2026-10-14 12:20 UTC508
        let turn = send(
ea51d9fda49datagen2026-10-14 12:20 UTC509
            &app,
ea51d9fda49datagen2026-10-14 12:20 UTC510
            "JELQ",
ea51d9fda49datagen2026-10-14 12:20 UTC511
            "/runs/cafebabe?technique=stretch&intensity=10",
ea51d9fda49datagen2026-10-14 12:20 UTC512
            &[],
ea51d9fda49datagen2026-10-14 12:20 UTC513
            "",
ea51d9fda49datagen2026-10-14 12:20 UTC514
        )
ea51d9fda49datagen2026-10-14 12:20 UTC515
        .await;
ea51d9fda49datagen2026-10-14 12:20 UTC516
        if turn
ea51d9fda49datagen2026-10-14 12:20 UTC517
            .headers()
ea51d9fda49datagen2026-10-14 12:20 UTC518
            .get("x-run-over")
ea51d9fda49datagen2026-10-14 12:20 UTC519
            .map(|v| v.to_str().unwrap())
ea51d9fda49datagen2026-10-14 12:20 UTC520
            == Some("ruptured")
ea51d9fda49datagen2026-10-14 12:20 UTC521
        {
ea51d9fda49datagen2026-10-14 12:20 UTC522
            assert_eq!(header(&turn, "x-eq"), "Ruptured");
ea51d9fda49datagen2026-10-14 12:20 UTC523
            return;
ea51d9fda49datagen2026-10-14 12:20 UTC524
        }
ea51d9fda49datagen2026-10-14 12:20 UTC525
    }
ea51d9fda49datagen2026-10-14 12:20 UTC526
    panic!("no seed ruptured");
ea51d9fda49datagen2026-10-14 12:20 UTC527
}