Skip to content

Commit cd679a2

Browse files
committed
test(gateway): cover public-base-url normalization, base_url, allowed_hosts
Adds unit tests for the public-gateway-base-url plumbing this PR introduces: - normalize_public_base_url: accepts an https origin (trailing slash trimmed), treats blank as None, and rejects non-https, embedded credentials, query / fragment, non-root paths, and unparseable input. - advertised_base_url / GatewayConfig::base_url: fall back to http://localhost when no public URL is set, otherwise advertise the trimmed origin. - GatewayConfig::allowed_hosts: always include loopback, add the public host (and host:port when present), and ignore an invalid public URL without panic. Signed-off-by: Mohammod Al Amin Ashik <maa.ashik00@gmail.com>
1 parent 0aab514 commit cd679a2

2 files changed

Lines changed: 104 additions & 0 deletions

File tree

  • apps/desktop/src-tauri/src/commands
  • crates/mcpmux-gateway/src/server

apps/desktop/src-tauri/src/commands/gateway.rs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1925,3 +1925,50 @@ pub struct PoolStatsResponse {
19251925
pub connected_instances: usize,
19261926
pub total_space_server_mappings: usize,
19271927
}
1928+
1929+
#[cfg(test)]
1930+
mod public_base_url_tests {
1931+
use super::{advertised_base_url, normalize_public_base_url};
1932+
1933+
#[test]
1934+
fn normalize_accepts_https_origin_and_trims_trailing_slash() {
1935+
assert_eq!(
1936+
normalize_public_base_url("https://mcp.example.com/").unwrap(),
1937+
Some("https://mcp.example.com".to_string())
1938+
);
1939+
assert_eq!(
1940+
normalize_public_base_url("https://mcp.example.com:8443").unwrap(),
1941+
Some("https://mcp.example.com:8443".to_string())
1942+
);
1943+
}
1944+
1945+
#[test]
1946+
fn normalize_treats_blank_as_none() {
1947+
assert_eq!(normalize_public_base_url("").unwrap(), None);
1948+
assert_eq!(normalize_public_base_url(" ").unwrap(), None);
1949+
}
1950+
1951+
#[test]
1952+
fn normalize_rejects_unsafe_or_non_origin_urls() {
1953+
// Non-https, credentials, query/fragment, non-root path, and garbage all rejected.
1954+
assert!(normalize_public_base_url("http://mcp.example.com").is_err());
1955+
assert!(normalize_public_base_url("https://user:pass@mcp.example.com").is_err());
1956+
assert!(normalize_public_base_url("https://mcp.example.com/?x=1").is_err());
1957+
assert!(normalize_public_base_url("https://mcp.example.com/#frag").is_err());
1958+
assert!(normalize_public_base_url("https://mcp.example.com/mcp").is_err());
1959+
assert!(normalize_public_base_url("not a url").is_err());
1960+
}
1961+
1962+
#[test]
1963+
fn advertised_base_url_falls_back_to_localhost() {
1964+
assert_eq!(advertised_base_url(None, 45818), "http://localhost:45818");
1965+
assert_eq!(
1966+
advertised_base_url(Some(" "), 45818),
1967+
"http://localhost:45818"
1968+
);
1969+
assert_eq!(
1970+
advertised_base_url(Some("https://mcp.example.com/"), 45818),
1971+
"https://mcp.example.com"
1972+
);
1973+
}
1974+
}

crates/mcpmux-gateway/src/server/mod.rs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -623,3 +623,60 @@ impl GatewayServerHandle {
623623
self.shutdown.is_some()
624624
}
625625
}
626+
627+
#[cfg(test)]
628+
mod config_tests {
629+
use super::*;
630+
631+
fn config_with(public: Option<&str>) -> GatewayConfig {
632+
GatewayConfig {
633+
public_base_url: public.map(str::to_string),
634+
..Default::default()
635+
}
636+
}
637+
638+
#[test]
639+
fn base_url_defaults_to_localhost() {
640+
let cfg = config_with(None);
641+
assert_eq!(cfg.base_url(), format!("http://localhost:{}", cfg.port));
642+
}
643+
644+
#[test]
645+
fn base_url_uses_public_origin_and_trims_trailing_slash() {
646+
assert_eq!(
647+
config_with(Some("https://mcp.example.com/")).base_url(),
648+
"https://mcp.example.com"
649+
);
650+
}
651+
652+
#[test]
653+
fn allowed_hosts_always_include_loopback() {
654+
let hosts = config_with(None).allowed_hosts();
655+
for h in ["localhost", "127.0.0.1", "::1"] {
656+
assert!(hosts.contains(&h.to_string()), "missing {h} in {hosts:?}");
657+
}
658+
}
659+
660+
#[test]
661+
fn allowed_hosts_include_public_host_and_optional_port() {
662+
let hosts = config_with(Some("https://mcp.example.com")).allowed_hosts();
663+
assert!(hosts.contains(&"mcp.example.com".to_string()), "{hosts:?}");
664+
665+
let hosts_port = config_with(Some("https://mcp.example.com:8443")).allowed_hosts();
666+
assert!(
667+
hosts_port.contains(&"mcp.example.com".to_string()),
668+
"{hosts_port:?}"
669+
);
670+
assert!(
671+
hosts_port.contains(&"mcp.example.com:8443".to_string()),
672+
"{hosts_port:?}"
673+
);
674+
}
675+
676+
#[test]
677+
fn allowed_hosts_ignores_invalid_public_url_without_panicking() {
678+
let hosts = config_with(Some("not a url")).allowed_hosts();
679+
assert!(hosts.contains(&"localhost".to_string()));
680+
assert!(!hosts.iter().any(|h| h.contains("not a url")));
681+
}
682+
}

0 commit comments

Comments
 (0)