use bacnet_types::enums::{ErrorClass, ErrorCode};
use super::tests::*;
use super::*;
use crate::schedule::ScheduleObject;
fn point_denial(rights_instance: u32, index: u32) -> AccessRightsDecision {
denied(
AccessEvent::DENIED_POINT_NO_ACCESS_RIGHTS,
Some(position(rights_instance, AccessRuleKind::Negative, index)),
)
}
#[test]
fn a_grant_after_an_out_of_time_rule_names_the_grant() {
let mut db = database();
let closed = add_closed_schedule(&mut db);
add_rights(
&mut db,
1,
vec![during(closed, Some(point(1))), anytime(Some(point(1)))],
vec![],
);
add_credential(&mut db, &[rights(1).into()]);
assert_eq!(decision(&db), granted(1, 2));
}
#[test]
fn a_disabled_rule_keeps_its_place_in_the_array() {
let mut db = database();
add_rights(
&mut db,
1,
vec![disabled(anytime(None)), anytime(Some(point(1)))],
vec![disabled(anytime(None)), anytime(Some(point(2)))],
);
add_credential(&mut db, &[rights(1).into()]);
assert_eq!(decision(&db), granted(1, 2));
assert_eq!(evaluate_at(&db, 2).decision, point_denial(1, 2));
}
#[test]
fn objects_are_tried_in_assignment_order() {
let mut db = database();
add_rights(&mut db, 1, vec![anytime(None)], vec![]);
add_rights(
&mut db,
2,
vec![disabled(anytime(None)), anytime(None)],
vec![],
);
add_credential(&mut db, &[rights(2).into(), rights(1).into()]);
assert_eq!(decision(&db), granted(2, 2));
for (order, expected) in [
([2, 1], point_denial(2, 1)),
(
[1, 2],
denied(
AccessEvent::DENIED_ZONE_NO_ACCESS_RIGHTS,
Some(position(1, AccessRuleKind::Negative, 1)),
),
),
] {
let mut db = database();
add_rights(&mut db, 1, vec![], vec![anytime(Some(zone(1)))]);
add_rights(&mut db, 2, vec![], vec![anytime(Some(point(1)))]);
let assigned: Vec<_> = order.iter().map(|&i| rights(i).into()).collect();
add_credential(&mut db, &assigned);
assert_eq!(decision(&db), expected, "{order:?}");
}
}
#[test]
fn an_unreadable_assignment_list_is_passed_back() {
for (served, expected) in [
(None, ErrorCode::UNKNOWN_PROPERTY),
(
Some(PropertyValue::Unsigned(0)),
ErrorCode::INVALID_DATA_TYPE,
),
] {
let mut db = database();
let values = served
.map(|value| vec![(PropertyIdentifier::ASSIGNED_ACCESS_RIGHTS, value)])
.unwrap_or_default();
db.add(Stub::new(credential(1), values)).unwrap();
match evaluate_access_rights(&db, credential(1), point(1)) {
Err(Error::Protocol { class, code }) => {
assert_eq!(class, ErrorClass::PROPERTY.to_raw() as u32);
assert_eq!(code, expected.to_raw() as u32, "{expected:?}");
}
other => panic!("expected PROPERTY / {expected:?}, got {other:?}"),
}
}
}
#[test]
fn a_negative_rule_with_a_time_range_never_true_bars_no_one() {
let mut db = database();
db.add(Box::new(
ScheduleObject::new(60, "EMPTY", PropertyValue::Null).unwrap(),
))
.unwrap();
let unspecified = present_value(oid(ObjectType::SCHEDULE, UNSPECIFIED));
let null = present_value(oid(ObjectType::SCHEDULE, 60));
add_rights(
&mut db,
1,
vec![anytime(None)],
vec![
during(unspecified, Some(point(1))),
during(null, Some(point(1))),
],
);
add_credential(&mut db, &[rights(1).into()]);
assert_eq!(decision(&db), granted(1, 1));
}
#[test]
fn a_negative_rule_naming_what_is_not_read_here_bars_no_one() {
let mut remote_time = present_value(oid(ObjectType::SCHEDULE, 1));
remote_time.device_identifier = Some(device(OTHER_DEVICE));
let mut wildcard_time = remote_time.clone();
wildcard_time.device_identifier = Some(device(UNSPECIFIED));
let negatives = [
BACnetAccessRule::new(None, Some(in_device(UNSPECIFIED, point(1))), true),
BACnetAccessRule::new(None, Some(in_device(OTHER_DEVICE, point(1))), true),
BACnetAccessRule::new(None, Some(in_device(UNSPECIFIED, zone(1))), true),
BACnetAccessRule::new(Some(wildcard_time), Some(point(1).into()), true),
BACnetAccessRule::new(Some(remote_time), Some(point(1).into()), true),
];
for negative in negatives {
let mut db = database();
let open = ScheduleObject::new(1, "OPEN", PropertyValue::Boolean(true)).unwrap();
db.add(Box::new(open)).unwrap();
add_rights(&mut db, 1, vec![anytime(None)], vec![negative.clone()]);
add_credential(&mut db, &[rights(1).into()]);
assert_eq!(decision(&db), granted(1, 1), "{negative:?}");
}
}