Finalize nx9-wg production release

This commit is contained in:
thakares committed 2026-08-18 22:27:36 +05:30
1 parent 4dfe42fe68
commit d704c1e131
30 files changed
+2502 -370

No files matched your search

@@ -491,6 +491,139 @@ async fn test_server_endpoint_persistence_validation_and_export_precedence() {
assert!(conf_str.contains("Endpoint = vpn.wan-domain.org:51820"));
}
#[tokio::test]
async fn test_structured_server_endpoint_settings_api_and_peer_export() {
let (state, _iface, peer, session_id) = setup_test_context().await;
let app = build_api_router(state.clone());
// 1. Invalid wireguard.server_host with embedded port is rejected with 422
let invalid_host_req = Request::builder()
.method("PUT")
.uri("/api/v1/system/settings")
.header("Content-Type", "application/json")
.header("Cookie", format!("nx9_session={session_id}"))
.body(Body::from(
serde_json::json!({
"key": "wireguard.server_host",
"value": "vpn.thakares.com:51820", // embedded port!
"is_secret": false
})
.to_string(),
))
.unwrap();
let resp = app.clone().oneshot(invalid_host_req).await.unwrap();
assert_eq!(resp.status(), StatusCode::UNPROCESSABLE_ENTITY);
// 2. Invalid wireguard.server_port (0) is rejected with 422
let invalid_port_req = Request::builder()
.method("PUT")
.uri("/api/v1/system/settings")
.header("Content-Type", "application/json")
.header("Cookie", format!("nx9_session={session_id}"))
.body(Body::from(
serde_json::json!({
"key": "wireguard.server_port",
"value": "0",
"is_secret": false
})
.to_string(),
))
.unwrap();
let resp = app.clone().oneshot(invalid_port_req).await.unwrap();
assert_eq!(resp.status(), StatusCode::UNPROCESSABLE_ENTITY);
// 3. Valid wireguard.server_host and wireguard.server_port save successfully
let set_host_req = Request::builder()
.method("PUT")
.uri("/api/v1/system/settings")
.header("Content-Type", "application/json")
.header("Cookie", format!("nx9_session={session_id}"))
.body(Body::from(
serde_json::json!({
"key": "wireguard.server_host",
"value": "vpn.thakares.com",
"is_secret": false
})
.to_string(),
))
.unwrap();
let resp = app.clone().oneshot(set_host_req).await.unwrap();
assert_eq!(resp.status(), StatusCode::OK);
let set_port_req = Request::builder()
.method("PUT")
.uri("/api/v1/system/settings")
.header("Content-Type", "application/json")
.header("Cookie", format!("nx9_session={session_id}"))
.body(Body::from(
serde_json::json!({
"key": "wireguard.server_port",
"value": "51820",
"is_secret": false
})
.to_string(),
))
.unwrap();
let resp = app.clone().oneshot(set_port_req).await.unwrap();
assert_eq!(resp.status(), StatusCode::OK);
// 4. Export config automatically resolves Endpoint = vpn.thakares.com:51820
let export_req = Request::builder()
.uri(format!("/api/v1/peers/{}/config", peer.id))
.header("Cookie", format!("nx9_session={session_id}"))
.body(Body::empty())
.unwrap();
let resp = app.clone().oneshot(export_req).await.unwrap();
assert_eq!(resp.status(), StatusCode::OK);
let conf_bytes = axum::body::to_bytes(resp.into_body(), usize::MAX)
.await
.unwrap();
let conf_str = String::from_utf8(conf_bytes.to_vec()).unwrap();
assert!(conf_str.contains("Endpoint = vpn.thakares.com:51820"));
// 5. Export QR returns valid SVG
let qr_req = Request::builder()
.uri(format!("/api/v1/peers/{}/qr", peer.id))
.header("Cookie", format!("nx9_session={session_id}"))
.body(Body::empty())
.unwrap();
let resp = app.clone().oneshot(qr_req).await.unwrap();
assert_eq!(resp.status(), StatusCode::OK);
// 6. Set IPv6 host -> exports [2001:db8::10]:51820
let set_v6_req = Request::builder()
.method("PUT")
.uri("/api/v1/system/settings")
.header("Content-Type", "application/json")
.header("Cookie", format!("nx9_session={session_id}"))
.body(Body::from(
serde_json::json!({
"key": "wireguard.server_host",
"value": "2001:db8::10",
"is_secret": false
})
.to_string(),
))
.unwrap();
let resp = app.clone().oneshot(set_v6_req).await.unwrap();
assert_eq!(resp.status(), StatusCode::OK);
let export_v6_req = Request::builder()
.uri(format!("/api/v1/peers/{}/config", peer.id))
.header("Cookie", format!("nx9_session={session_id}"))
.body(Body::empty())
.unwrap();
let resp = app.clone().oneshot(export_v6_req).await.unwrap();
assert_eq!(resp.status(), StatusCode::OK);
let conf_bytes = axum::body::to_bytes(resp.into_body(), usize::MAX)
.await
.unwrap();
let conf_str = String::from_utf8(conf_bytes.to_vec()).unwrap();
assert!(conf_str.contains("Endpoint = [2001:db8::10]:51820"));
}
#[tokio::test]
async fn test_peer_telemetry_enrichment_and_status_transitions() {
let (state_orig, iface, peer, session_id) = setup_test_context().await;