Skip to content

Commit fdb5d34

Browse files
authored
test(ib-fabric): expand fabric utility test enumerations (#2580)
1 parent d4c0966 commit fdb5d34

8 files changed

Lines changed: 534 additions & 198 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/ib-fabric/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ tracing = { workspace = true }
4848
url = { workspace = true }
4949

5050
[dev-dependencies]
51+
carbide-test-support = { path = "../test-support" }
5152
figment = { features = ["env", "test", "toml"], workspace = true }
5253

5354
[lints]

crates/ib-fabric/src/errors.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,46 @@ impl IbError {
4949
}
5050

5151
pub type IbResult<T> = Result<T, IbError>;
52+
53+
#[cfg(test)]
54+
mod tests {
55+
use carbide_test_support::value_scenarios;
56+
57+
use super::*;
58+
59+
#[derive(Clone, Copy, Debug)]
60+
enum ErrorCase {
61+
Fabric,
62+
NotFound,
63+
InvalidArgument,
64+
NotImplemented,
65+
Internal,
66+
}
67+
68+
fn error_for(case: ErrorCase) -> IbError {
69+
match case {
70+
ErrorCase::Fabric => IbError::IBFabricError("ufm failed".to_string()),
71+
ErrorCase::NotFound => IbError::NotFoundError {
72+
kind: "Machine",
73+
id: "machine-1".to_string(),
74+
},
75+
ErrorCase::InvalidArgument => IbError::InvalidArgument("bad pkey".to_string()),
76+
ErrorCase::NotImplemented => IbError::NotImplemented,
77+
ErrorCase::Internal => IbError::internal("unexpected state".to_string()),
78+
}
79+
}
80+
81+
#[test]
82+
fn formats_ib_errors() {
83+
value_scenarios!(
84+
run = |case| error_for(case).to_string();
85+
"user facing errors" {
86+
ErrorCase::Fabric => "Failed to call IBFabricManager: ufm failed".to_string(),
87+
ErrorCase::NotFound => "Machine not found: machine-1".to_string(),
88+
ErrorCase::InvalidArgument => "Argument is invalid: bad pkey".to_string(),
89+
ErrorCase::NotImplemented => "The function is not implemented".to_string(),
90+
ErrorCase::Internal => "Internal error: unexpected state".to_string(),
91+
}
92+
);
93+
}
94+
}

crates/ib-fabric/src/ib/disable.rs

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,3 +83,127 @@ impl IBFabric for DisableIBFabric {
8383
Err(IbError::IBFabricError("ib fabric is disabled".to_string()))
8484
}
8585
}
86+
87+
#[cfg(test)]
88+
mod tests {
89+
use carbide_test_support::Outcome::*;
90+
use carbide_test_support::{Case, check_cases_async};
91+
92+
use super::*;
93+
94+
#[derive(Clone, Copy, Debug)]
95+
enum DisabledOperation {
96+
GetFabricConfig,
97+
GetNetwork,
98+
GetNetworks,
99+
BindPorts,
100+
UpdateQos,
101+
FindPort,
102+
UnbindPorts,
103+
Versions,
104+
RawGet,
105+
}
106+
107+
fn ib_network() -> IBNetwork {
108+
IBNetwork {
109+
name: "tenant".to_string(),
110+
pkey: 1,
111+
ipoib: true,
112+
qos_conf: None,
113+
associated_guids: None,
114+
membership: None,
115+
}
116+
}
117+
118+
fn qos_conf() -> IBQosConf {
119+
IBQosConf {
120+
mtu: Default::default(),
121+
service_level: Default::default(),
122+
rate_limit: Default::default(),
123+
}
124+
}
125+
126+
async fn run_disabled_operation(operation: DisabledOperation) -> Result<(), String> {
127+
let fabric = DisableIBFabric {};
128+
match operation {
129+
DisabledOperation::GetFabricConfig => fabric.get_fabric_config().await.map(|_| ()),
130+
DisabledOperation::GetNetwork => fabric
131+
.get_ib_network(1, GetPartitionOptions::default())
132+
.await
133+
.map(|_| ()),
134+
DisabledOperation::GetNetworks => fabric
135+
.get_ib_networks(GetPartitionOptions::default())
136+
.await
137+
.map(|_| ()),
138+
DisabledOperation::BindPorts => {
139+
fabric
140+
.bind_ib_ports(ib_network(), vec!["guid-1".to_string()])
141+
.await
142+
}
143+
DisabledOperation::UpdateQos => fabric.update_partition_qos_conf(1, &qos_conf()).await,
144+
DisabledOperation::FindPort => fabric.find_ib_port(None).await.map(|_| ()),
145+
DisabledOperation::UnbindPorts => {
146+
fabric.unbind_ib_ports(1, vec!["guid-1".to_string()]).await
147+
}
148+
DisabledOperation::Versions => fabric.versions().await.map(|_| ()),
149+
DisabledOperation::RawGet => fabric.raw_get("/app/ufm_version").await.map(|_| ()),
150+
}
151+
.map_err(|error| error.to_string())
152+
}
153+
154+
#[tokio::test]
155+
async fn disabled_fabric_rejects_operations() {
156+
let disabled = "Failed to call IBFabricManager: ib fabric is disabled".to_string();
157+
check_cases_async(
158+
[
159+
Case {
160+
scenario: "get fabric config",
161+
input: DisabledOperation::GetFabricConfig,
162+
expect: FailsWith(disabled.clone()),
163+
},
164+
Case {
165+
scenario: "get network",
166+
input: DisabledOperation::GetNetwork,
167+
expect: FailsWith(disabled.clone()),
168+
},
169+
Case {
170+
scenario: "get networks",
171+
input: DisabledOperation::GetNetworks,
172+
expect: FailsWith(disabled.clone()),
173+
},
174+
Case {
175+
scenario: "bind ports",
176+
input: DisabledOperation::BindPorts,
177+
expect: FailsWith(disabled.clone()),
178+
},
179+
Case {
180+
scenario: "update qos",
181+
input: DisabledOperation::UpdateQos,
182+
expect: FailsWith(disabled.clone()),
183+
},
184+
Case {
185+
scenario: "find port",
186+
input: DisabledOperation::FindPort,
187+
expect: FailsWith(disabled.clone()),
188+
},
189+
Case {
190+
scenario: "unbind ports",
191+
input: DisabledOperation::UnbindPorts,
192+
expect: FailsWith(disabled.clone()),
193+
},
194+
Case {
195+
scenario: "versions",
196+
input: DisabledOperation::Versions,
197+
expect: FailsWith(disabled.clone()),
198+
},
199+
Case {
200+
scenario: "raw get",
201+
input: DisabledOperation::RawGet,
202+
expect: FailsWith(disabled),
203+
},
204+
],
205+
run_disabled_operation,
206+
)
207+
.await;
208+
}
209+
}

