From ce99d3ed92603c2746c662b0a493878e92725136 Mon Sep 17 00:00:00 2001 From: 1nonlypiece <1nonlypiece@users.noreply.github.com> Date: Wed, 24 Jun 2026 11:22:44 +0530 Subject: [PATCH] test: cover admin transfer edge cases and migration version guard Adds tests for cancel_admin_transfer, wrong-caller accept (#6), re-proposing over a pending entry, post-rotation admin authority, and the migrate_v1_to_v2 double-run guard (#11). Co-Authored-By: Claude Opus 4.8 (1M context) --- contracts/escrow/src/test.rs | 74 ++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/contracts/escrow/src/test.rs b/contracts/escrow/src/test.rs index 2d924db..6cec55b 100644 --- a/contracts/escrow/src/test.rs +++ b/contracts/escrow/src/test.rs @@ -697,3 +697,77 @@ fn test_pause_pause_unpause_ends_unpaused() { assert!(!client.is_paused()); } + +// --------------------------------------------------------------------------- +// Issue #20 — two-step admin handover edge cases and the migration version +// guard. Covers cancel-then-accept (#5), wrong-caller accept (#6), re-propose +// overwrite, post-rotation admin authority, and the double-migrate guard (#11). +// --------------------------------------------------------------------------- + +#[test] +#[should_panic(expected = "Error(Contract, #5)")] +fn test_i20_cancel_then_accept_fails() { + let env = Env::default(); + let (client, _admin) = setup_initialized(&env); + let next = Address::generate(&env); + client.propose_admin_transfer(&next); + client.cancel_admin_transfer(); + // Nothing pending after a cancel, so accept must fail with #5. + client.accept_admin_transfer(&next); +} + +#[test] +#[should_panic(expected = "Error(Contract, #6)")] +fn test_i20_wrong_caller_accept_rejected() { + let env = Env::default(); + let (client, _admin) = setup_initialized(&env); + let next = Address::generate(&env); + let intruder = Address::generate(&env); + client.propose_admin_transfer(&next); + // A caller other than the pending admin is rejected with #6. + client.accept_admin_transfer(&intruder); +} + +#[test] +fn test_i20_repropose_overwrites_pending() { + let env = Env::default(); + let (client, _admin) = setup_initialized(&env); + let first = Address::generate(&env); + let second = Address::generate(&env); + client.propose_admin_transfer(&first); + assert_eq!(client.get_pending_admin(), Some(first)); + client.propose_admin_transfer(&second); + assert_eq!(client.get_pending_admin(), Some(second.clone())); + // Only the most recent pending admin can accept. + client.accept_admin_transfer(&second); + assert_eq!(client.get_admin(), Some(second)); +} + +#[test] +fn test_i20_rotated_admin_can_act_after_handover() { + let env = Env::default(); + let (client, _admin) = setup_initialized(&env); + let next = Address::generate(&env); + client.propose_admin_transfer(&next); + client.accept_admin_transfer(&next); + // The rotated admin can now perform an admin-gated action. + client.pause(); + assert!(client.is_paused()); +} + +#[test] +fn test_i20_schema_version_is_two_after_init() { + let env = Env::default(); + let (client, _admin) = setup_initialized(&env); + // Fresh v2 init stamps SchemaVersion = 2 directly (no migration needed). + assert_eq!(client.get_schema_version(), 2); +} + +#[test] +#[should_panic(expected = "Error(Contract, #11)")] +fn test_i20_double_migrate_guard_rejects_on_v2() { + let env = Env::default(); + let (client, _admin) = setup_initialized(&env); + // Already at v2, so the v1->v2 migration refuses with #11. + client.migrate_v1_to_v2(); +}