Add unit tests

This commit is contained in:
Andras Schmelczer 2026-02-05 21:19:01 +00:00
parent 55598aaaa0
commit 5fe192d25a
9 changed files with 512 additions and 1037 deletions

View file

@ -66,7 +66,23 @@ pub fn parse_bounds(bounds_str: &str) -> Result<(f64, f64, f64, f64), (StatusCod
));
}
Ok((parts[0], parts[1], parts[2], parts[3]))
let (south, west, north, east) = (parts[0], parts[1], parts[2], parts[3]);
// Validate that bounds are not inverted
if south > north {
return Err((
StatusCode::BAD_REQUEST,
format!("Invalid bounds: south ({}) must be <= north ({})", south, north),
));
}
if west > east {
return Err((
StatusCode::BAD_REQUEST,
format!("Invalid bounds: west ({}) must be <= east ({})", west, east),
));
}
Ok((south, west, north, east))
}
#[cfg(test)]
@ -76,8 +92,14 @@ mod tests {
#[test]
fn parse_bounds_valid() {
assert_eq!(parse_bounds("1.0,2.0,3.0,4.0").unwrap(), (1.0, 2.0, 3.0, 4.0));
assert_eq!(parse_bounds("-51.5, -0.1, 51.6, 0.2").unwrap(), (-51.5, -0.1, 51.6, 0.2));
assert_eq!(
parse_bounds("1.0,2.0,3.0,4.0").unwrap(),
(1.0, 2.0, 3.0, 4.0)
);
assert_eq!(
parse_bounds("-51.5, -0.1, 51.6, 0.2").unwrap(),
(-51.5, -0.1, 51.6, 0.2)
);
}
#[test]
@ -88,6 +110,14 @@ mod tests {
assert!(parse_bounds("").is_err());
}
#[test]
fn parse_bounds_inverted_rejected() {
// south > north is rejected
assert!(parse_bounds("52.0,-0.5,51.0,0.5").is_err());
// west > east is rejected
assert!(parse_bounds("51.0,0.5,52.0,-0.5").is_err());
}
#[test]
fn h3_cell_bounds_applies_buffer() {
let cell = h3o::CellIndex::from_str("8928308280fffff").unwrap();
@ -102,39 +132,143 @@ mod tests {
#[test]
fn h3_cell_bounds_returns_degrees_not_radians() {
// Cell "8928308280fffff" is in San Francisco area (~37.77°N, ~-122.4°W)
let cell = h3o::CellIndex::from_str("8928308280fffff").unwrap();
let (min_lat, min_lon, max_lat, max_lon) = h3_cell_bounds(cell, 0.0);
// If h3o returned radians, values would be < π ≈ 3.14
// Latitude ~37.77° proves we're getting degrees, not radians
assert!(min_lat > 30.0 && min_lat < 45.0, "min_lat {} should be ~37° (degrees)", min_lat);
assert!(max_lat > 30.0 && max_lat < 45.0, "max_lat {} should be ~37° (degrees)", max_lat);
// Longitude ~-122° also proves degrees (radians would be < π)
assert!(min_lon < -100.0, "min_lon {} should be ~-122° (degrees)", min_lon);
assert!(max_lon < -100.0, "max_lon {} should be ~-122° (degrees)", max_lon);
assert!(
min_lat > 30.0 && min_lat < 45.0,
"min_lat {} should be ~37° (degrees)",
min_lat
);
assert!(
max_lat > 30.0 && max_lat < 45.0,
"max_lat {} should be ~37° (degrees)",
max_lat
);
assert!(
min_lon < -100.0,
"min_lon {} should be ~-122° (degrees)",
min_lon
);
assert!(
max_lon < -100.0,
"max_lon {} should be ~-122° (degrees)",
max_lon
);
}
#[test]
fn bounds_intersect_overlapping() {
// Two overlapping boxes
assert!(bounds_intersect(0.0, 0.0, 2.0, 2.0, 1.0, 1.0, 3.0, 3.0));
// Box B is inside box A
assert!(bounds_intersect(0.0, 0.0, 10.0, 10.0, 2.0, 2.0, 5.0, 5.0));
// Box A is inside box B
assert!(bounds_intersect(2.0, 2.0, 5.0, 5.0, 0.0, 0.0, 10.0, 10.0));
// Touching at edge
assert!(bounds_intersect(0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 2.0, 1.0));
}
#[test]
fn bounds_intersect_non_overlapping() {
// Box B is to the right of box A
assert!(!bounds_intersect(0.0, 0.0, 1.0, 1.0, 0.0, 2.0, 1.0, 3.0));
// Box B is above box A
assert!(!bounds_intersect(0.0, 0.0, 1.0, 1.0, 2.0, 0.0, 3.0, 1.0));
// Completely separate
assert!(!bounds_intersect(0.0, 0.0, 1.0, 1.0, 5.0, 5.0, 6.0, 6.0));
}
#[test]
fn parse_bounds_with_spaces() {
let (south, west, _north, _east) = parse_bounds("51.0, -0.5, 52.0, 0.5").unwrap();
assert_eq!(south, 51.0);
assert_eq!(west, -0.5);
}
#[test]
fn parse_bounds_negative_values() {
let (south, _west, north, _east) = parse_bounds("-51.5,-0.5,-50.0,0.5").unwrap();
assert_eq!(south, -51.5);
assert_eq!(north, -50.0);
}
#[test]
fn touching_at_corner_intersects() {
assert!(bounds_intersect(
0.0, 0.0, 1.0, 1.0, // Box A
1.0, 1.0, 2.0, 2.0 // Box B touches at (1,1)
));
}
#[test]
fn touching_at_edge_intersects() {
assert!(bounds_intersect(
0.0, 0.0, 1.0, 1.0, // Box A
1.0, 0.0, 2.0, 1.0 // Box B touches along right edge
));
}
#[test]
fn disjoint_diagonally_no_intersect() {
assert!(!bounds_intersect(
0.0, 0.0, 1.0, 1.0, // Box A
2.0, 2.0, 3.0, 3.0 // Box B diagonally away
));
}
#[test]
fn negative_coordinates_intersect() {
assert!(bounds_intersect(
-2.0, -2.0, -1.0, -1.0, // Box A (negative coords)
-1.5, -1.5, -0.5, -0.5 // Box B overlaps
));
}
#[test]
fn h3_cell_bounds_zero_buffer() {
let cell = h3o::CellIndex::from_str("8928308280fffff").unwrap();
let (south, west, north, east) = h3_cell_bounds(cell, 0.0);
assert!(south < north, "south {} should be < north {}", south, north);
assert!(west < east, "west {} should be < east {}", west, east);
assert!(south > 30.0 && south < 45.0);
assert!(west < -100.0);
}
#[test]
fn h3_cell_bounds_different_resolutions() {
let cell_high = h3o::CellIndex::from_str("8928308280fffff").unwrap();
let res5 = h3o::Resolution::try_from(5).unwrap();
let cell_low = cell_high.parent(res5).unwrap();
let (s_low, w_low, n_low, e_low) = h3_cell_bounds(cell_low, 0.0);
let (s_high, w_high, n_high, e_high) = h3_cell_bounds(cell_high, 0.0);
let area_low = (n_low - s_low) * (e_low - w_low);
let area_high = (n_high - s_high) * (e_high - w_high);
assert!(area_low > area_high, "Lower res should have larger area");
}
#[test]
fn parent_cell_at_lower_resolution() {
let child = h3o::CellIndex::from_str("8928308280fffff").unwrap();
let parent_res = h3o::Resolution::try_from(7).unwrap();
let parent = child.parent(parent_res).unwrap();
assert_eq!(parent.resolution(), parent_res);
assert!(parent.children(child.resolution()).any(|c| c == child));
}
#[test]
fn same_resolution_returns_self() {
let cell = h3o::CellIndex::from_str("8928308280fffff").unwrap();
let res = cell.resolution();
let parent = cell.parent(res);
assert_eq!(parent, Some(cell));
}
#[test]
fn higher_resolution_parent_fails() {
let cell = h3o::CellIndex::from_str("8928308280fffff").unwrap();
let higher_res = h3o::Resolution::try_from(10).unwrap();
let parent = cell.parent(higher_res);
assert!(parent.is_none());
}
}