wip(bridge): retained FIFA request/response mapper + docker packaging

RETAINED PRE-EXISTING WIP (brought forward after verification, not authored
here, not production-ready). Substantive mapper.rs proxy expansion plus a
multi-stage Dockerfile/.dockerignore (rustls, self-signed cert at startup).
No secrets/captures staged. Preserved off the detached base c58e7326a1.
This commit is contained in:
funman300
2026-08-20 16:08:24 +00:00
parent c58e7326a1
commit 658dbe2f24
12 changed files with 447 additions and 186 deletions
+59 -16
View File
@@ -1,3 +1,4 @@
use axum::routing::{any, delete, get, post};
use axum::{
body::Body,
http::{Request, StatusCode},
@@ -9,7 +10,6 @@ use openfut_bridge::{
proxy::ProxyState,
routes,
};
use axum::routing::{any, delete, get, post};
use tower::ServiceExt;
// ── Unit tests ────────────────────────────────────────────────────────────────
@@ -96,9 +96,15 @@ fn build_test_app() -> axum::Router {
.route("/_bridge/dashboard", get(routes::health::get_dashboard))
.route("/_bridge/captures", get(routes::admin::get_captures))
.route("/_bridge/captures", delete(routes::admin::delete_captures))
.route("/_bridge/unknown", get(routes::admin::get_unknown_endpoints))
.route(
"/_bridge/unknown",
get(routes::admin::get_unknown_endpoints),
)
.route("/_bridge/status", get(routes::admin::get_endpoint_status))
.route("/_bridge/captures/:id/replay", post(routes::admin::post_replay_capture))
.route(
"/_bridge/captures/:id/replay",
post(routes::admin::post_replay_capture),
)
.fallback(any(openfut_bridge::proxy::catch_all_handler))
.with_state(state)
}
@@ -107,11 +113,18 @@ fn build_test_app() -> axum::Router {
async fn test_bridge_health_endpoint() {
let app = build_test_app();
let resp = app
.oneshot(Request::builder().uri("/_bridge/health").body(Body::empty()).unwrap())
.oneshot(
Request::builder()
.uri("/_bridge/health")
.body(Body::empty())
.unwrap(),
)
.await
.unwrap();
assert_eq!(resp.status(), StatusCode::OK);
let body = axum::body::to_bytes(resp.into_body(), usize::MAX).await.unwrap();
let body = axum::body::to_bytes(resp.into_body(), usize::MAX)
.await
.unwrap();
let json: serde_json::Value = serde_json::from_slice(&body).unwrap();
assert_eq!(json["status"], "ok");
assert_eq!(json["service"], "openfut-bridge");
@@ -131,7 +144,9 @@ async fn test_bridge_placeholder_mode_returns_ok() {
.await
.unwrap();
assert_eq!(resp.status(), StatusCode::OK);
let body = axum::body::to_bytes(resp.into_body(), usize::MAX).await.unwrap();
let body = axum::body::to_bytes(resp.into_body(), usize::MAX)
.await
.unwrap();
let json: serde_json::Value = serde_json::from_slice(&body).unwrap();
assert_eq!(json["status"], "ok");
assert!(json["openfut_note"].is_string());
@@ -150,7 +165,9 @@ async fn test_bridge_captures_endpoint_returns_list() {
.await
.unwrap();
assert_eq!(resp.status(), StatusCode::OK);
let body = axum::body::to_bytes(resp.into_body(), usize::MAX).await.unwrap();
let body = axum::body::to_bytes(resp.into_body(), usize::MAX)
.await
.unwrap();
let json: serde_json::Value = serde_json::from_slice(&body).unwrap();
assert!(json["captures"].is_array());
assert!(json["total"].is_number());
@@ -170,7 +187,9 @@ async fn test_bridge_delete_captures() {
.await
.unwrap();
assert_eq!(resp.status(), StatusCode::OK);
let body = axum::body::to_bytes(resp.into_body(), usize::MAX).await.unwrap();
let body = axum::body::to_bytes(resp.into_body(), usize::MAX)
.await
.unwrap();
let json: serde_json::Value = serde_json::from_slice(&body).unwrap();
assert!(json["deleted"].is_number());
}
@@ -188,7 +207,9 @@ async fn test_bridge_status_endpoint() {
.await
.unwrap();
assert_eq!(resp.status(), StatusCode::OK);
let body = axum::body::to_bytes(resp.into_body(), usize::MAX).await.unwrap();
let body = axum::body::to_bytes(resp.into_body(), usize::MAX)
.await
.unwrap();
let json: serde_json::Value = serde_json::from_slice(&body).unwrap();
assert!(json["endpoints"].is_array());
}
@@ -208,15 +229,25 @@ async fn test_dashboard_returns_html() {
.await
.unwrap();
assert_eq!(resp.status(), StatusCode::OK);
let ct = resp.headers().get("content-type").unwrap().to_str().unwrap();
let ct = resp
.headers()
.get("content-type")
.unwrap()
.to_str()
.unwrap();
assert!(ct.contains("text/html"), "expected text/html, got {ct}");
let body = axum::body::to_bytes(resp.into_body(), usize::MAX).await.unwrap();
let body = axum::body::to_bytes(resp.into_body(), usize::MAX)
.await
.unwrap();
let html = String::from_utf8(body.to_vec()).unwrap();
assert!(html.contains("OpenFUT Dashboard"), "title missing");
assert!(html.contains("const CORE ="), "core URL injection missing");
// Placeholder URL injected in test mode
assert!(html.contains("127.0.0.1:9999"), "core URL not injected");
assert!(!html.contains("{{CORE_URL}}"), "template placeholder was not replaced");
assert!(
!html.contains("{{CORE_URL}}"),
"template placeholder was not replaced"
);
}
#[tokio::test]
@@ -231,7 +262,9 @@ async fn test_dashboard_contains_key_sections() {
)
.await
.unwrap();
let body = axum::body::to_bytes(resp.into_body(), usize::MAX).await.unwrap();
let body = axum::body::to_bytes(resp.into_body(), usize::MAX)
.await
.unwrap();
let html = String::from_utf8(body.to_vec()).unwrap();
// Verify all major tab sections are present
assert!(html.contains("tab-club"), "club tab missing");
@@ -250,8 +283,14 @@ async fn test_dashboard_contains_key_sections() {
assert!(html.contains("tab-statistics"), "statistics tab missing");
assert!(html.contains("tab-catalog"), "card catalog tab missing");
assert!(html.contains("tab-settings"), "settings tab missing");
assert!(html.contains("tab-notifications"), "notifications tab missing");
assert!(html.contains("tab-achievements"), "achievements tab missing");
assert!(
html.contains("tab-notifications"),
"notifications tab missing"
);
assert!(
html.contains("tab-achievements"),
"achievements tab missing"
);
}
#[tokio::test]
@@ -272,5 +311,9 @@ async fn test_tls_cert_generation() {
async fn test_tls_acceptor_construction() {
let (cert_pem, key_pem) = openfut_bridge::tls::generate_self_signed_cert().unwrap();
let acceptor = openfut_bridge::tls::make_tls_acceptor(&cert_pem, &key_pem);
assert!(acceptor.is_ok(), "acceptor construction failed: {:?}", acceptor.err());
assert!(
acceptor.is_ok(),
"acceptor construction failed: {:?}",
acceptor.err()
);
}