use crate::prelude::{
has_rdap_path, Event, EventActionValue, ExtensionId, Notice, NrType, Remark, StatusValue,
};
use {
crate::prelude::ObjectCommon,
std::{any::TypeId, str::FromStr, sync::LazyLock},
};
use {
crate::{
media_types::RDAP_MEDIA_TYPE,
prelude::Common,
response::{
autnum::Autnum,
domain::Domain,
entity::Entity,
nameserver::Nameserver,
network::Network,
types::{Link, NoticeOrRemark, PublicIds, RdapConformance},
},
},
chrono::DateTime,
};
use super::{
string::{StringCheck, StringListCheck},
Check, CheckItem, CheckParams, Checks, GetChecks, GetGroupChecks, RdapStructure,
};
impl GetChecks for RdapConformance {
fn get_checks(&self, index: Option<usize>, params: CheckParams) -> Checks {
let mut items = vec![];
if params.parent_type != params.root.get_type() {
items.push(Check::RdapConformanceInvalidParent.check_item())
};
for ext in self {
if !params.allow_unreg_ext {
let id = ExtensionId::from_str(ext);
if id.is_err() {
items.push(Check::UnknownExtension.check_item())
}
}
}
if self.is_empty() {
items.push(Check::RdapConformanceIsEmpty.check_item());
}
Checks {
rdap_struct: super::RdapStructure::RdapConformance,
index,
items,
sub_checks: vec![],
}
}
}
static RELATED_AND_SELF_LINK_PARENTS: LazyLock<Vec<TypeId>> = LazyLock::new(|| {
vec![
TypeId::of::<Domain>(),
TypeId::of::<Entity>(),
TypeId::of::<Autnum>(),
TypeId::of::<Network>(),
]
});
impl GetChecks for Link {
fn get_checks(&self, index: Option<usize>, params: CheckParams) -> Checks {
let mut items: Vec<CheckItem> = vec![];
if self.value.is_none() {
items.push(Check::LinkMissingValueProperty.check_item())
};
if self.href.is_none() {
items.push(Check::LinkMissingHrefProperty.check_item())
};
if let Some(rel) = &self.rel {
if rel.eq("related") {
if let Some(media_type) = &self.media_type {
if !media_type.eq(RDAP_MEDIA_TYPE)
&& RELATED_AND_SELF_LINK_PARENTS.contains(¶ms.parent_type)
{
items.push(Check::LinkRelatedIsNotRdap.check_item())
} else if media_type.eq(RDAP_MEDIA_TYPE) {
if let Some(ref href) = self.href {
if !has_rdap_path(href) {
items.push(Check::LinkRelatedNotToRdap.check_item())
}
}
}
} else {
items.push(Check::LinkRelatedHasNoType.check_item())
}
} else if rel.eq("self") {
if let Some(media_type) = &self.media_type {
if !media_type.eq(RDAP_MEDIA_TYPE) {
items.push(Check::LinkSelfIsNotRdap.check_item())
}
} else {
items.push(Check::LinkSelfHasNoType.check_item())
}
} else if RELATED_AND_SELF_LINK_PARENTS.contains(¶ms.parent_type) &&
params.root.get_type() != TypeId::of::<Nameserver>()
{
items.push(Check::LinkObjectClassHasNoSelf.check_item())
}
} else {
items.push(Check::LinkMissingRelProperty.check_item())
}
Checks {
rdap_struct: super::RdapStructure::Link,
index,
items,
sub_checks: vec![],
}
}
}
impl GetChecks for Notice {
fn get_checks(&self, index: Option<usize>, params: CheckParams) -> Checks {
get_checks_for_notice_or_remark(self, RdapStructure::Notice, index, params)
}
}
impl GetChecks for Remark {
fn get_checks(&self, index: Option<usize>, params: CheckParams) -> Checks {
get_checks_for_notice_or_remark(self, RdapStructure::Remark, index, params)
}
}
fn get_checks_for_notice_or_remark(
nr: &NoticeOrRemark,
rdap_struct: RdapStructure,
index: Option<usize>,
params: CheckParams,
) -> Checks {
let mut items: Vec<CheckItem> = vec![];
if let Some(description) = &nr.description {
if description.is_string() {
items.push(Check::NoticeOrRemarkDescriptionIsString.check_item())
}
} else {
items.push(Check::NoticeOrRemarkDescriptionIsAbsent.check_item())
};
if let Some(nr_type) = nr.nr_type() {
let s = NrType::from_str(nr_type);
if s.is_err() {
items.push(Check::NoticeOrRemarkUnknownType.check_item());
}
}
let mut sub_checks: Vec<Checks> = vec![];
if let Some(links) = &nr.links {
if links.is_empty() {
sub_checks.push(Checks {
rdap_struct: super::RdapStructure::Links,
index: None,
items: vec![Check::LinksArrayIsEmpty.check_item()],
sub_checks: vec![],
})
} else {
links.iter().enumerate().for_each(|(i, link)| {
sub_checks.push(
link.get_checks(Some(i), params.from_parent(TypeId::of::<NoticeOrRemark>())),
)
});
}
};
Checks {
rdap_struct,
index,
items,
sub_checks,
}
}
impl GetChecks for PublicIds {
fn get_checks(&self, index: Option<usize>, _params: CheckParams) -> Checks {
let mut items: Vec<CheckItem> = vec![];
self.iter().for_each(|pid| {
if let Some(id_type) = &pid.id_type {
if id_type.is_number() || id_type.is_bool() {
items.push(Check::PublicIdTypeIsNotString.check_item());
}
} else {
items.push(Check::PublicIdTypeIsAbsent.check_item());
}
if let Some(identifier) = &pid.identifier {
if identifier.is_number() || identifier.is_bool() {
items.push(Check::PublicIdIdentifierIsNotString.check_item());
}
} else {
items.push(Check::PublicIdIdentifierIsAbsent.check_item());
}
});
Checks {
rdap_struct: super::RdapStructure::PublicIds,
index,
items,
sub_checks: vec![],
}
}
}
impl GetGroupChecks for Common {
fn get_group_checks(&self, params: CheckParams) -> Vec<Checks> {
let mut sub_checks: Vec<Checks> = vec![];
if let Some(rdap_conformance) = &self.rdap_conformance {
sub_checks.push(rdap_conformance.get_checks(None, params))
};
if let Some(notices) = &self.notices {
if notices.is_empty() {
sub_checks.push(Checks {
rdap_struct: super::RdapStructure::Notices,
index: None,
items: vec![Check::NoticesArrayIsEmpty.check_item()],
sub_checks: vec![],
});
} else {
for (i, notice) in notices.iter().enumerate() {
sub_checks.push(notice.get_checks(Some(i), params));
}
}
}
if params.parent_type == params.root.get_type() && self.rdap_conformance.is_none() {
sub_checks.push(Checks {
rdap_struct: super::RdapStructure::RdapConformance,
index: None,
items: vec![Check::RdapConformanceMissing.check_item()],
sub_checks: vec![],
});
}
sub_checks
}
}
impl GetGroupChecks for ObjectCommon {
fn get_group_checks(&self, params: CheckParams) -> Vec<Checks> {
let mut sub_checks: Vec<Checks> = vec![];
if let Some(entities) = &self.entities {
if entities.is_empty() {
sub_checks.push(Checks {
rdap_struct: super::RdapStructure::Entities,
index: None,
items: vec![Check::EntityArrayIsEmpty.check_item()],
sub_checks: vec![],
})
} else {
for (i, entity) in entities.iter().enumerate() {
sub_checks.push(entity.get_checks(Some(i), params));
}
}
}
if let Some(links) = &self.links {
if links.is_empty() {
sub_checks.push(Checks {
rdap_struct: super::RdapStructure::Links,
index: None,
items: vec![Check::LinksArrayIsEmpty.check_item()],
sub_checks: vec![],
})
} else {
for (i, link) in links.iter().enumerate() {
sub_checks.push(link.get_checks(Some(i), params));
}
}
} else if params.root.get_type() != TypeId::of::<Nameserver>()
&& params.parent_type != TypeId::of::<Nameserver>()
{
sub_checks.push(Checks {
rdap_struct: super::RdapStructure::Links,
index: None,
items: vec![Check::LinkObjectClassHasNoSelf.check_item()],
sub_checks: vec![],
})
};
if let Some(remarks) = &self.remarks {
if remarks.is_empty() {
sub_checks.push(Checks {
rdap_struct: super::RdapStructure::Remarks,
index: None,
items: vec![Check::RemarksArrayIsEmpty.check_item()],
sub_checks: vec![],
})
} else {
for (i, remark) in remarks.iter().enumerate() {
sub_checks.push(remark.get_checks(Some(i), params));
}
}
};
if let Some(events) = &self.events {
if events.is_empty() {
sub_checks.push(Checks {
rdap_struct: super::RdapStructure::Events,
index: None,
items: vec![Check::EventsArrayIsEmpty.check_item()],
sub_checks: vec![],
})
} else {
events.iter().enumerate().for_each(|(i, e)| {
sub_checks.push(e.get_checks(Some(i), params));
});
}
}
if let Some(handle) = &self.handle {
if handle.is_whitespace_or_empty() {
sub_checks.push(Checks {
rdap_struct: super::RdapStructure::Handle,
index: None,
items: vec![Check::HandleIsEmpty.check_item()],
sub_checks: vec![],
})
}
if handle.is_number() || handle.is_bool() {
sub_checks.push(Checks {
rdap_struct: super::RdapStructure::Handle,
index: None,
items: vec![Check::HandleIsNotString.check_item()],
sub_checks: vec![],
})
}
}
if let Some(status) = &self.status {
let status = status.vec();
if status.is_empty_or_any_empty_or_whitespace() {
sub_checks.push(Checks {
rdap_struct: super::RdapStructure::Status,
index: None,
items: vec![Check::StatusIsEmpty.check_item()],
sub_checks: vec![],
})
} else {
for (i, value) in status.iter().enumerate() {
let status_value = StatusValue::from_str(value);
if status_value.is_err() {
sub_checks.push(Checks {
rdap_struct: super::RdapStructure::Status,
index: Some(i),
items: vec![Check::StatusValueUnknown.check_item()],
sub_checks: vec![],
})
}
}
}
}
if let Some(port43) = &self.port_43 {
if port43.is_whitespace_or_empty() {
sub_checks.push(Checks {
rdap_struct: super::RdapStructure::Port43,
index: None,
items: vec![Check::Port43IsEmpty.check_item()],
sub_checks: vec![],
})
}
}
sub_checks
}
}
impl GetChecks for Event {
fn get_checks(&self, index: Option<usize>, params: CheckParams) -> Checks {
let mut items = vec![];
if let Some(date) = &self.event_date {
let date = DateTime::parse_from_rfc3339(date);
if date.is_err() {
items.push(Check::EventDateIsNotRfc3339.check_item());
}
} else {
items.push(Check::EventDateIsAbsent.check_item());
}
if let Some(event_action) = &self.event_action {
let ea_value = EventActionValue::from_str(event_action);
if ea_value.is_err() {
items.push(Check::EventActionIsUnknown.check_item());
}
} else {
items.push(Check::EventActionIsAbsent.check_item());
}
let mut sub_checks = vec![];
if let Some(links) = &self.links {
if links.is_empty() {
sub_checks.push(Checks {
rdap_struct: super::RdapStructure::Links,
index: None,
items: vec![Check::LinksArrayIsEmpty.check_item()],
sub_checks: vec![],
})
} else {
for (i, link) in links.iter().enumerate() {
sub_checks.push(link.get_checks(Some(i), params));
}
}
}
Checks {
rdap_struct: super::RdapStructure::Event,
index,
items,
sub_checks: vec![],
}
}
}
#[cfg(test)]
#[allow(non_snake_case)]
mod tests {
use rstest::rstest;
use crate::{
check::{contains_check, Checks},
media_types::RDAP_MEDIA_TYPE,
prelude::{ToResponse, VectorStringish},
response::{
domain::Domain,
nameserver::Nameserver,
types::{Event, Link, Notice, NoticeOrRemark, PublicId, Remark},
RdapResponse,
},
};
use crate::check::{Check, CheckParams, GetChecks};
#[test]
fn check_link_with_no_rel_property() {
let rdap = Domain::builder()
.ldh_name("example.com")
.link(
Link::illegal()
.href("https://foo")
.value("https://foo")
.build(),
)
.build()
.to_response();
let checks = rdap.get_checks(None, CheckParams::for_rdap(&rdap));
assert!(contains_check(Check::LinkMissingRelProperty, &checks));
}
#[test]
fn check_link_with_no_val_property() {
let rdap = Domain::builder()
.ldh_name("example.com")
.link(Link::illegal().href("https://foo").rel("about").build())
.build()
.to_response();
let checks = rdap.get_checks(None, CheckParams::for_rdap(&rdap));
assert!(contains_check(Check::LinkMissingValueProperty, &checks));
}
#[test]
fn check_link_with_no_href_property() {
let rdap = Domain::builder()
.ldh_name("example.com")
.link(Link::illegal().value("https://foo").rel("about").build())
.build()
.to_response();
let checks = rdap.get_checks(None, CheckParams::for_rdap(&rdap));
assert!(contains_check(Check::LinkMissingHrefProperty, &checks));
}
#[test]
fn test_related_link_with_no_type_property() {
let rdap = Domain::builder()
.ldh_name("example.com")
.link(
Link::builder()
.href("https://foo")
.value("https://foo")
.rel("related")
.build(),
)
.build()
.to_response();
let checks = rdap.get_checks(None, CheckParams::for_rdap(&rdap));
assert!(contains_check(Check::LinkRelatedHasNoType, &checks));
}
#[test]
fn test_object_related_link_with_non_rdap_type() {
let rdap = Domain::builder()
.ldh_name("example.com")
.link(
Link::builder()
.href("https://foo")
.value("https://foo")
.rel("related")
.media_type("foo")
.build(),
)
.build()
.to_response();
let checks = rdap.get_checks(None, CheckParams::for_rdap(&rdap));
assert!(contains_check(Check::LinkRelatedIsNotRdap, &checks));
}
#[test]
fn test_object_related_link_with_not_rdap_path() {
let rdap = Domain::builder()
.ldh_name("example.com")
.link(
Link::builder()
.href("https://foo")
.value("https://foo")
.rel("related")
.media_type(RDAP_MEDIA_TYPE)
.build(),
)
.build()
.to_response();
let checks = rdap.get_checks(None, CheckParams::for_rdap(&rdap));
assert!(contains_check(Check::LinkRelatedNotToRdap, &checks));
}
#[test]
fn test_self_link_with_no_type_property() {
let rdap = Domain::builder()
.ldh_name("example.com")
.link(
Link::builder()
.href("https://foo")
.value("https://foo")
.rel("self")
.build(),
)
.build()
.to_response();
let checks = rdap.get_checks(None, CheckParams::for_rdap(&rdap));
assert!(contains_check(Check::LinkSelfHasNoType, &checks));
}
#[test]
fn test_self_link_with_non_rdap_type() {
let rdap = Domain::builder()
.ldh_name("example.com")
.link(
Link::builder()
.href("https://foo")
.value("https://foo")
.rel("self")
.media_type("foo")
.build(),
)
.build()
.to_response();
let checks = rdap.get_checks(None, CheckParams::for_rdap(&rdap));
assert!(find_any_check(&checks, Check::LinkSelfIsNotRdap));
}
#[test]
fn test_domain_with_self_link() {
let rdap = Domain::builder()
.ldh_name("example.com")
.link(
Link::builder()
.href("https://foo")
.value("https://foo")
.rel("self")
.media_type("application/rdap+json")
.build(),
)
.build()
.to_response();
let checks = rdap.get_checks(None, CheckParams::for_rdap(&rdap));
dbg!(&checks);
assert!(!find_any_check(&checks, Check::LinkObjectClassHasNoSelf));
}
#[test]
fn test_nameserver_with_self_link() {
let rdap = Nameserver::builder()
.ldh_name("example.com")
.link(
Link::builder()
.href("https://foo")
.value("https://foo")
.rel("self")
.media_type("application/rdap+json")
.build(),
)
.build()
.expect("unable to build nameserver")
.to_response();
let checks = rdap.get_checks(None, CheckParams::for_rdap(&rdap));
assert!(!find_any_check(&checks, Check::LinkObjectClassHasNoSelf));
}
#[test]
fn test_nameserver_with_self_link_and_notice() {
let rdap = Nameserver::response_obj()
.ldh_name("example.com")
.notice(Notice(
NoticeOrRemark::builder()
.description_entry("a notice")
.link(
Link::builder()
.href("https://tos")
.value("https://tos")
.rel("terms-of-service")
.media_type("text/html")
.build(),
)
.build(),
))
.link(
Link::builder()
.href("https://foo")
.value("https://foo")
.rel("self")
.media_type("application/rdap+json")
.build(),
)
.build()
.expect("build nameserver")
.to_response();
let checks = rdap.get_checks(None, CheckParams::for_rdap(&rdap));
dbg!(&checks);
assert!(!find_any_check(&checks, Check::LinkObjectClassHasNoSelf));
}
#[test]
fn test_nameserver_with_self_link_and_remark() {
let rdap = Nameserver::builder()
.ldh_name("example.com")
.remark(Remark(
NoticeOrRemark::builder()
.description_entry("a notice")
.links(vec![Link::builder()
.href("https://tos")
.value("https://tos")
.rel("terms-of-service")
.media_type("text/html")
.build()])
.build(),
))
.link(
Link::builder()
.href("https://foo")
.value("https://foo")
.rel("self")
.media_type("application/rdap+json")
.build(),
)
.build()
.expect("building nameserver")
.to_response();
let checks = rdap.get_checks(None, CheckParams::for_rdap(&rdap));
dbg!(&checks);
assert!(!find_any_check(&checks, Check::LinkObjectClassHasNoSelf));
}
#[test]
fn test_domain_with_no_self_link() {
let rdap = Domain::builder()
.ldh_name("example.com")
.link(
Link::builder()
.href("https://foo")
.value("https://foo")
.rel("no_self")
.media_type("foo")
.build(),
)
.build()
.to_response();
let checks = rdap.get_checks(None, CheckParams::for_rdap(&rdap));
assert!(contains_check(Check::LinkObjectClassHasNoSelf, &checks));
}
#[test]
fn test_domain_with_no_links() {
let rdap = Domain::builder()
.ldh_name("example.com")
.build()
.to_response();
let checks = rdap.get_checks(None, CheckParams::for_rdap(&rdap));
dbg!(&rdap);
checks
.sub(crate::check::RdapStructure::Links)
.expect("Links not found")
.items
.iter()
.find(|c| c.check == Check::LinkObjectClassHasNoSelf)
.expect("link missing check");
}
#[test]
fn test_event_with_no_date() {
let rdap = Domain::builder()
.ldh_name("example.com")
.event(Event::illegal().event_action("foo").build())
.build()
.to_response();
let checks = rdap.get_checks(None, CheckParams::for_rdap(&rdap));
assert!(contains_check(Check::EventDateIsAbsent, &checks));
}
#[test]
fn test_event_with_no_action() {
let rdap = Domain::builder()
.ldh_name("example.com")
.event(Event::illegal().event_date("1990-12-31T23:59:59Z").build())
.build()
.to_response();
let checks = rdap.get_checks(None, CheckParams::for_rdap(&rdap));
assert!(contains_check(Check::EventActionIsAbsent, &checks));
}
#[test]
fn test_event_with_unknown_action() {
let rdap = Domain::builder()
.ldh_name("example.com")
.event(
Event::builder()
.event_action("unknown")
.event_date("1990-12-31T23:59:59Z")
.build(),
)
.build()
.to_response();
let checks = rdap.get_checks(None, CheckParams::for_rdap(&rdap));
assert!(contains_check(Check::EventActionIsUnknown, &checks));
}
#[test]
fn test_event_with_bad_date() {
let rdap = Domain::builder()
.ldh_name("example.com")
.event(
Event::builder()
.event_action("foo")
.event_date("bar")
.build(),
)
.build()
.to_response();
let checks = rdap.get_checks(None, CheckParams::for_rdap(&rdap));
assert!(contains_check(Check::EventDateIsNotRfc3339, &checks));
}
#[test]
fn test_events_array_is_empty() {
let mut rdap = Domain::builder().ldh_name("example.com").build();
rdap.object_common.events = Some(vec![]);
let rdap = rdap.to_response();
let checks = rdap.get_checks(None, CheckParams::for_rdap(&rdap));
assert!(contains_check(Check::EventsArrayIsEmpty, &checks));
}
#[test]
fn test_events_array_is_absent() {
let rdap = Domain::builder()
.ldh_name("example.com")
.build()
.to_response();
let checks = rdap.get_checks(None, CheckParams::for_rdap(&rdap));
assert!(!contains_check(Check::EventsArrayIsEmpty, &checks));
}
#[test]
fn test_public_id_with_no_type() {
let rdap = Domain::builder()
.ldh_name("example.com")
.public_id(PublicId::illegal().identifier("thing").build())
.build()
.to_response();
let checks = rdap.get_checks(None, CheckParams::for_rdap(&rdap));
checks
.sub(crate::check::RdapStructure::PublicIds)
.expect("Public Ids not found")
.items
.iter()
.find(|c| c.check == Check::PublicIdTypeIsAbsent)
.expect("public id missing check");
}
#[test]
fn test_public_id_with_non_string_public_id_type() {
let json = r#"
{
"objectClassName" : "domain",
"ldhName" : "ns1.example.com",
"publicIds": [
{
"type": 1,
"identifier": "1"
}
]
}
"#;
let rdap = serde_json::from_str::<RdapResponse>(json).expect("parsing JSON");
let checks = rdap.get_checks(None, CheckParams::for_rdap(&rdap));
assert!(checks
.sub(crate::check::RdapStructure::PublicIds)
.expect("public ids not found")
.items
.iter()
.any(|c| c.check == Check::PublicIdTypeIsNotString));
}
#[test]
fn test_public_id_with_non_string_identifier() {
let json = r#"
{
"objectClassName" : "domain",
"handle" : "XXXX",
"ldhName" : "xn--fo-5ja.example",
"publicIds": [
{
"type": "thing",
"identifier": 1234
}
]
}
"#;
let rdap = serde_json::from_str::<RdapResponse>(json).expect("parsing JSON");
let checks = rdap.get_checks(None, CheckParams::for_rdap(&rdap));
assert!(checks
.sub(crate::check::RdapStructure::PublicIds)
.expect("Public Ids not found")
.items
.iter()
.any(|c| c.check == Check::PublicIdIdentifierIsNotString));
}
#[test]
fn test_public_id_with_no_identifier() {
let rdap = Domain::builder()
.ldh_name("example.com")
.public_id(PublicId::illegal().id_type("thing").build())
.build()
.to_response();
let checks = rdap.get_checks(None, CheckParams::for_rdap(&rdap));
checks
.sub(crate::check::RdapStructure::PublicIds)
.expect("Public Ids not found")
.items
.iter()
.find(|c| c.check == Check::PublicIdIdentifierIsAbsent)
.expect("public id missing check");
}
#[test]
fn test_notice_with_no_description() {
let notice = NoticeOrRemark {
title: None,
description: None,
links: None,
nr_type: None,
simple_redaction_keys: None,
};
let rdap = Domain::response_obj()
.ldh_name("example.com")
.notice(Notice(notice))
.build()
.to_response();
let checks = rdap.get_checks(None, CheckParams::for_rdap(&rdap));
dbg!(&checks);
assert!(contains_check(
Check::NoticeOrRemarkDescriptionIsAbsent,
&checks
));
}
#[test]
fn test_notice_with_unknown_type() {
let notice = Notice::builder()
.nr_type("unknown_type")
.description_entry("stuff")
.build();
let rdap = Domain::response_obj()
.ldh_name("example.com")
.notice(notice)
.build()
.to_response();
let checks = rdap.get_checks(None, CheckParams::for_rdap(&rdap));
dbg!(&checks);
assert!(contains_check(Check::NoticeOrRemarkUnknownType, &checks));
}
#[test]
fn test_entity_array_is_empty() {
let mut rdap = Domain::builder().ldh_name("example.com").build();
rdap.object_common.entities = Some(vec![]);
let rdap = rdap.to_response();
let checks = rdap.get_checks(None, CheckParams::for_rdap(&rdap));
dbg!(&checks);
assert!(contains_check(Check::EntityArrayIsEmpty, &checks));
}
#[test]
fn test_remarks_array_is_empty() {
let mut rdap = Domain::builder().ldh_name("example.com").build();
rdap.object_common.remarks = Some(vec![]);
let rdap = rdap.to_response();
let checks = rdap.get_checks(None, CheckParams::for_rdap(&rdap));
dbg!(&checks);
assert!(contains_check(Check::RemarksArrayIsEmpty, &checks));
}
#[test]
fn test_notices_array_is_empty() {
let mut rdap = Domain::builder().ldh_name("example.com").build();
rdap.common.notices = Some(vec![]);
let rdap = rdap.to_response();
let checks = rdap.get_checks(None, CheckParams::for_rdap(&rdap));
dbg!(&checks);
assert!(contains_check(Check::NoticesArrayIsEmpty, &checks));
}
#[test]
fn test_links_array_is_empty() {
let mut rdap = Domain::builder().ldh_name("example.com").build();
rdap.object_common.links = Some(vec![]);
let rdap = rdap.to_response();
let checks = rdap.get_checks(None, CheckParams::for_rdap(&rdap));
dbg!(&checks);
assert!(contains_check(Check::LinksArrayIsEmpty, &checks));
}
#[test]
fn test_nameserver_with_no_links() {
let rdap = Nameserver::builder()
.ldh_name("example.com")
.build()
.expect("building nameserver")
.to_response();
let checks = rdap.get_checks(None, CheckParams::for_rdap(&rdap));
assert!(checks.sub(crate::check::RdapStructure::Links).is_none());
}
#[test]
fn test_nameserver_with_no_self_links() {
let rdap = Nameserver::builder()
.ldh_name("example.com")
.link(
Link::builder()
.href("https://foo")
.value("https://foo")
.rel("no_self")
.media_type("foo")
.build(),
)
.build()
.expect("building nameserver")
.to_response();
let checks = rdap.get_checks(None, CheckParams::for_rdap(&rdap));
assert!(!contains_check(Check::LinkObjectClassHasNoSelf, &checks));
}
#[rstest]
#[case(vec![])]
#[case(vec!["".to_string()])]
#[case(vec![" ".to_string()])]
#[case(vec![" ".to_string(), "foo".to_string()])]
#[test]
fn test_nameserver_with_empty_status(#[case] status: Vec<String>) {
let mut ns = Nameserver::builder()
.ldh_name("ns1.example.com")
.build()
.unwrap();
ns.object_common.status = Some(VectorStringish::from(status));
let rdap = ns.to_response();
let checks = rdap.get_checks(None, CheckParams::for_rdap(&rdap));
assert!(checks
.sub(crate::check::RdapStructure::Status)
.expect("status not found")
.items
.iter()
.any(|c| c.check == Check::StatusIsEmpty));
}
#[test]
fn test_unknown_status_value() {
let domain = Domain::builder()
.ldh_name("foo.example")
.status("not_a_valid")
.build()
.to_response();
let checks = domain.get_checks(None, CheckParams::for_rdap(&domain));
assert!(contains_check(Check::StatusValueUnknown, &checks));
}
#[rstest]
#[case("")]
#[case(" ")]
#[test]
fn test_nameserver_with_empty_handle(#[case] handle: &str) {
let mut ns = Nameserver::builder()
.ldh_name("ns1.example.com")
.build()
.unwrap();
ns.object_common.handle = Some(handle.to_string().into());
let rdap = ns.to_response();
let checks = rdap.get_checks(None, CheckParams::for_rdap(&rdap));
assert!(checks
.sub(crate::check::RdapStructure::Handle)
.expect("handle not found")
.items
.iter()
.any(|c| c.check == Check::HandleIsEmpty));
}
#[test]
fn test_nameserver_with_non_string_handle() {
let json = r#"
{
"objectClassName" : "nameserver",
"ldhName" : "ns1.example.com",
"handle" : 1234
}
"#;
let rdap = serde_json::from_str::<RdapResponse>(json).expect("parsing JSON");
let checks = rdap.get_checks(None, CheckParams::for_rdap(&rdap));
assert!(checks
.sub(crate::check::RdapStructure::Handle)
.expect("handle not found")
.items
.iter()
.any(|c| c.check == Check::HandleIsNotString));
}
#[test]
fn test_rdap_conformance_is_empty() {
let mut rdap = Domain::builder().ldh_name("example.com").build();
rdap.common.rdap_conformance = Some(vec![]);
let rdap = rdap.to_response();
let checks = rdap.get_checks(None, CheckParams::for_rdap(&rdap));
dbg!(&checks);
assert!(contains_check(Check::RdapConformanceIsEmpty, &checks));
}
#[test]
fn test_rdap_conformance_not_in_root() {
let json = r#"
{
"rdapConformance": ["rdap_level_0"],
"objectClassName" : "domain",
"handle" : "XXXX",
"ldhName" : "xn--fo-5ja.example",
"entities" :
[
{
"rdapConformance": ["rdap_level_0"],
"objectClassName" : "entity",
"handle" : "XXXX"
}
]
}
"#;
let rdap = serde_json::from_str::<RdapResponse>(json).expect("parsing JSON");
let checks = rdap.get_checks(None, CheckParams::for_rdap(&rdap));
dbg!(&checks);
assert!(contains_check(Check::RdapConformanceInvalidParent, &checks));
}
fn find_any_check(checks: &Checks, check_type: Check) -> bool {
if checks.items.iter().any(|c| c.check == check_type) {
return true;
}
if checks
.sub_checks
.iter()
.any(|c| find_any_check(c, check_type))
{
return true;
}
false
}
}