atagen

jelq

public

an http based roguelike rpg about jelqing

Star0
Watch0
e028a4184d000c9c7b61f60e056db01c53aeea23
1
ssh://brewery@pls.lobotomise.me:33/atagen/jelq.git
src/tests/meta.rsRust252 lines9.1 KB
1
use super::*;
2
3
#[tokio::test]
4
async fn forged_ruler_cookies_are_lies() {
5
    let forged = send(
6
        &app(),
7
        "GET",
8
        "/leaderboard?sort=content-length",
9
        &[("cookie", "ruler=1")],
10
        "",
11
    )
12
    .await;
13
    assert_eq!(forged.status(), StatusCode::BAD_REQUEST);
14
}
15
16
#[tokio::test]
17
async fn listings_never_reveal_the_private_id() {
18
    let app = app();
19
    let created = send(&app, "POST", "/runs", &[], "").await;
20
    let location = header(&created, "location").to_owned();
21
    let board = text(send(&app, "GET", "/leaderboard", &[], "").await).await;
22
    let private = location.trim_start_matches("/runs/");
23
    assert!(!board.contains(private));
24
    let entry: Value = serde_json::from_str(&board).unwrap();
25
    let public = entry["entries"][0]["public_id"].as_str().unwrap();
26
    let guess = send(&app, "DELETE", &format!("/runs/{public}"), &[], "").await;
27
    assert_eq!(guess.status(), StatusCode::NOT_FOUND);
28
}
29
30
#[tokio::test]
31
async fn seeded_runs_are_unranked() {
32
    let app = app();
33
    send(&app, "POST", "/runs?seed=7", &[], "").await;
34
    let board = json(send(&app, "GET", "/leaderboard", &[], "").await).await;
35
    assert_eq!(board["entries"].as_array().unwrap().len(), 0);
36
    let all = json(send(&app, "GET", "/leaderboard?seeded=include", &[], "").await).await;
37
    assert_eq!(all["entries"][0]["ranked"], false);
38
}
39
40
#[tokio::test]
41
async fn idle_runs_are_evicted_and_the_server_has_a_limit() {
42
    let state = Arc::new(
43
        AppState::new(Config {
44
            idle_ttl: Duration::ZERO,
45
            ..Config::default()
46
        })
47
        .unwrap(),
48
    );
49
    let forgetful = router(Arc::clone(&state));
50
    let first = send(&forgetful, "POST", "/runs", &[], "").await;
51
    let location = header(&first, "location").to_owned();
52
    state.sweep(std::time::Instant::now()).unwrap();
53
    let gone = send(&forgetful, "GET", &location, &[], "").await;
54
    assert_eq!(gone.status(), StatusCode::NOT_FOUND);
55
    let board = json(send(&forgetful, "GET", "/leaderboard", &[], "").await).await;
56
    assert_eq!(
57
        board["entries"][0]["status"], "alive",
58
        "a swept run keeps its place on the leaderboard"
59
    );
60
61
    let tiny = crate::app(Config {
62
        max_runs: 1,
63
        ..Config::default()
64
    })
65
    .unwrap();
66
    send(&tiny, "POST", "/runs", &[], "").await;
67
    let full = send(&tiny, "POST", "/runs", &[], "").await;
68
    assert_eq!(full.status(), StatusCode::SERVICE_UNAVAILABLE);
69
}
70
71
#[tokio::test]
72
async fn shame_from_two_runs_adds_up() {
73
    let state = Arc::new(AppState::default());
74
    state.insert(Run::new("cafebabe".into(), "cafe".into(), SEED));
75
    state.insert(Run::new("deadbeef".into(), "dead".into(), 7));
76
    let app = router(state);
77
    let first = send(&app, "TRACE", "/runs/cafebabe", &[], "").await;
78
    assert_eq!(header(&first, "x-shame"), "1");
79
    let cookie = header(&first, "set-cookie")
80
        .split(';')
81
        .next()
82
        .unwrap()
83
        .to_owned();
84
    let second = send(&app, "TRACE", "/runs/deadbeef", &[("cookie", &cookie)], "").await;
85
    assert_eq!(header(&second, "x-shame"), "2");
86
}
87
88
#[tokio::test]
89
async fn the_root_negotiates() {
90
    let app = app();
91
    let browser = send(&app, "GET", "/", &[("accept", "text/html,*/*")], "").await;
92
    assert_eq!(browser.status(), StatusCode::SEE_OTHER);
93
    assert_eq!(header(&browser, "location"), "/ui/");
94
    let curl = send(&app, "GET", "/", &[("accept", "*/*")], "").await;
95
    assert_eq!(json(curl).await["ui"], "/ui/");
96
    let page = send(&app, "GET", "/ui/", &[], "").await;
97
    assert_eq!(page.status(), StatusCode::OK);
98
}
99
100
#[tokio::test]
101
async fn the_ui_replays_what_browsers_cannot_send() {
102
    let app = app();
103
    let envelope =
104
        r#"{"method": "TRACE", "path": "/graveyard", "headers": [["X-Confession", "I did it"]]}"#;
105
    let traced = json(send(&app, "POST", "/ui/send", &[], envelope).await).await;
106
    assert_eq!(traced["status"], 405);
107
    let envelope = r#"{"method": "GET", "path": "/", "headers": [["Accept", "text/html"]]}"#;
108
    let redirected = json(send(&app, "POST", "/ui/send", &[], envelope).await).await;
109
    assert_eq!(redirected["status"], 303);
110
    assert_eq!(redirected["reason"], "See Other");
111
}
112
113
#[tokio::test]
114
async fn the_ui_decodes_gzip_for_display() {
115
    let app = seeded_with(Floor::Bedroom, |run| run.items.kit.ring = true);
116
    let envelope =
117
        r#"{"method": "GET", "path": "/runs/cafebabe", "headers": [["Accept-Encoding", "gzip"]]}"#;
118
    let shown = json(send(&app, "POST", "/ui/send", &[], envelope).await).await;
119
    assert!(
120
        shown["body"]
121
            .as_str()
122
            .unwrap()
123
            .contains("\"public_id\":\"cafe\"")
124
    );
125
    let encoding = shown["headers"]
126
        .as_array()
127
        .unwrap()
128
        .iter()
129
        .any(|h| h[0] == "content-encoding" && h[1] == "gzip");
130
    assert!(encoding);
131
}
132
133
#[tokio::test]
134
async fn the_default_locale_is_british() {
135
    let Harness { app, .. } = harness(Floor::Nans, SEED, |_| {});
136
    let floor = send(&app, "GET", "/runs/cafebabe/floor", &[], "").await;
137
    assert_eq!(header(&floor, "content-language"), "en-GB");
138
    assert!(header(&floor, "vary").contains("Accept-Language"));
139
    assert_eq!(json(floor).await["name"], "Nan's house at Christmas");
140
}
141
142
#[tokio::test]
143
async fn a_full_server_buries_the_dead_to_make_room() {
144
    let app = crate::app(Config {
145
        max_runs: 1,
146
        ..Config::default()
147
    })
148
    .unwrap();
149
    let first = send(&app, "POST", "/runs", &[], "").await;
150
    let location = header(&first, "location").to_owned();
151
    let full = send(&app, "POST", "/runs", &[], "").await;
152
    assert_eq!(full.status(), StatusCode::SERVICE_UNAVAILABLE);
153
    send(&app, "DELETE", &location, &[], "").await;
154
    let second = send(&app, "POST", "/runs", &[], "").await;
155
    assert_eq!(second.status(), StatusCode::CREATED);
156
}
157
158
#[tokio::test]
159
async fn the_forum_respects_volume() {
160
    let state = Arc::new(AppState::default());
161
    for (id, length, girth) in [("slim", 7.0, 4.0), ("stout", 5.0, 5.5)] {
162
        let mut run = Run::new(id.into(), id.into(), 1);
163
        run.body.length = length;
164
        run.body.girth = girth;
165
        state.insert(run);
166
    }
167
    let app = router(state);
168
    let board = json(
169
        send(
170
            &app,
171
            "GET",
172
            "/leaderboard?sort=volume&seeded=include",
173
            &[],
174
            "",
175
        )
176
        .await,
177
    )
178
    .await;
179
    assert_eq!(board["entries"][0]["public_id"], "stout");
180
    assert!(board["entries"][0]["volume"].as_f64().unwrap() > 0.0);
181
}
182
183
#[tokio::test]
184
async fn the_dead_outlive_the_server() {
185
    let path = std::env::temp_dir().join(format!("jelqd-{}.sqlite", std::process::id()));
186
    let config = || Config {
187
        db: Some(path.clone()),
188
        ..Config::default()
189
    };
190
    let first = crate::app(config()).unwrap();
191
    let created = send(&first, "POST", "/runs", &[], "").await;
192
    let location = header(&created, "location").to_owned();
193
    send(&first, "DELETE", &location, &[], "").await;
194
    drop(first);
195
196
    let second = crate::app(config()).unwrap();
197
    let gone = send(&second, "GET", &location, &[], "").await;
198
    assert_eq!(gone.status(), StatusCode::GONE);
199
    let graveyard = json(send(&second, "GET", "/graveyard", &[], "").await).await;
200
    assert_eq!(graveyard["runs"].as_array().unwrap().len(), 1);
201
    let board = json(send(&second, "GET", "/leaderboard", &[], "").await).await;
202
    assert_eq!(board["entries"][0]["status"], "gone");
203
    for suffix in ["", "-wal", "-shm"] {
204
        let _ = std::fs::remove_file(format!("{}{suffix}", path.display()));
205
    }
206
}
207
208
#[tokio::test]
209
async fn the_graveyard_pages_by_fifty() {
210
    let app = app();
211
    for _ in 0..51 {
212
        let created = send(&app, "POST", "/runs", &[], "").await;
213
        let location = header(&created, "location").to_owned();
214
        send(&app, "DELETE", &location, &[], "").await;
215
    }
216
    let first = send(&app, "GET", "/graveyard", &[], "").await;
217
    assert!(header(&first, "link").contains("page=2>; rel=\"next\""));
218
    assert_eq!(json(first).await["runs"].as_array().unwrap().len(), 50);
219
    let second = send(&app, "GET", "/graveyard?page=2", &[], "").await;
220
    assert!(header(&second, "link").contains("page=1>; rel=\"prev\""));
221
    assert_eq!(json(second).await["runs"].as_array().unwrap().len(), 1);
222
}
223
224
#[tokio::test]
225
async fn a_prescribed_run_outlives_the_server() {
226
    let path = std::env::temp_dir().join(format!("jelqd-prescribed-{}.sqlite", std::process::id()));
227
    let config = || Config {
228
        db: Some(path.clone()),
229
        ..Config::default()
230
    };
231
    let state = Arc::new(AppState::new(config()).unwrap());
232
    let mut run = Run::new("cafebabe".into(), "cafe".into(), SEED);
233
    run.floor = Floor::Clinic;
234
    state.insert(run);
235
    let first = router(state);
236
    send(
237
        &first,
238
        "GET",
239
        "/runs/cafebabe/diagnosis",
240
        &[("authorization", "Bearer honesty")],
241
        "",
242
    )
243
    .await;
244
    drop(first);
245
246
    let second = crate::app(config()).unwrap();
247
    let board = json(send(&second, "GET", "/leaderboard?seeded=include", &[], "").await).await;
248
    assert_eq!(board["entries"][0]["status"], "prescribed");
249
    for suffix in ["", "-wal", "-shm"] {
250
        let _ = std::fs::remove_file(format!("{}{suffix}", path.display()));
251
    }
252
}