Server integrations / Paper and protocol tests (push) Failing after 20s
Server integrations / fabric / 1.20.1 (push) Failing after 11s
Server integrations / fabric / 1.21.1 (push) Failing after 14s
Server integrations / fabric / 26.1.2 (push) Failing after 17s
Server integrations / fabric / 26.2 (push) Failing after 12s
Server integrations / fabric / 26.3 (push) Failing after 6s
Server integrations / forge / 1.20.1 (push) Failing after 6s
Server integrations / forge / 1.21.1 (push) Failing after 8s
Server integrations / forge / 26.1.2 (push) Failing after 8s
Server integrations / forge / 26.2 (push) Failing after 8s
Server integrations / forge / 26.3 (push) Failing after 7s
CI / Web UIs (type-check + build) (panel/web) (push) Canceled after 0s
CI / Launcher (Windows build check) (push) Canceled after 2m53s
CI / Real installs (Mojang, Fabric, Quilt, Forge, NeoForge) (push) Canceled after 0s
CI / Docker image builds (push) Canceled after 0s
CI / Rust (fmt, clippy, tests) (push) Canceled after 1m22s
CI / Web UIs (type-check + build) (launcher) (push) Canceled after 1m27s
91 lines
4.1 KiB
Rust
91 lines
4.1 KiB
Rust
//! The claim index game servers poll instead of asking about every block.
|
|
|
|
mod common;
|
|
use common::*;
|
|
|
|
#[tokio::test]
|
|
async fn claim_index_follows_claims_and_membership() {
|
|
let t = setup().await;
|
|
let admin = t.login("admin", "supersecret").await;
|
|
let mut tokens = Vec::new();
|
|
for n in ["Alex", "Steve"] {
|
|
t.call("POST", "/api/admin/users", Some(&admin), Some(json!({"username": n, "password": "password123"}))).await;
|
|
tokens.push(t.login(n, "password123").await);
|
|
}
|
|
let alex_uuid = t.uuid("Alex").await;
|
|
let steve_uuid = t.uuid("Steve").await;
|
|
let (_, srv) = t.call("POST", "/api/admin/servers", Some(&admin), Some(json!({"name": "SMP", "instance_id": "smp"}))).await;
|
|
let sid = srv["server"]["id"].as_i64().unwrap();
|
|
let token = srv["token"].as_str().unwrap().to_string();
|
|
let index = |rev: Option<String>| {
|
|
let (t, token) = (&t, token.clone());
|
|
async move { t.call("POST", "/api/server/v1/guilds/claim-index", Some(&token), Some(json!({ "revision": rev }))).await }
|
|
};
|
|
|
|
let (s, empty) = index(None).await;
|
|
assert_eq!(s, StatusCode::OK);
|
|
assert_eq!(empty["unchanged"], false);
|
|
assert!(empty["claims"].as_array().unwrap().is_empty());
|
|
let rev0 = empty["revision"].as_str().unwrap().to_string();
|
|
let (_, same) = index(Some(rev0.clone())).await;
|
|
assert_eq!(same["unchanged"], true);
|
|
assert!(same.get("claims").is_none());
|
|
|
|
// A guild, then a claim: the revision moves and the index lists the chunk.
|
|
let (_, g) =
|
|
t.call("POST", "/api/v1/guilds", Some(&tokens[0]), Some(json!({"instance_id": "smp", "name": "Iron", "tag": "IRON"}))).await;
|
|
let gid = g["id"].as_str().unwrap().to_string();
|
|
let (_, r1) = index(Some(rev0.clone())).await;
|
|
assert_eq!(r1["unchanged"], false, "guild creation bumps the revision");
|
|
let claim = |x: i32| {
|
|
let (t, tok, gid) = (&t, tokens[0].clone(), gid.clone());
|
|
async move {
|
|
t.call(
|
|
"POST",
|
|
&format!("/api/v1/guilds/{gid}/claim"),
|
|
Some(&tok),
|
|
Some(json!({"server_id": sid, "dimension": "minecraft:overworld", "chunk_x": x, "chunk_z": -4})),
|
|
)
|
|
.await
|
|
}
|
|
};
|
|
let (s, c1) = claim(3).await;
|
|
assert_eq!(s, StatusCode::OK, "{c1}");
|
|
claim(4).await;
|
|
let (_, idx) = index(Some(r1["revision"].as_str().unwrap().to_string())).await;
|
|
assert_eq!(idx["unchanged"], false);
|
|
assert_eq!(idx["claims"].as_array().unwrap().len(), 2);
|
|
assert_eq!(idx["claims"][0], json!(["minecraft:overworld", 3, -4, 0]));
|
|
assert_eq!(idx["guilds"][0]["tag"], "IRON");
|
|
assert_eq!(idx["members"][&gid], json!([alex_uuid]));
|
|
let rev2 = idx["revision"].as_str().unwrap().to_string();
|
|
assert_eq!(index(Some(rev2.clone())).await.1["unchanged"], true);
|
|
|
|
// Membership changes bump it too.
|
|
sqlx::query("INSERT INTO guild_members (guild_id, uuid, name, role, joined_at) VALUES (?, ?, 'Steve', 'member', 'now')")
|
|
.bind(&gid)
|
|
.bind(&steve_uuid)
|
|
.execute(&t.db)
|
|
.await
|
|
.unwrap();
|
|
let (_, idx) = index(Some(rev2.clone())).await;
|
|
assert_eq!(idx["unchanged"], false);
|
|
assert_eq!(idx["members"][&gid].as_array().unwrap().len(), 2);
|
|
let rev3 = idx["revision"].as_str().unwrap().to_string();
|
|
|
|
// Unclaim, and deleting the guild (cascades the claims away).
|
|
let cid = c1["id"].as_i64().unwrap();
|
|
t.call("DELETE", &format!("/api/v1/guilds/claims/{cid}"), Some(&tokens[0]), None).await;
|
|
let (_, idx) = index(Some(rev3)).await;
|
|
assert_eq!(idx["claims"].as_array().unwrap().len(), 1);
|
|
let rev4 = idx["revision"].as_str().unwrap().to_string();
|
|
sqlx::query("DELETE FROM guilds WHERE id = ?").bind(&gid).execute(&t.db).await.unwrap();
|
|
let (_, idx) = index(Some(rev4)).await;
|
|
assert_eq!(idx["unchanged"], false, "cascaded deletes still fire the revision triggers");
|
|
assert!(idx["claims"].as_array().unwrap().is_empty());
|
|
|
|
// Unauthenticated callers get nothing.
|
|
let (s, _) = t.call("POST", "/api/server/v1/guilds/claim-index", None, Some(json!({}))).await;
|
|
assert_eq!(s, StatusCode::UNAUTHORIZED);
|
|
}
|