test(governance): add OpenGov track configuration unit tests
Verify all 18 tracks (15 standard + 3 welati) have correct production periods, unique IDs/names, proper origin mappings, and no leftover test values.
This commit is contained in:
@@ -128,3 +128,187 @@ fn location_conversion_works() {
|
||||
assert_eq!(got, expected, "{}", tc.description);
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// OpenGov Track Configuration Tests
|
||||
// =============================================================================
|
||||
|
||||
use governance::TracksInfo;
|
||||
use pezkuwichain_runtime_constants::time::{DAYS, HOURS, MINUTES};
|
||||
use pezpallet_referenda::TracksInfo as TracksInfoTrait;
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[test]
|
||||
fn governance_tracks_total_count() {
|
||||
let count = <TracksInfo as TracksInfoTrait<Balance, BlockNumber>>::tracks().count();
|
||||
assert_eq!(count, 18, "Expected 18 tracks (15 standard + 3 welati), got {count}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn governance_track_ids_are_unique() {
|
||||
let mut seen = HashSet::new();
|
||||
for track in <TracksInfo as TracksInfoTrait<Balance, BlockNumber>>::tracks() {
|
||||
assert!(
|
||||
seen.insert(track.id),
|
||||
"Duplicate track ID: {}",
|
||||
track.id
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn governance_track_names_are_unique() {
|
||||
let mut seen = HashSet::new();
|
||||
for track in <TracksInfo as TracksInfoTrait<Balance, BlockNumber>>::tracks() {
|
||||
let name = String::from_utf8_lossy(&track.info.name).to_string();
|
||||
assert!(seen.insert(name.clone()), "Duplicate track name: {name}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn governance_no_test_periods_remain() {
|
||||
// Ensure no track still uses the old test values (< 1 HOURS for decision_period).
|
||||
// All production decision periods should be at least 7 DAYS.
|
||||
for track in <TracksInfo as TracksInfoTrait<Balance, BlockNumber>>::tracks() {
|
||||
let name = String::from_utf8_lossy(&track.info.name).to_string();
|
||||
assert!(
|
||||
track.info.decision_period >= 7 * DAYS,
|
||||
"Track '{name}' (id={}) has decision_period={} blocks, expected >= {} (7 DAYS)",
|
||||
track.id,
|
||||
track.info.decision_period,
|
||||
7 * DAYS
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn governance_production_periods_match_spec() {
|
||||
// Build expected values: (track_id, prepare, decision, confirm, enact)
|
||||
let expected: Vec<(u16, &str, BlockNumber, BlockNumber, BlockNumber, BlockNumber)> = vec![
|
||||
(0, "root", 2 * HOURS, 28 * DAYS, 24 * HOURS, 24 * HOURS),
|
||||
(1, "whitelisted_caller", 30 * MINUTES, 28 * DAYS, 10 * MINUTES, 10 * MINUTES),
|
||||
(10, "staking_admin", 2 * HOURS, 14 * DAYS, 3 * HOURS, 10 * MINUTES),
|
||||
(11, "treasurer", 2 * HOURS, 28 * DAYS, 3 * HOURS, 24 * HOURS),
|
||||
(12, "lease_admin", 2 * HOURS, 14 * DAYS, 3 * HOURS, 10 * MINUTES),
|
||||
(13, "fellowship_admin", 2 * HOURS, 14 * DAYS, 3 * HOURS, 10 * MINUTES),
|
||||
(14, "general_admin", 2 * HOURS, 14 * DAYS, 3 * HOURS, 10 * MINUTES),
|
||||
(15, "auction_admin", 2 * HOURS, 14 * DAYS, 3 * HOURS, 10 * MINUTES),
|
||||
(20, "referendum_canceller", 2 * HOURS, 7 * DAYS, 3 * HOURS, 10 * MINUTES),
|
||||
(21, "referendum_killer", 2 * HOURS, 14 * DAYS, 3 * HOURS, 10 * MINUTES),
|
||||
(30, "small_tipper", 1 * MINUTES, 7 * DAYS, 10 * MINUTES, 1 * MINUTES),
|
||||
(31, "big_tipper", 10 * MINUTES, 7 * DAYS, 1 * HOURS, 10 * MINUTES),
|
||||
(32, "small_spender", 4 * HOURS, 28 * DAYS, 12 * HOURS, 24 * HOURS),
|
||||
(33, "medium_spender", 4 * HOURS, 28 * DAYS, 24 * HOURS, 24 * HOURS),
|
||||
(34, "big_spender", 4 * HOURS, 28 * DAYS, 48 * HOURS, 24 * HOURS),
|
||||
(40, "welati_election", 2 * HOURS, 14 * DAYS, 12 * HOURS, 24 * HOURS),
|
||||
(41, "welati_admin", 2 * HOURS, 7 * DAYS, 3 * HOURS, 10 * MINUTES),
|
||||
(42, "citizenship_admin", 2 * HOURS, 14 * DAYS, 6 * HOURS, 24 * HOURS),
|
||||
];
|
||||
|
||||
let tracks: HashMap<u16, _> = <TracksInfo as TracksInfoTrait<Balance, BlockNumber>>::tracks()
|
||||
.map(|t| (t.id, t.into_owned()))
|
||||
.collect();
|
||||
|
||||
for (id, name, prepare, decision, confirm, enact) in &expected {
|
||||
let track = tracks.get(id).unwrap_or_else(|| panic!("Track id={id} '{name}' not found"));
|
||||
let got_name = String::from_utf8_lossy(&track.info.name).trim_end_matches('\0').to_string();
|
||||
assert_eq!(&got_name, name, "Track id={id} name mismatch");
|
||||
assert_eq!(
|
||||
track.info.prepare_period, *prepare,
|
||||
"Track '{name}' prepare_period: got={}, expected={prepare}",
|
||||
track.info.prepare_period
|
||||
);
|
||||
assert_eq!(
|
||||
track.info.decision_period, *decision,
|
||||
"Track '{name}' decision_period: got={}, expected={decision}",
|
||||
track.info.decision_period
|
||||
);
|
||||
assert_eq!(
|
||||
track.info.confirm_period, *confirm,
|
||||
"Track '{name}' confirm_period: got={}, expected={confirm}",
|
||||
track.info.confirm_period
|
||||
);
|
||||
assert_eq!(
|
||||
track.info.min_enactment_period, *enact,
|
||||
"Track '{name}' min_enactment_period: got={}, expected={enact}",
|
||||
track.info.min_enactment_period
|
||||
);
|
||||
}
|
||||
|
||||
assert_eq!(expected.len(), tracks.len(), "Track count mismatch");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn governance_welati_tracks_exist() {
|
||||
let tracks: HashMap<u16, _> = <TracksInfo as TracksInfoTrait<Balance, BlockNumber>>::tracks()
|
||||
.map(|t| (t.id, t.into_owned()))
|
||||
.collect();
|
||||
|
||||
// welati_election
|
||||
let t40 = tracks.get(&40).expect("welati_election track (id=40) missing");
|
||||
assert_eq!(t40.info.max_deciding, 1, "welati_election should allow only 1 deciding");
|
||||
|
||||
// welati_admin
|
||||
let t41 = tracks.get(&41).expect("welati_admin track (id=41) missing");
|
||||
assert_eq!(t41.info.max_deciding, 10);
|
||||
|
||||
// citizenship_admin
|
||||
let t42 = tracks.get(&42).expect("citizenship_admin track (id=42) missing");
|
||||
assert_eq!(t42.info.max_deciding, 10);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn governance_decision_periods_are_in_days() {
|
||||
// Verify all decision periods are expressed as multiples of DAYS (not minutes)
|
||||
for track in <TracksInfo as TracksInfoTrait<Balance, BlockNumber>>::tracks() {
|
||||
let name = String::from_utf8_lossy(&track.info.name).to_string();
|
||||
let period = track.info.decision_period;
|
||||
let days = period / DAYS;
|
||||
assert!(
|
||||
period == days * DAYS,
|
||||
"Track '{name}' decision_period ({period} blocks) is not a whole number of days"
|
||||
);
|
||||
assert!(
|
||||
days >= 7,
|
||||
"Track '{name}' decision_period is only {days} days, expected at least 7"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn governance_track_for_origin_mapping() {
|
||||
use governance::pezpallet_custom_origins::Origin;
|
||||
|
||||
// Test that track_for() correctly maps each origin to its track ID
|
||||
let origin_to_track: Vec<(Origin, u16)> = vec![
|
||||
(Origin::WhitelistedCaller, 1),
|
||||
(Origin::StakingAdmin, 10),
|
||||
(Origin::Treasurer, 11),
|
||||
(Origin::LeaseAdmin, 12),
|
||||
(Origin::FellowshipAdmin, 13),
|
||||
(Origin::GeneralAdmin, 14),
|
||||
(Origin::AuctionAdmin, 15),
|
||||
(Origin::ReferendumCanceller, 20),
|
||||
(Origin::ReferendumKiller, 21),
|
||||
(Origin::SmallTipper, 30),
|
||||
(Origin::BigTipper, 31),
|
||||
(Origin::SmallSpender, 32),
|
||||
(Origin::MediumSpender, 33),
|
||||
(Origin::BigSpender, 34),
|
||||
(Origin::WelatiElection, 40),
|
||||
(Origin::WelatiAdmin, 41),
|
||||
(Origin::CitizenshipAdmin, 42),
|
||||
];
|
||||
|
||||
for (origin, expected_id) in origin_to_track {
|
||||
let pallet_origin: <RuntimeOrigin as pezframe_support::traits::OriginTrait>::PalletsOrigin =
|
||||
origin.clone().into();
|
||||
let result = <TracksInfo as TracksInfoTrait<Balance, BlockNumber>>::track_for(&pallet_origin);
|
||||
assert_eq!(
|
||||
result,
|
||||
Ok(expected_id),
|
||||
"Origin {:?} should map to track {expected_id}",
|
||||
origin
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user