You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
120 lines
4.2 KiB
120 lines
4.2 KiB
2 years ago
|
use std::collections::BTreeSet;
|
||
|
use std::fs::read_to_string;
|
||
|
use std::path::Path;
|
||
|
|
||
|
use config::{Config, FileFormat};
|
||
2 years ago
|
use eyre::Context;
|
||
2 years ago
|
use walkdir::WalkDir;
|
||
|
|
||
|
use hyperlane_base::{RawSettings, Settings};
|
||
1 year ago
|
use hyperlane_core::{config::*, KnownHyperlaneDomain};
|
||
2 years ago
|
|
||
|
/// Relative path to the `hyperlane-monorepo/rust/config/`
|
||
|
/// directory, which is where the agent's config files
|
||
|
/// currently live.
|
||
|
const AGENT_CONFIG_PATH_ROOT: &str = "../config";
|
||
|
|
||
|
/// We will not include any file paths of config/settings files
|
||
|
/// in the test suite if *any* substring of the file path matches
|
||
|
/// against one of the strings included in the blacklist below.
|
||
|
/// This is to ensure that e.g. when a backwards-incompatible
|
||
|
/// change is made in config file format, and agents can't parse
|
||
|
/// them anymore, we don't fail the test. (E.g. agents cannot
|
||
|
/// currently parse the older files in `config/dev/` or
|
||
|
/// `config/testnet`.
|
||
|
const BLACKLISTED_DIRS: &[&str] = &[
|
||
|
// Ignore only-local names of fake chains used by
|
||
|
// e.g. test suites.
|
||
|
"test/test_config.json",
|
||
|
];
|
||
|
|
||
|
fn is_blacklisted(path: &Path) -> bool {
|
||
|
BLACKLISTED_DIRS
|
||
|
.iter()
|
||
|
.any(|x| path.to_str().unwrap().contains(x))
|
||
|
}
|
||
|
|
||
|
#[derive(Clone, Debug, Ord, PartialEq, PartialOrd, Eq, Hash)]
|
||
|
struct ChainCoordinate {
|
||
|
name: String,
|
||
|
domain: u32,
|
||
|
}
|
||
|
|
||
|
fn config_paths(root: &Path) -> Vec<String> {
|
||
|
WalkDir::new(root)
|
||
|
.min_depth(2)
|
||
|
.into_iter()
|
||
|
.filter_map(|x| x.ok())
|
||
|
.map(|x| x.into_path())
|
||
|
.filter(|x| !is_blacklisted(x))
|
||
|
.map(|x| x.into_os_string())
|
||
|
.filter_map(|x| x.into_string().ok())
|
||
|
.collect()
|
||
|
}
|
||
|
|
||
|
/// Provides a vector of parsed `hyperlane_base::Settings` objects
|
||
|
/// built from all of the version-controlled agent configuration files.
|
||
|
/// This is purely a utility to allow us to test a handful of critical
|
||
|
/// properties related to those configs and shouldn't be used outside
|
||
|
/// of a test env. This test simply tries to do some sanity checks
|
||
|
/// against the integrity of that data.
|
||
|
fn hyperlane_settings() -> Vec<Settings> {
|
||
1 year ago
|
// Determine the config path based on the crate root so that
|
||
|
// the debugger can also find the config file.
|
||
|
let crate_root = env!("CARGO_MANIFEST_DIR");
|
||
|
let config_path = format!("{}/{}", crate_root, AGENT_CONFIG_PATH_ROOT);
|
||
|
let root = Path::new(config_path.as_str());
|
||
2 years ago
|
let paths = config_paths(root);
|
||
|
let files: Vec<String> = paths
|
||
|
.iter()
|
||
|
.filter_map(|x| read_to_string(x).ok())
|
||
|
.collect();
|
||
|
paths
|
||
|
.iter()
|
||
|
.zip(files.iter())
|
||
1 year ago
|
// Filter out config files that can't be parsed as json (e.g. env files)
|
||
|
.filter_map(|(p, f)| {
|
||
2 years ago
|
let raw: RawSettings = Config::builder()
|
||
|
.add_source(config::File::from_str(f.as_str(), FileFormat::Json))
|
||
|
.build()
|
||
1 year ago
|
.ok()?
|
||
2 years ago
|
.try_deserialize::<RawSettings>()
|
||
|
.unwrap_or_else(|e| {
|
||
|
panic!("!cfg({}): {:?}: {}", p, e, f);
|
||
|
});
|
||
2 years ago
|
Settings::from_config(raw, &ConfigPath::default())
|
||
2 years ago
|
.context("Config parsing error, please check the config reference (https://docs.hyperlane.xyz/docs/operators/agent-configuration/configuration-reference)")
|
||
1 year ago
|
.ok()
|
||
2 years ago
|
})
|
||
|
.collect()
|
||
|
}
|
||
|
|
||
|
fn chain_name_domain_records() -> BTreeSet<ChainCoordinate> {
|
||
|
hyperlane_settings()
|
||
|
.iter()
|
||
|
.flat_map(|x: &Settings| {
|
||
|
x.chains.values().map(|v| ChainCoordinate {
|
||
|
name: v.domain.name().into(),
|
||
|
domain: (&v.domain).try_into().expect("Invalid domain id"),
|
||
|
})
|
||
|
})
|
||
|
.collect()
|
||
|
}
|
||
|
|
||
|
#[test]
|
||
|
fn agent_json_config_consistency_checks() {
|
||
|
// Verify that the hard-coded, macro-maintained
|
||
|
// mapping in `hyperlane-core/src/chain.rs` named
|
||
|
// by the macro `domain_and_chain` is complete
|
||
|
// and in agreement with our on-disk json-based
|
||
|
// configuration data.
|
||
|
let chain_coords = chain_name_domain_records();
|
||
|
for ChainCoordinate { name, domain } in chain_coords.into_iter() {
|
||
|
assert_eq!(
|
||
|
KnownHyperlaneDomain::try_from(domain).unwrap().to_string(),
|
||
|
name
|
||
|
);
|
||
|
assert_eq!(name.parse::<KnownHyperlaneDomain>().unwrap() as u32, domain);
|
||
|
}
|
||
|
}
|