1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
//! mon がパスを 1 つも取らないことを確かめる。
//!
//! 病院が `/health` を診療案内に使う、`/static` を別の意味で使う、自前の 404 を出す。
//! どれも通らなければならない。取ってしまうと、そのパスがそのアプリでは使えなくなる。
use axum::Router;
use axum::body::{Body, to_bytes};
use axum::http::{Request, StatusCode};
use axum::routing::get;
use mon::state::AppState;
use mon::{Conf, database, relay, route, session};
use std::sync::Arc;
use tower::ServiceExt;
async fn app(routes: Router<AppState>) -> Router {
let conf = Conf::default();
let database = database::connect("sqlite::memory:").await.unwrap();
let store = session::store(&conf, &database);
session::prepare(&store).await.unwrap();
route::wrap(
routes,
AppState {
database,
client: relay::client(&conf).unwrap(),
session: store,
conf: Arc::new(conf),
},
)
}
async fn read(uri: &str, routes: Router<AppState>) -> (StatusCode, String) {
let request = Request::builder().uri(uri).body(Body::empty()).unwrap();
let response = app(routes).await.oneshot(request).await.unwrap();
let status = response.status();
let content = to_bytes(response.into_body(), 65536).await.unwrap();
(status, String::from_utf8_lossy(&content).into_owned())
}
/// 病院が `/health` を診療案内のページに使う
#[tokio::test]
async fn health_can_be_a_page() {
let routes = Router::new().route("/health", get(|| async { "健康について" }));
let (status, body) = read("/health", routes).await;
assert_eq!(status, StatusCode::OK);
assert_eq!(body, "健康について");
}
/// 自前の 404 を出す
#[tokio::test]
async fn the_fallback_is_theirs() {
let routes = Router::new().fallback(|| async { "こちらの 404" });
let (_, body) = read("/nowhere", routes).await;
assert_eq!(body, "こちらの 404");
}
/// `/static` を静的ファイルではない意味で使う
#[tokio::test]
async fn static_can_mean_something_else() {
let routes = Router::new().route("/static", get(|| async { "静的な話" }));
let (status, body) = read("/static", routes).await;
assert_eq!(status, StatusCode::OK);
assert_eq!(body, "静的な話");
}
/// `/api/session` を自分の用途に使う
#[tokio::test]
async fn the_session_path_is_theirs() {
let routes = Router::new().route("/api/session", get(|| async { "こちらのセッション" }));
let (status, body) = read("/api/session", routes).await;
assert_eq!(status, StatusCode::OK);
assert_eq!(body, "こちらのセッション");
}
/// mon の部品は好きな名前に置ける
#[tokio::test]
async fn the_parts_go_where_they_are_put() {
let routes = Router::new().route("/_alive", get(route::health));
let (status, body) = read("/_alive", routes).await;
assert_eq!(status, StatusCode::OK);
assert!(body.contains("ok"), "{body}");
}
/// 何も置かなければ、何も答えない
#[tokio::test]
async fn an_empty_router_answers_nothing() {
let (status, _) = read("/health", Router::new()).await;
assert_eq!(status, StatusCode::NOT_FOUND);
}