use crate::rise_set::*;
use crate::*;
use chrono::{Datelike, Timelike, TimeZone, Utc};
#[test]
fn test_equatorial_object() {
let location = Location {
latitude_deg: 0.0, longitude_deg: 0.0,
altitude_m: 0.0,
};
let date = Utc.with_ymd_and_hms(2024, 3, 20, 12, 0, 0).unwrap(); let result = rise_transit_set(0.0, 0.0, date, &location, None).unwrap();
assert!(result.is_some(), "Equatorial object should rise and set at equator");
let (rise, transit, set) = result.unwrap();
let duration = (set - rise).num_hours();
assert!((11..=13).contains(&duration),
"Equatorial object should be up ~12 hours, got {}", duration);
let rise_to_transit = (transit - rise).num_minutes();
let transit_to_set = (set - transit).num_minutes();
assert!((rise_to_transit - transit_to_set).abs() < 60,
"Transit should be centered, rise->transit: {} min, transit->set: {} min",
rise_to_transit, transit_to_set);
}
#[test]
fn test_polar_extremes() {
let north_pole = Location {
latitude_deg: 90.0,
longitude_deg: 0.0,
altitude_m: 0.0,
};
let date = Utc.with_ymd_and_hms(2024, 6, 21, 12, 0, 0).unwrap();
let result = rise_transit_set(0.0, 45.0, date, &north_pole, None).unwrap();
assert!(result.is_none());
let result = rise_transit_set(0.0, -45.0, date, &north_pole, None).unwrap();
assert!(result.is_none());
}
#[test]
fn test_next_rise_set() {
let location = Location {
latitude_deg: 40.0,
longitude_deg: -74.0,
altitude_m: 0.0,
};
let start = Utc.with_ymd_and_hms(2024, 8, 4, 0, 0, 0).unwrap();
let ra = 100.0;
let dec = 20.0;
let next_rise_time = next_rise(ra, dec, start, &location, None).unwrap();
let next_set_time = next_set(ra, dec, start, &location, None).unwrap();
assert!(next_rise_time.is_some(), "Star at dec=20° should rise at lat=40°");
assert!(next_set_time.is_some(), "Star at dec=20° should set at lat=40°");
let rise = next_rise_time.unwrap();
let set = next_set_time.unwrap();
assert!(rise > start, "Next rise should be after start time");
assert!(set > start, "Next set should be after start time");
if rise > set {
let implied_duration = 24 - (rise - set).num_hours();
assert!(implied_duration > 12 && implied_duration < 20,
"Object should be up 12-20 hours, implied duration: {} hours", implied_duration);
}
}
#[test]
fn test_sun_polar_day() {
let arctic = Location {
latitude_deg: 75.0,
longitude_deg: 0.0,
altitude_m: 0.0,
};
let summer = Utc.with_ymd_and_hms(2024, 6, 21, 12, 0, 0).unwrap();
let result = sun_rise_set(summer, &arctic).unwrap();
assert!(result.is_none());
}
#[test]
fn test_rise_set_wraparound() {
let location = Location {
latitude_deg: 45.0,
longitude_deg: 0.0,
altitude_m: 0.0,
};
let dt = Utc.with_ymd_and_hms(2024, 8, 4, 12, 0, 0).unwrap();
let result = rise_transit_set(180.0, 30.0, dt, &location, None).unwrap();
assert!(result.is_some());
let (rise, transit, set) = result.unwrap();
let transit_hour = transit.hour();
assert!((transit_hour as i32 - 15).abs() <= 2,
"RA 180° should transit around 15:00 UTC in August, got {:02}:00", transit_hour);
assert!(transit > rise, "Transit should be after rise");
assert!(set > transit, "Set should be after transit");
let duration_hours = (set - rise).num_hours();
assert!((6..18).contains(&duration_hours),
"Object at Dec=30° should be up 6-18 hours at lat=45°, got {} hours", duration_hours);
}
#[test]
fn test_rise_set_search_failure() {
let location = Location {
latitude_deg: 89.0,
longitude_deg: 0.0,
altitude_m: 0.0,
};
let dt = Utc.with_ymd_and_hms(2024, 6, 21, 12, 0, 0).unwrap();
let result = rise_transit_set(0.0, 89.5, dt, &location, Some(-18.0)).unwrap();
match result {
None => {
},
Some((rise, transit, set)) => {
assert!(rise.year() >= 2024);
assert!(transit.year() >= 2024);
assert!(set.year() >= 2024);
}
}
}
#[test]
fn test_rise_set_edge_cases() {
let location = Location {
latitude_deg: 45.0,
longitude_deg: 0.0,
altitude_m: 0.0,
};
let dt = Utc.with_ymd_and_hms(2024, 8, 4, 12, 0, 0).unwrap();
let result = rise_transit_set(180.0, 0.0, dt, &location, None).unwrap();
assert!(result.is_some(), "Object on celestial equator should rise/set at 45° latitude");
let (rise, transit, set) = result.unwrap();
let duration = (set - rise).num_hours();
assert!((11..=13).contains(&duration),
"Celestial equator object should be visible ~12 hours at 45° lat, got {} hours", duration);
let expected_transit_hour = 15;
assert!((transit.hour() as i32 - expected_transit_hour).abs() <= 1,
"RA 180° should transit around {}:00 UTC, got {:02}:{:02}",
expected_transit_hour, transit.hour(), transit.minute());
let polar_location = Location {
latitude_deg: 85.0,
longitude_deg: 0.0,
altitude_m: 0.0,
};
let winter = Utc.with_ymd_and_hms(2024, 12, 21, 12, 0, 0).unwrap();
let _result = sun_rise_set(winter, &polar_location).unwrap();
}
#[test]
fn test_next_set_no_set_within_search() {
let location = Location {
latitude_deg: 70.0, longitude_deg: 0.0,
altitude_m: 0.0,
};
let summer = Utc.with_ymd_and_hms(2024, 6, 21, 0, 0, 0).unwrap();
let result = next_set(0.0, 80.0, summer, &location, None).unwrap();
assert!(result.is_none(), "Circumpolar object should not set");
}