atagen

jelq

public

an http based roguelike rpg about jelqing

Star0
Watch0
aef6f6498ea2ef0c39d1079a0ca0bc99afdd9bac
1
ssh://brewery@pls.lobotomise.me:33/atagen/jelq.git
src/tests/household.rsRust398 lines12.0 KB
1
use super::*;
2
3
#[tokio::test]
4
async fn mum_accepts_a_known_excuse() {
5
    let app = seeded_with(Floor::Bedroom, mum_at_the_door);
6
    let response = send(&app, "GET", "/runs/cafebabe", &[], "").await;
7
    assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
8
    assert_eq!(header(&response, "via"), "1.1 mum");
9
    let response = send(
10
        &app,
11
        "GET",
12
        "/runs/cafebabe",
13
        &[("authorization", "Excuse \"homework\"")],
14
        "",
15
    )
16
    .await;
17
    assert_eq!(response.status(), StatusCode::OK);
18
}
19
20
#[tokio::test]
21
async fn mum_runs_out_of_patience() {
22
    let app = seeded_with(Floor::Bedroom, mum_at_the_door);
23
    let lie = [("authorization", "Excuse \"it is for science\"")];
24
    for _ in 0..2 {
25
        let response = send(&app, "GET", "/runs/cafebabe", &lie, "").await;
26
        assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
27
    }
28
    let caught = send(&app, "GET", "/runs/cafebabe", &lie, "").await;
29
    assert_eq!(caught.status(), StatusCode::GONE);
30
    assert_eq!(
31
        json(caught).await["type"],
32
        "https://jelq.dev/problems/caught"
33
    );
34
}
35
36
#[tokio::test]
37
async fn mum_remembers_excuses_by_christmas() {
38
    let app = seeded_with(Floor::Nans, |run| {
39
        mum_at_the_door(run);
40
        run.household.heard.push(Excuse::Homework);
41
    });
42
    let basic = send(
43
        &app,
44
        "GET",
45
        "/runs/cafebabe",
46
        &[("authorization", "Excuse \"changing\"")],
47
        "",
48
    )
49
    .await;
50
    assert_eq!(basic.status(), StatusCode::UNAUTHORIZED);
51
    assert!(header(&basic, "www-authenticate").starts_with("Digest"));
52
    let replay = send(
53
        &app,
54
        "GET",
55
        "/runs/cafebabe",
56
        &[(
57
            "authorization",
58
            "Digest username=\"homework\", nonce=\"abc\"",
59
        )],
60
        "",
61
    )
62
    .await;
63
    assert_eq!(
64
        json(replay).await["type"],
65
        "https://jelq.dev/problems/excuse-replayed"
66
    );
67
    let fresh = send(
68
        &app,
69
        "GET",
70
        "/runs/cafebabe",
71
        &[(
72
            "authorization",
73
            "Digest username=\"wrapping presents\", nonce=\"abc\"",
74
        )],
75
        "",
76
    )
77
    .await;
78
    assert_eq!(fresh.status(), StatusCode::OK);
79
}
80
81
#[tokio::test]
82
async fn a_pulled_muscle_is_a_true_excuse() {
83
    let app = seeded_with(Floor::Nans, |run| {
84
        mum_at_the_door(run);
85
        run.body.conditions.pulled_muscle = true;
86
        run.household.heard.push(Excuse::PulledAMuscle);
87
    });
88
    let response = send(
89
        &app,
90
        "GET",
91
        "/runs/cafebabe",
92
        &[(
93
            "authorization",
94
            "Digest username=\"pulled a muscle\", nonce=\"abc\"",
95
        )],
96
        "",
97
    )
98
    .await;
99
    assert_eq!(response.status(), StatusCode::OK);
100
}
101
102
#[tokio::test]
103
async fn ignoring_mum_has_consequences() {
104
    let app = seeded_with(Floor::Bedroom, |run| {
105
        mum_at_the_door(run);
106
        run.here.turns = 6;
107
    });
108
    let knock = send(&app, "GET", "/runs/cafebabe", &[], "").await;
109
    assert_eq!(
110
        json(knock).await["type"],
111
        "https://jelq.dev/problems/someone-at-the-door"
112
    );
113
    let leave = send(&app, "POST", "/runs/cafebabe/floor", &[], "").await;
114
    assert_eq!(
115
        leave.status(),
116
        StatusCode::UNAUTHORIZED,
117
        "no leaving with Mum at the door"
118
    );
119
    send(&app, "GET", "/runs/cafebabe", &[], "").await;
120
    let opened = send(&app, "GET", "/runs/cafebabe", &[], "").await;
121
    assert_eq!(opened.status(), StatusCode::GONE);
122
    assert_eq!(
123
        json(opened).await["type"],
124
        "https://jelq.dev/problems/opened"
125
    );
126
}
127
128
#[tokio::test]
129
async fn mom_speaks_american() {
130
    let app = seeded_with(Floor::Nans, mum_at_the_door);
131
    let us = [("accept-language", "en-US")];
132
    let knock = send(&app, "GET", "/runs/cafebabe", &us, "").await;
133
    assert_eq!(header(&knock, "content-language"), "en-US");
134
    assert_eq!(header(&knock, "via"), "1.1 mom");
135
    assert!(
136
        json(knock).await["detail"]
137
            .as_str()
138
            .unwrap()
139
            .contains("Someone is at the door.")
140
    );
141
    let excuse = [
142
        ("accept-language", "en-US"),
143
        (
144
            "authorization",
145
            "Digest username=\"carving the turkey\", nonce=\"abc\"",
146
        ),
147
    ];
148
    let answered = send(&app, "GET", "/runs/cafebabe", &excuse, "").await;
149
    assert_eq!(answered.status(), StatusCode::OK);
150
    let floor = json(send(&app, "GET", "/runs/cafebabe/floor", &us, "").await).await;
151
    assert_eq!(floor["name"], "Grandma's house for Thanksgiving");
152
}
153
154
#[tokio::test]
155
async fn an_empty_bottle_is_declined_before_the_enemy_stirs() {
156
    let control = seeded_with(Floor::ChangingRoom, |run| {
157
        run.body.warmed_up = true;
158
        run.standing.suspicion = SUSPICION_LIMIT;
159
    });
160
    let lube = [("content-type", "application/baby-oil")];
161
    let caught = send(
162
        &control,
163
        "JELQ",
164
        "/runs/cafebabe?technique=wet&intensity=5",
165
        &lube,
166
        "",
167
    )
168
    .await;
169
    assert_eq!(caught.status(), StatusCode::UNAVAILABLE_FOR_LEGAL_REASONS);
170
171
    let app = seeded_with(Floor::ChangingRoom, |run| {
172
        run.body.warmed_up = true;
173
        run.standing.suspicion = SUSPICION_LIMIT;
174
        run.items.lubes.baby_oil = 0;
175
    });
176
    let refused = send(
177
        &app,
178
        "JELQ",
179
        "/runs/cafebabe?technique=wet&intensity=5",
180
        &lube,
181
        "",
182
    )
183
    .await;
184
    assert_eq!(refused.status(), StatusCode::UNSUPPORTED_MEDIA_TYPE);
185
    let view = send(&app, "GET", "/runs/cafebabe", &[], "").await;
186
    assert_eq!(header(&view, "x-suspicion"), "5/5");
187
    assert_eq!(header(&view, "x-shame"), "0");
188
}
189
190
#[tokio::test]
191
async fn respectable_girth_waves_off_the_gym_staff() {
192
    let staff = |girth: f64| {
193
        seeded_with(Floor::ChangingRoom, move |run| {
194
            run.body.warmed_up = true;
195
            run.body.eq = 45;
196
            run.body.girth = girth;
197
            run.standing.suspicion = SUSPICION_LIMIT;
198
        })
199
    };
200
    let thin = send(
201
        &staff(4.5),
202
        "JELQ",
203
        "/runs/cafebabe?technique=edge",
204
        &[],
205
        "",
206
    )
207
    .await;
208
    assert_eq!(thin.status(), StatusCode::UNAVAILABLE_FOR_LEGAL_REASONS);
209
    assert_eq!(header(&thin, "x-shame"), "5");
210
211
    let thick = send(
212
        &staff(5.2),
213
        "JELQ",
214
        "/runs/cafebabe?technique=edge",
215
        &[],
216
        "",
217
    )
218
    .await;
219
    assert_eq!(thick.status(), StatusCode::OK);
220
    assert!(
221
        warnings(&thick)
222
            .iter()
223
            .any(|w| w.contains("the gym staff have seen worse"))
224
    );
225
}
226
227
#[tokio::test]
228
async fn the_kitchen_is_only_on_the_home_floors() {
229
    let absent = seeded_with(Floor::Bathroom, |_| {});
230
    let no_kitchen = send(&absent, "POST", "/runs/cafebabe/kitchen", &[], "").await;
231
    assert_eq!(no_kitchen.status(), StatusCode::NOT_FOUND);
232
    let view = send(&absent, "GET", "/runs/cafebabe", &[], "").await;
233
    assert!(!header(&view, "link").contains("/kitchen"));
234
235
    let app = seeded_with(Floor::Bedroom, |_| {});
236
    let view = send(&app, "GET", "/runs/cafebabe", &[], "").await;
237
    assert!(header(&view, "link").contains("/kitchen"));
238
    let raid = send(&app, "POST", "/runs/cafebabe/kitchen", &[], "").await;
239
    assert_ne!(raid.status(), StatusCode::NOT_FOUND);
240
}
241
242
#[tokio::test]
243
async fn any_other_action_takes_the_melon_off_the_table() {
244
    for (method, uri) in [
245
        ("JELQ", "/runs/cafebabe?technique=warmup"),
246
        ("POST", "/runs/cafebabe/drawer"),
247
    ] {
248
        let app = seeded_with(Floor::Bedroom, |run| {
249
            run.body.eq = 95;
250
            run.here.kitchen.melon_offered = true;
251
        });
252
        send(&app, method, uri, &[], "").await;
253
        let melon = send(&app, "POST", "/runs/cafebabe/kitchen/watermelon", &[], "").await;
254
        assert_eq!(melon.status(), StatusCode::CONFLICT, "{method} {uri}");
255
    }
256
}
257
258
#[tokio::test]
259
async fn the_truth_works_under_any_scheme_and_costs_ten() {
260
    let app = seeded_with(Floor::Bedroom, mum_at_the_door);
261
    let truth = [("authorization", "Whatever honesty")];
262
    let confessed = send(&app, "GET", "/runs/cafebabe", &truth, "").await;
263
    assert_eq!(confessed.status(), StatusCode::OK);
264
    assert_eq!(header(&confessed, "x-shame"), "10");
265
}
266
267
#[tokio::test]
268
async fn mum_refuses_basic_and_stale_nonces() {
269
    let bedroom = seeded_with(Floor::Bedroom, mum_at_the_door);
270
    let basic = send(
271
        &bedroom,
272
        "GET",
273
        "/runs/cafebabe",
274
        &[("authorization", "Basic aG9tZXdvcms=")],
275
        "",
276
    )
277
    .await;
278
    assert_eq!(basic.status(), StatusCode::UNAUTHORIZED);
279
    assert!(
280
        json(basic).await["type"]
281
            .as_str()
282
            .unwrap()
283
            .ends_with("/basic")
284
    );
285
286
    let nans = seeded_with(Floor::Nans, mum_at_the_door);
287
    let stale = [(
288
        "authorization",
289
        "Digest username=\"on a call\", nonce=\"yesterday\"",
290
    )];
291
    let refused = send(&nans, "GET", "/runs/cafebabe", &stale, "").await;
292
    assert_eq!(refused.status(), StatusCode::UNAUTHORIZED);
293
    assert!(
294
        json(refused).await["type"]
295
            .as_str()
296
            .unwrap()
297
            .ends_with("stale-nonce")
298
    );
299
}
300
301
#[tokio::test]
302
async fn american_players_can_say_studying() {
303
    let app = seeded_with(Floor::Bedroom, mum_at_the_door);
304
    let excuse = [
305
        ("accept-language", "en-US"),
306
        ("authorization", "Excuse \"Studying\""),
307
    ];
308
    let response = send(&app, "GET", "/runs/cafebabe", &excuse, "").await;
309
    assert_eq!(response.status(), StatusCode::OK);
310
}
311
312
#[tokio::test]
313
async fn raids_get_noisier_and_the_household_notices() {
314
    let told = |suspicion: u8| {
315
        (0..400u64)
316
            .filter(|&seed| {
317
                let mut run = Run::new("cafebabe".into(), "cafe".into(), seed);
318
                run.body.eq = 40;
319
                run.standing.suspicion = suspicion;
320
                run.raid_kitchen().is_err() && run.here.kitchen.closed
321
            })
322
            .count()
323
    };
324
    let quiet = told(0);
325
    let noisy = told(4);
326
    assert!(
327
        (60..=150).contains(&quiet),
328
        "a quiet house told {quiet} of 400 raids, about 100 expected"
329
    );
330
    assert!(
331
        (240..=340).contains(&noisy),
332
        "a noisy house told {noisy} of 400 raids, about 300 expected"
333
    );
334
335
    let mut run = Run::new("cafebabe".into(), "cafe".into(), SEED);
336
    run.body.eq = 40;
337
    let _ = run.raid_kitchen();
338
    assert_eq!(run.standing.suspicion, 1, "every raid makes a little noise");
339
}
340
341
#[tokio::test]
342
async fn one_telling_closes_the_kitchen_and_its_link() {
343
    let app = seeded_with(Floor::Bedroom, |run| run.standing.suspicion = 4);
344
    let mut told = None;
345
    for _ in 0..20 {
346
        let raid = send(&app, "POST", "/runs/cafebabe/kitchen", &[], "").await;
347
        if raid.status() == StatusCode::GONE {
348
            told = Some(raid);
349
            break;
350
        }
351
        assert!(
352
            warnings(&raid).iter().all(|w| !w.contains("Stop raiding")),
353
            "there is no warning before the telling"
354
        );
355
    }
356
    let told = told.expect("a noisy house tells within twenty raids");
357
    assert!(
358
        warnings(&told)
359
            .iter()
360
            .any(|w| w.contains("Stop raiding the kitchen."))
361
    );
362
    assert_eq!(json(told).await["detail"], "The kitchen is off limits.");
363
    let view = send(&app, "GET", "/runs/cafebabe", &[], "").await;
364
    assert!(!header(&view, "link").contains("/kitchen"));
365
}
366
367
#[tokio::test]
368
async fn an_excuse_without_a_scheme_is_explained_for_free() {
369
    let app = seeded_with(Floor::Bedroom, mum_at_the_door);
370
    for said in ["homework", "on a call", "homework", "homework"] {
371
        let response = send(
372
            &app,
373
            "GET",
374
            "/runs/cafebabe",
375
            &[("authorization", said)],
376
            "",
377
        )
378
        .await;
379
        assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
380
        assert!(
381
            json(response).await["detail"]
382
                .as_str()
383
                .unwrap()
384
                .contains("Excuse \"homework\"")
385
        );
386
    }
387
    let view = send(&app, "GET", "/runs/cafebabe", &[], "").await;
388
    assert_eq!(header(&view, "x-shame"), "0");
389
    let excused = send(
390
        &app,
391
        "GET",
392
        "/runs/cafebabe",
393
        &[("authorization", "Excuse \"homework\"")],
394
        "",
395
    )
396
    .await;
397
    assert_eq!(excused.status(), StatusCode::OK);
398
}