use std::time::{Duration as StdDuration, Instant};
use metering::session::{MeterSample, SessionSplitConfig, split_session};
use metering::{
AggregationConfig, FillGapsConfig, IntervalResolution, MeterInterval, QualityConfig,
QualityFlag, ResampleConfig, ValidationConfig, aggregate, calendar, fill_gaps, resample,
score_intervals, validate_intervals,
};
use rust_decimal::{Decimal, dec};
use time::Duration;
use time::macros::date;
const BUDGET: StdDuration = StdDuration::from_secs(60);
fn year_2026() -> Vec<MeterInterval> {
let start = calendar::day_start_utc(date!(2026 - 01 - 01));
let end = calendar::day_start_utc(date!(2027 - 01 - 01));
let count = (end - start).whole_seconds() / 900;
(0..count)
.map(|i| {
let quarter_of_day = i % 96;
let kwh = if (24..80).contains(&quarter_of_day) {
dec!(2.5)
} else {
dec!(0.5)
};
MeterInterval::quarter_hour(start + Duration::minutes(15 * i), kwh)
})
.collect()
}
#[test]
fn a_settlement_year_runs_in_linear_time() {
let started = Instant::now();
let mut year = year_2026();
assert_eq!(year.len(), 35_040, "2026 is not a leap year");
for i in (0..year.len()).step_by(347) {
year[i].quality = QualityFlag::Faulty;
}
let report = validate_intervals(
&year,
&ValidationConfig::default().over_period(year[0].from, year[year.len() - 1].to),
);
assert!(report.evaluated.len() >= 8);
let graded = score_intervals(&year, &QualityConfig::default());
assert_eq!(graded.intervals_analysed, year.len());
let period = aggregate(&year, &AggregationConfig::rlm());
assert!(period.uniform_resolution);
assert!(period.benutzungsdauer_h().is_some());
assert_eq!(resample(&year, &ResampleConfig::to_hourly()).len(), 8_760);
assert_eq!(resample(&year, &ResampleConfig::to_daily()).len(), 365);
assert_eq!(resample(&year, &ResampleConfig::to_monthly()).len(), 12);
assert_eq!(resample(&year, &ResampleConfig::to_yearly()).len(), 1);
let billable: Vec<MeterInterval> = year
.iter()
.filter(|iv| iv.quality.is_billable())
.cloned()
.collect();
let filled = fill_gaps(
&billable,
&FillGapsConfig::new(
IntervalResolution::QuarterHour,
year[0].from,
year[year.len() - 1].to,
),
);
assert_eq!(filled.intervals.len(), year.len());
assert_eq!(filled.substituted_count(), 101);
assert!(filled.placed_everything());
assert!(
started.elapsed() < BUDGET,
"a settlement year took {:?} — that is a change of complexity class, not a slow machine",
started.elapsed()
);
}
#[test]
fn a_minute_sampled_day_places_in_linear_time() {
let started = Instant::now();
let start = calendar::day_start_utc(date!(2026 - 06 - 01));
let samples: Vec<MeterSample> = (0..=1_440)
.map(|m| MeterSample::new(start + Duration::minutes(m), Decimal::from(m)))
.collect();
let slots = split_session(
start,
start + Duration::hours(24),
Decimal::from(1_440u32),
&samples,
&SessionSplitConfig::quarter_hourly(),
)
.expect("a well-formed day");
assert_eq!(slots.len(), 96);
assert_eq!(
slots.iter().map(|s| s.value).sum::<Decimal>(),
Decimal::from(1_440u32)
);
assert!(slots.iter().all(|s| s.quality == QualityFlag::Measured));
assert!(started.elapsed() < BUDGET, "{:?}", started.elapsed());
}