From b196c1150fcae6bdb3f6f4fae05f5f45ea74e3a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=BE=B7=E5=AE=87?= Date: Sat, 3 Oct 2026 18:25:15 +0800 Subject: [PATCH] =?UTF-8?q?=E5=90=8E=E7=AB=AF=EF=BC=9A=E8=A1=A5=E5=85=85?= =?UTF-8?q?=E6=B8=B8=E7=8E=A9=E4=B8=8A=E6=8A=A5=E7=AB=AF=E7=82=B9=E4=B8=8E?= =?UTF-8?q?=E8=BA=AB=E4=BB=BD=E8=A7=A3=E6=9E=90=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - modules/game_distribution.rs:新增公开路由在未连接 SpacetimeDB 时可达、无需登录且 no-store 的断言 - 补 clientId 去空白/截断 128 字符与缺失 UA 回退的单测 --- .../src/modules/game_distribution.rs | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/server-rs/crates/api-server/src/modules/game_distribution.rs b/server-rs/crates/api-server/src/modules/game_distribution.rs index 4035b596e..008a047e8 100644 --- a/server-rs/crates/api-server/src/modules/game_distribution.rs +++ b/server-rs/crates/api-server/src/modules/game_distribution.rs @@ -4179,4 +4179,53 @@ mod tests { assert_eq!(generic.summary, "一款由陶泥儿创作的益智游戏"); assert_eq!(generic.category, "益智"); } + + #[tokio::test] + async fn game_play_route_is_public_and_no_store_without_spacetime_connection() { + use axum::http::Request; + use tower::ServiceExt; + + let app = + crate::app::build_router(AppState::new(crate::config::AppConfig::default()).unwrap()); + let response = app + .oneshot( + Request::builder() + .method("POST") + .uri("/api/game-distribution/games/game_1/plays") + .header(header::CONTENT_TYPE, "application/json") + .body(Body::from(r#"{"clientId":"client-1"}"#)) + .unwrap(), + ) + .await + .unwrap(); + // 未连接 SpacetimeDB 时公开可见性读取失败,但路由可达且不需要登录;只有确认公开后才计数。 + assert_eq!(response.status(), StatusCode::BAD_GATEWAY); + assert_eq!(response.headers()[header::CACHE_CONTROL], "no-store"); + } + + #[test] + fn play_request_client_id_trims_limits_and_rejects_blank() { + assert_eq!(request_client_id(&Bytes::from_static(b"")), None); + assert_eq!(request_client_id(&Bytes::from_static(b"not json")), None); + assert_eq!(request_client_id(&Bytes::from_static(b"{}")), None); + assert_eq!( + request_client_id(&Bytes::from_static(br#"{"clientId":" abc "}"#)), + Some("abc".to_string()) + ); + assert_eq!( + request_client_id(&Bytes::from_static(br#"{"clientId":" "}"#)), + None + ); + let long = "x".repeat(200); + let body = Bytes::from(format!(r#"{{"clientId":"{long}"}}"#)); + assert_eq!(request_client_id(&body).unwrap().chars().count(), 128); + } + + #[test] + fn play_report_user_agent_is_bounded_and_falls_back() { + assert_eq!(user_agent_tag(&HeaderMap::new()), "unknown"); + let mut headers = HeaderMap::new(); + headers.insert(header::USER_AGENT, " test-agent ".parse().unwrap()); + assert_eq!(user_agent_tag(&headers), "test-agent"); + } }