bpi_rs/web_widget/
banner.rs1use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Clone, Deserialize, Serialize)]
8pub struct RegionBanner {
9 pub image: String, pub title: String, pub sub_title: String, pub url: String, pub rid: i64, }
15
16#[derive(Debug, Clone, Deserialize, Serialize)]
18pub struct RegionBannerData {
19 pub region_banner_list: Vec<RegionBanner>, }
21
22#[cfg(test)]
23mod tests {
24 use super::*;
25 use crate::probe::contract::HttpMethod;
26 use crate::probe::endpoint_contract::EndpointContract;
27 use crate::video::video_zone_v2::{Douga, VideoPartitionV2};
28 use crate::web_widget::params::WebWidgetRegionBannerParams;
29 use crate::{ApiEnvelope, BpiClient, BpiResult};
30
31 use tracing::info;
32
33 fn contract() -> BpiResult<EndpointContract> {
34 EndpointContract::from_slice(include_bytes!(
35 "../../tests/contracts/web_widget/region-banner/contract.json"
36 ))
37 }
38
39 #[ignore = "legacy live API test; requires explicit BPI_LIVE_TEST review"]
40 #[tokio::test]
41 async fn test_get_region_banner() {
42 let bpi = BpiClient::new().expect("client should build");
43 let resp = bpi
45 .web_widget()
46 .region_banner(WebWidgetRegionBannerParams::new(VideoPartitionV2::Douga(
47 Douga::Douga,
48 )))
49 .await;
50 info!("响应: {:?}", resp);
51 assert!(resp.is_ok());
52
53 let data = resp.unwrap();
54 info!("分区轮播图: {:?}", data);
55 }
56
57 #[test]
58 fn web_widget_region_banner_contract_matches_endpoint_request() -> BpiResult<()> {
59 let contract = contract()?;
60 let partition = VideoPartitionV2::Douga(Douga::Douga);
61
62 assert_eq!(contract.name, "web_widget.region_banner");
63 assert_eq!(contract.request.method, HttpMethod::Get);
64 assert_eq!(
65 contract.request.url.as_str(),
66 "https://api.bilibili.com/x/web-show/region/banner"
67 );
68 assert_eq!(
69 contract.request.query.get("region_id").map(String::as_str),
70 Some(partition.tid().to_string().as_str())
71 );
72 assert_eq!(contract.cases.len(), 3);
73 assert_eq!(
74 contract.cases[0].response.rust_model.as_deref(),
75 Some("RegionBannerData")
76 );
77 Ok(())
78 }
79
80 #[test]
81 fn web_widget_region_banner_response_fixtures_parse_declared_model() -> BpiResult<()> {
82 for bytes in [
83 include_bytes!(
84 "../../tests/contracts/web_widget/region-banner/responses/anonymous.success.json"
85 )
86 .as_slice(),
87 include_bytes!(
88 "../../tests/contracts/web_widget/region-banner/responses/normal.success.json"
89 )
90 .as_slice(),
91 include_bytes!(
92 "../../tests/contracts/web_widget/region-banner/responses/vip.success.json"
93 )
94 .as_slice(),
95 ] {
96 let payload = ApiEnvelope::<RegionBannerData>::from_slice(bytes)?.into_payload()?;
97
98 assert!(!payload.region_banner_list.is_empty());
99 }
100 Ok(())
101 }
102
103 fn local_probe_body(profile: &str) -> Option<serde_json::Value> {
104 let path = format!(
105 "target/bpi-probe-runs/web_widget/public/region-banner/{profile}.response.json"
106 );
107 let bytes = std::fs::read(path).ok()?;
108 let value: serde_json::Value = serde_json::from_slice(&bytes).ok()?;
109 value
110 .get("response")
111 .and_then(|response| response.get("body"))
112 .cloned()
113 }
114
115 #[test]
116 fn web_widget_region_banner_model_matches_local_probe_outputs_when_available() -> BpiResult<()>
117 {
118 for profile in ["anonymous", "normal", "vip"] {
119 let Some(body) = local_probe_body(profile) else {
120 continue;
121 };
122 let payload =
123 serde_json::from_value::<ApiEnvelope<RegionBannerData>>(body)?.into_payload()?;
124
125 assert!(!payload.region_banner_list.is_empty());
126 }
127 Ok(())
128 }
129}