252 lines
9.2 KiB
PHP
252 lines
9.2 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature;
|
|
|
|
use App\Models\Institution;
|
|
use App\Models\InstitutionLink;
|
|
use App\Models\Lists\InstitutionCategory;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Illuminate\Http\UploadedFile;
|
|
use Illuminate\Support\Facades\Storage;
|
|
use Tests\TestCase;
|
|
|
|
class InstitutionControllerTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
private const BASE_ROUTE = '/api/institutions';
|
|
|
|
private const TEST_MUSEUM = 'Test Museum';
|
|
|
|
private const JSON_HEADERS = ['Accept' => 'application/json'];
|
|
|
|
private const OLD_LOGO_PATH = 'institution_logo/old.jpg';
|
|
|
|
private const GONE_LOGO_PATH = 'institution_logo/gone.jpg';
|
|
|
|
/**
|
|
* @param array<string, mixed> $overrides
|
|
* @return array<string, mixed>
|
|
*/
|
|
private function validPayload(array $overrides = []): array
|
|
{
|
|
return array_merge([
|
|
'category_id' => InstitutionCategory::factory()->create()->id,
|
|
'name' => self::TEST_MUSEUM,
|
|
'abbreviation' => 'TM',
|
|
'address' => 'Some Road 1',
|
|
'city' => 'Lund',
|
|
'lat' => 55.7,
|
|
'lon' => 13.19,
|
|
'color' => '#ffffff',
|
|
'is_storage_place' => true,
|
|
'logo' => UploadedFile::fake()->image('logo.png'),
|
|
], $overrides);
|
|
}
|
|
|
|
// --- Lettura (tier.app) --------------------------------------------------
|
|
|
|
public function test_index_lists_institutions_for_an_operational_user(): void
|
|
{
|
|
$this->actingAs($this->operationalUser(), 'sanctum');
|
|
Institution::factory()->count(2)->create();
|
|
|
|
$this->getJson(self::BASE_ROUTE)
|
|
->assertOk()
|
|
->assertJsonStructure(['message', 'data', 'meta' => ['current_page', 'total']])
|
|
->assertJsonCount(2, 'data');
|
|
}
|
|
|
|
public function test_index_filters_by_category_and_search(): void
|
|
{
|
|
$this->actingAs($this->operationalUser(), 'sanctum');
|
|
$category = InstitutionCategory::factory()->create();
|
|
Institution::factory()->create(['category_id' => $category->id, 'name' => 'Blekinge Museum']);
|
|
Institution::factory()->create(['name' => 'Other Place']);
|
|
|
|
$this->getJson(self::BASE_ROUTE."?category_id={$category->id}")
|
|
->assertOk()
|
|
->assertJsonCount(1, 'data')
|
|
->assertJsonPath('data.0.name', 'Blekinge Museum');
|
|
|
|
$this->getJson(self::BASE_ROUTE.'?search=Blekinge')
|
|
->assertOk()
|
|
->assertJsonCount(1, 'data');
|
|
}
|
|
|
|
public function test_index_can_list_only_trashed(): void
|
|
{
|
|
$this->actingAs($this->operationalUser(), 'sanctum');
|
|
$trashed = Institution::factory()->create();
|
|
Institution::factory()->create();
|
|
$trashed->delete();
|
|
|
|
$this->getJson(self::BASE_ROUTE)->assertJsonCount(1, 'data');
|
|
$this->getJson(self::BASE_ROUTE.'?trashed=only')
|
|
->assertOk()
|
|
->assertJsonCount(1, 'data')
|
|
->assertJsonPath('data.0.id', $trashed->id);
|
|
$this->getJson(self::BASE_ROUTE.'?trashed=with')->assertJsonCount(2, 'data');
|
|
}
|
|
|
|
public function test_show_returns_institution_with_category_and_links(): void
|
|
{
|
|
$this->actingAs($this->operationalUser(), 'sanctum');
|
|
$institution = Institution::factory()->create();
|
|
InstitutionLink::factory()->create(['institution_id' => $institution->id]);
|
|
|
|
$this->getJson(self::BASE_ROUTE."/{$institution->uuid}")
|
|
->assertOk()
|
|
->assertJsonPath('data.uuid', $institution->uuid)
|
|
->assertJsonStructure(['data' => ['category', 'links']]);
|
|
}
|
|
|
|
public function test_show_resolves_by_uuid_not_id(): void
|
|
{
|
|
$this->actingAs($this->operationalUser(), 'sanctum');
|
|
$institution = Institution::factory()->create();
|
|
|
|
// L'id interno non è una route key valida.
|
|
$this->getJson(self::BASE_ROUTE."/{$institution->id}")->assertNotFound();
|
|
}
|
|
|
|
// --- Scrittura (tier.admin) ---------------------------------------------
|
|
|
|
public function test_admin_can_create_an_institution_with_a_logo(): void
|
|
{
|
|
Storage::fake('public');
|
|
$this->actingAs($this->adminUser(), 'sanctum');
|
|
|
|
$this->post(self::BASE_ROUTE, $this->validPayload(), self::JSON_HEADERS)
|
|
->assertCreated()
|
|
->assertJsonPath('data.name', self::TEST_MUSEUM)
|
|
->assertJsonStructure(['data' => ['uuid', 'category']]);
|
|
|
|
$institution = Institution::firstWhere('name', self::TEST_MUSEUM);
|
|
$this->assertNotNull($institution);
|
|
$this->assertNotEmpty($institution->uuid);
|
|
$this->assertTrue(Storage::disk('public')->exists($institution->logo));
|
|
}
|
|
|
|
public function test_create_applies_default_color_and_storage_flag(): void
|
|
{
|
|
Storage::fake('public');
|
|
$this->actingAs($this->adminUser(), 'sanctum');
|
|
|
|
$payload = $this->validPayload();
|
|
unset($payload['color'], $payload['is_storage_place']);
|
|
|
|
$this->post(self::BASE_ROUTE, $payload, self::JSON_HEADERS)->assertCreated();
|
|
|
|
$this->assertDatabaseHas('institutions', [
|
|
'name' => self::TEST_MUSEUM,
|
|
'color' => '#c5cae9',
|
|
'is_storage_place' => true,
|
|
]);
|
|
}
|
|
|
|
public function test_create_validates_required_fields(): void
|
|
{
|
|
$this->actingAs($this->adminUser(), 'sanctum');
|
|
|
|
$this->postJson(self::BASE_ROUTE, [])
|
|
->assertStatus(422)
|
|
->assertJsonValidationErrors(['category_id', 'name', 'abbreviation', 'lat', 'lon', 'logo']);
|
|
}
|
|
|
|
public function test_admin_can_update_an_institution_keeping_the_logo(): void
|
|
{
|
|
$this->actingAs($this->adminUser(), 'sanctum');
|
|
$institution = Institution::factory()->create(['logo' => 'institution_logo/keep.jpg', 'name' => 'Old']);
|
|
|
|
$payload = $this->validPayload(['name' => 'New Name', 'category_id' => $institution->category_id]);
|
|
unset($payload['logo']);
|
|
|
|
$this->put(self::BASE_ROUTE."/{$institution->uuid}", $payload, self::JSON_HEADERS)
|
|
->assertOk()
|
|
->assertJsonPath('data.name', 'New Name');
|
|
|
|
$this->assertSame('institution_logo/keep.jpg', $institution->fresh()->logo);
|
|
}
|
|
|
|
public function test_update_replaces_the_logo_when_a_new_one_is_uploaded(): void
|
|
{
|
|
Storage::fake('public');
|
|
$this->actingAs($this->adminUser(), 'sanctum');
|
|
Storage::disk('public')->put(self::OLD_LOGO_PATH, 'x');
|
|
$institution = Institution::factory()->create(['logo' => self::OLD_LOGO_PATH]);
|
|
|
|
$payload = $this->validPayload(['category_id' => $institution->category_id]);
|
|
|
|
$this->put(self::BASE_ROUTE."/{$institution->uuid}", $payload, self::JSON_HEADERS)->assertOk();
|
|
|
|
$this->assertFalse(Storage::disk('public')->exists(self::OLD_LOGO_PATH));
|
|
$this->assertTrue(Storage::disk('public')->exists($institution->fresh()->logo));
|
|
}
|
|
|
|
public function test_admin_can_soft_delete_an_institution(): void
|
|
{
|
|
$this->actingAs($this->adminUser(), 'sanctum');
|
|
$institution = Institution::factory()->create();
|
|
|
|
$this->deleteJson(self::BASE_ROUTE."/{$institution->uuid}")->assertOk();
|
|
|
|
$this->assertSoftDeleted($institution);
|
|
}
|
|
|
|
public function test_admin_can_restore_a_trashed_institution(): void
|
|
{
|
|
$this->actingAs($this->adminUser(), 'sanctum');
|
|
$institution = Institution::factory()->create();
|
|
$institution->delete();
|
|
|
|
$this->postJson(self::BASE_ROUTE."/{$institution->uuid}/restore")
|
|
->assertOk()
|
|
->assertJsonPath('data.uuid', $institution->uuid);
|
|
|
|
$this->assertNotSoftDeleted($institution);
|
|
}
|
|
|
|
public function test_admin_can_force_delete_an_institution_and_its_logo_and_links(): void
|
|
{
|
|
Storage::fake('public');
|
|
$this->actingAs($this->adminUser(), 'sanctum');
|
|
Storage::disk('public')->put(self::GONE_LOGO_PATH, 'x');
|
|
$institution = Institution::factory()->create(['logo' => self::GONE_LOGO_PATH]);
|
|
$link = InstitutionLink::factory()->create(['institution_id' => $institution->id]);
|
|
$institution->delete();
|
|
|
|
$this->deleteJson(self::BASE_ROUTE."/{$institution->uuid}/force")->assertOk();
|
|
|
|
$this->assertDatabaseMissing('institutions', ['id' => $institution->id]);
|
|
$this->assertDatabaseMissing('institution_links', ['id' => $link->id]);
|
|
Storage::assertMissing(self::GONE_LOGO_PATH);
|
|
}
|
|
|
|
// --- Autorizzazione ------------------------------------------------------
|
|
|
|
public function test_a_non_admin_cannot_write(): void
|
|
{
|
|
$this->actingAs($this->operationalUser(), 'sanctum');
|
|
$institution = Institution::factory()->create();
|
|
|
|
$this->postJson(self::BASE_ROUTE, [])->assertForbidden();
|
|
$this->deleteJson(self::BASE_ROUTE."/{$institution->uuid}")->assertForbidden();
|
|
}
|
|
|
|
public function test_guests_can_read_institutions(): void
|
|
{
|
|
Institution::factory()->create();
|
|
|
|
$this->getJson(self::BASE_ROUTE)->assertOk();
|
|
}
|
|
|
|
public function test_guests_cannot_write_institutions(): void
|
|
{
|
|
$institution = Institution::factory()->create();
|
|
|
|
$this->postJson(self::BASE_ROUTE, [])->assertUnauthorized();
|
|
$this->deleteJson(self::BASE_ROUTE."/{$institution->uuid}")->assertUnauthorized();
|
|
}
|
|
}
|