Skip to content

Commit a6741cf

Browse files
committed
step: [#220] implement HttpApiSection DTO
1 parent 701cc28 commit a6741cf

File tree

3 files changed

+98
-1
lines changed

3 files changed

+98
-1
lines changed

docs/implementation-plans/issue-220-test-command-architecture.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ Phase 0: Architecture Fix
4343
[x] Step 0.1: Create tracker DTO module structure
4444
[x] Step 0.2: Implement UdpTrackerSection DTO
4545
[x] Step 0.3: Implement HttpTrackerSection DTO
46-
[ ] Step 0.4: Implement HttpApiSection DTO
46+
[x] Step 0.4: Implement HttpApiSection DTO
4747
[ ] Step 0.5: Implement TrackerCoreSection DTO
4848
[ ] Step 0.6: Implement TrackerSection DTO
4949
[ ] Step 0.7: Update domain types to use SocketAddr
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
use std::net::SocketAddr;
2+
3+
use serde::{Deserialize, Serialize};
4+
5+
use crate::application::command_handlers::create::config::errors::CreateConfigError;
6+
use crate::domain::tracker::HttpApiConfig;
7+
8+
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
9+
pub struct HttpApiSection {
10+
pub bind_address: String,
11+
pub admin_token: String,
12+
}
13+
14+
impl HttpApiSection {
15+
/// Converts this DTO to a domain `HttpApiConfig`
16+
///
17+
/// # Errors
18+
///
19+
/// Returns `CreateConfigError::InvalidBindAddress` if the bind address cannot be parsed as a valid IP:PORT combination.
20+
pub fn to_http_api_config(&self) -> Result<HttpApiConfig, CreateConfigError> {
21+
// Validate that the bind address can be parsed as SocketAddr
22+
let _bind_address = self.bind_address.parse::<SocketAddr>().map_err(|e| {
23+
CreateConfigError::InvalidBindAddress {
24+
address: self.bind_address.clone(),
25+
source: e,
26+
}
27+
})?;
28+
29+
// For now, keep as String since domain type still uses String
30+
// This will be updated in Step 0.7 when we enhance domain types
31+
Ok(HttpApiConfig {
32+
bind_address: self.bind_address.clone(),
33+
admin_token: self.admin_token.clone(),
34+
})
35+
}
36+
}
37+
38+
#[cfg(test)]
39+
mod tests {
40+
use super::*;
41+
42+
#[test]
43+
fn it_should_convert_valid_config_to_http_api_config() {
44+
let section = HttpApiSection {
45+
bind_address: "0.0.0.0:1212".to_string(),
46+
admin_token: "MyAccessToken".to_string(),
47+
};
48+
49+
let result = section.to_http_api_config();
50+
assert!(result.is_ok());
51+
52+
let config = result.unwrap();
53+
assert_eq!(config.bind_address, "0.0.0.0:1212");
54+
assert_eq!(config.admin_token, "MyAccessToken");
55+
}
56+
57+
#[test]
58+
fn it_should_fail_for_invalid_bind_address() {
59+
let section = HttpApiSection {
60+
bind_address: "invalid-address".to_string(),
61+
admin_token: "token".to_string(),
62+
};
63+
64+
let result = section.to_http_api_config();
65+
assert!(result.is_err());
66+
67+
if let Err(CreateConfigError::InvalidBindAddress { address, .. }) = result {
68+
assert_eq!(address, "invalid-address");
69+
} else {
70+
panic!("Expected InvalidBindAddress error");
71+
}
72+
}
73+
74+
#[test]
75+
fn it_should_be_serializable() {
76+
let section = HttpApiSection {
77+
bind_address: "0.0.0.0:1212".to_string(),
78+
admin_token: "MyAccessToken".to_string(),
79+
};
80+
81+
let json = serde_json::to_string(&section).unwrap();
82+
assert!(json.contains("bind_address"));
83+
assert!(json.contains("0.0.0.0:1212"));
84+
assert!(json.contains("admin_token"));
85+
assert!(json.contains("MyAccessToken"));
86+
}
87+
88+
#[test]
89+
fn it_should_be_deserializable() {
90+
let json = r#"{"bind_address":"0.0.0.0:1212","admin_token":"MyAccessToken"}"#;
91+
let section: HttpApiSection = serde_json::from_str(json).unwrap();
92+
assert_eq!(section.bind_address, "0.0.0.0:1212");
93+
assert_eq!(section.admin_token, "MyAccessToken");
94+
}
95+
}

src/application/command_handlers/create/config/tracker/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44
//! environment creation. These types use raw primitives (String) for
55
//! JSON deserialization and convert to rich domain types (`SocketAddr`).
66
7+
mod http_api_section;
78
mod http_tracker_section;
89
mod udp_tracker_section;
910

11+
pub use http_api_section::HttpApiSection;
1012
pub use http_tracker_section::HttpTrackerSection;
1113
pub use udp_tracker_section::UdpTrackerSection;

0 commit comments

Comments
 (0)