atagen

jelq

public

an http based roguelike rpg about jelqing

Star0
Watch0
727d064fae3881054441752bc0a2afb71e1e63dd
1
ssh://brewery@pls.lobotomise.me:33/atagen/jelq.git
src/tests/body.rsRust400 lines12.7 KB
1
use super::*;
2
3
#[tokio::test]
4
async fn deleting_the_foreskin_requires_certainty() {
5
    let Harness { app, .. } = harness(Floor::Bedroom, SEED, |_| {});
6
    let response = send(&app, "DELETE", "/runs/cafebabe/foreskin", &[], "").await;
7
    assert_eq!(response.status(), StatusCode::PRECONDITION_REQUIRED);
8
    let look = send(&app, "GET", "/runs/cafebabe/foreskin", &[], "").await;
9
    assert_eq!(look.status(), StatusCode::OK);
10
    let etag = header(&look, "etag").to_owned();
11
    assert!(text(look).await.contains('~'));
12
    let response = send(
13
        &app,
14
        "DELETE",
15
        "/runs/cafebabe/foreskin",
16
        &[("if-match", &etag)],
17
        "",
18
    )
19
    .await;
20
    assert_eq!(response.status(), StatusCode::NO_CONTENT);
21
    let response = send(&app, "GET", "/runs/cafebabe/foreskin", &[], "").await;
22
    assert_eq!(response.status(), StatusCode::GONE);
23
}
24
25
#[tokio::test]
26
async fn length_supports_inch_ranges() {
27
    let Harness { app, .. } = harness(Floor::Bedroom, SEED, |_| {});
28
    let response = send(
29
        &app,
30
        "GET",
31
        "/runs/cafebabe/length",
32
        &[("range", "inches=0-1")],
33
        "",
34
    )
35
    .await;
36
    assert_eq!(response.status(), StatusCode::PARTIAL_CONTENT);
37
    assert!(header(&response, "content-range").starts_with("inches 0.00-1.00/"));
38
    assert_eq!(text(response).await.len(), 25);
39
    let absurd = send(
40
        &app,
41
        "GET",
42
        "/runs/cafebabe/length",
43
        &[("range", "inches=40-50")],
44
        "",
45
    )
46
    .await;
47
    assert_eq!(absurd.status(), StatusCode::RANGE_NOT_SATISFIABLE);
48
    let malformed = send(
49
        &app,
50
        "GET",
51
        "/runs/cafebabe/length",
52
        &[("range", "inches=6.17")],
53
        "",
54
    )
55
    .await;
56
    assert_eq!(malformed.status(), StatusCode::OK);
57
    let suffix = send(
58
        &app,
59
        "GET",
60
        "/runs/cafebabe/length",
61
        &[("range", "inches=-1")],
62
        "",
63
    )
64
    .await;
65
    assert_eq!(suffix.status(), StatusCode::PARTIAL_CONTENT);
66
    assert!(text(suffix).await.ends_with('D'));
67
    for (range, status) in [
68
        ("inches=1-", StatusCode::PARTIAL_CONTENT),
69
        ("inches=40-", StatusCode::RANGE_NOT_SATISFIABLE),
70
        ("inches=-0", StatusCode::RANGE_NOT_SATISFIABLE),
71
        ("inches=2-1", StatusCode::OK),
72
        ("inches=NaN-2", StatusCode::OK),
73
        ("inches=0-inf", StatusCode::OK),
74
    ] {
75
        let response = send(
76
            &app,
77
            "GET",
78
            "/runs/cafebabe/length",
79
            &[("range", range)],
80
            "",
81
        )
82
        .await;
83
        assert_eq!(response.status(), status, "{range}");
84
    }
85
}
86
87
#[tokio::test]
88
async fn the_displayed_length_is_accepted_as_honest() {
89
    let app = seeded_with(Floor::Forum, |run| run.body.length = 5.2496);
90
    let response = send(
91
        &app,
92
        "GET",
93
        "/runs/cafebabe/elder",
94
        &[("proxy-authorization", "Ruler 5.25")],
95
        "",
96
    )
97
    .await;
98
    assert_eq!(response.status(), StatusCode::OK);
99
}
100
101
#[tokio::test]
102
async fn dry_jelqs_build_friction_and_real_lube_does_not() {
103
    let app = seeded_with(Floor::Bedroom, |run| {
104
        run.body.warmed_up = true;
105
        run.body.foreskin = Some(0.9);
106
    });
107
    let dry = send(&app, "JELQ", "/runs/cafebabe?technique=dry", &[], "").await;
108
    assert_eq!(header(&dry, "x-friction"), "2/10");
109
    let lube = [("content-type", "application/baby-oil")];
110
    let wet = send(
111
        &app,
112
        "JELQ",
113
        "/runs/cafebabe?technique=wet&intensity=5",
114
        &lube,
115
        "",
116
    )
117
    .await;
118
    assert_eq!(header(&wet, "x-friction"), "2/10");
119
    let spit = [("content-type", "text/plain; charset=spit")];
120
    let glisten = send(
121
        &app,
122
        "JELQ",
123
        "/runs/cafebabe?technique=wet&intensity=5",
124
        &spit,
125
        "",
126
    )
127
    .await;
128
    assert_eq!(header(&glisten, "x-friction"), "3/10");
129
}
130
131
#[tokio::test]
132
async fn switching_grips_makes_the_friction_cheaper() {
133
    let app = seeded_with(Floor::Bedroom, |run| {
134
        run.body.warmed_up = true;
135
        run.body.foreskin = Some(0.9);
136
    });
137
    let dry = send(&app, "JELQ", "/runs/cafebabe?technique=dry", &[], "").await;
138
    assert_eq!(header(&dry, "x-friction"), "2/10");
139
    send(
140
        &app,
141
        "GET",
142
        "/runs/cafebabe/grip",
143
        &[("upgrade", "underhand"), ("connection", "upgrade")],
144
        "",
145
    )
146
    .await;
147
    let switched = send(&app, "JELQ", "/runs/cafebabe?technique=dry", &[], "").await;
148
    assert_eq!(header(&switched, "x-friction"), "3/10");
149
    let settled = send(&app, "JELQ", "/runs/cafebabe?technique=dry", &[], "").await;
150
    assert_eq!(header(&settled, "x-friction"), "5/10");
151
}
152
153
#[tokio::test]
154
async fn holding_and_meditating_shed_friction() {
155
    let app = seeded_with(Floor::Bedroom, |run| {
156
        run.body.eq = 80;
157
        run.body.friction = 5;
158
    });
159
    let held = send(&app, "HOLD", "/runs/cafebabe", &[], "").await;
160
    assert_eq!(held.status(), StatusCode::NO_CONTENT);
161
    let view = send(&app, "GET", "/runs/cafebabe", &[], "").await;
162
    assert_eq!(header(&view, "x-friction"), "3/10");
163
164
    let taught = seeded_with(Floor::Bedroom, |run| {
165
        run.body.friction = 5;
166
    });
167
    let calmed = send(
168
        &taught,
169
        "JELQ",
170
        "/runs/cafebabe?technique=meditate",
171
        &[],
172
        "",
173
    )
174
    .await;
175
    assert_eq!(calmed.status(), StatusCode::OK);
176
    let view = send(&taught, "GET", "/runs/cafebabe", &[], "").await;
177
    assert_eq!(header(&view, "x-friction"), "3/10");
178
}
179
180
#[tokio::test]
181
async fn a_friction_burn_halves_the_gains() {
182
    let lube = [("content-type", "application/baby-oil")];
183
    let path = "/runs/cafebabe?technique=wet&intensity=5";
184
    for seed in 0..60u64 {
185
        let steady = app_with(Floor::Bedroom, seed, |run| {
186
            run.body.warmed_up = true;
187
            run.body.eq = 60;
188
            run.items.lubes.baby_oil = 9;
189
        });
190
        let hurt_app = app_with(Floor::Bedroom, seed, |run| {
191
            run.body.warmed_up = true;
192
            run.body.eq = 60;
193
            run.items.lubes.baby_oil = 9;
194
            run.body.conditions.friction_burn = true;
195
        });
196
        let calm = send(&steady, "JELQ", path, &lube, "").await;
197
        let hurt = send(&hurt_app, "JELQ", path, &lube, "").await;
198
        if calm.status() != StatusCode::OK || hurt.status() != StatusCode::OK {
199
            continue;
200
        }
201
        let calm_gain = json(calm).await["length"].as_f64().unwrap();
202
        let hurt_gain = json(hurt).await["length"].as_f64().unwrap();
203
        assert!(
204
            (hurt_gain * 2.0 - calm_gain).abs() < 1e-9,
205
            "seed {seed} burned {hurt_gain} against {calm_gain}"
206
        );
207
        return;
208
    }
209
    panic!("no seed in 60 produced two clean rolls to compare");
210
}
211
212
#[tokio::test]
213
async fn the_burn_warns_and_the_ice_treats_it() {
214
    let app = seeded_with(Floor::Bedroom, |run| {
215
        run.body.warmed_up = true;
216
        run.body.eq = 60;
217
        run.items.lubes.baby_oil = 9;
218
        run.body.friction = 7;
219
        run.body.conditions.friction_burn = true;
220
    });
221
    let lube = [("content-type", "application/baby-oil")];
222
    let jelq = send(
223
        &app,
224
        "JELQ",
225
        "/runs/cafebabe?technique=wet&intensity=5",
226
        &lube,
227
        "",
228
    )
229
    .await;
230
    let warnings: Vec<_> = jelq
231
        .headers()
232
        .get_all("warning")
233
        .iter()
234
        .filter_map(|w| w.to_str().ok())
235
        .collect();
236
    assert!(warnings.contains(&"199 - \"friction burn\""));
237
    send(&app, "JELQ", "/runs/cafebabe?technique=ice", &[], "").await;
238
    let view = send(&app, "GET", "/runs/cafebabe", &[], "").await;
239
    let warnings: Vec<_> = view
240
        .headers()
241
        .get_all("warning")
242
        .iter()
243
        .filter_map(|w| w.to_str().ok())
244
        .collect();
245
    assert!(!warnings.contains(&"199 - \"friction burn\""));
246
}
247
248
#[tokio::test]
249
async fn friction_rises_with_vigour_and_edging_chafes() {
250
    let spit = [("content-type", "text/plain; charset=spit")];
251
    for (intensity, friction) in [(5, "1/10"), (7, "2/10"), (10, "3/10")] {
252
        let app = seeded_with(Floor::Hotel, |run| {
253
            run.here.wifi_accepted = true;
254
            run.body.warmed_up = true;
255
            run.body.eq = 55;
256
            run.body.foreskin = Some(0.5);
257
        });
258
        let uri = format!("/runs/cafebabe?technique=wet&intensity={intensity}");
259
        let jelq = send(&app, "JELQ", &uri, &spit, "").await;
260
        assert_eq!(
261
            header(&jelq, "x-friction"),
262
            friction,
263
            "intensity {intensity}"
264
        );
265
    }
266
    let app = seeded_with(Floor::Hotel, |run| {
267
        run.here.wifi_accepted = true;
268
        run.body.warmed_up = true;
269
        run.body.eq = 55;
270
        run.body.foreskin = Some(0.5);
271
    });
272
    let edge = send(&app, "JELQ", "/runs/cafebabe?technique=edge", &[], "").await;
273
    assert_eq!(header(&edge, "x-friction"), "2/10");
274
}
275
276
#[tokio::test]
277
async fn the_body_changes_how_the_skin_takes_it() {
278
    let spit = [("content-type", "text/plain; charset=spit")];
279
    for (foreskin, girth, friction) in [(0.5, 4.5, "1/10"), (1.2, 4.5, "0/10"), (0.5, 5.8, "2/10")]
280
    {
281
        let app = seeded_with(Floor::Hotel, |run| {
282
            run.here.wifi_accepted = true;
283
            run.body.warmed_up = true;
284
            run.body.eq = 55;
285
            run.body.foreskin = Some(foreskin);
286
            run.body.girth = girth;
287
        });
288
        let jelq = send(&app, "JELQ", "/runs/cafebabe?technique=wet", &spit, "").await;
289
        assert_eq!(header(&jelq, "x-friction"), friction, "{foreskin} {girth}");
290
    }
291
}
292
293
#[tokio::test]
294
async fn a_thick_girth_outgrows_the_underhand_grip() {
295
    let app = seeded_with(Floor::Bedroom, |run| run.body.girth = 5.8);
296
    let grip = send(
297
        &app,
298
        "GET",
299
        "/runs/cafebabe/grip",
300
        &[("upgrade", "underhand"), ("connection", "upgrade")],
301
        "",
302
    )
303
    .await;
304
    assert_eq!(grip.status(), StatusCode::PAYLOAD_TOO_LARGE);
305
}
306
307
#[tokio::test]
308
async fn a_long_run_fills_the_jeans_before_a_thick_one() {
309
    let app = seeded_with(Floor::Bedroom, |run| {
310
        run.body.warmed_up = true;
311
        run.items.kit.extender = true;
312
        run.body.girth = 4.8;
313
        run.body.length = 7.5;
314
    });
315
    let lube = [("content-type", "application/baby-oil")];
316
    let full = send(&app, "JELQ", "/runs/cafebabe?technique=wet", &lube, "").await;
317
    assert_eq!(full.status(), StatusCode::INSUFFICIENT_STORAGE);
318
}
319
320
#[tokio::test]
321
async fn a_refused_jelq_leaves_the_lube_alone() {
322
    let app = seeded_with(Floor::Bedroom, |run| {
323
        run.body.warmed_up = true;
324
        run.items.kit.extender = true;
325
        run.body.girth = 6.0;
326
        run.body.length = 6.0;
327
    });
328
    let lube = [("content-type", "application/baby-oil")];
329
    let full = send(
330
        &app,
331
        "JELQ",
332
        "/runs/cafebabe?technique=wet&intensity=5",
333
        &lube,
334
        "",
335
    )
336
    .await;
337
    assert_eq!(full.status(), StatusCode::INSUFFICIENT_STORAGE);
338
    let inv = send(&app, "GET", "/runs/cafebabe/inventory", &[], "").await;
339
    let body = json(inv).await;
340
    let held = body["items"].as_array().unwrap();
341
    assert!(held.iter().any(|i| i["name"] == "baby-oil"));
342
}
343
344
#[tokio::test]
345
async fn lubes_run_out_but_spit_never_does() {
346
    let app = seeded_with(Floor::Bedroom, |run| {
347
        run.body.warmed_up = true;
348
        run.items.lubes.baby_oil = 1;
349
    });
350
    let lube = [("content-type", "application/baby-oil")];
351
    send(&app, "JELQ", "/runs/cafebabe?technique=wet", &lube, "").await;
352
    let second = send(&app, "JELQ", "/runs/cafebabe?technique=wet", &lube, "").await;
353
    assert_eq!(second.status(), StatusCode::UNSUPPORTED_MEDIA_TYPE);
354
355
    let spit = [("content-type", "text/plain; charset=spit")];
356
    for _ in 0..3 {
357
        let wet = send(&app, "JELQ", "/runs/cafebabe?technique=wet", &spit, "").await;
358
        assert_ne!(
359
            wet.status(),
360
            StatusCode::UNSUPPORTED_MEDIA_TYPE,
361
            "spit never runs out"
362
        );
363
    }
364
}
365
366
#[tokio::test]
367
async fn patching_costs_a_little_eq() {
368
    let app = seeded_with(Floor::Bedroom, |run| run.body.eq = 50);
369
    let patch = send(
370
        &app,
371
        "PATCH",
372
        "/runs/cafebabe",
373
        &[("content-type", "application/json-patch+json")],
374
        r#"[{"op": "test", "path": "/girth", "value": 1.0}]"#,
375
    )
376
    .await;
377
    assert_eq!(patch.status(), StatusCode::CONFLICT);
378
    assert_eq!(header(&patch, "x-eq"), "47");
379
}
380
381
#[tokio::test]
382
async fn a_combo_chafes_twice_and_costs_two_stamina() {
383
    let app = seeded_with(Floor::Hotel, |run| {
384
        run.here.wifi_accepted = true;
385
        run.body.warmed_up = true;
386
        run.body.eq = 55;
387
        run.body.foreskin = Some(0.5);
388
    });
389
    let spit = [("content-type", "text/plain; charset=spit")];
390
    let combo = send(
391
        &app,
392
        "JELQ",
393
        "/runs/cafebabe?technique=wet,stretch",
394
        &spit,
395
        "",
396
    )
397
    .await;
398
    assert_eq!(header(&combo, "x-friction"), "2/10");
399
    assert_eq!(header(&combo, "x-stamina"), "8/10");
400
}