Files
tycho-protocol-sdk/protocol-testing/src/config.rs
TAMARA LIPOWSKI 2c0c4d97fb refactor: Move module_name to integration test yaml
It's not good to keep this in the test_runner because then we have multiple points that we need to edit the test config, which can be hard to find.

We need this because the module name for uniswap_v2 is so far different from the rest of the protocols.
2025-09-23 00:42:23 -04:00

149 lines
5.4 KiB
Rust

use std::collections::{HashMap, HashSet};
use colored::Colorize;
use serde::{Deserialize, Serialize};
use similar::{ChangeTag, TextDiff};
use tycho_common::{dto::ProtocolComponent, Bytes};
/// Represents a ProtocolComponent with its main attributes
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct ProtocolComponentExpectation {
pub id: String,
pub tokens: Vec<Bytes>,
#[serde(default)]
pub static_attributes: HashMap<String, Bytes>,
pub creation_tx: Bytes,
}
/// Represents a ProtocolComponent with test configuration
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct ProtocolComponentWithTestConfig {
#[serde(flatten)]
pub base: ProtocolComponentExpectation,
#[serde(default = "default_false")]
pub skip_simulation: bool,
}
impl ProtocolComponentExpectation {
pub fn compare(&self, other: &ProtocolComponent, colorize_output: bool) -> Option<String> {
let mut diffs = Vec::new();
// Compare id (case-insensitive)
if self.id.to_lowercase() != other.id.to_lowercase() {
let diff = self.format_diff("id", &self.id, &other.id, colorize_output);
diffs.push(format!("Field 'id' mismatch for {}:\n{}", self.id, diff));
}
// Compare tokens (order-independent)
let self_tokens_set: HashSet<_> = self.tokens.iter().collect();
let other_tokens_set: HashSet<_> = other.tokens.iter().collect();
if self_tokens_set != other_tokens_set {
let self_tokens = format!("{:?}", self.tokens);
let other_tokens = format!("{:?}", other.tokens);
let diff = self.format_diff("tokens", &self_tokens, &other_tokens, colorize_output);
diffs.push(format!("Field 'tokens' mismatch for {}:\n{}", self.id, diff));
}
// Compare static_attributes
for (key, value) in &self.static_attributes {
let other_value = other.static_attributes.get(key);
match other_value {
Some(other_value) => {
if value != other_value {
let self_value = format!("{value:?}");
let other_value = format!("{other_value:?}");
let diff = self.format_diff(
"static_attributes",
&self_value,
&other_value,
colorize_output,
);
diffs.push(format!(
"Field 'static_attributes' mismatch for {}:\n{}",
self.id, diff
));
}
}
None => {
diffs.push(format!(
"Field 'static_attributes' mismatch for {}: Key '{}' not found. Available attributes: {:?}",
self.id, key, other.static_attributes,
));
}
}
}
// Compare creation_tx
if self.creation_tx != other.creation_tx {
let self_tx = format!("{}", self.creation_tx.clone());
let other_tx = format!("{}", other.creation_tx.clone());
let diff = self.format_diff("creation_tx", &self_tx, &other_tx, colorize_output);
diffs.push(format!("Field 'creation_tx' mismatch for {}:\n{}", self.id, diff));
}
if diffs.is_empty() {
None
} else {
Some(diffs.join("\n"))
}
}
fn format_diff(&self, _field_name: &str, left: &str, right: &str, colorize: bool) -> String {
let diff = TextDiff::from_lines(left, right);
let mut result = String::new();
for change in diff.iter_all_changes() {
let formatted = match change.tag() {
ChangeTag::Delete => {
if colorize {
format!("{}", format!("-{}", change.value().trim_end()).red())
} else {
format!("-{}", change.value().trim_end())
}
}
ChangeTag::Insert => {
if colorize {
format!("{}", format!("+{}", change.value().trim_end()).green())
} else {
format!("+{}", change.value().trim_end())
}
}
ChangeTag::Equal => {
format!(" {}", change.value().trim_end())
}
};
result.push_str(&formatted);
result.push('\n');
}
result
}
}
fn default_false() -> bool {
false
}
/// Configuration for an individual test
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct IntegrationTest {
pub name: String,
pub start_block: u64,
pub stop_block: u64,
pub initialized_accounts: Option<Vec<String>>,
pub expected_components: Vec<ProtocolComponentWithTestConfig>,
}
/// Main integration test configuration
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct IntegrationTestsConfig {
pub substreams_yaml_path: String,
pub adapter_contract: Option<String>,
pub adapter_build_signature: Option<String>,
pub adapter_build_args: Option<String>,
pub initialized_accounts: Option<Vec<String>>,
pub skip_balance_check: bool,
pub protocol_type_names: Vec<String>,
pub protocol_system: String,
pub module_name: String,
pub tests: Vec<IntegrationTest>,
}