rtr client 开发
This commit is contained in:
@@ -114,7 +114,7 @@ async fn router_key_length_matches_wire_size() {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn router_key_read_rejects_reserved_zero_octet() {
|
||||
async fn router_key_read_accepts_non_zero_reserved_zero_octet() {
|
||||
let (mut client, mut server) = duplex(1024);
|
||||
let mut bytes = vec![1, RouterKey::PDU, 1, 1];
|
||||
bytes.extend_from_slice(&(8u32 + 20 + 4 + 4).to_be_bytes());
|
||||
@@ -124,9 +124,9 @@ async fn router_key_read_rejects_reserved_zero_octet() {
|
||||
|
||||
client.write_all(&bytes).await.unwrap();
|
||||
|
||||
let err = RouterKey::read(&mut server).await.unwrap_err();
|
||||
assert_eq!(err.kind(), std::io::ErrorKind::InvalidData);
|
||||
assert!(err.to_string().contains("zero octet"));
|
||||
let decoded = RouterKey::read(&mut server).await.unwrap();
|
||||
assert_eq!(decoded.asn(), Asn::from(64496u32));
|
||||
assert_eq!(decoded.spki(), &[1, 2, 3, 4]);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -159,6 +159,19 @@ async fn aspa_read_rejects_unsorted_provider_list() {
|
||||
assert!(err.to_string().contains("strictly increasing"));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn aspa_read_accepts_non_zero_reserved_zero_octet() {
|
||||
let (mut client, mut server) = duplex(1024);
|
||||
let mut bytes = vec![2, Aspa::PDU, 1, 9];
|
||||
bytes.extend_from_slice(&(16u32).to_be_bytes());
|
||||
bytes.extend_from_slice(&64496u32.to_be_bytes());
|
||||
bytes.extend_from_slice(&64497u32.to_be_bytes());
|
||||
|
||||
client.write_all(&bytes).await.unwrap();
|
||||
|
||||
let _decoded = Aspa::read(&mut server).await.unwrap();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn aspa_read_rejects_withdraw_with_providers() {
|
||||
let (mut client, mut server) = duplex(1024);
|
||||
|
||||
43
tests/test_pipeline.rs
Normal file
43
tests/test_pipeline.rs
Normal file
@@ -0,0 +1,43 @@
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use rpki::source::pipeline::{PayloadLoadConfig, load_payloads_from_latest_sources};
|
||||
use tempfile::tempdir;
|
||||
|
||||
fn data_dir() -> String {
|
||||
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("data")
|
||||
.to_string_lossy()
|
||||
.to_string()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn load_payloads_rejects_entire_slurm_set_when_any_file_is_invalid() {
|
||||
let slurm_dir = tempdir().expect("create temp slurm dir");
|
||||
|
||||
let valid = r#"{
|
||||
"slurmVersion": 1,
|
||||
"validationOutputFilters": {
|
||||
"prefixFilters": [],
|
||||
"bgpsecFilters": []
|
||||
},
|
||||
"locallyAddedAssertions": {
|
||||
"prefixAssertions": [],
|
||||
"bgpsecAssertions": []
|
||||
}
|
||||
}"#;
|
||||
|
||||
fs::write(slurm_dir.path().join("01-valid.slurm"), valid).expect("write valid slurm");
|
||||
fs::write(slurm_dir.path().join("02-invalid.slurm"), "{").expect("write invalid slurm");
|
||||
|
||||
let config = PayloadLoadConfig {
|
||||
ccr_dir: data_dir(),
|
||||
slurm_dir: Some(slurm_dir.path().to_string_lossy().to_string()),
|
||||
strict_ccr_validation: false,
|
||||
};
|
||||
|
||||
let err = load_payloads_from_latest_sources(&config).unwrap_err();
|
||||
let text = err.to_string();
|
||||
assert!(text.contains("failed to parse SLURM file"));
|
||||
assert!(text.contains("02-invalid.slurm"));
|
||||
}
|
||||
@@ -24,7 +24,47 @@ fn sample_ski_b64() -> String {
|
||||
STANDARD_NO_PAD.encode(sample_ski())
|
||||
}
|
||||
|
||||
fn log_slurm_input(name: &str, json: &str) {
|
||||
println!("[{name}] SLURM input:\n{json}");
|
||||
}
|
||||
|
||||
fn log_slurm_ok(name: &str, slurm: &SlurmFile) {
|
||||
println!(
|
||||
"[{name}] parsed ok: version={}, prefix_filters={}, bgpsec_filters={}, aspa_filters={}, prefix_assertions={}, bgpsec_assertions={}, aspa_assertions={}",
|
||||
slurm.version().as_u32(),
|
||||
slurm.validation_output_filters().prefix_filters.len(),
|
||||
slurm.validation_output_filters().bgpsec_filters.len(),
|
||||
slurm.validation_output_filters().aspa_filters.len(),
|
||||
slurm.locally_added_assertions().prefix_assertions.len(),
|
||||
slurm.locally_added_assertions().bgpsec_assertions.len(),
|
||||
slurm.locally_added_assertions().aspa_assertions.len(),
|
||||
);
|
||||
}
|
||||
|
||||
fn log_slurm_err(name: &str, err: &impl std::fmt::Display) {
|
||||
println!("[{name}] rejected: {err}");
|
||||
}
|
||||
|
||||
fn log_payload_result(name: &str, payloads: &[Payload]) {
|
||||
println!("[{name}] payload result count={}", payloads.len());
|
||||
for payload in payloads {
|
||||
println!("[{name}] payload: {:?}", payload);
|
||||
}
|
||||
}
|
||||
|
||||
fn assert_invalid_slurm(json: &str, expected: &str) {
|
||||
log_slurm_input("invalid_slurm", json);
|
||||
let err = SlurmFile::from_slice(json.as_bytes()).unwrap_err();
|
||||
log_slurm_err("invalid_slurm", &err);
|
||||
let err_text = err.to_string();
|
||||
assert!(
|
||||
err_text.contains(expected),
|
||||
"expected error containing '{expected}', got '{err_text}'"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
// Parses a baseline RFC 8416 v1 SLURM file with prefix and BGPsec entries.
|
||||
fn parses_rfc8416_v1_slurm() {
|
||||
let ski_b64 = sample_ski_b64();
|
||||
let router_public_key = STANDARD_NO_PAD.encode(sample_spki());
|
||||
@@ -50,7 +90,9 @@ fn parses_rfc8416_v1_slurm() {
|
||||
}}"#
|
||||
);
|
||||
|
||||
log_slurm_input("parses_rfc8416_v1_slurm", &json);
|
||||
let slurm = SlurmFile::from_slice(json.as_bytes()).unwrap();
|
||||
log_slurm_ok("parses_rfc8416_v1_slurm", &slurm);
|
||||
|
||||
assert_eq!(slurm.version(), SlurmVersion::V1);
|
||||
assert_eq!(slurm.validation_output_filters().prefix_filters.len(), 1);
|
||||
@@ -62,6 +104,7 @@ fn parses_rfc8416_v1_slurm() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
// Parses a v2 SLURM file carrying the ASPA extension members from the draft.
|
||||
fn parses_v2_slurm_with_aspa_extensions() {
|
||||
let json = r#"{
|
||||
"slurmVersion": 2,
|
||||
@@ -81,7 +124,9 @@ fn parses_v2_slurm_with_aspa_extensions() {
|
||||
}
|
||||
}"#;
|
||||
|
||||
log_slurm_input("parses_v2_slurm_with_aspa_extensions", json);
|
||||
let slurm = SlurmFile::from_slice(json.as_bytes()).unwrap();
|
||||
log_slurm_ok("parses_v2_slurm_with_aspa_extensions", &slurm);
|
||||
|
||||
assert_eq!(slurm.version(), SlurmVersion::V2);
|
||||
assert_eq!(slurm.validation_output_filters().aspa_filters.len(), 1);
|
||||
@@ -89,6 +134,7 @@ fn parses_v2_slurm_with_aspa_extensions() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
// Rejects ASPA members in a v1 file because they are not part of RFC 8416 v1.
|
||||
fn rejects_v1_file_with_aspa_members() {
|
||||
let json = r#"{
|
||||
"slurmVersion": 1,
|
||||
@@ -103,11 +149,253 @@ fn rejects_v1_file_with_aspa_members() {
|
||||
}
|
||||
}"#;
|
||||
|
||||
log_slurm_input("rejects_v1_file_with_aspa_members", json);
|
||||
let err = SlurmFile::from_slice(json.as_bytes()).unwrap_err();
|
||||
log_slurm_err("rejects_v1_file_with_aspa_members", &err);
|
||||
assert!(err.to_string().contains("unknown field"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
// Rejects malformed v1 top-level objects and nested containers that violate RFC 8416 member rules.
|
||||
fn rejects_invalid_v1_file_structure() {
|
||||
let cases = [
|
||||
(
|
||||
r#"{
|
||||
"validationOutputFilters": {
|
||||
"prefixFilters": [],
|
||||
"bgpsecFilters": []
|
||||
},
|
||||
"locallyAddedAssertions": {
|
||||
"prefixAssertions": [],
|
||||
"bgpsecAssertions": []
|
||||
}
|
||||
}"#,
|
||||
"missing field `slurmVersion`",
|
||||
),
|
||||
(
|
||||
r#"{
|
||||
"slurmVersion": 3,
|
||||
"validationOutputFilters": {
|
||||
"prefixFilters": [],
|
||||
"bgpsecFilters": []
|
||||
},
|
||||
"locallyAddedAssertions": {
|
||||
"prefixAssertions": [],
|
||||
"bgpsecAssertions": []
|
||||
}
|
||||
}"#,
|
||||
"unsupported slurmVersion 3",
|
||||
),
|
||||
(
|
||||
r#"{
|
||||
"slurmVersion": 1,
|
||||
"locallyAddedAssertions": {
|
||||
"prefixAssertions": [],
|
||||
"bgpsecAssertions": []
|
||||
}
|
||||
}"#,
|
||||
"missing field `validationOutputFilters`",
|
||||
),
|
||||
(
|
||||
r#"{
|
||||
"slurmVersion": 1,
|
||||
"validationOutputFilters": {
|
||||
"prefixFilters": [],
|
||||
"bgpsecFilters": []
|
||||
}
|
||||
}"#,
|
||||
"missing field `locallyAddedAssertions`",
|
||||
),
|
||||
(
|
||||
r#"{
|
||||
"slurmVersion": 1,
|
||||
"validationOutputFilters": {
|
||||
"prefixFilters": [],
|
||||
"bgpsecFilters": []
|
||||
},
|
||||
"locallyAddedAssertions": {
|
||||
"prefixAssertions": [],
|
||||
"bgpsecAssertions": []
|
||||
},
|
||||
"extra": true
|
||||
}"#,
|
||||
"unknown field `extra`",
|
||||
),
|
||||
(
|
||||
r#"{
|
||||
"slurmVersion": 1,
|
||||
"validationOutputFilters": {
|
||||
"bgpsecFilters": []
|
||||
},
|
||||
"locallyAddedAssertions": {
|
||||
"prefixAssertions": [],
|
||||
"bgpsecAssertions": []
|
||||
}
|
||||
}"#,
|
||||
"missing field `prefixFilters`",
|
||||
),
|
||||
(
|
||||
r#"{
|
||||
"slurmVersion": 1,
|
||||
"validationOutputFilters": {
|
||||
"prefixFilters": []
|
||||
},
|
||||
"locallyAddedAssertions": {
|
||||
"prefixAssertions": [],
|
||||
"bgpsecAssertions": []
|
||||
}
|
||||
}"#,
|
||||
"missing field `bgpsecFilters`",
|
||||
),
|
||||
(
|
||||
r#"{
|
||||
"slurmVersion": 1,
|
||||
"validationOutputFilters": {
|
||||
"prefixFilters": [],
|
||||
"bgpsecFilters": [],
|
||||
"aspaFilters": []
|
||||
},
|
||||
"locallyAddedAssertions": {
|
||||
"prefixAssertions": [],
|
||||
"bgpsecAssertions": []
|
||||
}
|
||||
}"#,
|
||||
"unknown field `aspaFilters`",
|
||||
),
|
||||
(
|
||||
r#"{
|
||||
"slurmVersion": 1,
|
||||
"validationOutputFilters": {
|
||||
"prefixFilters": [],
|
||||
"bgpsecFilters": []
|
||||
},
|
||||
"locallyAddedAssertions": {
|
||||
"bgpsecAssertions": []
|
||||
}
|
||||
}"#,
|
||||
"missing field `prefixAssertions`",
|
||||
),
|
||||
(
|
||||
r#"{
|
||||
"slurmVersion": 1,
|
||||
"validationOutputFilters": {
|
||||
"prefixFilters": [],
|
||||
"bgpsecFilters": []
|
||||
},
|
||||
"locallyAddedAssertions": {
|
||||
"prefixAssertions": []
|
||||
}
|
||||
}"#,
|
||||
"missing field `bgpsecAssertions`",
|
||||
),
|
||||
(
|
||||
r#"{
|
||||
"slurmVersion": 1,
|
||||
"validationOutputFilters": {
|
||||
"prefixFilters": [],
|
||||
"bgpsecFilters": []
|
||||
},
|
||||
"locallyAddedAssertions": {
|
||||
"prefixAssertions": [],
|
||||
"bgpsecAssertions": [],
|
||||
"aspaAssertions": []
|
||||
}
|
||||
}"#,
|
||||
"unknown field `aspaAssertions`",
|
||||
),
|
||||
];
|
||||
|
||||
for (json, expected) in cases {
|
||||
assert_invalid_slurm(json, expected);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
// Rejects malformed v1 member objects that contain unknown fields or omit mandatory members.
|
||||
fn rejects_invalid_v1_member_structure() {
|
||||
let ski_b64 = sample_ski_b64();
|
||||
let router_public_key = STANDARD_NO_PAD.encode(sample_spki());
|
||||
|
||||
let cases = vec![
|
||||
(
|
||||
r#"{
|
||||
"slurmVersion": 1,
|
||||
"validationOutputFilters": {
|
||||
"prefixFilters": [
|
||||
{ "prefix": "192.0.2.0/24", "unexpected": true }
|
||||
],
|
||||
"bgpsecFilters": []
|
||||
},
|
||||
"locallyAddedAssertions": {
|
||||
"prefixAssertions": [],
|
||||
"bgpsecAssertions": []
|
||||
}
|
||||
}"#
|
||||
.to_string(),
|
||||
"unknown field `unexpected`",
|
||||
),
|
||||
(
|
||||
format!(
|
||||
r#"{{
|
||||
"slurmVersion": 1,
|
||||
"validationOutputFilters": {{
|
||||
"prefixFilters": [],
|
||||
"bgpsecFilters": [
|
||||
{{ "asn": 64496, "SKI": "{ski_b64}", "unexpected": true }}
|
||||
]
|
||||
}},
|
||||
"locallyAddedAssertions": {{
|
||||
"prefixAssertions": [],
|
||||
"bgpsecAssertions": []
|
||||
}}
|
||||
}}"#
|
||||
),
|
||||
"unknown field `unexpected`",
|
||||
),
|
||||
(
|
||||
r#"{
|
||||
"slurmVersion": 1,
|
||||
"validationOutputFilters": {
|
||||
"prefixFilters": [],
|
||||
"bgpsecFilters": []
|
||||
},
|
||||
"locallyAddedAssertions": {
|
||||
"prefixAssertions": [
|
||||
{ "prefix": "198.51.100.0/24" }
|
||||
],
|
||||
"bgpsecAssertions": []
|
||||
}
|
||||
}"#
|
||||
.to_string(),
|
||||
"missing field `asn`",
|
||||
),
|
||||
(
|
||||
format!(
|
||||
r#"{{
|
||||
"slurmVersion": 1,
|
||||
"validationOutputFilters": {{
|
||||
"prefixFilters": [],
|
||||
"bgpsecFilters": []
|
||||
}},
|
||||
"locallyAddedAssertions": {{
|
||||
"prefixAssertions": [],
|
||||
"bgpsecAssertions": [
|
||||
{{ "asn": 64501, "SKI": "{ski_b64}", "routerPublicKey": "{router_public_key}", "unexpected": true }}
|
||||
]
|
||||
}}
|
||||
}}"#
|
||||
),
|
||||
"unknown field `unexpected`",
|
||||
),
|
||||
];
|
||||
|
||||
for (json, expected) in cases {
|
||||
assert_invalid_slurm(&json, expected);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
// Rejects non-canonical prefixes and unsorted ASPA provider lists during validation.
|
||||
fn rejects_non_canonical_prefixes_and_unsorted_aspa_providers() {
|
||||
let non_canonical = r#"{
|
||||
"slurmVersion": 1,
|
||||
@@ -122,7 +410,15 @@ fn rejects_non_canonical_prefixes_and_unsorted_aspa_providers() {
|
||||
"bgpsecAssertions": []
|
||||
}
|
||||
}"#;
|
||||
log_slurm_input(
|
||||
"rejects_non_canonical_prefixes_and_unsorted_aspa_providers.non_canonical",
|
||||
non_canonical,
|
||||
);
|
||||
let non_canonical_err = SlurmFile::from_slice(non_canonical.as_bytes()).unwrap_err();
|
||||
log_slurm_err(
|
||||
"rejects_non_canonical_prefixes_and_unsorted_aspa_providers.non_canonical",
|
||||
&non_canonical_err,
|
||||
);
|
||||
assert!(non_canonical_err.to_string().contains("not canonical"));
|
||||
|
||||
let unsorted_aspa = r#"{
|
||||
@@ -140,11 +436,213 @@ fn rejects_non_canonical_prefixes_and_unsorted_aspa_providers() {
|
||||
]
|
||||
}
|
||||
}"#;
|
||||
log_slurm_input(
|
||||
"rejects_non_canonical_prefixes_and_unsorted_aspa_providers.unsorted_aspa",
|
||||
unsorted_aspa,
|
||||
);
|
||||
let aspa_err = SlurmFile::from_slice(unsorted_aspa.as_bytes()).unwrap_err();
|
||||
log_slurm_err(
|
||||
"rejects_non_canonical_prefixes_and_unsorted_aspa_providers.unsorted_aspa",
|
||||
&aspa_err,
|
||||
);
|
||||
assert!(aspa_err.to_string().contains("strictly increasing"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
// Rejects malformed v2 top-level objects and nested containers that violate the ASPA SLURM draft.
|
||||
fn rejects_invalid_v2_file_structure() {
|
||||
let cases = [
|
||||
(
|
||||
r#"{
|
||||
"slurmVersion": 2,
|
||||
"validationOutputFilters": {
|
||||
"prefixFilters": [],
|
||||
"bgpsecFilters": []
|
||||
},
|
||||
"locallyAddedAssertions": {
|
||||
"prefixAssertions": [],
|
||||
"bgpsecAssertions": [],
|
||||
"aspaAssertions": []
|
||||
}
|
||||
}"#,
|
||||
"missing field `aspaFilters`",
|
||||
),
|
||||
(
|
||||
r#"{
|
||||
"slurmVersion": 2,
|
||||
"validationOutputFilters": {
|
||||
"prefixFilters": [],
|
||||
"bgpsecFilters": [],
|
||||
"aspaFilters": []
|
||||
},
|
||||
"locallyAddedAssertions": {
|
||||
"prefixAssertions": [],
|
||||
"bgpsecAssertions": []
|
||||
}
|
||||
}"#,
|
||||
"missing field `aspaAssertions`",
|
||||
),
|
||||
(
|
||||
r#"{
|
||||
"slurmVersion": 2,
|
||||
"validationOutputFilters": {
|
||||
"prefixFilters": [],
|
||||
"bgpsecFilters": [],
|
||||
"aspaFilters": [],
|
||||
"extra": true
|
||||
},
|
||||
"locallyAddedAssertions": {
|
||||
"prefixAssertions": [],
|
||||
"bgpsecAssertions": [],
|
||||
"aspaAssertions": []
|
||||
}
|
||||
}"#,
|
||||
"unknown field `extra`",
|
||||
),
|
||||
(
|
||||
r#"{
|
||||
"slurmVersion": 2,
|
||||
"validationOutputFilters": {
|
||||
"prefixFilters": [],
|
||||
"bgpsecFilters": [],
|
||||
"aspaFilters": []
|
||||
},
|
||||
"locallyAddedAssertions": {
|
||||
"prefixAssertions": [],
|
||||
"bgpsecAssertions": [],
|
||||
"aspaAssertions": [],
|
||||
"extra": true
|
||||
}
|
||||
}"#,
|
||||
"unknown field `extra`",
|
||||
),
|
||||
(
|
||||
r#"{
|
||||
"slurmVersion": 2,
|
||||
"validationOutputFilters": {
|
||||
"prefixFilters": [],
|
||||
"bgpsecFilters": [],
|
||||
"aspaFilters": []
|
||||
},
|
||||
"locallyAddedAssertions": {
|
||||
"prefixAssertions": [],
|
||||
"bgpsecAssertions": [],
|
||||
"aspaAssertions": []
|
||||
},
|
||||
"extra": true
|
||||
}"#,
|
||||
"unknown field `extra`",
|
||||
),
|
||||
];
|
||||
|
||||
for (json, expected) in cases {
|
||||
assert_invalid_slurm(json, expected);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
// Rejects malformed v2 ASPA member objects that omit required fields or contain unknown fields.
|
||||
fn rejects_invalid_v2_member_structure() {
|
||||
let cases = [
|
||||
(
|
||||
r#"{
|
||||
"slurmVersion": 2,
|
||||
"validationOutputFilters": {
|
||||
"prefixFilters": [],
|
||||
"bgpsecFilters": [],
|
||||
"aspaFilters": [
|
||||
{ "comment": "missing customer" }
|
||||
]
|
||||
},
|
||||
"locallyAddedAssertions": {
|
||||
"prefixAssertions": [],
|
||||
"bgpsecAssertions": [],
|
||||
"aspaAssertions": []
|
||||
}
|
||||
}"#,
|
||||
"missing field `customerAsn`",
|
||||
),
|
||||
(
|
||||
r#"{
|
||||
"slurmVersion": 2,
|
||||
"validationOutputFilters": {
|
||||
"prefixFilters": [],
|
||||
"bgpsecFilters": [],
|
||||
"aspaFilters": [
|
||||
{ "customerAsn": 64496, "unexpected": true }
|
||||
]
|
||||
},
|
||||
"locallyAddedAssertions": {
|
||||
"prefixAssertions": [],
|
||||
"bgpsecAssertions": [],
|
||||
"aspaAssertions": []
|
||||
}
|
||||
}"#,
|
||||
"unknown field `unexpected`",
|
||||
),
|
||||
(
|
||||
r#"{
|
||||
"slurmVersion": 2,
|
||||
"validationOutputFilters": {
|
||||
"prefixFilters": [],
|
||||
"bgpsecFilters": [],
|
||||
"aspaFilters": []
|
||||
},
|
||||
"locallyAddedAssertions": {
|
||||
"prefixAssertions": [],
|
||||
"bgpsecAssertions": [],
|
||||
"aspaAssertions": [
|
||||
{ "customerAsn": 64500 }
|
||||
]
|
||||
}
|
||||
}"#,
|
||||
"missing field `providerAsns`",
|
||||
),
|
||||
(
|
||||
r#"{
|
||||
"slurmVersion": 2,
|
||||
"validationOutputFilters": {
|
||||
"prefixFilters": [],
|
||||
"bgpsecFilters": [],
|
||||
"aspaFilters": []
|
||||
},
|
||||
"locallyAddedAssertions": {
|
||||
"prefixAssertions": [],
|
||||
"bgpsecAssertions": [],
|
||||
"aspaAssertions": [
|
||||
{ "providerAsns": [64501] }
|
||||
]
|
||||
}
|
||||
}"#,
|
||||
"missing field `customerAsn`",
|
||||
),
|
||||
(
|
||||
r#"{
|
||||
"slurmVersion": 2,
|
||||
"validationOutputFilters": {
|
||||
"prefixFilters": [],
|
||||
"bgpsecFilters": [],
|
||||
"aspaFilters": []
|
||||
},
|
||||
"locallyAddedAssertions": {
|
||||
"prefixAssertions": [],
|
||||
"bgpsecAssertions": [],
|
||||
"aspaAssertions": [
|
||||
{ "customerAsn": 64500, "providerAsns": [64501], "unexpected": true }
|
||||
]
|
||||
}
|
||||
}"#,
|
||||
"unknown field `unexpected`",
|
||||
),
|
||||
];
|
||||
|
||||
for (json, expected) in cases {
|
||||
assert_invalid_slurm(json, expected);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
// Applies filters first, then adds assertions, while removing duplicate payloads.
|
||||
fn applies_filters_before_assertions_and_excludes_duplicates() {
|
||||
let ski = Ski::from_bytes(sample_ski());
|
||||
let spki = sample_spki();
|
||||
@@ -178,7 +676,12 @@ fn applies_filters_before_assertions_and_excludes_duplicates() {
|
||||
}}
|
||||
}}"#
|
||||
);
|
||||
log_slurm_input("applies_filters_before_assertions_and_excludes_duplicates", &json);
|
||||
let slurm = SlurmFile::from_slice(json.as_bytes()).unwrap();
|
||||
log_slurm_ok(
|
||||
"applies_filters_before_assertions_and_excludes_duplicates",
|
||||
&slurm,
|
||||
);
|
||||
|
||||
let input = vec![
|
||||
Payload::RouteOrigin(RouteOrigin::new(
|
||||
@@ -196,6 +699,10 @@ fn applies_filters_before_assertions_and_excludes_duplicates() {
|
||||
];
|
||||
|
||||
let output = slurm.apply(&input);
|
||||
log_payload_result(
|
||||
"applies_filters_before_assertions_and_excludes_duplicates",
|
||||
&output,
|
||||
);
|
||||
|
||||
assert_eq!(output.len(), 4);
|
||||
assert!(output.iter().any(|payload| matches!(
|
||||
@@ -221,6 +728,7 @@ fn applies_filters_before_assertions_and_excludes_duplicates() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
// Rejects non-RFC hex SKI encoding and ASPA assertions that self-reference the customer ASN.
|
||||
fn rejects_hex_encoded_ski_and_aspa_customer_in_providers() {
|
||||
let ski_hex = hex::encode(sample_ski());
|
||||
let router_public_key = STANDARD_NO_PAD.encode(sample_spki());
|
||||
@@ -241,7 +749,15 @@ fn rejects_hex_encoded_ski_and_aspa_customer_in_providers() {
|
||||
}}
|
||||
}}"#
|
||||
);
|
||||
log_slurm_input(
|
||||
"rejects_hex_encoded_ski_and_aspa_customer_in_providers.invalid_ski",
|
||||
&invalid_ski,
|
||||
);
|
||||
let ski_err = SlurmFile::from_slice(invalid_ski.as_bytes()).unwrap_err();
|
||||
log_slurm_err(
|
||||
"rejects_hex_encoded_ski_and_aspa_customer_in_providers.invalid_ski",
|
||||
&ski_err,
|
||||
);
|
||||
let ski_err_text = ski_err.to_string();
|
||||
assert!(
|
||||
ski_err_text.contains("invalid SKI base64")
|
||||
@@ -263,13 +779,22 @@ fn rejects_hex_encoded_ski_and_aspa_customer_in_providers() {
|
||||
]
|
||||
}
|
||||
}"#;
|
||||
log_slurm_input(
|
||||
"rejects_hex_encoded_ski_and_aspa_customer_in_providers.invalid_aspa",
|
||||
invalid_aspa,
|
||||
);
|
||||
let aspa_err = SlurmFile::from_slice(invalid_aspa.as_bytes()).unwrap_err();
|
||||
log_slurm_err(
|
||||
"rejects_hex_encoded_ski_and_aspa_customer_in_providers.invalid_aspa",
|
||||
&aspa_err,
|
||||
);
|
||||
assert!(aspa_err
|
||||
.to_string()
|
||||
.contains("providerAsns must not contain customerAsn"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
// Merges non-overlapping SLURM files and upgrades the merged policy version as needed.
|
||||
fn merges_multiple_slurm_files_without_conflict() {
|
||||
let a = r#"{
|
||||
"slurmVersion": 1,
|
||||
@@ -301,6 +826,9 @@ fn merges_multiple_slurm_files_without_conflict() {
|
||||
}
|
||||
}"#;
|
||||
|
||||
log_slurm_input("merges_multiple_slurm_files_without_conflict.a", a);
|
||||
log_slurm_input("merges_multiple_slurm_files_without_conflict.b", b);
|
||||
|
||||
let merged = SlurmFile::merge_named(vec![
|
||||
(
|
||||
"a.slurm".to_string(),
|
||||
@@ -312,6 +840,7 @@ fn merges_multiple_slurm_files_without_conflict() {
|
||||
),
|
||||
])
|
||||
.unwrap();
|
||||
log_slurm_ok("merges_multiple_slurm_files_without_conflict.merged", &merged);
|
||||
|
||||
assert_eq!(merged.version(), SlurmVersion::V2);
|
||||
assert_eq!(merged.locally_added_assertions().prefix_assertions.len(), 1);
|
||||
@@ -319,6 +848,7 @@ fn merges_multiple_slurm_files_without_conflict() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
// Rejects multiple SLURM files whose policy scopes overlap and would conflict when merged.
|
||||
fn rejects_conflicting_multiple_slurm_files() {
|
||||
let a = r#"{
|
||||
"slurmVersion": 1,
|
||||
@@ -348,6 +878,9 @@ fn rejects_conflicting_multiple_slurm_files() {
|
||||
}
|
||||
}"#;
|
||||
|
||||
log_slurm_input("rejects_conflicting_multiple_slurm_files.a", a);
|
||||
log_slurm_input("rejects_conflicting_multiple_slurm_files.b", b);
|
||||
|
||||
let err = SlurmFile::merge_named(vec![
|
||||
(
|
||||
"a.slurm".to_string(),
|
||||
@@ -359,6 +892,7 @@ fn rejects_conflicting_multiple_slurm_files() {
|
||||
),
|
||||
])
|
||||
.unwrap_err();
|
||||
log_slurm_err("rejects_conflicting_multiple_slurm_files", &err);
|
||||
|
||||
assert!(err.to_string().contains("conflicting SLURM files"));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user