atagen

jelq

public

an http based roguelike rpg about jelqing

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

Blame: src/tests/household.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 mum_accepts_a_known_excuse() {
8b8e83329a1datagen2026-10-12 12:01 UTC5
    let app = seeded_with(Floor::Bedroom, mum_at_the_door);
8b8e83329a1datagen2026-10-12 12:01 UTC6
    let response = send(&app, "GET", "/runs/cafebabe", &[], "").await;
8b8e83329a1datagen2026-10-12 12:01 UTC7
    assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
8b8e83329a1datagen2026-10-12 12:01 UTC8
    assert_eq!(header(&response, "via"), "1.1 mum");
8b8e83329a1datagen2026-10-12 12:01 UTC9
    let response = send(
8b8e83329a1datagen2026-10-12 12:01 UTC10
        &app,
8b8e83329a1datagen2026-10-12 12:01 UTC11
        "GET",
8b8e83329a1datagen2026-10-12 12:01 UTC12
        "/runs/cafebabe",
8b8e83329a1datagen2026-10-12 12:01 UTC13
        &[("authorization", "Excuse \"homework\"")],
8b8e83329a1datagen2026-10-12 12:01 UTC14
        "",
8b8e83329a1datagen2026-10-12 12:01 UTC15
    )
8b8e83329a1datagen2026-10-12 12:01 UTC16
    .await;
8b8e83329a1datagen2026-10-12 12:01 UTC17
    assert_eq!(response.status(), StatusCode::OK);
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 mum_runs_out_of_patience() {
8b8e83329a1datagen2026-10-12 12:01 UTC22
    let app = seeded_with(Floor::Bedroom, mum_at_the_door);
8b8e83329a1datagen2026-10-12 12:01 UTC23
    let lie = [("authorization", "Excuse \"it is for science\"")];
8b8e83329a1datagen2026-10-12 12:01 UTC24
    for _ in 0..2 {
8b8e83329a1datagen2026-10-12 12:01 UTC25
        let response = send(&app, "GET", "/runs/cafebabe", &lie, "").await;
8b8e83329a1datagen2026-10-12 12:01 UTC26
        assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
8b8e83329a1datagen2026-10-12 12:01 UTC27
    }
8b8e83329a1datagen2026-10-12 12:01 UTC28
    let caught = send(&app, "GET", "/runs/cafebabe", &lie, "").await;
8b8e83329a1datagen2026-10-12 12:01 UTC29
    assert_eq!(caught.status(), StatusCode::GONE);
8b8e83329a1datagen2026-10-12 12:01 UTC30
    assert_eq!(
8b8e83329a1datagen2026-10-12 12:01 UTC31
        json(caught).await["type"],
8b8e83329a1datagen2026-10-12 12:01 UTC32
        "https://jelq.dev/problems/caught"
8b8e83329a1datagen2026-10-12 12:01 UTC33
    );
8b8e83329a1datagen2026-10-12 12:01 UTC34
}
8b8e83329a1datagen2026-10-12 12:01 UTC35
8b8e83329a1datagen2026-10-12 12:01 UTC36
#[tokio::test]
8b8e83329a1datagen2026-10-12 12:01 UTC37
async fn mum_remembers_excuses_by_christmas() {
8b8e83329a1datagen2026-10-12 12:01 UTC38
    let app = seeded_with(Floor::Nans, |run| {
8b8e83329a1datagen2026-10-12 12:01 UTC39
        mum_at_the_door(run);
8b8e83329a1datagen2026-10-12 12:01 UTC40
        run.household.heard.push(Excuse::Homework);
8b8e83329a1datagen2026-10-12 12:01 UTC41
    });
8b8e83329a1datagen2026-10-12 12:01 UTC42
    let basic = send(
8b8e83329a1datagen2026-10-12 12:01 UTC43
        &app,
8b8e83329a1datagen2026-10-12 12:01 UTC44
        "GET",
8b8e83329a1datagen2026-10-12 12:01 UTC45
        "/runs/cafebabe",
8b8e83329a1datagen2026-10-12 12:01 UTC46
        &[("authorization", "Excuse \"changing\"")],
8b8e83329a1datagen2026-10-12 12:01 UTC47
        "",
8b8e83329a1datagen2026-10-12 12:01 UTC48
    )
8b8e83329a1datagen2026-10-12 12:01 UTC49
    .await;
8b8e83329a1datagen2026-10-12 12:01 UTC50
    assert_eq!(basic.status(), StatusCode::UNAUTHORIZED);
8b8e83329a1datagen2026-10-12 12:01 UTC51
    assert!(header(&basic, "www-authenticate").starts_with("Digest"));
8b8e83329a1datagen2026-10-12 12:01 UTC52
    let replay = send(
8b8e83329a1datagen2026-10-12 12:01 UTC53
        &app,
8b8e83329a1datagen2026-10-12 12:01 UTC54
        "GET",
8b8e83329a1datagen2026-10-12 12:01 UTC55
        "/runs/cafebabe",
8b8e83329a1datagen2026-10-12 12:01 UTC56
        &[(
8b8e83329a1datagen2026-10-12 12:01 UTC57
            "authorization",
8b8e83329a1datagen2026-10-12 12:01 UTC58
            "Digest username=\"homework\", nonce=\"abc\"",
8b8e83329a1datagen2026-10-12 12:01 UTC59
        )],
8b8e83329a1datagen2026-10-12 12:01 UTC60
        "",
8b8e83329a1datagen2026-10-12 12:01 UTC61
    )
8b8e83329a1datagen2026-10-12 12:01 UTC62
    .await;
8b8e83329a1datagen2026-10-12 12:01 UTC63
    assert_eq!(
8b8e83329a1datagen2026-10-12 12:01 UTC64
        json(replay).await["type"],
8b8e83329a1datagen2026-10-12 12:01 UTC65
        "https://jelq.dev/problems/excuse-replayed"
8b8e83329a1datagen2026-10-12 12:01 UTC66
    );
8b8e83329a1datagen2026-10-12 12:01 UTC67
    let fresh = send(
8b8e83329a1datagen2026-10-12 12:01 UTC68
        &app,
8b8e83329a1datagen2026-10-12 12:01 UTC69
        "GET",
8b8e83329a1datagen2026-10-12 12:01 UTC70
        "/runs/cafebabe",
8b8e83329a1datagen2026-10-12 12:01 UTC71
        &[(
8b8e83329a1datagen2026-10-12 12:01 UTC72
            "authorization",
8b8e83329a1datagen2026-10-12 12:01 UTC73
            "Digest username=\"wrapping presents\", nonce=\"abc\"",
8b8e83329a1datagen2026-10-12 12:01 UTC74
        )],
8b8e83329a1datagen2026-10-12 12:01 UTC75
        "",
8b8e83329a1datagen2026-10-12 12:01 UTC76
    )
8b8e83329a1datagen2026-10-12 12:01 UTC77
    .await;
8b8e83329a1datagen2026-10-12 12:01 UTC78
    assert_eq!(fresh.status(), StatusCode::OK);
8b8e83329a1datagen2026-10-12 12:01 UTC79
}
8b8e83329a1datagen2026-10-12 12:01 UTC80
8b8e83329a1datagen2026-10-12 12:01 UTC81
#[tokio::test]
8b8e83329a1datagen2026-10-12 12:01 UTC82
async fn a_pulled_muscle_is_a_true_excuse() {
8b8e83329a1datagen2026-10-12 12:01 UTC83
    let app = seeded_with(Floor::Nans, |run| {
8b8e83329a1datagen2026-10-12 12:01 UTC84
        mum_at_the_door(run);
8b8e83329a1datagen2026-10-12 12:01 UTC85
        run.body.conditions.pulled_muscle = true;
8b8e83329a1datagen2026-10-12 12:01 UTC86
        run.household.heard.push(Excuse::PulledAMuscle);
8b8e83329a1datagen2026-10-12 12:01 UTC87
    });
8b8e83329a1datagen2026-10-12 12:01 UTC88
    let response = send(
8b8e83329a1datagen2026-10-12 12:01 UTC89
        &app,
8b8e83329a1datagen2026-10-12 12:01 UTC90
        "GET",
8b8e83329a1datagen2026-10-12 12:01 UTC91
        "/runs/cafebabe",
8b8e83329a1datagen2026-10-12 12:01 UTC92
        &[(
8b8e83329a1datagen2026-10-12 12:01 UTC93
            "authorization",
8b8e83329a1datagen2026-10-12 12:01 UTC94
            "Digest username=\"pulled a muscle\", nonce=\"abc\"",
8b8e83329a1datagen2026-10-12 12:01 UTC95
        )],
8b8e83329a1datagen2026-10-12 12:01 UTC96
        "",
8b8e83329a1datagen2026-10-12 12:01 UTC97
    )
8b8e83329a1datagen2026-10-12 12:01 UTC98
    .await;
8b8e83329a1datagen2026-10-12 12:01 UTC99
    assert_eq!(response.status(), StatusCode::OK);
8b8e83329a1datagen2026-10-12 12:01 UTC100
}
8b8e83329a1datagen2026-10-12 12:01 UTC101
8b8e83329a1datagen2026-10-12 12:01 UTC102
#[tokio::test]
8b8e83329a1datagen2026-10-12 12:01 UTC103
async fn ignoring_mum_has_consequences() {
8b8e83329a1datagen2026-10-12 12:01 UTC104
    let app = seeded_with(Floor::Bedroom, |run| {
8b8e83329a1datagen2026-10-12 12:01 UTC105
        mum_at_the_door(run);
8b8e83329a1datagen2026-10-12 12:01 UTC106
        run.here.turns = 6;
8b8e83329a1datagen2026-10-12 12:01 UTC107
    });
8b8e83329a1datagen2026-10-12 12:01 UTC108
    let knock = send(&app, "GET", "/runs/cafebabe", &[], "").await;
8b8e83329a1datagen2026-10-12 12:01 UTC109
    assert_eq!(
8b8e83329a1datagen2026-10-12 12:01 UTC110
        json(knock).await["type"],
8b8e83329a1datagen2026-10-12 12:01 UTC111
        "https://jelq.dev/problems/someone-at-the-door"
8b8e83329a1datagen2026-10-12 12:01 UTC112
    );
8b8e83329a1datagen2026-10-12 12:01 UTC113
    let leave = send(&app, "POST", "/runs/cafebabe/floor", &[], "").await;
8b8e83329a1datagen2026-10-12 12:01 UTC114
    assert_eq!(
8b8e83329a1datagen2026-10-12 12:01 UTC115
        leave.status(),
8b8e83329a1datagen2026-10-12 12:01 UTC116
        StatusCode::UNAUTHORIZED,
8b8e83329a1datagen2026-10-12 12:01 UTC117
        "no leaving with Mum at the door"
8b8e83329a1datagen2026-10-12 12:01 UTC118
    );
8b8e83329a1datagen2026-10-12 12:01 UTC119
    send(&app, "GET", "/runs/cafebabe", &[], "").await;
8b8e83329a1datagen2026-10-12 12:01 UTC120
    let opened = send(&app, "GET", "/runs/cafebabe", &[], "").await;
8b8e83329a1datagen2026-10-12 12:01 UTC121
    assert_eq!(opened.status(), StatusCode::GONE);
8b8e83329a1datagen2026-10-12 12:01 UTC122
    assert_eq!(
8b8e83329a1datagen2026-10-12 12:01 UTC123
        json(opened).await["type"],
8b8e83329a1datagen2026-10-12 12:01 UTC124
        "https://jelq.dev/problems/opened"
8b8e83329a1datagen2026-10-12 12:01 UTC125
    );
8b8e83329a1datagen2026-10-12 12:01 UTC126
}
8b8e83329a1datagen2026-10-12 12:01 UTC127
8b8e83329a1datagen2026-10-12 12:01 UTC128
#[tokio::test]
8b8e83329a1datagen2026-10-12 12:01 UTC129
async fn mom_speaks_american() {
8b8e83329a1datagen2026-10-12 12:01 UTC130
    let app = seeded_with(Floor::Nans, mum_at_the_door);
8b8e83329a1datagen2026-10-12 12:01 UTC131
    let us = [("accept-language", "en-US")];
8b8e83329a1datagen2026-10-12 12:01 UTC132
    let knock = send(&app, "GET", "/runs/cafebabe", &us, "").await;
8b8e83329a1datagen2026-10-12 12:01 UTC133
    assert_eq!(header(&knock, "content-language"), "en-US");
8b8e83329a1datagen2026-10-12 12:01 UTC134
    assert_eq!(header(&knock, "via"), "1.1 mom");
8b8e83329a1datagen2026-10-12 12:01 UTC135
    assert!(
8b8e83329a1datagen2026-10-12 12:01 UTC136
        json(knock).await["detail"]
8b8e83329a1datagen2026-10-12 12:01 UTC137
            .as_str()
8b8e83329a1datagen2026-10-12 12:01 UTC138
            .unwrap()
8b8e83329a1datagen2026-10-12 12:01 UTC139
            .contains("Someone is at the door.")
8b8e83329a1datagen2026-10-12 12:01 UTC140
    );
8b8e83329a1datagen2026-10-12 12:01 UTC141
    let excuse = [
8b8e83329a1datagen2026-10-12 12:01 UTC142
        ("accept-language", "en-US"),
8b8e83329a1datagen2026-10-12 12:01 UTC143
        (
8b8e83329a1datagen2026-10-12 12:01 UTC144
            "authorization",
8b8e83329a1datagen2026-10-12 12:01 UTC145
            "Digest username=\"carving the turkey\", nonce=\"abc\"",
8b8e83329a1datagen2026-10-12 12:01 UTC146
        ),
8b8e83329a1datagen2026-10-12 12:01 UTC147
    ];
8b8e83329a1datagen2026-10-12 12:01 UTC148
    let answered = send(&app, "GET", "/runs/cafebabe", &excuse, "").await;
8b8e83329a1datagen2026-10-12 12:01 UTC149
    assert_eq!(answered.status(), StatusCode::OK);
8b8e83329a1datagen2026-10-12 12:01 UTC150
    let floor = json(send(&app, "GET", "/runs/cafebabe/floor", &us, "").await).await;
8b8e83329a1datagen2026-10-12 12:01 UTC151
    assert_eq!(floor["name"], "Grandma's house for Thanksgiving");
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 an_empty_bottle_is_declined_before_the_enemy_stirs() {
8b8e83329a1datagen2026-10-12 12:01 UTC156
    let control = seeded_with(Floor::ChangingRoom, |run| {
8b8e83329a1datagen2026-10-12 12:01 UTC157
        run.standing.suspicion = SUSPICION_LIMIT;
8b8e83329a1datagen2026-10-12 12:01 UTC158
    });
8b8e83329a1datagen2026-10-12 12:01 UTC159
    let lube = [("content-type", "application/baby-oil")];
8b8e83329a1datagen2026-10-12 12:01 UTC160
    let caught = send(
8b8e83329a1datagen2026-10-12 12:01 UTC161
        &control,
8b8e83329a1datagen2026-10-12 12:01 UTC162
        "JELQ",
8b8e83329a1datagen2026-10-12 12:01 UTC163
        "/runs/cafebabe?technique=wet&intensity=5",
8b8e83329a1datagen2026-10-12 12:01 UTC164
        &lube,
8b8e83329a1datagen2026-10-12 12:01 UTC165
        "",
8b8e83329a1datagen2026-10-12 12:01 UTC166
    )
8b8e83329a1datagen2026-10-12 12:01 UTC167
    .await;
8b8e83329a1datagen2026-10-12 12:01 UTC168
    assert_eq!(caught.status(), StatusCode::UNAVAILABLE_FOR_LEGAL_REASONS);
8b8e83329a1datagen2026-10-12 12:01 UTC169
8b8e83329a1datagen2026-10-12 12:01 UTC170
    let app = seeded_with(Floor::ChangingRoom, |run| {
8b8e83329a1datagen2026-10-12 12:01 UTC171
        run.standing.suspicion = SUSPICION_LIMIT;
8b8e83329a1datagen2026-10-12 12:01 UTC172
        run.items.lubes.baby_oil = 0;
8b8e83329a1datagen2026-10-12 12:01 UTC173
    });
8b8e83329a1datagen2026-10-12 12:01 UTC174
    let refused = send(
8b8e83329a1datagen2026-10-12 12:01 UTC175
        &app,
8b8e83329a1datagen2026-10-12 12:01 UTC176
        "JELQ",
8b8e83329a1datagen2026-10-12 12:01 UTC177
        "/runs/cafebabe?technique=wet&intensity=5",
8b8e83329a1datagen2026-10-12 12:01 UTC178
        &lube,
8b8e83329a1datagen2026-10-12 12:01 UTC179
        "",
8b8e83329a1datagen2026-10-12 12:01 UTC180
    )
8b8e83329a1datagen2026-10-12 12:01 UTC181
    .await;
8b8e83329a1datagen2026-10-12 12:01 UTC182
    assert_eq!(refused.status(), StatusCode::UNSUPPORTED_MEDIA_TYPE);
8b8e83329a1datagen2026-10-12 12:01 UTC183
    let view = send(&app, "GET", "/runs/cafebabe", &[], "").await;
8b8e83329a1datagen2026-10-12 12:01 UTC184
    assert_eq!(header(&view, "x-suspicion"), "5/5");
8b8e83329a1datagen2026-10-12 12:01 UTC185
    assert_eq!(header(&view, "x-shame"), "0");
8b8e83329a1datagen2026-10-12 12:01 UTC186
}
8b8e83329a1datagen2026-10-12 12:01 UTC187
8b8e83329a1datagen2026-10-12 12:01 UTC188
#[tokio::test]
8b8e83329a1datagen2026-10-12 12:01 UTC189
async fn respectable_girth_waves_off_the_gym_staff() {
8b8e83329a1datagen2026-10-12 12:01 UTC190
    let staff = |girth: f64| {
8b8e83329a1datagen2026-10-12 12:01 UTC191
        seeded_with(Floor::ChangingRoom, move |run| {
8b8e83329a1datagen2026-10-12 12:01 UTC192
            run.body.eq = 45;
8b8e83329a1datagen2026-10-12 12:01 UTC193
            run.body.girth = girth;
8b8e83329a1datagen2026-10-12 12:01 UTC194
            run.standing.suspicion = SUSPICION_LIMIT;
8b8e83329a1datagen2026-10-12 12:01 UTC195
        })
8b8e83329a1datagen2026-10-12 12:01 UTC196
    };
8b8e83329a1datagen2026-10-12 12:01 UTC197
    let thin = send(
8b8e83329a1datagen2026-10-12 12:01 UTC198
        &staff(4.5),
8b8e83329a1datagen2026-10-12 12:01 UTC199
        "JELQ",
8b8e83329a1datagen2026-10-12 12:01 UTC200
        "/runs/cafebabe?technique=edge",
8b8e83329a1datagen2026-10-12 12:01 UTC201
        &[],
8b8e83329a1datagen2026-10-12 12:01 UTC202
        "",
8b8e83329a1datagen2026-10-12 12:01 UTC203
    )
8b8e83329a1datagen2026-10-12 12:01 UTC204
    .await;
8b8e83329a1datagen2026-10-12 12:01 UTC205
    assert_eq!(thin.status(), StatusCode::UNAVAILABLE_FOR_LEGAL_REASONS);
8b8e83329a1datagen2026-10-12 12:01 UTC206
    assert_eq!(header(&thin, "x-shame"), "5");
8b8e83329a1datagen2026-10-12 12:01 UTC207
8b8e83329a1datagen2026-10-12 12:01 UTC208
    let thick = send(
8b8e83329a1datagen2026-10-12 12:01 UTC209
        &staff(5.2),
8b8e83329a1datagen2026-10-12 12:01 UTC210
        "JELQ",
8b8e83329a1datagen2026-10-12 12:01 UTC211
        "/runs/cafebabe?technique=edge",
8b8e83329a1datagen2026-10-12 12:01 UTC212
        &[],
8b8e83329a1datagen2026-10-12 12:01 UTC213
        "",
8b8e83329a1datagen2026-10-12 12:01 UTC214
    )
8b8e83329a1datagen2026-10-12 12:01 UTC215
    .await;
8b8e83329a1datagen2026-10-12 12:01 UTC216
    assert_eq!(thick.status(), StatusCode::OK);
8b8e83329a1datagen2026-10-12 12:01 UTC217
    assert!(
8b8e83329a1datagen2026-10-12 12:01 UTC218
        warnings(&thick)
8b8e83329a1datagen2026-10-12 12:01 UTC219
            .iter()
8b8e83329a1datagen2026-10-12 12:01 UTC220
            .any(|w| w.contains("the gym staff have seen worse"))
8b8e83329a1datagen2026-10-12 12:01 UTC221
    );
8b8e83329a1datagen2026-10-12 12:01 UTC222
}
8b8e83329a1datagen2026-10-12 12:01 UTC223
8b8e83329a1datagen2026-10-12 12:01 UTC224
#[tokio::test]
8b8e83329a1datagen2026-10-12 12:01 UTC225
async fn the_kitchen_is_only_on_the_home_floors() {
8b8e83329a1datagen2026-10-12 12:01 UTC226
    let absent = seeded_with(Floor::Bathroom, |_| {});
8b8e83329a1datagen2026-10-12 12:01 UTC227
    let no_kitchen = send(&absent, "POST", "/runs/cafebabe/kitchen", &[], "").await;
8b8e83329a1datagen2026-10-12 12:01 UTC228
    assert_eq!(no_kitchen.status(), StatusCode::NOT_FOUND);
8b8e83329a1datagen2026-10-12 12:01 UTC229
    let view = send(&absent, "GET", "/runs/cafebabe", &[], "").await;
8b8e83329a1datagen2026-10-12 12:01 UTC230
    assert!(!header(&view, "link").contains("/kitchen"));
8b8e83329a1datagen2026-10-12 12:01 UTC231
8b8e83329a1datagen2026-10-12 12:01 UTC232
    let app = seeded_with(Floor::Bedroom, |_| {});
8b8e83329a1datagen2026-10-12 12:01 UTC233
    let view = send(&app, "GET", "/runs/cafebabe", &[], "").await;
8b8e83329a1datagen2026-10-12 12:01 UTC234
    assert!(header(&view, "link").contains("/kitchen"));
8b8e83329a1datagen2026-10-12 12:01 UTC235
    let raid = send(&app, "POST", "/runs/cafebabe/kitchen", &[], "").await;
8b8e83329a1datagen2026-10-12 12:01 UTC236
    assert_ne!(raid.status(), StatusCode::NOT_FOUND);
8b8e83329a1datagen2026-10-12 12:01 UTC237
}
8b8e83329a1datagen2026-10-12 12:01 UTC238
8b8e83329a1datagen2026-10-12 12:01 UTC239
#[tokio::test]
8b8e83329a1datagen2026-10-12 12:01 UTC240
async fn any_other_action_takes_the_melon_off_the_table() {
8b8e83329a1datagen2026-10-12 12:01 UTC241
    for (method, uri) in [
8b8e83329a1datagen2026-10-12 12:01 UTC242
        ("JELQ", "/runs/cafebabe?technique=warmup"),
8b8e83329a1datagen2026-10-12 12:01 UTC243
        ("POST", "/runs/cafebabe/drawer"),
8b8e83329a1datagen2026-10-12 12:01 UTC244
    ] {
8b8e83329a1datagen2026-10-12 12:01 UTC245
        let app = seeded_with(Floor::Bedroom, |run| {
8b8e83329a1datagen2026-10-12 12:01 UTC246
            run.body.eq = 95;
8b8e83329a1datagen2026-10-12 12:01 UTC247
            run.here.kitchen.melon_offered = true;
8b8e83329a1datagen2026-10-12 12:01 UTC248
        });
8b8e83329a1datagen2026-10-12 12:01 UTC249
        send(&app, method, uri, &[], "").await;
8b8e83329a1datagen2026-10-12 12:01 UTC250
        let melon = send(&app, "POST", "/runs/cafebabe/kitchen/watermelon", &[], "").await;
8b8e83329a1datagen2026-10-12 12:01 UTC251
        assert_eq!(melon.status(), StatusCode::CONFLICT, "{method} {uri}");
8b8e83329a1datagen2026-10-12 12:01 UTC252
    }
8b8e83329a1datagen2026-10-12 12:01 UTC253
}
8b8e83329a1datagen2026-10-12 12:01 UTC254
8b8e83329a1datagen2026-10-12 12:01 UTC255
#[tokio::test]
8b8e83329a1datagen2026-10-12 12:01 UTC256
async fn the_truth_works_under_any_scheme_and_costs_ten() {
8b8e83329a1datagen2026-10-12 12:01 UTC257
    let app = seeded_with(Floor::Bedroom, mum_at_the_door);
8b8e83329a1datagen2026-10-12 12:01 UTC258
    let truth = [("authorization", "Whatever honesty")];
8b8e83329a1datagen2026-10-12 12:01 UTC259
    let confessed = send(&app, "GET", "/runs/cafebabe", &truth, "").await;
8b8e83329a1datagen2026-10-12 12:01 UTC260
    assert_eq!(confessed.status(), StatusCode::OK);
8b8e83329a1datagen2026-10-12 12:01 UTC261
    assert_eq!(header(&confessed, "x-shame"), "10");
8b8e83329a1datagen2026-10-12 12:01 UTC262
}
8b8e83329a1datagen2026-10-12 12:01 UTC263
8b8e83329a1datagen2026-10-12 12:01 UTC264
#[tokio::test]
8b8e83329a1datagen2026-10-12 12:01 UTC265
async fn mum_refuses_basic_and_stale_nonces() {
8b8e83329a1datagen2026-10-12 12:01 UTC266
    let bedroom = seeded_with(Floor::Bedroom, mum_at_the_door);
8b8e83329a1datagen2026-10-12 12:01 UTC267
    let basic = send(
8b8e83329a1datagen2026-10-12 12:01 UTC268
        &bedroom,
8b8e83329a1datagen2026-10-12 12:01 UTC269
        "GET",
8b8e83329a1datagen2026-10-12 12:01 UTC270
        "/runs/cafebabe",
8b8e83329a1datagen2026-10-12 12:01 UTC271
        &[("authorization", "Basic aG9tZXdvcms=")],
8b8e83329a1datagen2026-10-12 12:01 UTC272
        "",
8b8e83329a1datagen2026-10-12 12:01 UTC273
    )
8b8e83329a1datagen2026-10-12 12:01 UTC274
    .await;
8b8e83329a1datagen2026-10-12 12:01 UTC275
    assert_eq!(basic.status(), StatusCode::UNAUTHORIZED);
8b8e83329a1datagen2026-10-12 12:01 UTC276
    assert!(
8b8e83329a1datagen2026-10-12 12:01 UTC277
        json(basic).await["type"]
8b8e83329a1datagen2026-10-12 12:01 UTC278
            .as_str()
8b8e83329a1datagen2026-10-12 12:01 UTC279
            .unwrap()
8b8e83329a1datagen2026-10-12 12:01 UTC280
            .ends_with("/basic")
8b8e83329a1datagen2026-10-12 12:01 UTC281
    );
8b8e83329a1datagen2026-10-12 12:01 UTC282
8b8e83329a1datagen2026-10-12 12:01 UTC283
    let nans = seeded_with(Floor::Nans, mum_at_the_door);
8b8e83329a1datagen2026-10-12 12:01 UTC284
    let stale = [(
8b8e83329a1datagen2026-10-12 12:01 UTC285
        "authorization",
8b8e83329a1datagen2026-10-12 12:01 UTC286
        "Digest username=\"on a call\", nonce=\"yesterday\"",
8b8e83329a1datagen2026-10-12 12:01 UTC287
    )];
8b8e83329a1datagen2026-10-12 12:01 UTC288
    let refused = send(&nans, "GET", "/runs/cafebabe", &stale, "").await;
8b8e83329a1datagen2026-10-12 12:01 UTC289
    assert_eq!(refused.status(), StatusCode::UNAUTHORIZED);
8b8e83329a1datagen2026-10-12 12:01 UTC290
    assert!(
8b8e83329a1datagen2026-10-12 12:01 UTC291
        json(refused).await["type"]
8b8e83329a1datagen2026-10-12 12:01 UTC292
            .as_str()
8b8e83329a1datagen2026-10-12 12:01 UTC293
            .unwrap()
8b8e83329a1datagen2026-10-12 12:01 UTC294
            .ends_with("stale-nonce")
8b8e83329a1datagen2026-10-12 12:01 UTC295
    );
8b8e83329a1datagen2026-10-12 12:01 UTC296
}
8b8e83329a1datagen2026-10-12 12:01 UTC297
8b8e83329a1datagen2026-10-12 12:01 UTC298
#[tokio::test]
8b8e83329a1datagen2026-10-12 12:01 UTC299
async fn american_players_can_say_studying() {
8b8e83329a1datagen2026-10-12 12:01 UTC300
    let app = seeded_with(Floor::Bedroom, mum_at_the_door);
8b8e83329a1datagen2026-10-12 12:01 UTC301
    let excuse = [
8b8e83329a1datagen2026-10-12 12:01 UTC302
        ("accept-language", "en-US"),
8b8e83329a1datagen2026-10-12 12:01 UTC303
        ("authorization", "Excuse \"Studying\""),
8b8e83329a1datagen2026-10-12 12:01 UTC304
    ];
8b8e83329a1datagen2026-10-12 12:01 UTC305
    let response = send(&app, "GET", "/runs/cafebabe", &excuse, "").await;
8b8e83329a1datagen2026-10-12 12:01 UTC306
    assert_eq!(response.status(), StatusCode::OK);
8b8e83329a1datagen2026-10-12 12:01 UTC307
}
8b8e83329a1datagen2026-10-12 12:01 UTC308
8b8e83329a1datagen2026-10-12 12:01 UTC309
#[tokio::test]
8b8e83329a1datagen2026-10-12 12:01 UTC310
async fn raids_get_noisier_and_the_household_notices() {
8b8e83329a1datagen2026-10-12 12:01 UTC311
    let told = |suspicion: u8| {
8b8e83329a1datagen2026-10-12 12:01 UTC312
        (0..400u64)
8b8e83329a1datagen2026-10-12 12:01 UTC313
            .filter(|&seed| {
8b8e83329a1datagen2026-10-12 12:01 UTC314
                let mut run = Run::new("cafebabe".into(), "cafe".into(), seed);
8b8e83329a1datagen2026-10-12 12:01 UTC315
                run.body.eq = 40;
8b8e83329a1datagen2026-10-12 12:01 UTC316
                run.standing.suspicion = suspicion;
f16ab17c8ff6atagen2026-10-13 11:14 UTC317
                run.raid_kitchen().is_err() && run.here.kitchen.closed
8b8e83329a1datagen2026-10-12 12:01 UTC318
            })
8b8e83329a1datagen2026-10-12 12:01 UTC319
            .count()
8b8e83329a1datagen2026-10-12 12:01 UTC320
    };
8b8e83329a1datagen2026-10-12 12:01 UTC321
    let quiet = told(0);
8b8e83329a1datagen2026-10-12 12:01 UTC322
    let noisy = told(4);
8b8e83329a1datagen2026-10-12 12:01 UTC323
    assert!(
8b8e83329a1datagen2026-10-12 12:01 UTC324
        (60..=150).contains(&quiet),
8b8e83329a1datagen2026-10-12 12:01 UTC325
        "a quiet house told {quiet} of 400 raids, about 100 expected"
8b8e83329a1datagen2026-10-12 12:01 UTC326
    );
8b8e83329a1datagen2026-10-12 12:01 UTC327
    assert!(
8b8e83329a1datagen2026-10-12 12:01 UTC328
        (240..=340).contains(&noisy),
8b8e83329a1datagen2026-10-12 12:01 UTC329
        "a noisy house told {noisy} of 400 raids, about 300 expected"
8b8e83329a1datagen2026-10-12 12:01 UTC330
    );
8b8e83329a1datagen2026-10-12 12:01 UTC331
8b8e83329a1datagen2026-10-12 12:01 UTC332
    let mut run = Run::new("cafebabe".into(), "cafe".into(), SEED);
8b8e83329a1datagen2026-10-12 12:01 UTC333
    run.body.eq = 40;
f16ab17c8ff6atagen2026-10-13 11:14 UTC334
    let _ = run.raid_kitchen();
8b8e83329a1datagen2026-10-12 12:01 UTC335
    assert_eq!(run.standing.suspicion, 1, "every raid makes a little noise");
8b8e83329a1datagen2026-10-12 12:01 UTC336
}
f16ab17c8ff6atagen2026-10-13 11:14 UTC337
f16ab17c8ff6atagen2026-10-13 11:14 UTC338
#[tokio::test]
f16ab17c8ff6atagen2026-10-13 11:14 UTC339
async fn one_telling_closes_the_kitchen_and_its_link() {
f16ab17c8ff6atagen2026-10-13 11:14 UTC340
    let app = seeded_with(Floor::Bedroom, |run| run.standing.suspicion = 4);
f16ab17c8ff6atagen2026-10-13 11:14 UTC341
    let mut told = None;
f16ab17c8ff6atagen2026-10-13 11:14 UTC342
    for _ in 0..20 {
f16ab17c8ff6atagen2026-10-13 11:14 UTC343
        let raid = send(&app, "POST", "/runs/cafebabe/kitchen", &[], "").await;
f16ab17c8ff6atagen2026-10-13 11:14 UTC344
        if raid.status() == StatusCode::GONE {
f16ab17c8ff6atagen2026-10-13 11:14 UTC345
            told = Some(raid);
f16ab17c8ff6atagen2026-10-13 11:14 UTC346
            break;
f16ab17c8ff6atagen2026-10-13 11:14 UTC347
        }
f16ab17c8ff6atagen2026-10-13 11:14 UTC348
        assert!(
f16ab17c8ff6atagen2026-10-13 11:14 UTC349
            warnings(&raid).iter().all(|w| !w.contains("Stop raiding")),
f16ab17c8ff6atagen2026-10-13 11:14 UTC350
            "there is no warning before the telling"
f16ab17c8ff6atagen2026-10-13 11:14 UTC351
        );
f16ab17c8ff6atagen2026-10-13 11:14 UTC352
    }
f16ab17c8ff6atagen2026-10-13 11:14 UTC353
    let told = told.expect("a noisy house tells within twenty raids");
f16ab17c8ff6atagen2026-10-13 11:14 UTC354
    assert!(
aef6f6498ea2atagen2026-10-13 11:23 UTC355
        warnings(&told)
aef6f6498ea2atagen2026-10-13 11:23 UTC356
            .iter()
aef6f6498ea2atagen2026-10-13 11:23 UTC357
            .any(|w| w.contains("Stop raiding the kitchen."))
f16ab17c8ff6atagen2026-10-13 11:14 UTC358
    );
aef6f6498ea2atagen2026-10-13 11:23 UTC359
    assert_eq!(json(told).await["detail"], "The kitchen is off limits.");
f16ab17c8ff6atagen2026-10-13 11:14 UTC360
    let view = send(&app, "GET", "/runs/cafebabe", &[], "").await;
f16ab17c8ff6atagen2026-10-13 11:14 UTC361
    assert!(!header(&view, "link").contains("/kitchen"));
f16ab17c8ff6atagen2026-10-13 11:14 UTC362
}
f16ab17c8ff6atagen2026-10-13 11:14 UTC363
f16ab17c8ff6atagen2026-10-13 11:14 UTC364
#[tokio::test]
f16ab17c8ff6atagen2026-10-13 11:14 UTC365
async fn an_excuse_without_a_scheme_is_explained_for_free() {
f16ab17c8ff6atagen2026-10-13 11:14 UTC366
    let app = seeded_with(Floor::Bedroom, mum_at_the_door);
f16ab17c8ff6atagen2026-10-13 11:14 UTC367
    for said in ["homework", "on a call", "homework", "homework"] {
f16ab17c8ff6atagen2026-10-13 11:14 UTC368
        let response = send(
f16ab17c8ff6atagen2026-10-13 11:14 UTC369
            &app,
f16ab17c8ff6atagen2026-10-13 11:14 UTC370
            "GET",
f16ab17c8ff6atagen2026-10-13 11:14 UTC371
            "/runs/cafebabe",
f16ab17c8ff6atagen2026-10-13 11:14 UTC372
            &[("authorization", said)],
f16ab17c8ff6atagen2026-10-13 11:14 UTC373
            "",
f16ab17c8ff6atagen2026-10-13 11:14 UTC374
        )
f16ab17c8ff6atagen2026-10-13 11:14 UTC375
        .await;
f16ab17c8ff6atagen2026-10-13 11:14 UTC376
        assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
f16ab17c8ff6atagen2026-10-13 11:14 UTC377
        assert!(
f16ab17c8ff6atagen2026-10-13 11:14 UTC378
            json(response).await["detail"]
f16ab17c8ff6atagen2026-10-13 11:14 UTC379
                .as_str()
f16ab17c8ff6atagen2026-10-13 11:14 UTC380
                .unwrap()
f16ab17c8ff6atagen2026-10-13 11:14 UTC381
                .contains("Excuse \"homework\"")
f16ab17c8ff6atagen2026-10-13 11:14 UTC382
        );
f16ab17c8ff6atagen2026-10-13 11:14 UTC383
    }
f16ab17c8ff6atagen2026-10-13 11:14 UTC384
    let view = send(&app, "GET", "/runs/cafebabe", &[], "").await;
f16ab17c8ff6atagen2026-10-13 11:14 UTC385
    assert_eq!(header(&view, "x-shame"), "0");
f16ab17c8ff6atagen2026-10-13 11:14 UTC386
    let excused = send(
f16ab17c8ff6atagen2026-10-13 11:14 UTC387
        &app,
f16ab17c8ff6atagen2026-10-13 11:14 UTC388
        "GET",
f16ab17c8ff6atagen2026-10-13 11:14 UTC389
        "/runs/cafebabe",
f16ab17c8ff6atagen2026-10-13 11:14 UTC390
        &[("authorization", "Excuse \"homework\"")],
f16ab17c8ff6atagen2026-10-13 11:14 UTC391
        "",
f16ab17c8ff6atagen2026-10-13 11:14 UTC392
    )
f16ab17c8ff6atagen2026-10-13 11:14 UTC393
    .await;
f16ab17c8ff6atagen2026-10-13 11:14 UTC394
    assert_eq!(excused.status(), StatusCode::OK);
f16ab17c8ff6atagen2026-10-13 11:14 UTC395
}