mirror of
https://github.com/horizon-rust/horizon.git
synced 2026-11-08 17:19:10 -10:00
Merge pull request #8844 from TiggerTakkyTTT/test/server-api-backup-goldens
🤖 Agent: Server API backup goldens: test-only, the download case reads the real streamed bytes. The union of this wave's 15 PRs on main acd184c1 passed fmt, clippy -D warnings, 3388 tests and the Python suite on the desktop. management_protocol (--fresh, on that union): every case of this PR matches vanilla; the only 3 differences were #8204's heartbeat cases, fixed there. Other harnesses PASS on the union: flat_generation, flat_place, chunk_generation, datapack_worldgen, command_exec, pick_block_data, powder_snow_collision, snow_freeze, boat_authority, vehicle_container. dedicated_server could not run locally (the vanilla oracle's world generation hit its watchdog under the desktop's memory pressure), so it and the rest of the selection run in CI run-parity.
This commit is contained in:
2 files changed
+190
No files matched your search
@@ -1244,6 +1244,131 @@ mod tests {
|
||||
std::fs::remove_dir_all(&dir).unwrap();
|
||||
}
|
||||
|
||||
fn backup_reply(mut response: Response) -> Value {
|
||||
let body = if let Some(write) = response.stream.take() {
|
||||
let listener = TcpListener::bind("127.0.0.1:0").unwrap();
|
||||
let mut client = std::net::TcpStream::connect(listener.local_addr().unwrap()).unwrap();
|
||||
let (server, _) = listener.accept().unwrap();
|
||||
write(&server);
|
||||
server.shutdown(std::net::Shutdown::Write).unwrap();
|
||||
let mut bytes = Vec::new();
|
||||
std::io::Read::read_to_end(&mut client, &mut bytes).unwrap();
|
||||
Value::String(
|
||||
bytes
|
||||
.iter()
|
||||
.map(|byte| format!("{byte:02x}"))
|
||||
.collect::<Vec<_>>()
|
||||
.join(""),
|
||||
)
|
||||
} else {
|
||||
serde_json::from_str(&response.body).unwrap()
|
||||
};
|
||||
json!({
|
||||
"status": response.status,
|
||||
"content_type": response.content_type,
|
||||
"headers": response.headers,
|
||||
"body": body,
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn backup_replies_match_api_v1_goldens() {
|
||||
let golden: Value =
|
||||
serde_json::from_str(include_str!("../testdata/api-v1-backups.json")).unwrap();
|
||||
let dir = std::env::temp_dir().join(format!(
|
||||
"horizon-server-api-backup-goldens-{}",
|
||||
std::process::id()
|
||||
));
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
std::fs::create_dir_all(dir.join("backups")).unwrap();
|
||||
std::fs::create_dir_all(dir.join("world/dimensions/minecraft/the_end")).unwrap();
|
||||
std::fs::write(dir.join(crate::config::FILE), crate::config::TEMPLATE).unwrap();
|
||||
std::fs::write(dir.join("world/level.dat"), b"level").unwrap();
|
||||
std::fs::write(
|
||||
dir.join("world/dimensions/minecraft/the_end/region.mca"),
|
||||
b"end",
|
||||
)
|
||||
.unwrap();
|
||||
let name = "2026-11-04_00-00-00-golden.zip";
|
||||
std::fs::write(dir.join("backups").join(name), b"PK\x05\x06zip").unwrap();
|
||||
let shared = Shared::with_install(&dir, None);
|
||||
shared.set_phase(Phase::Stopped);
|
||||
let call = |method: &str, target: &str, body: Value| {
|
||||
let (path, query) = target.split_once('?').unwrap_or((target, ""));
|
||||
route(
|
||||
&shared,
|
||||
&Request {
|
||||
method: method.into(),
|
||||
path: path.into(),
|
||||
query: query.into(),
|
||||
headers: Vec::new(),
|
||||
body: body.to_string().into_bytes(),
|
||||
},
|
||||
)
|
||||
};
|
||||
let check = |case: &str, response: Response| {
|
||||
assert_eq!(backup_reply(response), golden[case], "{case}");
|
||||
};
|
||||
check("list", call("GET", "/api/v1/backups", Value::Null));
|
||||
check(
|
||||
"download",
|
||||
call(
|
||||
"GET",
|
||||
&format!("/api/v1/backups/download?name={name}"),
|
||||
Value::Null,
|
||||
),
|
||||
);
|
||||
check(
|
||||
"restore",
|
||||
call("POST", "/api/v1/restore", json!({ "name": name })),
|
||||
);
|
||||
check(
|
||||
"restore_dimension",
|
||||
call(
|
||||
"POST",
|
||||
"/api/v1/restore",
|
||||
json!({ "name": name, "dimension": "the_end" }),
|
||||
),
|
||||
);
|
||||
check(
|
||||
"bad_dimension",
|
||||
call(
|
||||
"POST",
|
||||
"/api/v1/restore",
|
||||
json!({ "name": name, "dimension": "../end" }),
|
||||
),
|
||||
);
|
||||
check(
|
||||
"restore_missing",
|
||||
call("POST", "/api/v1/restore", json!({ "name": "missing" })),
|
||||
);
|
||||
check(
|
||||
"delete_missing",
|
||||
call("DELETE", "/api/v1/backups?name=../server.toml", Value::Null),
|
||||
);
|
||||
check(
|
||||
"delete",
|
||||
call(
|
||||
"DELETE",
|
||||
&format!("/api/v1/backups?name={name}"),
|
||||
Value::Null,
|
||||
),
|
||||
);
|
||||
check("list_empty", call("GET", "/api/v1/backups", Value::Null));
|
||||
|
||||
let mut created = backup_reply(call(
|
||||
"POST",
|
||||
"/api/v1/backups",
|
||||
json!({ "name": "created" }),
|
||||
));
|
||||
let created_name = created["body"]["name"].as_str().unwrap();
|
||||
assert!(created_name.ends_with("-created.zip"));
|
||||
assert!(dir.join("backups").join(created_name).is_file());
|
||||
created["body"]["name"] = json!("$BACKUP");
|
||||
assert_eq!(created, golden["create"]);
|
||||
std::fs::remove_dir_all(&dir).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stopped_supervisor_can_edit_lists_and_start_again() {
|
||||
let dir =
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
{
|
||||
"list": {
|
||||
"status": 200,
|
||||
"content_type": "application/json",
|
||||
"headers": [],
|
||||
"body": {"backups": [{"name": "2026-11-04_00-00-00-golden.zip", "bytes": 7}]}
|
||||
},
|
||||
"download": {
|
||||
"status": 200,
|
||||
"content_type": "application/octet-stream",
|
||||
"headers": [
|
||||
["Content-Length", "7"],
|
||||
["Content-Disposition", "attachment; filename=\"2026-11-04_00-00-00-golden.zip\""]
|
||||
],
|
||||
"body": "504b05067a6970"
|
||||
},
|
||||
"restore": {
|
||||
"status": 200,
|
||||
"content_type": "application/json",
|
||||
"headers": [],
|
||||
"body": {"name": "2026-11-04_00-00-00-golden.zip", "dimension": null}
|
||||
},
|
||||
"restore_dimension": {
|
||||
"status": 200,
|
||||
"content_type": "application/json",
|
||||
"headers": [],
|
||||
"body": {"name": "2026-11-04_00-00-00-golden.zip", "dimension": "minecraft:the_end"}
|
||||
},
|
||||
"bad_dimension": {
|
||||
"status": 400,
|
||||
"content_type": "application/json",
|
||||
"headers": [],
|
||||
"body": {"error": "\"../end\" is not a dimension id (like minecraft:the_nether)"}
|
||||
},
|
||||
"restore_missing": {
|
||||
"status": 404,
|
||||
"content_type": "application/json",
|
||||
"headers": [],
|
||||
"body": {"error": "no backup \"missing\" in backups/ (`horizon-server backups` lists them)"}
|
||||
},
|
||||
"delete_missing": {
|
||||
"status": 404,
|
||||
"content_type": "application/json",
|
||||
"headers": [],
|
||||
"body": {"error": "no such backup (`GET /api/v1/backups` lists them)"}
|
||||
},
|
||||
"delete": {
|
||||
"status": 200,
|
||||
"content_type": "application/json",
|
||||
"headers": [],
|
||||
"body": {"name": "2026-11-04_00-00-00-golden.zip"}
|
||||
},
|
||||
"list_empty": {
|
||||
"status": 200,
|
||||
"content_type": "application/json",
|
||||
"headers": [],
|
||||
"body": {"backups": []}
|
||||
},
|
||||
"create": {
|
||||
"status": 200,
|
||||
"content_type": "application/json",
|
||||
"headers": [],
|
||||
"body": {"name": "$BACKUP"}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user