@@ -283,6 +283,190 @@ defmodule Projecthub.HttpApi.Test do
283
283
end
284
284
end
285
285
286
+ describe "GET /api/<version>/projects pagination" do
287
+ setup do
288
+ # Setup three projects to test pagination
289
+ p1_id = uuid ( )
290
+ p2_id = uuid ( )
291
+ p3_id = uuid ( )
292
+ p1 = create ( "project1" , p1_id )
293
+ p2 = create ( "project2" , p2_id )
294
+ p3 = create ( "project3" , p3_id )
295
+
296
+ FunRegistry . set! ( FakeServices.RbacService , :list_accessible_projects , fn _ , _ ->
297
+ InternalApi.RBAC.ListAccessibleProjectsResponse . new ( project_ids: [ p1_id , p2_id , p3_id ] )
298
+ end )
299
+
300
+ FunRegistry . set! ( FakeServices.ProjectService , :list , fn req , _ ->
301
+ alias InternalApi.Projecthub , as: PH
302
+ # Simulate pagination
303
+ page = req . pagination . page
304
+ page_size = req . pagination . page_size
305
+ all_projects = [ p1 , p2 , p3 ]
306
+ projects = Enum . slice ( all_projects , ( page - 1 ) * page_size , page_size )
307
+
308
+ PH.ListResponse . new (
309
+ metadata:
310
+ PH.ResponseMeta . new (
311
+ status: PH.ResponseMeta.Status . new ( code: PH.ResponseMeta.Code . value ( :OK ) )
312
+ ) ,
313
+ projects: projects ,
314
+ pagination:
315
+ PH.PaginationResponse . new (
316
+ page_number: page ,
317
+ page_size: page_size ,
318
+ total_entries: length ( all_projects ) ,
319
+ total_pages: div ( length ( all_projects ) + page_size - 1 , page_size )
320
+ )
321
+ )
322
+ end )
323
+
324
+ :ok
325
+ end
326
+
327
+ test "returns correct pagination headers for /projects" do
328
+ { :ok , response } =
329
+ HTTPoison . get (
330
+ "http://localhost:#{ @ port } /api/#{ @ version } /projects?page=1&page_size=2" ,
331
+ @ headers
332
+ )
333
+
334
+ assert response . status_code == 200
335
+ assert response . headers |> Enum . any? ( fn { k , v } -> k == "x-page" and v == "1" end )
336
+ assert response . headers |> Enum . any? ( fn { k , v } -> k == "x-page-size" and v == "2" end )
337
+ assert response . headers |> Enum . any? ( fn { k , v } -> k == "x-total-count" and v == "3" end )
338
+ assert response . headers |> Enum . any? ( fn { k , v } -> k == "x-has-more" and v == "true" end )
339
+ projects = Poison . decode! ( response . body )
340
+ assert length ( projects ) == 2
341
+ end
342
+
343
+ test "returns correct pagination headers for /projects when there are no more projects" do
344
+ { :ok , response } =
345
+ HTTPoison . get (
346
+ "http://localhost:#{ @ port } /api/#{ @ version } /projects?page=2&page_size=2" ,
347
+ @ headers
348
+ )
349
+
350
+ assert response . status_code == 200
351
+ assert response . headers |> Enum . any? ( fn { k , v } -> k == "x-page" and v == "2" end )
352
+ assert response . headers |> Enum . any? ( fn { k , v } -> k == "x-page-size" and v == "2" end )
353
+ assert response . headers |> Enum . any? ( fn { k , v } -> k == "x-total-count" and v == "3" end )
354
+ assert response . headers |> Enum . any? ( fn { k , v } -> k == "x-has-more" and v == "false" end )
355
+ projects = Poison . decode! ( response . body )
356
+ assert length ( projects ) == 1
357
+ end
358
+
359
+ test "returns 404 on out-of-range page" do
360
+ FunRegistry . set! ( FakeServices.ProjectService , :list , fn _ , _ ->
361
+ alias InternalApi.Projecthub , as: PH
362
+
363
+ PH.ListResponse . new (
364
+ metadata:
365
+ PH.ResponseMeta . new (
366
+ status: PH.ResponseMeta.Status . new ( code: PH.ResponseMeta.Code . value ( :NOT_FOUND ) )
367
+ ) ,
368
+ projects: [ ]
369
+ )
370
+ end )
371
+
372
+ { :ok , response } =
373
+ HTTPoison . get (
374
+ "http://localhost:#{ @ port } /api/#{ @ version } /projects?page=10&page_size=2" ,
375
+ @ headers
376
+ )
377
+
378
+ assert response . status_code == 404
379
+ end
380
+
381
+ test "returns 400 on bad request" do
382
+ FunRegistry . set! ( FakeServices.ProjectService , :list , fn _ , _ ->
383
+ alias InternalApi.Projecthub , as: PH
384
+
385
+ PH.ListResponse . new (
386
+ metadata:
387
+ PH.ResponseMeta . new (
388
+ status:
389
+ PH.ResponseMeta.Status . new ( code: PH.ResponseMeta.Code . value ( :FAILED_PRECONDITION ) )
390
+ ) ,
391
+ projects: [ ] ,
392
+ pagination:
393
+ PH.PaginationResponse . new (
394
+ total_count: 0 ,
395
+ page_number: 0 ,
396
+ page_size: 0 ,
397
+ total_pages: 0
398
+ )
399
+ )
400
+ end )
401
+
402
+ { :ok , response } =
403
+ HTTPoison . get (
404
+ "http://localhost:#{ @ port } /api/#{ @ version } /projects?page=foo&page_size=bar" ,
405
+ @ headers
406
+ )
407
+
408
+ assert response . status_code == 400
409
+ end
410
+
411
+ test "returns 400 on negative page" do
412
+ { :ok , response } =
413
+ HTTPoison . get (
414
+ "http://localhost:#{ @ port } /api/#{ @ version } /projects?page=-1&page_size=2" ,
415
+ @ headers
416
+ )
417
+
418
+ assert response . status_code == 400
419
+ assert Poison . decode! ( response . body ) [ "message" ] =~ "page must be at least 1"
420
+ end
421
+
422
+ test "returns 400 on zero page_size" do
423
+ { :ok , response } =
424
+ HTTPoison . get (
425
+ "http://localhost:#{ @ port } /api/#{ @ version } /projects?page=1&page_size=0" ,
426
+ @ headers
427
+ )
428
+
429
+ assert response . status_code == 400
430
+ assert Poison . decode! ( response . body ) [ "message" ] =~ "page_size must be at least 1"
431
+ end
432
+
433
+ test "returns 400 on too large page" do
434
+ { :ok , response } =
435
+ HTTPoison . get (
436
+ "http://localhost:#{ @ port } /api/#{ @ version } /projects?page=9999&page_size=2" ,
437
+ @ headers
438
+ )
439
+
440
+ assert response . status_code == 400 or response . status_code == 200
441
+
442
+ if response . status_code == 400 do
443
+ assert Poison . decode! ( response . body ) [ "message" ] =~ "page must be at most"
444
+ end
445
+ end
446
+
447
+ test "returns 400 on too large page_size" do
448
+ { :ok , response } =
449
+ HTTPoison . get (
450
+ "http://localhost:#{ @ port } /api/#{ @ version } /projects?page=1&page_size=9999" ,
451
+ @ headers
452
+ )
453
+
454
+ assert response . status_code == 400
455
+ assert Poison . decode! ( response . body ) [ "message" ] =~ "page_size must be at most"
456
+ end
457
+
458
+ test "returns 400 on non-numeric page_size" do
459
+ { :ok , response } =
460
+ HTTPoison . get (
461
+ "http://localhost:#{ @ port } /api/#{ @ version } /projects?page=1&page_size=abc" ,
462
+ @ headers
463
+ )
464
+
465
+ assert response . status_code == 400
466
+ assert Poison . decode! ( response . body ) [ "message" ] =~ "page_size must be a number"
467
+ end
468
+ end
469
+
286
470
describe "GET /api/<version>/projects/:name with authorized user" do
287
471
setup do
288
472
FunRegistry . set! ( FakeServices.RbacService , :list_user_permissions , fn _ , _ ->
0 commit comments