@@ -525,3 +525,277 @@ def test_new_hooks_version_gated(server: Server, test_case: HookTestCase) -> Non
525525
526526 # Cleanup
527527 session .unset_hook (f"{ test_case .hook } [0]" )
528+
529+
530+ # =============================================================================
531+ # Bulk Operations API Tests
532+ # =============================================================================
533+
534+
535+ class TestBulkOperationsAPI :
536+ """Tests for hook bulk operations API."""
537+
538+ def test_get_hook_indices_empty (self , server : Server ) -> None :
539+ """Test get_hook_indices returns empty list when no hooks set."""
540+ session = server .new_session (session_name = "test_bulk_ops" )
541+
542+ indices = session .get_hook_indices ("session-renamed" )
543+ assert indices == []
544+
545+ def test_get_hook_indices_sequential (self , server : Server ) -> None :
546+ """Test get_hook_indices with sequential indices."""
547+ session = server .new_session (session_name = "test_bulk_ops" )
548+
549+ # Set hooks at indices 0, 1, 2
550+ session .set_hook ("session-renamed[0]" , "display-message 'hook 0'" )
551+ session .set_hook ("session-renamed[1]" , "display-message 'hook 1'" )
552+ session .set_hook ("session-renamed[2]" , "display-message 'hook 2'" )
553+
554+ indices = session .get_hook_indices ("session-renamed" )
555+ assert indices == [0 , 1 , 2 ]
556+
557+ # Cleanup
558+ session .clear_hook ("session-renamed" )
559+
560+ def test_get_hook_indices_sparse (self , server : Server ) -> None :
561+ """Test get_hook_indices with sparse (non-sequential) indices."""
562+ session = server .new_session (session_name = "test_bulk_ops" )
563+
564+ # Set hooks at non-sequential indices
565+ session .set_hook ("session-renamed[0]" , "display-message 'hook 0'" )
566+ session .set_hook ("session-renamed[5]" , "display-message 'hook 5'" )
567+ session .set_hook ("session-renamed[10]" , "display-message 'hook 10'" )
568+
569+ indices = session .get_hook_indices ("session-renamed" )
570+ assert indices == [0 , 5 , 10 ]
571+
572+ # Cleanup
573+ session .clear_hook ("session-renamed" )
574+
575+ def test_get_hook_values_empty (self , server : Server ) -> None :
576+ """Test get_hook_values returns empty SparseArray when no hooks set."""
577+ session = server .new_session (session_name = "test_bulk_ops" )
578+
579+ values = session .get_hook_values ("session-renamed" )
580+ assert isinstance (values , SparseArray )
581+ assert len (values ) == 0
582+ assert values .as_list () == []
583+
584+ def test_get_hook_values (self , server : Server ) -> None :
585+ """Test get_hook_values returns SparseArray with correct values."""
586+ session = server .new_session (session_name = "test_bulk_ops" )
587+
588+ # Set hooks
589+ session .set_hook ("session-renamed[0]" , "display-message 'hook 0'" )
590+ session .set_hook ("session-renamed[5]" , "display-message 'hook 5'" )
591+
592+ values = session .get_hook_values ("session-renamed" )
593+ assert isinstance (values , SparseArray )
594+ assert 0 in values
595+ assert 5 in values
596+ assert 3 not in values
597+ assert "display-message" in values [0 ]
598+ assert "display-message" in values [5 ]
599+
600+ # Cleanup
601+ session .clear_hook ("session-renamed" )
602+
603+ def test_get_hook_values_iter (self , server : Server ) -> None :
604+ """Test iterating over hook values in sorted order."""
605+ session = server .new_session (session_name = "test_bulk_ops" )
606+
607+ # Set hooks at sparse indices
608+ session .set_hook ("session-renamed[5]" , "display-message 'fifth'" )
609+ session .set_hook ("session-renamed[0]" , "display-message 'zeroth'" )
610+ session .set_hook ("session-renamed[2]" , "display-message 'second'" )
611+
612+ values = session .get_hook_values ("session-renamed" )
613+ value_list = list (values .iter_values ())
614+
615+ # Values should be in sorted index order
616+ assert len (value_list ) == 3
617+ assert "zeroth" in value_list [0 ]
618+ assert "second" in value_list [1 ]
619+ assert "fifth" in value_list [2 ]
620+
621+ # Cleanup
622+ session .clear_hook ("session-renamed" )
623+
624+ def test_set_hooks_bulk_with_dict (self , server : Server ) -> None :
625+ """Test set_hooks_bulk with explicit index dict."""
626+ session = server .new_session (session_name = "test_bulk_ops" )
627+
628+ session .set_hooks_bulk (
629+ "session-renamed" ,
630+ {
631+ 0 : "display-message 'hook 0'" ,
632+ 1 : "display-message 'hook 1'" ,
633+ 5 : "display-message 'hook 5'" ,
634+ },
635+ )
636+
637+ indices = session .get_hook_indices ("session-renamed" )
638+ assert indices == [0 , 1 , 5 ]
639+
640+ values = session .get_hook_values ("session-renamed" )
641+ assert "hook 0" in values [0 ]
642+ assert "hook 1" in values [1 ]
643+ assert "hook 5" in values [5 ]
644+
645+ # Cleanup
646+ session .clear_hook ("session-renamed" )
647+
648+ def test_set_hooks_bulk_with_list (self , server : Server ) -> None :
649+ """Test set_hooks_bulk with list (sequential indices)."""
650+ session = server .new_session (session_name = "test_bulk_ops" )
651+
652+ session .set_hooks_bulk (
653+ "session-renamed" ,
654+ [
655+ "display-message 'hook 0'" ,
656+ "display-message 'hook 1'" ,
657+ "display-message 'hook 2'" ,
658+ ],
659+ )
660+
661+ indices = session .get_hook_indices ("session-renamed" )
662+ assert indices == [0 , 1 , 2 ]
663+
664+ # Cleanup
665+ session .clear_hook ("session-renamed" )
666+
667+ def test_set_hooks_bulk_with_sparse_array (self , server : Server ) -> None :
668+ """Test set_hooks_bulk with SparseArray."""
669+ session = server .new_session (session_name = "test_bulk_ops" )
670+
671+ sparse : SparseArray [str ] = SparseArray ()
672+ sparse .add (0 , "display-message 'from sparse 0'" )
673+ sparse .add (10 , "display-message 'from sparse 10'" )
674+
675+ session .set_hooks_bulk ("session-renamed" , sparse )
676+
677+ indices = session .get_hook_indices ("session-renamed" )
678+ assert indices == [0 , 10 ]
679+
680+ # Cleanup
681+ session .clear_hook ("session-renamed" )
682+
683+ def test_set_hooks_bulk_clear_existing (self , server : Server ) -> None :
684+ """Test set_hooks_bulk with clear_existing=True."""
685+ session = server .new_session (session_name = "test_bulk_ops" )
686+
687+ # Set initial hooks
688+ session .set_hooks_bulk (
689+ "session-renamed" ,
690+ {0 : "display-message 'old 0'" , 1 : "display-message 'old 1'" },
691+ )
692+
693+ # Replace with new hooks
694+ session .set_hooks_bulk (
695+ "session-renamed" ,
696+ {0 : "display-message 'new 0'" },
697+ clear_existing = True ,
698+ )
699+
700+ indices = session .get_hook_indices ("session-renamed" )
701+ assert indices == [0 ]
702+
703+ values = session .get_hook_values ("session-renamed" )
704+ assert "new 0" in values [0 ]
705+
706+ # Cleanup
707+ session .clear_hook ("session-renamed" )
708+
709+ def test_clear_hook (self , server : Server ) -> None :
710+ """Test clear_hook removes all indexed values."""
711+ session = server .new_session (session_name = "test_bulk_ops" )
712+
713+ # Set multiple hooks
714+ session .set_hooks_bulk (
715+ "session-renamed" ,
716+ {0 : "display-message 'hook 0'" , 5 : "display-message 'hook 5'" },
717+ )
718+
719+ # Verify they exist
720+ assert session .get_hook_indices ("session-renamed" ) == [0 , 5 ]
721+
722+ # Clear all
723+ session .clear_hook ("session-renamed" )
724+
725+ # Verify cleared
726+ assert session .get_hook_indices ("session-renamed" ) == []
727+
728+ def test_append_hook_to_empty (self , server : Server ) -> None :
729+ """Test append_hook when no hooks exist (starts at index 0)."""
730+ session = server .new_session (session_name = "test_bulk_ops" )
731+
732+ session .append_hook ("session-renamed" , "display-message 'appended'" )
733+
734+ indices = session .get_hook_indices ("session-renamed" )
735+ assert indices == [0 ]
736+
737+ values = session .get_hook_values ("session-renamed" )
738+ assert "appended" in values [0 ]
739+
740+ # Cleanup
741+ session .clear_hook ("session-renamed" )
742+
743+ def test_append_hook_sequential (self , server : Server ) -> None :
744+ """Test append_hook adds at next sequential index."""
745+ session = server .new_session (session_name = "test_bulk_ops" )
746+
747+ # Set initial hook
748+ session .set_hook ("session-renamed[0]" , "display-message 'initial'" )
749+
750+ # Append
751+ session .append_hook ("session-renamed" , "display-message 'appended'" )
752+
753+ indices = session .get_hook_indices ("session-renamed" )
754+ assert indices == [0 , 1 ]
755+
756+ # Cleanup
757+ session .clear_hook ("session-renamed" )
758+
759+ def test_append_hook_after_sparse (self , server : Server ) -> None :
760+ """Test append_hook adds at max+1 even with sparse indices."""
761+ session = server .new_session (session_name = "test_bulk_ops" )
762+
763+ # Set hooks at sparse indices
764+ session .set_hook ("session-renamed[0]" , "display-message 'at 0'" )
765+ session .set_hook ("session-renamed[10]" , "display-message 'at 10'" )
766+
767+ # Append should be at index 11
768+ session .append_hook ("session-renamed" , "display-message 'appended'" )
769+
770+ indices = session .get_hook_indices ("session-renamed" )
771+ assert indices == [0 , 10 , 11 ]
772+
773+ values = session .get_hook_values ("session-renamed" )
774+ assert "appended" in values [11 ]
775+
776+ # Cleanup
777+ session .clear_hook ("session-renamed" )
778+
779+ def test_method_chaining (self , server : Server ) -> None :
780+ """Test that bulk operations support method chaining."""
781+ session = server .new_session (session_name = "test_bulk_ops" )
782+
783+ # Chain operations
784+ result = (
785+ session .set_hooks_bulk (
786+ "session-renamed" ,
787+ ["display-message 'hook 0'" ],
788+ )
789+ .append_hook ("session-renamed" , "display-message 'hook 1'" )
790+ .append_hook ("session-renamed" , "display-message 'hook 2'" )
791+ )
792+
793+ # Should return the session
794+ assert result is session
795+
796+ # Verify all hooks set
797+ indices = session .get_hook_indices ("session-renamed" )
798+ assert indices == [0 , 1 , 2 ]
799+
800+ # Cleanup
801+ session .clear_hook ("session-renamed" )
0 commit comments