Files
SCOPENET-MC/panel/server/tests/livemap.rs
T

100 lines
4.6 KiB
Rust

//! Live position and world-mirror ingest from game servers.
mod common;
use common::*;
fn record(x: i32, z: i32, ts: u32, data: &[u8]) -> Vec<u8> {
let mut b = Vec::new();
b.extend_from_slice(&x.to_be_bytes());
b.extend_from_slice(&z.to_be_bytes());
b.extend_from_slice(&ts.to_be_bytes());
b.push(2);
b.extend_from_slice(&(data.len() as u32).to_be_bytes());
b.extend_from_slice(data);
b
}
async fn post_bytes(t: &TestApp, uri: &str, token: &str, body: Vec<u8>) -> (StatusCode, Value) {
let req = Request::builder().method("POST").uri(uri).header("authorization", format!("Bearer {token}")).body(Body::from(body)).unwrap();
t.send(req).await
}
#[tokio::test]
async fn live_map_ingest_and_player_positions() {
let t = setup().await;
let admin = t.login("admin", "supersecret").await;
let (_, v) = t.call("POST", "/api/admin/servers", Some(&admin), Some(json!({"name": "Survival"}))).await;
let id = v["server"]["id"].as_i64().unwrap();
let token = v["token"].as_str().unwrap().to_string();
// Off by default: nothing is accepted.
let (s, _) = post_bytes(&t, "/api/server/v1/livemap/chunks?dim=overworld", &token, record(0, 0, 5, b"abc")).await;
assert_eq!(s, StatusCode::FORBIDDEN);
let (_, hello) = t.call("POST", "/api/server/v1/hello", Some(&token), Some(json!({}))).await;
assert_eq!(hello["live_map"]["enabled"], false);
let (s, v) =
t.call("PUT", &format!("/api/admin/servers/{id}"), Some(&admin), Some(json!({"name": "Survival", "live_map_enabled": true}))).await;
assert_eq!(s, StatusCode::OK, "{v}");
assert_eq!(v["live_map_enabled"], true);
let (_, hello) = t.call("POST", "/api/server/v1/hello", Some(&token), Some(json!({}))).await;
assert_eq!(hello["live_map"]["enabled"], true);
// Chunks land in the region mirror and show up in the manifest.
let mut batch = record(1, 2, 100, b"hello-chunk");
batch.extend(record(-1, 40, 101, b"other-region"));
let (s, v) = post_bytes(&t, "/api/server/v1/livemap/chunks?dim=overworld", &token, batch).await;
assert_eq!(s, StatusCode::OK, "{v}");
assert_eq!(v["stored"], 2);
let (s, _) = post_bytes(&t, "/api/server/v1/livemap/chunks?dim=../x", &token, record(0, 0, 1, b"x")).await;
assert_eq!(s, StatusCode::BAD_REQUEST);
let (s, _) = post_bytes(&t, "/api/server/v1/livemap/chunks", &token, vec![1, 2, 3]).await;
assert_eq!(s, StatusCode::BAD_REQUEST);
let (_, m) = t.call("GET", "/api/server/v1/livemap/manifest?dim=overworld", Some(&token), None).await;
assert_eq!(m["regions"].as_array().unwrap().len(), 2);
// level.dat must be a gzip file.
let (s, _) = post_bytes(&t, "/api/server/v1/livemap/level", &token, b"not gzip at all, sorry!".to_vec()).await;
assert_eq!(s, StatusCode::BAD_REQUEST);
let mut gz = vec![0x1f, 0x8b];
gz.extend([0u8; 30]);
let (s, _) = post_bytes(&t, "/api/server/v1/livemap/level", &token, gz).await;
assert_eq!(s, StatusCode::OK);
// Players, sanitised.
let (s, _) = t
.call(
"POST",
"/api/server/v1/livemap/players",
Some(&token),
Some(json!({"players": [
{"uuid": "b50ad385-829d-3141-a216-7e7d7539ba7f", "name": "Notch", "dimension": "minecraft:the_nether", "x": 10.5, "y": 70.0, "z": -4.0, "yaw": 90.0},
{"uuid": "bad", "name": "Nowhere", "x": 1e99, "y": 0, "z": 0}
]})),
)
.await;
assert_eq!(s, StatusCode::OK);
// Viewers need a session.
let (s, _) = t.call("GET", &format!("/api/v1/servers/{id}/livemap"), None, None).await;
assert_eq!(s, StatusCode::UNAUTHORIZED);
let (s, info) = t.call("GET", &format!("/api/v1/servers/{id}/livemap"), Some(&admin), None).await;
assert_eq!(s, StatusCode::OK);
assert_eq!(info["enabled"], true);
assert_eq!(info["players"], 1);
assert!(info["dimensions"][0]["available"].as_bool().unwrap());
assert_eq!(info["ready"], true);
let (s, st) = t.call("GET", &format!("/api/admin/servers/{id}/livemap"), Some(&admin), None).await;
assert_eq!(s, StatusCode::OK);
assert_eq!(st["stats"]["chunks_received"], 2);
// Public list advertises the map; reset wipes it.
let (_, list) = t.call("GET", "/api/v1/servers/public", None, None).await;
assert_eq!(list["servers"][0]["live_map"], true);
let (s, _) = t.call("DELETE", &format!("/api/admin/servers/{id}/livemap"), Some(&admin), None).await;
assert_eq!(s, StatusCode::OK);
let (_, m) = t.call("GET", "/api/server/v1/livemap/manifest?dim=overworld", Some(&token), None).await;
assert!(m["regions"].as_array().unwrap().is_empty());
}