@@ -574,3 +574,209 @@ def test_send_without_params_respects_composer_config(
574574 f"simulate_transactions was called { patched .call_count } times, "
575575 "but should not be called when populate_app_call_resources=False"
576576 )
577+
578+
579+ class TestGatherSignatures :
580+ """Tests for the gather_signatures method."""
581+
582+ def test_should_successfully_sign_a_single_transaction (
583+ self , algorand : AlgorandClient , funded_account : AddressWithSigners
584+ ) -> None :
585+ """Test that a single transaction is signed successfully."""
586+ composer = algorand .new_group ()
587+ composer .add_payment (
588+ PaymentParams (
589+ sender = funded_account .addr ,
590+ receiver = funded_account .addr ,
591+ amount = AlgoAmount .from_micro_algo (1000 ),
592+ )
593+ )
594+
595+ signed_txns = composer .gather_signatures ()
596+
597+ assert len (signed_txns ) == 1
598+ assert signed_txns [0 ] is not None
599+ assert signed_txns [0 ].sig is not None
600+
601+ def test_should_successfully_sign_multiple_transactions_with_same_signer (
602+ self , algorand : AlgorandClient , funded_account : AddressWithSigners
603+ ) -> None :
604+ """Test that multiple transactions from the same sender are signed correctly."""
605+ composer = algorand .new_group ()
606+ composer .add_payment (
607+ PaymentParams (
608+ sender = funded_account .addr ,
609+ receiver = funded_account .addr ,
610+ amount = AlgoAmount .from_micro_algo (1000 ),
611+ )
612+ )
613+ composer .add_payment (
614+ PaymentParams (
615+ sender = funded_account .addr ,
616+ receiver = funded_account .addr ,
617+ amount = AlgoAmount .from_micro_algo (2000 ),
618+ )
619+ )
620+
621+ signed_txns = composer .gather_signatures ()
622+
623+ assert len (signed_txns ) == 2
624+ assert signed_txns [0 ].sig is not None
625+ assert signed_txns [1 ].sig is not None
626+
627+ def test_should_successfully_sign_transactions_with_multiple_different_signers (
628+ self , algorand : AlgorandClient , funded_account : AddressWithSigners
629+ ) -> None :
630+ """Test that transactions from different senders are each signed correctly."""
631+ # Create and fund a second account
632+ sender2 = algorand .account .random ()
633+ algorand .send .payment (
634+ PaymentParams (
635+ sender = funded_account .addr ,
636+ receiver = sender2 .addr ,
637+ amount = AlgoAmount .from_algo (10 ),
638+ )
639+ )
640+
641+ composer = algorand .new_group ()
642+ composer .add_payment (
643+ PaymentParams (
644+ sender = funded_account .addr ,
645+ receiver = sender2 .addr ,
646+ amount = AlgoAmount .from_micro_algo (1000 ),
647+ )
648+ )
649+ composer .add_payment (
650+ PaymentParams (
651+ sender = sender2 .addr ,
652+ receiver = funded_account .addr ,
653+ amount = AlgoAmount .from_micro_algo (1000 ),
654+ signer = sender2 .signer ,
655+ )
656+ )
657+
658+ signed_txns = composer .gather_signatures ()
659+
660+ assert len (signed_txns ) == 2
661+ assert signed_txns [0 ].sig is not None
662+ assert signed_txns [1 ].sig is not None
663+
664+ def test_should_throw_error_when_no_transactions_to_sign (self , algorand : AlgorandClient ) -> None :
665+ """Test that an error is thrown when there are no transactions to sign."""
666+ composer = algorand .new_group ()
667+
668+ with pytest .raises (ValueError , match = "Cannot build an empty transaction group" ):
669+ composer .gather_signatures ()
670+
671+ def test_should_throw_error_when_signer_returns_fewer_signed_transactions_than_expected (
672+ self , algorand : AlgorandClient , funded_account : AddressWithSigners
673+ ) -> None :
674+ """Test error handling when a signer returns fewer signed transactions than requested."""
675+ from collections .abc import Sequence
676+
677+ from algokit_transact .models .transaction import Transaction
678+
679+ real_signer = algorand .account .get_signer (funded_account .addr )
680+
681+ # Create a faulty signer that returns fewer signed transactions than requested
682+ def faulty_signer (txns : Sequence [Transaction ], indexes : Sequence [int ]) -> list [bytes ]:
683+ # Only return one signed transaction even if multiple are requested
684+ return real_signer (txns , [indexes [0 ]])
685+
686+ composer = algorand .new_group ()
687+ composer .add_payment (
688+ PaymentParams (
689+ sender = funded_account .addr ,
690+ receiver = funded_account .addr ,
691+ amount = AlgoAmount .from_micro_algo (1000 ),
692+ signer = faulty_signer ,
693+ )
694+ )
695+ composer .add_payment (
696+ PaymentParams (
697+ sender = funded_account .addr ,
698+ receiver = funded_account .addr ,
699+ amount = AlgoAmount .from_micro_algo (2000 ),
700+ signer = faulty_signer ,
701+ )
702+ )
703+
704+ with pytest .raises (ValueError , match = r"Transactions at indexes \[1\] were not signed" ):
705+ composer .gather_signatures ()
706+
707+ def test_should_throw_error_when_signer_returns_none_signed_transaction (
708+ self , algorand : AlgorandClient , funded_account : AddressWithSigners
709+ ) -> None :
710+ """Test error handling when a signer returns None values."""
711+ from collections .abc import Sequence
712+
713+ from algokit_transact .models .transaction import Transaction
714+
715+ # Create a faulty signer that returns array of Nones
716+ def faulty_signer (_txns : Sequence [Transaction ], indexes : Sequence [int ]) -> list [bytes ]:
717+ return [None ] * len (indexes ) # type: ignore[list-item]
718+
719+ composer = algorand .new_group ()
720+ composer .add_payment (
721+ PaymentParams (
722+ sender = funded_account .addr ,
723+ receiver = funded_account .addr ,
724+ amount = AlgoAmount .from_micro_algo (1000 ),
725+ signer = faulty_signer ,
726+ )
727+ )
728+
729+ # Should provide a clear error message indicating which transaction was not signed
730+ with pytest .raises (ValueError , match = r"Transactions at indexes \[0\] were not signed" ):
731+ composer .gather_signatures ()
732+
733+ def test_should_throw_error_when_signer_returns_empty_array (
734+ self , algorand : AlgorandClient , funded_account : AddressWithSigners
735+ ) -> None :
736+ """Test error handling when a signer returns an empty array."""
737+ from collections .abc import Sequence
738+
739+ from algokit_transact .models .transaction import Transaction
740+
741+ # Create a faulty signer that returns empty array
742+ def faulty_signer (_txns : Sequence [Transaction ], _indexes : Sequence [int ]) -> list [bytes ]:
743+ return []
744+
745+ composer = algorand .new_group ()
746+ composer .add_payment (
747+ PaymentParams (
748+ sender = funded_account .addr ,
749+ receiver = funded_account .addr ,
750+ amount = AlgoAmount .from_micro_algo (1000 ),
751+ signer = faulty_signer ,
752+ )
753+ )
754+
755+ with pytest .raises (ValueError , match = r"Transactions at indexes \[0\] were not signed" ):
756+ composer .gather_signatures ()
757+
758+ def test_should_throw_error_when_signer_returns_invalid_signed_transaction_data (
759+ self , algorand : AlgorandClient , funded_account : AddressWithSigners
760+ ) -> None :
761+ """Test error handling when a signer returns malformed signed transaction data."""
762+ from collections .abc import Sequence
763+
764+ from algokit_transact .models .transaction import Transaction
765+
766+ # Create a faulty signer that returns invalid data
767+ def faulty_signer (_txns : Sequence [Transaction ], indexes : Sequence [int ]) -> list [bytes ]:
768+ return [bytes ([1 , 2 , 3 ]) for _ in indexes ]
769+
770+ composer = algorand .new_group ()
771+ composer .add_payment (
772+ PaymentParams (
773+ sender = funded_account .addr ,
774+ receiver = funded_account .addr ,
775+ amount = AlgoAmount .from_micro_algo (1000 ),
776+ signer = faulty_signer ,
777+ )
778+ )
779+
780+ # Should provide a clear error message indicating which transaction had invalid data
781+ with pytest .raises (ValueError , match = "Invalid signed transaction at index 0" ):
782+ composer .gather_signatures ()
0 commit comments