@@ -841,6 +841,8 @@ def test_filter_cached(self, client: TestClient) -> None:
841841 cached_response .globalCounts = {}
842842 cached_response .orCounts = []
843843 cached_response .specTitles = {}
844+ cached_response .offset = 0
845+ cached_response .limit = None
844846
845847 with (
846848 patch (DB_CONFIG_PATCH , return_value = True ),
@@ -849,6 +851,115 @@ def test_filter_cached(self, client: TestClient) -> None:
849851 response = client .get ("/plots/filter" )
850852 assert response .status_code == 200
851853
854+ def test_filter_with_limit (self , client : TestClient , mock_spec ) -> None :
855+ """Filter with limit should return limited images but total of all."""
856+ # Add a second impl to have 2 images
857+ mock_impl2 = MagicMock ()
858+ mock_impl2 .library_id = "seaborn"
859+ mock_impl2 .preview_url = TEST_IMAGE_URL
860+ mock_impl2 .preview_thumb = TEST_THUMB_URL
861+ mock_impl2 .preview_html = None
862+ mock_impl2 .quality_score = 85.0
863+ mock_impl2 .impl_tags = {}
864+ mock_spec .impls .append (mock_impl2 )
865+
866+ mock_spec_repo = MagicMock ()
867+ mock_spec_repo .get_all = AsyncMock (return_value = [mock_spec ])
868+
869+ with (
870+ patch (DB_CONFIG_PATCH , return_value = True ),
871+ patch ("api.routers.plots.get_cache" , return_value = None ),
872+ patch ("api.routers.plots.set_cache" ),
873+ patch ("api.routers.plots.SpecRepository" , return_value = mock_spec_repo ),
874+ ):
875+ response = client .get ("/plots/filter?limit=1" )
876+ assert response .status_code == 200
877+ data = response .json ()
878+ assert len (data ["images" ]) == 1
879+ assert data ["total" ] == 2
880+ assert data ["limit" ] == 1
881+ assert data ["offset" ] == 0
882+
883+ def test_filter_with_offset (self , client : TestClient , mock_spec ) -> None :
884+ """Filter with offset should skip images."""
885+ mock_impl2 = MagicMock ()
886+ mock_impl2 .library_id = "seaborn"
887+ mock_impl2 .preview_url = TEST_IMAGE_URL
888+ mock_impl2 .preview_thumb = TEST_THUMB_URL
889+ mock_impl2 .preview_html = None
890+ mock_impl2 .quality_score = 85.0
891+ mock_impl2 .impl_tags = {}
892+ mock_spec .impls .append (mock_impl2 )
893+
894+ mock_spec_repo = MagicMock ()
895+ mock_spec_repo .get_all = AsyncMock (return_value = [mock_spec ])
896+
897+ with (
898+ patch (DB_CONFIG_PATCH , return_value = True ),
899+ patch ("api.routers.plots.get_cache" , return_value = None ),
900+ patch ("api.routers.plots.set_cache" ),
901+ patch ("api.routers.plots.SpecRepository" , return_value = mock_spec_repo ),
902+ ):
903+ response = client .get ("/plots/filter?offset=1" )
904+ assert response .status_code == 200
905+ data = response .json ()
906+ assert len (data ["images" ]) == 1
907+ assert data ["total" ] == 2
908+ assert data ["offset" ] == 1
909+
910+ def test_filter_with_limit_and_offset (self , client : TestClient , mock_spec ) -> None :
911+ """Filter with limit and offset combined."""
912+ mock_impl2 = MagicMock ()
913+ mock_impl2 .library_id = "seaborn"
914+ mock_impl2 .preview_url = TEST_IMAGE_URL
915+ mock_impl2 .preview_thumb = TEST_THUMB_URL
916+ mock_impl2 .preview_html = None
917+ mock_impl2 .quality_score = 85.0
918+ mock_impl2 .impl_tags = {}
919+ mock_impl3 = MagicMock ()
920+ mock_impl3 .library_id = "plotly"
921+ mock_impl3 .preview_url = TEST_IMAGE_URL
922+ mock_impl3 .preview_thumb = TEST_THUMB_URL
923+ mock_impl3 .preview_html = None
924+ mock_impl3 .quality_score = 80.0
925+ mock_impl3 .impl_tags = {}
926+ mock_spec .impls .extend ([mock_impl2 , mock_impl3 ])
927+
928+ mock_spec_repo = MagicMock ()
929+ mock_spec_repo .get_all = AsyncMock (return_value = [mock_spec ])
930+
931+ with (
932+ patch (DB_CONFIG_PATCH , return_value = True ),
933+ patch ("api.routers.plots.get_cache" , return_value = None ),
934+ patch ("api.routers.plots.set_cache" ),
935+ patch ("api.routers.plots.SpecRepository" , return_value = mock_spec_repo ),
936+ ):
937+ response = client .get ("/plots/filter?offset=1&limit=1" )
938+ assert response .status_code == 200
939+ data = response .json ()
940+ assert len (data ["images" ]) == 1
941+ assert data ["total" ] == 3
942+ assert data ["offset" ] == 1
943+ assert data ["limit" ] == 1
944+
945+ def test_filter_default_returns_all (self , client : TestClient , mock_spec ) -> None :
946+ """Filter without pagination params returns all images (backward compat)."""
947+ mock_spec_repo = MagicMock ()
948+ mock_spec_repo .get_all = AsyncMock (return_value = [mock_spec ])
949+
950+ with (
951+ patch (DB_CONFIG_PATCH , return_value = True ),
952+ patch ("api.routers.plots.get_cache" , return_value = None ),
953+ patch ("api.routers.plots.set_cache" ),
954+ patch ("api.routers.plots.SpecRepository" , return_value = mock_spec_repo ),
955+ ):
956+ response = client .get ("/plots/filter" )
957+ assert response .status_code == 200
958+ data = response .json ()
959+ assert len (data ["images" ]) == data ["total" ]
960+ assert data ["offset" ] == 0
961+ assert data ["limit" ] is None
962+
852963
853964class TestPlotsHelperFunctions :
854965 """Tests for plots.py helper functions."""
0 commit comments