@@ -800,6 +800,113 @@ fn test_exit_early_no_position() {
800800 assert_eq ! ( client. get_user_stake( & call_id, & staker, & 1u32 ) , 0 ) ;
801801}
802802
803+ // ── SC-019: User Stake Read Tests ─────────────────────────────────────────────
804+
805+ #[ test]
806+ fn test_get_user_stake_missing_returns_zero ( ) {
807+ let env = Env :: default ( ) ;
808+ env. mock_all_auths ( ) ;
809+ let contract_id = env. register_contract ( None , CallRegistry ) ;
810+ let client = CallRegistryClient :: new ( & env, & contract_id) ;
811+ let admin = Address :: generate ( & env) ;
812+ client. initialize ( & admin) ;
813+
814+ let user = Address :: generate ( & env) ;
815+
816+ // 1. Missing stake returns 0 (not a panic)
817+ assert_eq ! ( client. get_user_stake( & 0u64 , & user, & 0u32 ) , 0 ) ;
818+ assert ! ( !client. has_stake( & 0u64 , & user, & 0u32 ) ) ;
819+
820+ // 3. Confirm the read path is idempotent
821+ assert_eq ! ( client. get_user_stake( & 0u64 , & user, & 0u32 ) , 0 ) ;
822+ assert ! ( !client. has_stake( & 0u64 , & user, & 0u32 ) ) ;
823+ }
824+
825+ #[ test]
826+ fn test_get_user_stake_existing_and_idempotent ( ) {
827+ let env = Env :: default ( ) ;
828+ env. mock_all_auths ( ) ;
829+ let contract_id = env. register_contract ( None , CallRegistry ) ;
830+ let client = CallRegistryClient :: new ( & env, & contract_id) ;
831+ let admin = Address :: generate ( & env) ;
832+ client. initialize ( & admin) ;
833+
834+ let creator = Address :: generate ( & env) ;
835+ let staker = Address :: generate ( & env) ;
836+ let stake_token_admin = Address :: generate ( & env) ;
837+ let stake_token_contract = env. register_stellar_asset_contract_v2 ( stake_token_admin. clone ( ) ) ;
838+ let stake_token = stake_token_contract. address ( ) ;
839+ let stake_token_admin_client = token:: StellarAssetClient :: new ( & env, & stake_token) ;
840+
841+ stake_token_admin_client. mint ( & creator, & 1000 ) ;
842+ stake_token_admin_client. mint ( & staker, & 1000 ) ;
843+ client. whitelist_token_admin ( & stake_token) ;
844+
845+ let end_ts = env. ledger ( ) . timestamp ( ) + 1000 ;
846+ let call_id = client. create_call (
847+ & creator,
848+ & stake_token,
849+ & 100 ,
850+ & end_ts,
851+ & default_metadata ( & env) ,
852+ ) ;
853+
854+ client. stake_on_call ( & call_id, & staker, & 1000 , & 1u32 ) ;
855+
856+ // 2. Existing stake returns the correct value
857+ // participant_count = 1 -> fee = 50bp. 1000 * 50 / 10000 = 5. net = 995
858+ assert_eq ! ( client. get_user_stake( & call_id, & staker, & 1u32 ) , 995 ) ;
859+ assert ! ( client. has_stake( & call_id, & staker, & 1u32 ) ) ;
860+
861+ // Idempotency / No TTL bump
862+ // Note: The Soroban SDK does not currently expose a `get_ttl()` method for persistent
863+ // storage in tests, so we cannot explicitly `assert_eq!(ttl_before, ttl_after)`.
864+ // This is verified logically (read-only `.get()` operation without `.set()` or `.extend_ttl()`).
865+ // Since get_ttl might not be available, we at least verify state doesn't change
866+ assert_eq ! ( client. get_user_stake( & call_id, & staker, & 1u32 ) , 995 ) ;
867+ }
868+
869+ #[ test]
870+ fn test_get_user_stake_after_early_exit ( ) {
871+ let env = Env :: default ( ) ;
872+ env. mock_all_auths ( ) ;
873+ let contract_id = env. register_contract ( None , CallRegistry ) ;
874+ let client = CallRegistryClient :: new ( & env, & contract_id) ;
875+ let admin = Address :: generate ( & env) ;
876+ client. initialize ( & admin) ;
877+
878+ let creator = Address :: generate ( & env) ;
879+ let staker = Address :: generate ( & env) ;
880+ let stake_token_admin = Address :: generate ( & env) ;
881+ let stake_token_contract = env. register_stellar_asset_contract_v2 ( stake_token_admin. clone ( ) ) ;
882+ let stake_token = stake_token_contract. address ( ) ;
883+ let stake_token_admin_client = token:: StellarAssetClient :: new ( & env, & stake_token) ;
884+
885+ stake_token_admin_client. mint ( & creator, & 1000 ) ;
886+ stake_token_admin_client. mint ( & staker, & 1000 ) ;
887+ client. whitelist_token_admin ( & stake_token) ;
888+
889+ let end_ts = env. ledger ( ) . timestamp ( ) + 1000 ;
890+ let call_id = client. create_call (
891+ & creator,
892+ & stake_token,
893+ & 100 ,
894+ & end_ts,
895+ & default_metadata ( & env) ,
896+ ) ;
897+
898+ client. stake_on_call ( & call_id, & staker, & 1000 , & 1u32 ) ;
899+
900+ assert_eq ! ( client. get_user_stake( & call_id, & staker, & 1u32 ) , 995 ) ;
901+
902+ // Early exit
903+ client. exit_early ( & call_id, & staker) ;
904+
905+ // 4. Test interaction with early_exit - should correctly return 0 and has_stake = false
906+ assert_eq ! ( client. get_user_stake( & call_id, & staker, & 1u32 ) , 0 ) ;
907+ assert ! ( !client. has_stake( & call_id, & staker, & 1u32 ) ) ;
908+ }
909+
803910#[ test]
804911#[ should_panic( expected = "No stake found" ) ]
805912fn test_exit_early_no_stake ( ) {
0 commit comments