@@ -795,3 +795,136 @@ async def test_collections_search_free_text(app_client, txn_client, ctx):
795795 assert target_collection ["id" ] in [
796796 c ["id" ] for c in found_collections
797797 ], f"Target collection { target_collection ['id' ]} not found within POST search results"
798+
799+
800+ @pytest .mark .asyncio
801+ async def test_collections_pagination_all_endpoints (app_client , txn_client , ctx ):
802+ """Test pagination works correctly across all collection endpoints."""
803+ # Create test data
804+ test_prefix = f"pagination-{ uuid .uuid4 ().hex [:8 ]} "
805+ base_collection = ctx .collection
806+
807+ # Create 10 test collections with predictable IDs for sorting
808+ test_collections = []
809+ for i in range (10 ):
810+ test_coll = base_collection .copy ()
811+ test_coll ["id" ] = f"{ test_prefix } -{ i :02d} "
812+ test_coll ["title" ] = f"Test Collection { i } "
813+ test_collections .append (test_coll )
814+ await create_collection (txn_client , test_coll )
815+
816+ await refresh_indices (txn_client )
817+
818+ # Define endpoints to test
819+ endpoints = [
820+ {"method" : "GET" , "path" : "/collections" , "param" : "limit" },
821+ {"method" : "GET" , "path" : "/collections-search" , "param" : "limit" },
822+ {"method" : "POST" , "path" : "/collections-search" , "body_key" : "limit" },
823+ ]
824+
825+ # Test pagination for each endpoint
826+ for endpoint in endpoints :
827+ # Test first page with limit=3
828+ limit = 3
829+
830+ # Make the request
831+ if endpoint ["method" ] == "GET" :
832+ params = [(endpoint ["param" ], str (limit ))]
833+ resp = await app_client .get (endpoint ["path" ], params = params )
834+ else : # POST
835+ body = {endpoint ["body_key" ]: limit }
836+ resp = await app_client .post (endpoint ["path" ], json = body )
837+
838+ assert (
839+ resp .status_code == 200
840+ ), f"Failed for { endpoint ['method' ]} { endpoint ['path' ]} "
841+ resp_json = resp .json ()
842+
843+ # # Filter to our test collections
844+ # if endpoint["path"] == "/collections":
845+ # found_collections = resp_json
846+ # else: # For collection-search endpoints
847+ found_collections = resp_json ["collections" ]
848+
849+ test_found = [c for c in found_collections if c ["id" ].startswith (test_prefix )]
850+
851+ # Should return exactly limit collections
852+ assert (
853+ len (test_found ) == limit
854+ ), f"Expected { limit } collections, got { len (test_found )} "
855+
856+ # Verify collections are in correct order (ascending by ID)
857+ expected_ids = [f"{ test_prefix } -{ i :02d} " for i in range (limit )]
858+ for i , expected_id in enumerate (expected_ids ):
859+ assert test_found [i ]["id" ] == expected_id
860+
861+ # Test second page using the token from the first page
862+ if "token" in resp_json and resp_json ["token" ]:
863+ token = resp_json ["token" ]
864+
865+ # Make the request with token
866+ if endpoint ["method" ] == "GET" :
867+ params = [(endpoint ["param" ], str (limit )), ("token" , token )]
868+ resp = await app_client .get (endpoint ["path" ], params = params )
869+ else : # POST
870+ body = {endpoint ["body_key" ]: limit , "token" : token }
871+ resp = await app_client .post (endpoint ["path" ], json = body )
872+
873+ assert (
874+ resp .status_code == 200
875+ ), f"Failed for { endpoint ['method' ]} { endpoint ['path' ]} with token"
876+ resp_json = resp .json ()
877+
878+ # Filter to our test collections
879+ if endpoint ["path" ] == "/collections" :
880+ found_collections = resp_json
881+ else : # For collection-search endpoints
882+ found_collections = resp_json ["collections" ]
883+
884+ test_found = [
885+ c for c in found_collections if c ["id" ].startswith (test_prefix )
886+ ]
887+
888+ # Should return next set of collections
889+ expected_ids = [f"{ test_prefix } -{ i :02d} " for i in range (limit , limit * 2 )]
890+ assert len (test_found ) == min (
891+ limit , len (expected_ids )
892+ ), f"Expected { min (limit , len (expected_ids ))} collections, got { len (test_found )} "
893+
894+ # Verify collections are in correct order
895+ for i , expected_id in enumerate (expected_ids [: len (test_found )]):
896+ assert test_found [i ]["id" ] == expected_id
897+
898+ # Test with sortby parameter to ensure token works with sorting
899+ if endpoint ["method" ] == "GET" :
900+ params = [("sortby" , "-id" ), (endpoint ["param" ], str (limit ))]
901+ resp = await app_client .get (endpoint ["path" ], params = params )
902+ else : # POST
903+ body = {
904+ "sortby" : [{"field" : "id" , "direction" : "desc" }],
905+ endpoint ["body_key" ]: limit ,
906+ }
907+ resp = await app_client .post (endpoint ["path" ], json = body )
908+
909+ assert (
910+ resp .status_code == 200
911+ ), f"Failed for { endpoint ['method' ]} { endpoint ['path' ]} with sortby"
912+ resp_json = resp .json ()
913+
914+ found_collections = resp_json ["collections" ]
915+
916+ test_found = [c for c in found_collections if c ["id" ].startswith (test_prefix )]
917+
918+ # Verify collections are sorted in descending order
919+ # We expect the highest IDs first (09, 08, 07, etc.)
920+ expected_ids = sorted (
921+ [f"{ test_prefix } -{ i :02d} " for i in range (10 )], reverse = True
922+ )[:limit ]
923+
924+ # Filter expected_ids to only include collections that actually exist in the response
925+ expected_ids = [
926+ id for id in expected_ids if any (c ["id" ] == id for c in found_collections )
927+ ]
928+
929+ for i , expected_id in enumerate (expected_ids ):
930+ assert test_found [i ]["id" ] == expected_id
0 commit comments