@@ -965,6 +965,92 @@ mod test_online {
965965 String :: from_utf8_lossy( & out. stderr)
966966 ) ;
967967 }
968+
969+ // lock → list → unlock cycle.
970+ #[ test]
971+ fn test_lock_unlock_and_list_utxos ( ) {
972+ let ( cli, mut cmd_init, env) = setup_online_wallet ( ) ;
973+ cmd_init. assert ( ) . success ( ) ;
974+ fund_and_sync_wallet ( & cli, & env) ;
975+
976+ let unspent = run_wallet_json ( & cli, & [ "unspent" ] ) ;
977+ let outpoint = unspent[ "items" ] [ 0 ] [ "outpoint" ]
978+ . as_str ( )
979+ . expect ( "one utxo" )
980+ . to_string ( ) ;
981+
982+ let locked = run_wallet_json ( & cli, & [ "lock_utxo" , "--utxo" , & outpoint] ) ;
983+ assert_eq ! ( locked[ "count" ] . as_u64( ) , Some ( 1 ) , "lock output: {locked}" ) ;
984+
985+ // separate process — must still be locked (persistence)
986+ let listed = run_wallet_json ( & cli, & [ "locked_utxos" ] ) ;
987+ assert_eq ! (
988+ listed[ "count" ] . as_u64( ) ,
989+ Some ( 1 ) ,
990+ "lock must persist across processes: {listed}"
991+ ) ;
992+ assert ! (
993+ listed[ "items" ]
994+ . as_array( )
995+ . unwrap( )
996+ . iter( )
997+ . any( |i| i. as_str( ) == Some ( outpoint. as_str( ) ) ) ,
998+ "locked_utxos should list {outpoint}: {listed}"
999+ ) ;
1000+
1001+ let after = run_wallet_json ( & cli, & [ "unlock_utxo" , "--utxo" , & outpoint] ) ;
1002+ assert_eq ! (
1003+ after[ "count" ] . as_u64( ) ,
1004+ Some ( 0 ) ,
1005+ "unlock should clear it: {after}"
1006+ ) ;
1007+ let final_list = run_wallet_json ( & cli, & [ "locked_utxos" ] ) ;
1008+ assert_eq ! (
1009+ final_list[ "count" ] . as_u64( ) ,
1010+ Some ( 0 ) ,
1011+ "empty after unlock: {final_list}"
1012+ ) ;
1013+ }
1014+
1015+ // a locked UTXO is excluded from coin selection.
1016+ #[ test]
1017+ fn test_locked_utxo_excluded_from_coin_selection ( ) {
1018+ let ( cli, mut cmd_init, env) = setup_online_wallet ( ) ;
1019+ cmd_init. assert ( ) . success ( ) ;
1020+ fund_and_sync_wallet ( & cli, & env) ;
1021+
1022+ let unspent = run_wallet_json ( & cli, & [ "unspent" ] ) ;
1023+ let outpoint = unspent[ "items" ] [ 0 ] [ "outpoint" ]
1024+ . as_str ( )
1025+ . unwrap ( )
1026+ . to_string ( ) ;
1027+
1028+ run_wallet_json ( & cli, & [ "lock_utxo" , "--utxo" , & outpoint] ) ;
1029+
1030+ // only UTXO is locked → create_tx must fail
1031+ let out = cli
1032+ . wallet_cmd ( & [
1033+ "--wallet" ,
1034+ WALLET_NAME ,
1035+ "create_tx" ,
1036+ "--to" ,
1037+ & format ! ( "{RECIPIENT}:20000" ) ,
1038+ ] )
1039+ . output ( )
1040+ . unwrap ( ) ;
1041+ assert ! (
1042+ !out. status. success( ) ,
1043+ "create_tx must fail when the only UTXO is locked"
1044+ ) ;
1045+
1046+ // unlock → create_tx succeeds
1047+ run_wallet_json ( & cli, & [ "unlock_utxo" , "--utxo" , & outpoint] ) ;
1048+ let psbt = run_wallet_json ( & cli, & [ "create_tx" , "--to" , & format ! ( "{RECIPIENT}:20000" ) ] ) ;
1049+ assert ! (
1050+ psbt[ "psbt" ] . as_str( ) . is_some( ) ,
1051+ "create_tx should succeed after unlock: {psbt}"
1052+ ) ;
1053+ }
9681054}
9691055
9701056#[ cfg( feature = "esplora" ) ]
0 commit comments