From 0b454dfc809b3fca99ac7374f77331027cdfe9e7 Mon Sep 17 00:00:00 2001 From: HolgerHatGarKeineNode <123783602+HolgerHatGarKeineNode@users.noreply.github.com> Date: Fri, 12 Jun 2026 18:00:14 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=A7=AA=20Add=20API=20tests=20and=20update?= =?UTF-8?q?=20controllers=20for=20city=20and=20venue=20details?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ✏️ Added feature tests for cities and venues, including pagination limits and `withDetails` parameter handling. - ✏️ Updated `CityController` to support `withDetails`, returning country code and flag URL while lifting pagination limits. - ✏️ Updated `VenueController` to support `withDetails`, lifting pagination limits and enriching venue responses with city details. --- app/Http/Controllers/Api/CityController.php | 16 +++++-- app/Http/Controllers/Api/VenueController.php | 3 +- tests/Feature/Api/CityVenueReadApiTest.php | 50 ++++++++++++++++++++ 3 files changed, 65 insertions(+), 4 deletions(-) create mode 100644 tests/Feature/Api/CityVenueReadApiTest.php diff --git a/app/Http/Controllers/Api/CityController.php b/app/Http/Controllers/Api/CityController.php index b17573e..d637fa7 100644 --- a/app/Http/Controllers/Api/CityController.php +++ b/app/Http/Controllers/Api/CityController.php @@ -30,10 +30,13 @@ class CityController extends Controller */ #[QueryParameter(name: 'search', description: 'Teilstring-Suche im Namen der Stadt.', required: false, type: 'string')] #[QueryParameter(name: 'selected', description: 'Lädt gezielt die angegebenen IDs.', required: false, type: 'array')] + #[QueryParameter(name: 'withDetails', description: 'Presence-Flag: liefert zusätzlich Country-Code und Flaggen-URL und hebt die Begrenzung auf 10 Einträge auf.', required: false, type: 'string')] public function index(Request $request) { + $withDetails = $request->exists('withDetails'); + return City::query() - ->with(['country:id,name']) + ->with([$withDetails ? 'country:id,name,code' : 'country:id,name']) ->select('id', 'name', 'country_id') ->orderBy('name') ->when( @@ -44,9 +47,16 @@ class CityController extends Controller ->when( $request->exists('selected'), fn (Builder $query) => $query->whereIn('id', $this->numericIds($request)), - fn (Builder $query) => $query->limit(10) + fn (Builder $query) => $withDetails ? $query : $query->limit(10) ) - ->get(); + ->get() + ->map(function (City $city) use ($withDetails) { + if ($withDetails) { + $city->flag = asset('vendor/blade-country-flags/4x3-'.$city->country->code.'.svg'); + } + + return $city; + }); } /** diff --git a/app/Http/Controllers/Api/VenueController.php b/app/Http/Controllers/Api/VenueController.php index 00ef62b..229b213 100644 --- a/app/Http/Controllers/Api/VenueController.php +++ b/app/Http/Controllers/Api/VenueController.php @@ -32,6 +32,7 @@ class VenueController extends Controller */ #[QueryParameter(name: 'search', description: 'Teilstring-Suche im Namen des Veranstaltungsortes.', required: false, type: 'string')] #[QueryParameter(name: 'selected', description: 'Lädt gezielt die angegebenen Veranstaltungsort-IDs (umgeht die Begrenzung auf 10 Einträge).', required: false, type: 'array')] + #[QueryParameter(name: 'withDetails', description: 'Presence-Flag: hebt die Begrenzung auf 10 Einträge auf.', required: false, type: 'string')] public function index(Request $request) { return Venue::query() @@ -46,7 +47,7 @@ class VenueController extends Controller ->when( $request->exists('selected'), fn (Builder $query) => $query->whereIn('id', $this->numericIds($request)), - fn (Builder $query) => $query->limit(10) + fn (Builder $query) => $request->exists('withDetails') ? $query : $query->limit(10) ) ->get() ->map(function (Venue $venue) { diff --git a/tests/Feature/Api/CityVenueReadApiTest.php b/tests/Feature/Api/CityVenueReadApiTest.php new file mode 100644 index 0000000..c5282b0 --- /dev/null +++ b/tests/Feature/Api/CityVenueReadApiTest.php @@ -0,0 +1,50 @@ +count(11)->create(['country_id' => Country::factory()->create()->id]); + + $this->getJson('/api/cities') + ->assertSuccessful() + ->assertJsonCount(10); +}); + +it('returns all cities with country code and flag on GET /api/cities?withDetails', function () { + $cities = City::factory()->count(11)->create(['country_id' => Country::factory()->create()->id]); + + $response = $this->getJson('/api/cities?withDetails') + ->assertSuccessful() + ->assertJsonCount(11); + + $first = collect($response->json())->firstWhere('id', $cities->first()->id); + + expect($first) + ->toHaveKeys(['id', 'name', 'country_id', 'country', 'flag']) + ->and($first['country'])->toHaveKeys(['id', 'name', 'code']) + ->and($first['flag'])->toContain('4x3-'.$cities->first()->country->code.'.svg'); +}); + +it('limits GET /api/venues to 10 entries without withDetails', function () { + Venue::factory()->count(11)->create(['city_id' => City::factory()->create()->id]); + + $this->getJson('/api/venues') + ->assertSuccessful() + ->assertJsonCount(10); +}); + +it('returns all venues on GET /api/venues?withDetails', function () { + $venues = Venue::factory()->count(11)->create(['city_id' => City::factory()->create()->id]); + + $response = $this->getJson('/api/venues?withDetails') + ->assertSuccessful() + ->assertJsonCount(11); + + $first = collect($response->json())->firstWhere('id', $venues->first()->id); + + expect($first) + ->toHaveKeys(['id', 'name', 'city_id', 'flag', 'description', 'city']) + ->and($first['city']['country'])->toHaveKeys(['id', 'name', 'code']); +});