208 lines
8.1 KiB
Rust
208 lines
8.1 KiB
Rust
// file: crates/ksp-onchain-transport-lib/unit_tests/settings.rs
|
|
// version: 1
|
|
|
|
fn non_zero(value: u32) -> std::num::NonZeroU32 {
|
|
return std::num::NonZeroU32::new(value).expect("test non-zero value must remain non-zero");
|
|
}
|
|
|
|
fn valid_settings(url_text: &str) -> super::HttpTransportSettings {
|
|
let url = super::HttpEndpointUrl::parse(url_text).expect("test URL must be valid");
|
|
let limits = super::HttpRoleLimits::new(
|
|
std::option::Option::Some(non_zero(10)),
|
|
std::option::Option::Some(non_zero(20)),
|
|
std::option::Option::Some(non_zero(4)),
|
|
std::option::Option::Some(std::time::Duration::from_millis(500)),
|
|
);
|
|
let role = super::HttpEndpointRoleSettings::new(super::HttpRoleName::new("default"), true, std::vec![super::HttpRequestKind::wildcard()], 100, limits);
|
|
let endpoint = super::HttpEndpointSettings::new(
|
|
"devnet_public",
|
|
true,
|
|
super::HttpProviderName::new("solana-public"),
|
|
super::HttpClusterName::new("devnet"),
|
|
url,
|
|
std::time::Duration::from_secs(5),
|
|
std::time::Duration::from_secs(15),
|
|
std::option::Option::Some(8),
|
|
std::vec![role],
|
|
);
|
|
return super::HttpTransportSettings::new(
|
|
std::vec![endpoint],
|
|
super::HttpRetrySettings::new(2, std::time::Duration::from_millis(100), std::time::Duration::from_secs(2)),
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn endpoint_url_accepts_http_and_https() {
|
|
assert!(super::HttpEndpointUrl::parse("https://api.devnet.solana.com").is_ok());
|
|
assert!(super::HttpEndpointUrl::parse("http://127.0.0.1:8899").is_ok());
|
|
}
|
|
|
|
#[test]
|
|
fn endpoint_url_rejects_non_http_schemes() {
|
|
let result = super::HttpEndpointUrl::parse("ws://api.devnet.solana.com");
|
|
let error = result.expect_err("WebSocket URL must not be accepted by HTTP settings");
|
|
assert_eq!(error.code(), crate::ERROR_CODE_INVALID_SETTINGS);
|
|
}
|
|
|
|
#[test]
|
|
fn endpoint_url_debug_redacts_secret_material() {
|
|
let url = super::HttpEndpointUrl::parse("https://provider.invalid/rpc?api-key=SECRET-CANARY").expect("test URL must parse");
|
|
let rendered = format!("{url:?}");
|
|
assert!(rendered.contains("<redacted>"));
|
|
assert!(!rendered.contains("SECRET-CANARY"));
|
|
assert!(!rendered.contains("provider.invalid"));
|
|
}
|
|
|
|
#[test]
|
|
fn valid_transport_settings_pass_validation() {
|
|
let settings = valid_settings("https://api.devnet.solana.com");
|
|
assert!(settings.validate().is_ok());
|
|
}
|
|
|
|
#[test]
|
|
fn transport_settings_debug_does_not_leak_endpoint_url() {
|
|
let settings = valid_settings("https://provider.invalid/rpc?api-key=SECRET-CANARY");
|
|
let rendered = format!("{settings:?}");
|
|
assert!(!rendered.contains("SECRET-CANARY"));
|
|
assert!(!rendered.contains("provider.invalid"));
|
|
assert!(rendered.contains("HttpEndpointUrl(<redacted>)"));
|
|
}
|
|
|
|
#[test]
|
|
fn transport_settings_require_one_enabled_endpoint() {
|
|
let url = super::HttpEndpointUrl::parse("https://api.devnet.solana.com").expect("test URL must parse");
|
|
let role = super::HttpEndpointRoleSettings::new(
|
|
super::HttpRoleName::new("default"),
|
|
true,
|
|
std::vec![super::HttpRequestKind::wildcard()],
|
|
100,
|
|
super::HttpRoleLimits::new(std::option::Option::None, std::option::Option::None, std::option::Option::None, std::option::Option::None),
|
|
);
|
|
let endpoint = super::HttpEndpointSettings::new(
|
|
"disabled",
|
|
false,
|
|
super::HttpProviderName::new("provider"),
|
|
super::HttpClusterName::new("devnet"),
|
|
url,
|
|
std::time::Duration::from_secs(1),
|
|
std::time::Duration::from_secs(1),
|
|
std::option::Option::None,
|
|
std::vec![role],
|
|
);
|
|
let settings = super::HttpTransportSettings::new(
|
|
std::vec![endpoint],
|
|
super::HttpRetrySettings::new(1, std::time::Duration::from_millis(1), std::time::Duration::from_millis(2)),
|
|
);
|
|
let error = settings.validate().expect_err("all-disabled settings must fail");
|
|
assert_eq!(error.code(), crate::ERROR_CODE_INVALID_SETTINGS);
|
|
}
|
|
|
|
#[test]
|
|
fn transport_settings_reject_duplicate_endpoint_names() {
|
|
let first = valid_settings("https://one.invalid");
|
|
let second = valid_settings("https://two.invalid");
|
|
let settings = super::HttpTransportSettings::new(std::vec![first.endpoints()[0].clone(), second.endpoints()[0].clone()], first.retry().clone());
|
|
let error = settings.validate().expect_err("duplicate endpoint names must fail");
|
|
assert_eq!(error.code(), crate::ERROR_CODE_INVALID_SETTINGS);
|
|
}
|
|
|
|
#[test]
|
|
fn transport_settings_reject_duplicate_roles() {
|
|
let base = valid_settings("https://api.devnet.solana.com");
|
|
let endpoint = &base.endpoints()[0];
|
|
let duplicated_endpoint = super::HttpEndpointSettings::new(
|
|
endpoint.name(),
|
|
true,
|
|
endpoint.provider().clone(),
|
|
endpoint.cluster().clone(),
|
|
endpoint.url().clone(),
|
|
endpoint.connect_timeout(),
|
|
endpoint.request_timeout(),
|
|
endpoint.max_idle_connections_per_host(),
|
|
std::vec![endpoint.roles()[0].clone(), endpoint.roles()[0].clone()],
|
|
);
|
|
let settings = super::HttpTransportSettings::new(std::vec![duplicated_endpoint], base.retry().clone());
|
|
assert!(settings.validate().is_err());
|
|
}
|
|
|
|
#[test]
|
|
fn transport_settings_reject_wildcard_mixed_with_specific_kind() {
|
|
let base = valid_settings("https://api.devnet.solana.com");
|
|
let endpoint = &base.endpoints()[0];
|
|
let role = super::HttpEndpointRoleSettings::new(
|
|
super::HttpRoleName::new("default"),
|
|
true,
|
|
std::vec![super::HttpRequestKind::wildcard(), super::HttpRequestKind::new("get_balance")],
|
|
100,
|
|
endpoint.roles()[0].limits().clone(),
|
|
);
|
|
let modified_endpoint = super::HttpEndpointSettings::new(
|
|
endpoint.name(),
|
|
true,
|
|
endpoint.provider().clone(),
|
|
endpoint.cluster().clone(),
|
|
endpoint.url().clone(),
|
|
endpoint.connect_timeout(),
|
|
endpoint.request_timeout(),
|
|
endpoint.max_idle_connections_per_host(),
|
|
std::vec![role],
|
|
);
|
|
let settings = super::HttpTransportSettings::new(std::vec![modified_endpoint], base.retry().clone());
|
|
assert!(settings.validate().is_err());
|
|
}
|
|
|
|
#[test]
|
|
fn transport_settings_reject_burst_without_rps() {
|
|
let base = valid_settings("https://api.devnet.solana.com");
|
|
let endpoint = &base.endpoints()[0];
|
|
let role = super::HttpEndpointRoleSettings::new(
|
|
super::HttpRoleName::new("default"),
|
|
true,
|
|
std::vec![super::HttpRequestKind::wildcard()],
|
|
100,
|
|
super::HttpRoleLimits::new(std::option::Option::None, std::option::Option::Some(non_zero(2)), std::option::Option::None, std::option::Option::None),
|
|
);
|
|
let modified_endpoint = super::HttpEndpointSettings::new(
|
|
endpoint.name(),
|
|
true,
|
|
endpoint.provider().clone(),
|
|
endpoint.cluster().clone(),
|
|
endpoint.url().clone(),
|
|
endpoint.connect_timeout(),
|
|
endpoint.request_timeout(),
|
|
endpoint.max_idle_connections_per_host(),
|
|
std::vec![role],
|
|
);
|
|
let settings = super::HttpTransportSettings::new(std::vec![modified_endpoint], base.retry().clone());
|
|
assert!(settings.validate().is_err());
|
|
}
|
|
|
|
#[test]
|
|
fn transport_settings_reject_reversed_retry_backoff() {
|
|
let base = valid_settings("https://api.devnet.solana.com");
|
|
let settings = super::HttpTransportSettings::new(
|
|
base.endpoints().to_vec(),
|
|
super::HttpRetrySettings::new(2, std::time::Duration::from_secs(2), std::time::Duration::from_secs(1)),
|
|
);
|
|
assert!(settings.validate().is_err());
|
|
}
|
|
|
|
#[test]
|
|
fn transport_settings_reject_zero_request_timeout() {
|
|
let base = valid_settings("https://api.devnet.solana.com");
|
|
let endpoint = &base.endpoints()[0];
|
|
let modified_endpoint = super::HttpEndpointSettings::new(
|
|
endpoint.name(),
|
|
true,
|
|
endpoint.provider().clone(),
|
|
endpoint.cluster().clone(),
|
|
endpoint.url().clone(),
|
|
endpoint.connect_timeout(),
|
|
std::time::Duration::ZERO,
|
|
endpoint.max_idle_connections_per_host(),
|
|
endpoint.roles().to_vec(),
|
|
);
|
|
let settings = super::HttpTransportSettings::new(std::vec![modified_endpoint], base.retry().clone());
|
|
assert!(settings.validate().is_err());
|
|
}
|