Skip to content

Commit f6208fb

Browse files
authored
chore: better tests (#13)
* fix tests * more tests * hardening * more tests * more tests * more * remove noise * cleanup * more tests * integration tests * tests
1 parent caa8a59 commit f6208fb

50 files changed

Lines changed: 3907 additions & 466 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

bin/evd/src/genesis_config.rs

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,3 +76,139 @@ impl AccountConfig {
7676
.map_err(|e| format!("invalid eth address '{}': {}", self.eth_address, e))
7777
}
7878
}
79+
80+
#[cfg(test)]
81+
mod tests {
82+
use super::*;
83+
use std::path::PathBuf;
84+
use std::sync::atomic::{AtomicU64, Ordering};
85+
86+
static TEMP_FILE_COUNTER: AtomicU64 = AtomicU64::new(0);
87+
88+
fn temp_genesis_file(contents: &str) -> PathBuf {
89+
let mut path = std::env::temp_dir();
90+
let id = TEMP_FILE_COUNTER.fetch_add(1, Ordering::Relaxed);
91+
path.push(format!("evd-genesis-{}-{id}.json", std::process::id()));
92+
std::fs::write(&path, contents).unwrap();
93+
path
94+
}
95+
96+
#[test]
97+
fn load_fails_for_missing_file() {
98+
let path = "/definitely/not/present/evd-genesis.json";
99+
let err = EvdGenesisConfig::load(path)
100+
.err()
101+
.expect("missing file must fail");
102+
assert!(err.contains("failed to read genesis file"));
103+
}
104+
105+
#[test]
106+
fn load_fails_for_empty_accounts() {
107+
let path = temp_genesis_file(
108+
r#"{
109+
"token": {
110+
"name": "Evolve",
111+
"symbol": "EV",
112+
"decimals": 6,
113+
"icon_url": "https://example.com/icon.png",
114+
"description": "token"
115+
},
116+
"minter_id": 100,
117+
"accounts": []
118+
}"#,
119+
);
120+
121+
let err = EvdGenesisConfig::load(path.to_str().unwrap())
122+
.err()
123+
.expect("empty accounts fail");
124+
assert!(err.contains("at least one account"));
125+
let _ = std::fs::remove_file(path);
126+
}
127+
128+
#[test]
129+
fn load_fails_for_invalid_account_address() {
130+
let path = temp_genesis_file(
131+
r#"{
132+
"token": {
133+
"name": "Evolve",
134+
"symbol": "EV",
135+
"decimals": 6,
136+
"icon_url": "https://example.com/icon.png",
137+
"description": "token"
138+
},
139+
"minter_id": 100,
140+
"accounts": [
141+
{ "eth_address": "not-an-address", "balance": 1 }
142+
]
143+
}"#,
144+
);
145+
146+
let err = EvdGenesisConfig::load(path.to_str().unwrap())
147+
.err()
148+
.expect("invalid address must fail");
149+
assert!(err.contains("account[0]"));
150+
assert!(err.contains("invalid eth address"));
151+
let _ = std::fs::remove_file(path);
152+
}
153+
154+
#[test]
155+
fn load_succeeds_for_valid_config() {
156+
let path = temp_genesis_file(
157+
r#"{
158+
"token": {
159+
"name": "Evolve",
160+
"symbol": "EV",
161+
"decimals": 6,
162+
"icon_url": "https://example.com/icon.png",
163+
"description": "token"
164+
},
165+
"minter_id": 100,
166+
"accounts": [
167+
{
168+
"eth_address": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
169+
"balance": 1000
170+
}
171+
]
172+
}"#,
173+
);
174+
175+
let cfg = EvdGenesisConfig::load(path.to_str().unwrap()).expect("valid config should load");
176+
assert_eq!(cfg.minter_id, 100);
177+
assert_eq!(cfg.accounts.len(), 1);
178+
assert_eq!(cfg.accounts[0].balance, 1000);
179+
let _ = std::fs::remove_file(path);
180+
}
181+
182+
#[test]
183+
fn parse_address_rejects_invalid_and_accepts_valid() {
184+
let bad = AccountConfig {
185+
eth_address: "bad-address".to_string(),
186+
balance: 1,
187+
};
188+
assert!(bad.parse_address().is_err());
189+
190+
let good = AccountConfig {
191+
eth_address: "0x0000000000000000000000000000000000000001".to_string(),
192+
balance: 1,
193+
};
194+
assert!(good.parse_address().is_ok());
195+
}
196+
197+
#[test]
198+
fn token_to_metadata_maps_all_fields() {
199+
let token = TokenConfig {
200+
name: "Evolve".to_string(),
201+
symbol: "EV".to_string(),
202+
decimals: 6,
203+
icon_url: "https://example.com/token.png".to_string(),
204+
description: "Sample token".to_string(),
205+
};
206+
let metadata = token.to_metadata();
207+
208+
assert_eq!(metadata.name, "Evolve");
209+
assert_eq!(metadata.symbol, "EV");
210+
assert_eq!(metadata.decimals, 6);
211+
assert_eq!(metadata.icon_url, "https://example.com/token.png");
212+
assert_eq!(metadata.description, "Sample token");
213+
}
214+
}