crates/ib-fabric/src/ib/mod.rs

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,3 +176,161 @@ impl IBFabricManager for IBFabricManagerImpl {
176176
}
177177
}
178178
}
179+
180+
#[cfg(test)]
181+
mod tests {
182+
use async_trait::async_trait;
183+
use carbide_secrets::SecretsError;
184+
use carbide_test_support::Outcome::*;
185+
use carbide_test_support::scenarios;
186+
187+
use super::*;
188+
189+
struct NoopCredentialReader;
190+
191+
#[async_trait]
192+
impl CredentialReader for NoopCredentialReader {
193+
async fn get_credentials(
194+
&self,
195+
_key: &CredentialKey,
196+
) -> Result<Option<Credentials>, SecretsError> {
197+
Ok(None)
198+
}
199+
}
200+
201+
#[derive(Clone, Copy, Debug)]
202+
enum ManagerCase {
203+
ValidDisabled,
204+
EmptyEndpointList,
205+
MultipleEndpoints,
206+
InvalidEndpoint,
207+
}
208+
209+
#[derive(Debug, PartialEq)]
210+
struct ManagerConfigSummary {
211+
endpoint_count: usize,
212+
manager_type: &'static str,
213+
max_partition_per_tenant: i32,
214+
allow_insecure_fabric_configuration: bool,
215+
fabric_manager_run_interval_secs: u64,
216+
}
217+
218+
fn credential_reader() -> Arc<dyn CredentialReader> {
219+
Arc::new(NoopCredentialReader)
220+
}
221+
222+
fn manager_type_name(manager_type: IBFabricManagerType) -> &'static str {
223+
match manager_type {
224+
IBFabricManagerType::Disable => "disable",
225+
#[cfg(feature = "test-support")]
226+
IBFabricManagerType::Mock => "mock",
227+
IBFabricManagerType::Rest => "rest",
228+
}
229+
}
230+
231+
fn summarize_manager_config(config: IBFabricManagerConfig) -> ManagerConfigSummary {
232+
ManagerConfigSummary {
233+
endpoint_count: config.endpoints.len(),
234+
manager_type: manager_type_name(config.manager_type),
235+
max_partition_per_tenant: config.max_partition_per_tenant,
236+
allow_insecure_fabric_configuration: config.allow_insecure_fabric_configuration,
237+
fabric_manager_run_interval_secs: config.fabric_manager_run_interval.as_secs(),
238+
}
239+
}
240+
241+
fn config_for_case(case: ManagerCase) -> IBFabricManagerConfig {
242+
let mut config = IBFabricManagerConfig::default();
243+
match case {
244+
ManagerCase::ValidDisabled => {
245+
config.endpoints.insert(
246+
"fabric-a".to_string(),
247+
vec!["https://127.0.0.1:443".to_string()],
248+
);
249+
}
250+
ManagerCase::EmptyEndpointList => {
251+
config.endpoints.insert("fabric-a".to_string(), vec![]);
252+
}
253+
ManagerCase::MultipleEndpoints => {
254+
config.endpoints.insert(
255+
"fabric-a".to_string(),
256+
vec![
257+
"https://127.0.0.1:443".to_string(),
258+
"https://127.0.0.2:443".to_string(),
259+
],
260+
);
261+
}
262+
ManagerCase::InvalidEndpoint => {
263+
config
264+
.endpoints
265+
.insert("fabric-a".to_string(), vec!["not a uri".to_string()]);
266+
}
267+
}
268+
config
269+
}
270+
271+
fn create_manager(case: ManagerCase) -> Result<ManagerConfigSummary, &'static str> {
272+
create_ib_fabric_manager(credential_reader(), config_for_case(case))
273+
.map(|manager| summarize_manager_config(manager.get_config()))
274+
.map_err(manager_error_kind)
275+
}
276+
277+
fn manager_error_kind(error: eyre::Report) -> &'static str {
278+
let error = error.to_string();
279+
if error.contains("Exactly 1 endpoint") {
280+
"endpoint-count"
281+
} else if error.contains("not a valid HTTP(S) URI") {
282+
"invalid-uri"
283+
} else {
284+
"unknown"
285+
}
286+
}
287+
288+
#[test]
289+
fn default_manager_config_uses_disabled_defaults() {
290+
assert_eq!(
291+
summarize_manager_config(IBFabricManagerConfig::default()),
292+
ManagerConfigSummary {
293+
endpoint_count: 0,
294+
manager_type: "disable",
295+
max_partition_per_tenant: config::IBFabricConfig::default_max_partition_per_tenant(
296+
),
297+
allow_insecure_fabric_configuration: false,
298+
fabric_manager_run_interval_secs: 60,
299+
}
300+
);
301+
}
302+
303+
#[test]
304+
fn validates_manager_endpoints() {
305+
scenarios!(create_manager:
306+
"valid config" {
307+
ManagerCase::ValidDisabled => Yields(ManagerConfigSummary {
308+
endpoint_count: 1,
309+
manager_type: "disable",
310+
max_partition_per_tenant: config::IBFabricConfig::default_max_partition_per_tenant(),
311+
allow_insecure_fabric_configuration: false,
312+
fabric_manager_run_interval_secs: 60,
313+
}),
314+
}
315+
316+
"invalid endpoints" {
317+
ManagerCase::EmptyEndpointList => FailsWith("endpoint-count"),
318+
ManagerCase::MultipleEndpoints => FailsWith("endpoint-count"),
319+
ManagerCase::InvalidEndpoint => FailsWith("invalid-uri"),
320+
}
321+
);
322+
}
323+
324+
#[tokio::test]
325+
async fn disabled_manager_returns_disabled_client() {
326+
let manager =
327+
create_ib_fabric_manager(credential_reader(), IBFabricManagerConfig::default())
328+
.unwrap();
329+
let client = manager.new_client("fabric-a").await.unwrap();
330+
331+
assert_eq!(
332+
client.get_fabric_config().await.unwrap_err().to_string(),
333+
"Failed to call IBFabricManager: ib fabric is disabled"
334+
);
335+
}
336+
}

0 commit comments

Comments
 (0)