use crate::core::{Calendar, CalendarError, DeadlineSpec, Timestamp};
use super::conformance::Report;
pub fn check(calendar: &dyn Calendar, supported: &[DeadlineSpec], report: &mut Report) {
let from = Timestamp::from_unix_timestamp(1_760_000_000).expect("a valid test instant");
report.checked += 1;
if supported.is_empty() {
report.record(
"a calendar resolves something",
"no supported spec was given, so this battery would pass without \
resolving anything",
);
return;
}
the_digest_is_stable(calendar, report);
for spec in supported {
resolution_is_pure(calendar, from, spec, report);
a_count_at_the_edge_of_its_type_is_refused(calendar, from, spec, report);
a_deadline_is_after_its_start(calendar, from, spec, report);
}
a_rule_it_does_not_know_is_refused(calendar, from, report);
}
fn the_digest_is_stable(calendar: &dyn Calendar, r: &mut Report) {
r.checked += 1;
if calendar.digest() != calendar.digest() {
r.record(
"the digest identifies the ruleset",
"two reads of the digest disagreed, so the instants this calendar \
produced cannot be attributed to any ruleset at all",
);
}
}
fn resolution_is_pure(
calendar: &dyn Calendar,
from: Timestamp,
spec: &DeadlineSpec,
r: &mut Report,
) {
r.checked += 1;
let first = calendar.resolve(from, spec);
let second = calendar.resolve(from, spec);
match (first, second) {
(Ok(a), Ok(b)) if a == b => {}
(Ok(a), Ok(b)) => r.record(
"resolution is pure",
format!(
"'{}' resolved to {a} and then to {b}; two runs a millisecond apart \
would disagree about one regulatory window",
spec.kind
),
),
(Err(a), Err(b)) if a.to_string() == b.to_string() => {}
(a, b) => r.record(
"resolution is pure",
format!(
"'{}' answered {a:?} and then {b:?}; a rule that refuses \
intermittently is a rule nobody can act on",
spec.kind
),
),
}
}
fn a_count_at_the_edge_of_its_type_is_refused(
calendar: &dyn Calendar,
from: Timestamp,
spec: &DeadlineSpec,
r: &mut Report,
) {
for hostile in hostile_variants(spec) {
r.checked += 1;
match calendar.resolve(from, &hostile) {
Err(_) => {}
Ok(at) => r.record(
"a count no instant can carry is refused",
format!(
"'{}' with {} resolved to {at}; a window that long is a typo with \
no correct reading, and an obligation registered at that instant \
is one nobody will ever be warned about",
hostile.kind, hostile.params
),
),
}
}
}
fn a_deadline_is_after_its_start(
calendar: &dyn Calendar,
from: Timestamp,
spec: &DeadlineSpec,
r: &mut Report,
) {
let mut probes = vec![spec.clone()];
probes.extend(with_every_count(spec, 0));
probes.extend(with_every_count(spec, -1));
for probe in probes {
r.checked += 1;
if let Ok(at) = calendar.resolve(from, &probe)
&& at <= from
{
r.record(
"a deadline is after its start",
format!(
"'{}' with {} resolved to {at}, not after {from}; an obligation \
due when it is registered is breached before anyone is warned",
probe.kind, probe.params
),
);
}
}
}
fn with_every_count(spec: &DeadlineSpec, count: i64) -> Option<DeadlineSpec> {
let serde_json::Value::Object(params) = &spec.params else {
return None;
};
let mut probe = params.clone();
let mut touched = false;
for value in probe.values_mut() {
if value.is_i64() || value.is_u64() {
*value = serde_json::Value::from(count);
touched = true;
}
}
touched.then(|| DeadlineSpec::new(spec.kind.clone(), serde_json::Value::Object(probe)))
}
fn hostile_variants(spec: &DeadlineSpec) -> Vec<DeadlineSpec> {
[i64::MAX, i64::MIN]
.into_iter()
.filter_map(|extreme| with_every_count(spec, extreme))
.collect()
}
fn a_rule_it_does_not_know_is_refused(calendar: &dyn Calendar, from: Timestamp, r: &mut Report) {
r.checked += 1;
let unknown = DeadlineSpec::new(
"agentplane.conformance/no-such-rule",
serde_json::json!({ "n": 5 }),
);
match calendar.resolve(from, &unknown) {
Err(CalendarError::UnknownKind(_)) => {}
Err(other) => r.record(
"an unknown rule is refused by name",
format!(
"a rule this calendar does not implement was refused as {other}, so a \
deployment cannot tell a missing rule from a malformed one"
),
),
Ok(at) => r.record(
"an unknown rule is refused, never approximated",
format!(
"a rule this calendar does not implement resolved to {at}; a wrong \
working-day answer is worse than no answer, because it looks right"
),
),
}
}