bin/txload/src/main.rs

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -525,3 +525,127 @@ async fn run_loadtest(config: LoadtestConfig) -> Result<(), String> {
525525

526526
Ok(())
527527
}
528+
529+
#[cfg(test)]
530+
mod tests {
531+
use super::*;
532+
533+
fn fixed_key() -> SigningKey {
534+
parse_signing_key("0x1111111111111111111111111111111111111111111111111111111111111111")
535+
.expect("fixed key should parse")
536+
}
537+
538+
#[test]
539+
fn parse_signing_key_accepts_prefixed_and_unprefixed_hex() {
540+
let with_prefix =
541+
parse_signing_key("0x1111111111111111111111111111111111111111111111111111111111111111")
542+
.expect("with-prefix key should parse");
543+
let without_prefix =
544+
parse_signing_key("1111111111111111111111111111111111111111111111111111111111111111")
545+
.expect("without-prefix key should parse");
546+
547+
assert_eq!(
548+
wallet_address(&with_prefix),
549+
wallet_address(&without_prefix)
550+
);
551+
}
552+
553+
#[test]
554+
fn parse_signing_key_rejects_wrong_length() {
555+
let err = parse_signing_key("0x1234").expect_err("short key must fail");
556+
assert!(err.contains("private key must be 32 bytes"));
557+
}
558+
559+
#[test]
560+
fn parse_hex_u64_handles_common_cases() {
561+
assert_eq!(parse_hex_u64("0x0").unwrap(), 0);
562+
assert_eq!(parse_hex_u64("0Xff").unwrap(), 255);
563+
assert_eq!(parse_hex_u64("a").unwrap(), 10);
564+
assert_eq!(parse_hex_u64("").unwrap(), 0);
565+
assert!(parse_hex_u64("0xzz").is_err());
566+
}
567+
568+
#[test]
569+
fn build_transfer_calldata_has_selector_and_borsh_args() {
570+
let recipient = AccountId::new(42);
571+
let amount = 999_u128;
572+
573+
let calldata = build_transfer_calldata(recipient, amount).unwrap();
574+
assert!(calldata.len() > 4);
575+
assert_eq!(&calldata[0..4], &transfer_selector());
576+
577+
let decoded: (AccountId, u128) = borsh::from_slice(&calldata[4..]).unwrap();
578+
assert_eq!(decoded.0, recipient);
579+
assert_eq!(decoded.1, amount);
580+
}
581+
582+
#[test]
583+
fn create_signed_tx_is_type_2_and_deterministic() {
584+
let key = fixed_key();
585+
let params = TxBuildParams {
586+
chain_id: 1,
587+
to: wallet_address(&key),
588+
gas_limit: 21_000,
589+
max_priority_fee_per_gas: 1_000_000_000,
590+
max_fee_per_gas: 2_000_000_000,
591+
};
592+
let input = vec![1, 2, 3, 4];
593+
594+
let tx1 = create_signed_tx(&key, 7, input.clone(), params);
595+
let tx2 = create_signed_tx(&key, 7, input, params);
596+
597+
assert!(!tx1.is_empty());
598+
assert_eq!(tx1[0], 0x02);
599+
assert_eq!(tx1, tx2);
600+
}
601+
602+
#[tokio::test]
603+
async fn run_loadtest_validates_config_before_network() {
604+
let key = fixed_key();
605+
let token_account = wallet_address(&key);
606+
607+
let base = LoadtestConfig {
608+
rpc_url: "http://127.0.0.1:8545".to_string(),
609+
chain_id: 1,
610+
token_account,
611+
funder_key: key,
612+
workers: 2,
613+
target_tps: 1,
614+
duration_secs: 1,
615+
fanout_amount: 1,
616+
tx_amount: 1,
617+
gas_limit: 21_000,
618+
max_priority_fee_per_gas: 1,
619+
max_fee_per_gas: 1,
620+
funding_settle_secs: 0,
621+
request_timeout_ms: 100,
622+
};
623+
624+
let err_workers = run_loadtest(LoadtestConfig { workers: 1, ..base })
625+
.await
626+
.expect_err("workers < 2 should fail");
627+
assert!(err_workers.contains("workers must be >= 2"));
628+
629+
let key2 = fixed_key();
630+
let token2 = wallet_address(&key2);
631+
let err_tps = run_loadtest(LoadtestConfig {
632+
rpc_url: "http://127.0.0.1:8545".to_string(),
633+
chain_id: 1,
634+
token_account: token2,
635+
funder_key: key2,
636+
workers: 2,
637+
target_tps: 0,
638+
duration_secs: 1,
639+
fanout_amount: 1,
640+
tx_amount: 1,
641+
gas_limit: 21_000,
642+
max_priority_fee_per_gas: 1,
643+
max_fee_per_gas: 1,
644+
funding_settle_secs: 0,
645+
request_timeout_ms: 100,
646+
})
647+
.await
648+
.expect_err("target_tps == 0 should fail");
649+
assert!(err_tps.contains("target_tps must be > 0"));
650+
}
651+
}

0 commit comments

Comments
 (0)