Skip to content

Commit cb07b44

Browse files
authored
test(api-db,health): consolidate duplicated CRUD and IP-candidate tests (#2714)
1 parent 3ad290d commit cb07b44

9 files changed

Lines changed: 157 additions & 178 deletions

File tree

crates/api-db/src/expected_machine/tests.rs

Lines changed: 8 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -172,29 +172,15 @@ async fn test_update_bmc_credentials(pool: sqlx::PgPool) -> Result<(), Box<dyn s
172172
#[crate::sqlx_test]
173173
async fn test_delete(pool: sqlx::PgPool) -> () {
174174
create_fixture_expected_machines(&pool).await;
175-
let mut txn = pool
176-
.begin()
177-
.await
178-
.expect("unable to create transaction on database pool");
179-
let machine = get_expected_machine_1(&mut txn)
180-
.await
181-
.expect("Expected machine not found");
182-
183-
assert_eq!(machine.data.serial_number, "VVG121GG");
184-
185-
db::expected_machine::delete_by_mac(&mut txn, machine.bmc_mac_address)
186-
.await
187-
.expect("Error deleting expected_machine");
188-
189-
txn.commit().await.expect("Failed to commit transaction");
190-
let mut txn = pool
191-
.begin()
192-
.await
193-
.expect("unable to create transaction on database pool");
194-
195-
get_expected_machine_1(&mut txn).await;
175+
let mac = "0a:0b:0c:0d:0e:0f".parse().unwrap();
196176

197-
assert!(get_expected_machine_1(&mut txn).await.is_none())
177+
crate::test_support::expected_host::assert_delete_by_mac_removes_row(
178+
&pool,
179+
mac,
180+
async |txn, mac| db::expected_machine::delete_by_mac(txn, mac).await,
181+
async |txn, mac| db::expected_machine::find_by_bmc_mac_address(txn, mac).await,
182+
)
183+
.await;
198184
}
199185

200186
#[crate::sqlx_test]

crates/api-db/src/expected_power_shelf/tests.rs

Lines changed: 8 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -144,29 +144,16 @@ async fn test_update_bmc_credentials(pool: sqlx::PgPool) -> Result<(), Box<dyn s
144144

145145
#[crate::sqlx_test]
146146
async fn test_delete(pool: sqlx::PgPool) -> () {
147-
let mut txn = pool
148-
.begin()
149-
.await
150-
.expect("unable to create transaction on database pool");
147+
let mut txn = pool.begin().await.unwrap();
151148
let shelves = create_expected_power_shelves(&mut txn).await;
152-
let power_shelf = &shelves[0];
153-
154-
assert_eq!(power_shelf.serial_number, "PS-SN-001");
155-
156-
db::expected_power_shelf::delete_by_mac(&mut txn, power_shelf.bmc_mac_address)
157-
.await
158-
.expect("Error deleting expected_power_shelf");
159-
149+
let mac = shelves[0].bmc_mac_address;
160150
txn.commit().await.expect("Failed to commit transaction");
161-
let mut txn = pool
162-
.begin()
163-
.await
164-
.expect("unable to create transaction on database pool");
165151

166-
assert!(
167-
db::expected_power_shelf::find_by_bmc_mac_address(&mut txn, shelves[0].bmc_mac_address)
168-
.await
169-
.unwrap()
170-
.is_none()
152+
crate::test_support::expected_host::assert_delete_by_mac_removes_row(
153+
&pool,
154+
mac,
155+
async |txn, mac| db::expected_power_shelf::delete_by_mac(txn, mac).await,
156+
async |txn, mac| db::expected_power_shelf::find_by_bmc_mac_address(txn, mac).await,
171157
)
158+
.await;
172159
}

crates/api-db/src/expected_rack/tests.rs

Lines changed: 0 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -81,23 +81,6 @@ async fn seed_expected_racks(txn: &mut sqlx::PgConnection) -> Vec<RackId> {
8181
ids
8282
}
8383

84-
#[crate::sqlx_test]
85-
async fn test_db_find_by_rack_id(pool: sqlx::PgPool) -> Result<(), Box<dyn std::error::Error>> {
86-
let mut txn = pool.begin().await?;
87-
let ids = seed_expected_racks(&mut txn).await;
88-
89-
let expected_rack = find_by_rack_id(&mut txn, &ids[0])
90-
.await?
91-
.expect("Expected rack not found");
92-
93-
assert_eq!(expected_rack.rack_id, ids[0]);
94-
assert_eq!(expected_rack.rack_profile_id.as_str(), "NVL72");
95-
assert_eq!(expected_rack.metadata.name, "rack-1");
96-
assert_eq!(expected_rack.metadata.description, "Test rack 1");
97-
98-
Ok(())
99-
}
100-
10184
#[crate::sqlx_test]
10285
async fn test_db_find_all(pool: sqlx::PgPool) -> Result<(), Box<dyn std::error::Error>> {
10386
let mut txn = pool.begin().await?;
@@ -117,45 +100,6 @@ async fn test_db_find_nonexistent(pool: sqlx::PgPool) -> Result<(), Box<dyn std:
117100
Ok(())
118101
}
119102

120-
#[crate::sqlx_test]
121-
async fn test_db_create_and_find(pool: sqlx::PgPool) -> Result<(), Box<dyn std::error::Error>> {
122-
let mut txn = pool.begin().await?;
123-
124-
let rack_id = new_rack_id();
125-
let metadata = Metadata {
126-
name: "test-rack".to_string(),
127-
description: "A test rack".to_string(),
128-
labels: [("env".to_string(), "test".to_string())]
129-
.into_iter()
130-
.collect(),
131-
};
132-
133-
let created = create(
134-
&mut txn,
135-
&ExpectedRack {
136-
rack_id: rack_id.clone(),
137-
rack_profile_id: RackProfileId::new("NVL72"),
138-
139-
metadata,
140-
},
141-
)
142-
.await?;
143-
144-
assert_eq!(created.rack_id, rack_id);
145-
assert_eq!(created.rack_profile_id.as_str(), "NVL72");
146-
assert_eq!(created.metadata.name, "test-rack");
147-
assert_eq!(created.metadata.labels.get("env").unwrap(), "test");
148-
149-
let found = find_by_rack_id(&mut txn, &rack_id)
150-
.await?
151-
.expect("Should find the rack we just created");
152-
153-
assert_eq!(found.rack_id, rack_id);
154-
assert_eq!(found.rack_profile_id.as_str(), "NVL72");
155-
156-
Ok(())
157-
}
158-
159103
#[crate::sqlx_test]
160104
async fn test_db_duplicate_create(pool: sqlx::PgPool) -> Result<(), Box<dyn std::error::Error>> {
161105
let mut txn = pool.begin().await?;

crates/api-db/src/expected_switch/tests.rs

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -164,25 +164,14 @@ async fn test_update_bmc_credentials(pool: sqlx::PgPool) -> Result<(), Box<dyn s
164164
async fn test_delete(pool: sqlx::PgPool) -> () {
165165
let mut txn = pool.begin().await.unwrap();
166166
let switches = create_expected_switches(&mut txn).await;
167-
168-
let switch = &switches[0];
169-
170-
assert_eq!(switch.serial_number, "SW-SN-001");
171-
172-
db::expected_switch::delete_by_mac(&mut txn, switch.bmc_mac_address)
173-
.await
174-
.expect("Error deleting expected_switch");
175-
167+
let mac = switches[0].bmc_mac_address;
176168
txn.commit().await.expect("Failed to commit transaction");
177-
let mut txn = pool
178-
.begin()
179-
.await
180-
.expect("unable to create transaction on database pool");
181169

182-
assert!(
183-
db::expected_switch::find_by_bmc_mac_address(&mut txn, switches[0].bmc_mac_address)
184-
.await
185-
.unwrap()
186-
.is_none()
170+
crate::test_support::expected_host::assert_delete_by_mac_removes_row(
171+
&pool,
172+
mac,
173+
async |txn, mac| db::expected_switch::delete_by_mac(txn, mac).await,
174+
async |txn, mac| db::expected_switch::find_by_bmc_mac_address(txn, mac).await,
187175
)
176+
.await;
188177
}

crates/api-db/src/ip_allocator.rs

Lines changed: 50 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -575,6 +575,9 @@ fn next_available_prefix(
575575

576576
#[cfg(test)]
577577
mod tests {
578+
use carbide_test_support::Outcome::*;
579+
use carbide_test_support::{Case, check_cases};
580+
578581
use super::*;
579582

580583
#[test]
@@ -911,14 +914,55 @@ mod tests {
911914

912915
#[test]
913916
fn test_v4_candidate() {
917+
// A /30 from 192.168.1.0/24 always lands on 192.168.1.8/30 once .0 and the
918+
// .4/30 block are taken — and stays there however the already-allocated set
919+
// is spelled: with a bare pair, with a duplicate /32, or with a smaller /31
920+
// already covered by the .4/30. The candidate search collapses all three.
921+
struct AllocatedSet {
922+
scenario: &'static str,
923+
allocated_cidrs: Vec<IpNetwork>,
924+
}
925+
914926
let cidr = "192.168.1.0/24".parse().unwrap();
915927
let prefix_length = 30;
916-
let allocated_cidrs = vec![
917-
"192.168.1.0/32".parse().unwrap(),
918-
"192.168.1.4/30".parse().unwrap(),
919-
];
920-
let next_prefix = next_available_prefix(cidr, prefix_length, allocated_cidrs).unwrap();
921-
assert!(next_prefix.is_some_and(|prefix| prefix.to_string() == "192.168.1.8/30"));
928+
929+
check_cases(
930+
[
931+
AllocatedSet {
932+
scenario: "distinct networks",
933+
allocated_cidrs: vec![
934+
"192.168.1.0/32".parse().unwrap(),
935+
"192.168.1.4/30".parse().unwrap(),
936+
],
937+
},
938+
AllocatedSet {
939+
scenario: "duplicate /32",
940+
allocated_cidrs: vec![
941+
"192.168.1.0/32".parse().unwrap(),
942+
"192.168.1.0/32".parse().unwrap(),
943+
"192.168.1.4/30".parse().unwrap(),
944+
],
945+
},
946+
AllocatedSet {
947+
scenario: "covered /31 inside the /30",
948+
allocated_cidrs: vec![
949+
"192.168.1.0/32".parse().unwrap(),
950+
"192.168.1.0/32".parse().unwrap(),
951+
"192.168.1.4/31".parse().unwrap(),
952+
"192.168.1.4/30".parse().unwrap(),
953+
],
954+
},
955+
]
956+
.map(|set| Case {
957+
scenario: set.scenario,
958+
input: set.allocated_cidrs,
959+
expect: Yields(Some("192.168.1.8/30".parse().unwrap())),
960+
}),
961+
|allocated_cidrs| {
962+
next_available_prefix(cidr, prefix_length, allocated_cidrs)
963+
.map_err(|e| e.to_string())
964+
},
965+
);
922966
}
923967

924968
#[test]
@@ -936,33 +980,6 @@ mod tests {
936980
assert_eq!(next_prefix.ip().to_string(), "192.168.1.1");
937981
}
938982

939-
#[test]
940-
fn test_v4_candidate_with_duplicate() {
941-
let cidr = "192.168.1.0/24".parse().unwrap();
942-
let prefix_length = 30;
943-
let allocated_cidrs = vec![
944-
"192.168.1.0/32".parse().unwrap(),
945-
"192.168.1.0/32".parse().unwrap(),
946-
"192.168.1.4/30".parse().unwrap(),
947-
];
948-
let next_prefix = next_available_prefix(cidr, prefix_length, allocated_cidrs).unwrap();
949-
assert!(next_prefix.is_some_and(|prefix| prefix.to_string() == "192.168.1.8/30"));
950-
}
951-
952-
#[test]
953-
fn test_v4_candidate_with_covered() {
954-
let cidr = "192.168.1.0/24".parse().unwrap();
955-
let prefix_length = 30;
956-
let allocated_cidrs = vec![
957-
"192.168.1.0/32".parse().unwrap(),
958-
"192.168.1.0/32".parse().unwrap(),
959-
"192.168.1.4/31".parse().unwrap(),
960-
"192.168.1.4/30".parse().unwrap(),
961-
];
962-
let next_prefix = next_available_prefix(cidr, prefix_length, allocated_cidrs).unwrap();
963-
assert!(next_prefix.is_some_and(|prefix| prefix.to_string() == "192.168.1.8/30"));
964-
}
965-
966983
#[test]
967984
fn test_v6_candidate() {
968985
// Ode to the 2012 Aston Martin DB9.

crates/api-db/src/route_servers.rs

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -402,32 +402,31 @@ mod tests {
402402
Ok(())
403403
}
404404

405-
// test_sync_with_duplicate_addresses_in_input tests to
406-
// make sure sync handles gracefully when the input array
407-
// contains duplicate addresses.
405+
// test_sync_with_duplicate_addresses_in_input pins the contract that
406+
// `replace` does not de-duplicate its input: addresses are handed straight
407+
// to a single batched INSERT, so a repeated address trips the
408+
// `address inet NOT NULL UNIQUE` constraint and the whole call is rejected.
409+
// Callers are responsible for passing a unique address set.
408410
#[crate::sqlx_test]
409411
async fn test_sync_with_duplicate_addresses_in_input(
410412
pool: sqlx::PgPool,
411413
) -> Result<(), Box<dyn std::error::Error>> {
412414
let mut txn = pool.begin().await?;
413415

414-
// Create input with duplicates
416+
// Input carrying the same address twice.
415417
let mut addresses = test_ips(3);
416-
addresses.push(addresses[0]); // Add duplicate
418+
addresses.push(addresses[0]);
417419

418-
// Should handle gracefully (database constraint will handle uniqueness)
419420
let result = super::replace(&mut txn, &addresses, RouteServerSourceType::ConfigFile).await;
420421

421-
// This might succeed (if database handles duplicates) or fail - both are acceptable
422-
// The important thing is that it doesn't panic
422+
// The duplicate must trip the UNIQUE constraint specifically, not merely
423+
// fail for some unrelated reason.
423424
match result {
424-
Ok(_) => {
425-
let entries = super::get(txn.as_mut()).await?;
426-
assert_eq!(entries.len(), 3); // Should only have unique entries
427-
}
428-
Err(_) => {
429-
// Also acceptable if database rejects duplicates
430-
}
425+
Err(DatabaseError::Sqlx(e)) if matches!(&e.source, sqlx::Error::Database(db) if db.is_unique_violation()) =>
426+
{}
427+
other => panic!(
428+
"replace should reject a duplicate address with a unique-violation error, got: {other:?}"
429+
),
431430
}
432431

433432
Ok(())

0 commit comments

Comments
 (0)