1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Serialize, Clone, Deserialize)]
6pub struct RoomPendantFrame {
7 pub name: String,
9 pub value: String,
11 pub position: i32,
13 pub desc: String,
15 pub area: i32,
17 pub area_old: i32,
19 pub bg_color: String,
21 pub bg_pic: String,
23 pub use_old_area: bool,
25}
26
27#[derive(Debug, Serialize, Clone, Deserialize)]
28pub struct RoomPendantBadge {
29 pub name: String,
31 pub position: i32,
33 pub value: String,
35 pub desc: String,
37}
38
39#[derive(Debug, Serialize, Clone, Deserialize)]
40pub struct RoomPendants {
41 pub frame: RoomPendantFrame,
43 pub mobile_frame: Option<RoomPendantFrame>,
45 pub badge: RoomPendantBadge,
47 pub mobile_badge: Option<RoomPendantBadge>,
49}
50
51#[derive(Debug, Serialize, Clone, Deserialize)]
52pub struct RoomStudioInfo {
53 #[serde(flatten)]
55 pub extra: serde_json::Value,
56}
57
58#[derive(Debug, Serialize, Clone, Deserialize)]
59pub struct RoomInfoData {
60 pub uid: i64,
62 pub room_id: i64,
64 pub short_id: i64,
66 pub attention: i64,
68 pub online: i64,
70 pub is_portrait: bool,
72 pub description: String,
74 pub live_status: i32,
76 pub area_id: i32,
78 pub parent_area_id: i32,
80 pub parent_area_name: String,
82 pub old_area_id: i32,
84 pub background: String,
86 pub title: String,
88 pub user_cover: String,
90 pub keyframe: String,
92 pub live_time: String,
94 pub tags: String,
96 pub room_silent_type: String,
98 pub room_silent_level: i32,
100 pub room_silent_second: i64,
102 pub area_name: String,
104 pub hot_words: Vec<String>,
106 pub hot_words_status: i32,
108 pub new_pendants: RoomPendants,
110 pub pk_status: i32,
112 pub pk_id: i64,
114 pub allow_change_area_time: i64,
116 pub allow_upload_cover_time: i64,
118 pub studio_info: Option<RoomStudioInfo>,
120}
121
122#[cfg(test)]
123mod tests {
124 use super::*;
125 use crate::probe::contract::HttpMethod;
126 use crate::probe::endpoint_contract::EndpointContract;
127 use crate::{ApiEnvelope, BpiClient, BpiError, BpiResult};
128
129 fn contract() -> BpiResult<EndpointContract> {
130 EndpointContract::from_slice(include_bytes!(
131 "../../tests/contracts/live/public-core/room-info/contract.json"
132 ))
133 }
134
135 #[ignore = "legacy live API test; requires explicit BPI_LIVE_TEST review"]
136 #[tokio::test]
137 async fn test_get_room_info() -> Result<(), Box<BpiError>> {
138 let bpi = BpiClient::new().expect("client should build");
139 let data = bpi.live().room_info(23174842).await?;
140
141 assert_eq!(data.room_id, 23174842);
142 Ok(())
143 }
144
145 #[test]
146 fn live_room_info_contract_matches_endpoint_request() -> BpiResult<()> {
147 let contract = contract()?;
148
149 assert_eq!(contract.name, "live.room_info");
150 assert_eq!(contract.request.method, HttpMethod::Get);
151 assert_eq!(
152 contract.request.url.as_str(),
153 "https://api.live.bilibili.com/room/v1/Room/get_info"
154 );
155 assert_eq!(
156 contract.request.query.get("room_id").map(String::as_str),
157 Some("23174842")
158 );
159 assert_eq!(contract.cases.len(), 3);
160 assert_eq!(
161 contract.cases[0].response.rust_model.as_deref(),
162 Some("RoomInfoData")
163 );
164 Ok(())
165 }
166
167 #[test]
168 fn live_room_info_response_fixture_parses_declared_model() -> BpiResult<()> {
169 let payload = ApiEnvelope::<RoomInfoData>::from_slice(include_bytes!(
170 "../../tests/contracts/live/public-core/room-info/responses/success.json"
171 ))?
172 .into_payload()?;
173
174 assert_eq!(payload.room_id, 23174842);
175 assert_eq!(payload.live_status, 1);
176 Ok(())
177 }
178
179 fn local_probe_body(profile: &str) -> Option<serde_json::Value> {
180 let path =
181 format!("target/bpi-probe-runs/live/public-core/room-info/{profile}.response.json");
182 let bytes = std::fs::read(path).ok()?;
183 let value: serde_json::Value = serde_json::from_slice(&bytes).ok()?;
184 value
185 .get("response")
186 .and_then(|response| response.get("body"))
187 .cloned()
188 }
189
190 #[test]
191 fn live_room_info_model_matches_local_probe_outputs_when_available() -> BpiResult<()> {
192 for profile in ["anonymous", "normal", "vip"] {
193 if let Some(body) = local_probe_body(profile) {
194 let payload =
195 serde_json::from_value::<ApiEnvelope<RoomInfoData>>(body)?.into_payload()?;
196 assert_eq!(payload.room_id, 23174842);
197 }
198 }
199 Ok(())
200 }
201}