auth tests

This commit is contained in:
Giuseppe Naponiello
2026-06-22 14:31:44 +02:00
parent 34a8ebc6fb
commit cc4e174880
75 changed files with 4679 additions and 168 deletions

View File

@@ -0,0 +1,66 @@
<?php
namespace Tests\Concerns;
use App\Models\Lists\UserRole;
use App\Models\User;
/**
* Helper per costruire utenti nei vari stati del setup, senza ripetere ovunque
* lo stesso array di attributi. La factory di default crea un utente verificato
* ma con must_change_password=true (default di colonna) setup incompleto: qui
* forniamo scorciatoie per gli stati che servono ai test (operativo, admin,
* di sistema, setup incompleto, non verificato).
*/
trait CreatesUsers
{
/** Ruolo applicativo, riusato se già presente (idempotente in-test). */
protected function role(string $name): UserRole
{
return UserRole::firstOrCreate(['name' => $name], ['description' => $name.' role']);
}
/** Utente pienamente operativo: verificato, setup completo, ruolo User. */
protected function operationalUser(array $overrides = []): User
{
return User::factory()->create(array_merge([
'role_id' => $this->role(UserRole::USER)->id,
'email_verified_at' => now(),
'must_change_password' => false,
'two_factor_setup_completed_at' => now(),
], $overrides));
}
/** Operativo con ruolo Admin → supera tier.admin / IsAdmin. */
protected function adminUser(array $overrides = []): User
{
return $this->operationalUser(array_merge([
'role_id' => $this->role(UserRole::ADMIN)->id,
], $overrides));
}
/** Utente di sistema (destinatario dei contenuti riassegnati, non eliminabile). */
protected function systemUser(array $overrides = []): User
{
return $this->operationalUser(array_merge([
'is_system' => true,
'role_id' => $this->role(UserRole::ADMIN)->id,
], $overrides));
}
/** Autenticato ma setup incompleto (deve cambiare password). */
protected function setupIncompleteUser(array $overrides = []): User
{
return $this->operationalUser(array_merge([
'must_change_password' => true,
], $overrides));
}
/** Setup completo ma email non verificata → blocco di `verified`. */
protected function unverifiedUser(array $overrides = []): User
{
return $this->operationalUser(array_merge([
'email_verified_at' => null,
], $overrides));
}
}

View File

@@ -0,0 +1,73 @@
<?php
namespace Tests\Feature\Auth;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
/**
* Verifica la scala di accesso (tier.app / tier.admin) attraverso rotte reali:
* - tier.app user-roles index (auth + verified + setup.complete)
* - tier.admin→ users index (+ admin)
* Copre i rami di EnsureSetupComplete, del middleware `verified` e di IsAdmin.
*/
class AccessTierTest extends TestCase
{
use RefreshDatabase;
private const APP_ROUTE = '/api/user-roles';
private const ADMIN_ROUTE = '/api/users';
public function test_tier_app_allows_a_fully_operational_user(): void
{
$this->actingAs($this->operationalUser(), 'sanctum');
$this->getJson(self::APP_ROUTE)->assertOk();
}
public function test_tier_app_requires_authentication(): void
{
$this->getJson(self::APP_ROUTE)->assertUnauthorized();
}
public function test_tier_app_blocks_user_who_must_change_password(): void
{
$this->actingAs($this->setupIncompleteUser(), 'sanctum');
$this->getJson(self::APP_ROUTE)
->assertForbidden()
->assertJsonPath('setup_status', 'password_required');
}
public function test_tier_app_blocks_user_without_two_factor_setup(): void
{
$user = $this->operationalUser(['two_factor_setup_completed_at' => null]);
$this->actingAs($user, 'sanctum');
$this->getJson(self::APP_ROUTE)
->assertForbidden()
->assertJsonPath('setup_status', '2fa_setup_required');
}
public function test_tier_app_blocks_unverified_user(): void
{
$this->actingAs($this->unverifiedUser(), 'sanctum');
$this->getJson(self::APP_ROUTE)->assertForbidden();
}
public function test_tier_admin_allows_admin(): void
{
$this->actingAs($this->adminUser(), 'sanctum');
$this->getJson(self::ADMIN_ROUTE)->assertOk();
}
public function test_tier_admin_forbids_non_admin(): void
{
$this->actingAs($this->operationalUser(), 'sanctum');
$this->getJson(self::ADMIN_ROUTE)->assertForbidden();
}
}

View File

@@ -0,0 +1,86 @@
<?php
namespace Tests\Feature\Auth;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class AuthControllerTest extends TestCase
{
use RefreshDatabase;
public function test_me_returns_authenticated_user_with_setup_status(): void
{
$user = $this->operationalUser();
$this->actingAs($user, 'sanctum');
$response = $this->getJson('/api/user');
$response->assertOk()
->assertJsonPath('id', $user->id)
->assertJsonPath('email', $user->email)
->assertJsonPath('setup_status', 'complete');
// Gli attributi sensibili non devono mai essere serializzati.
$this->assertArrayNotHasKey('password', $response->json());
$this->assertArrayNotHasKey('two_factor_secret', $response->json());
$this->assertArrayNotHasKey('two_factor_recovery_codes', $response->json());
}
public function test_me_is_reachable_during_setup(): void
{
// La rotta /user è nel tier "transitional": deve restare accessibile anche
// a setup incompleto, così il frontend può leggere setup_status.
$user = $this->setupIncompleteUser();
$this->actingAs($user, 'sanctum');
$this->getJson('/api/user')
->assertOk()
->assertJsonPath('setup_status', 'password_required');
}
public function test_me_requires_authentication(): void
{
$this->getJson('/api/user')->assertUnauthorized();
}
public function test_logout_succeeds_and_forgets_cookies(): void
{
$user = $this->operationalUser();
$this->actingAs($user, 'sanctum');
$response = $this->postJson('/api/logout');
$response->assertOk()
->assertJsonPath('message', config('messages.auth.logout_success'))
->assertCookieExpired('XSRF-TOKEN');
}
public function test_destroy_account_soft_deletes_the_user(): void
{
$user = $this->operationalUser();
$this->actingAs($user, 'sanctum');
$this->deleteJson('/api/account')
->assertOk()
->assertJsonPath('message', config('messages.account.deleted'));
$this->assertSoftDeleted('users', ['id' => $user->id]);
}
public function test_destroy_account_is_forbidden_for_the_system_user(): void
{
$user = $this->systemUser();
$this->actingAs($user, 'sanctum');
$this->deleteJson('/api/account')->assertForbidden();
$this->assertDatabaseHas('users', ['id' => $user->id, 'deleted_at' => null]);
}
public function test_session_endpoints_require_authentication(): void
{
$this->postJson('/api/logout')->assertUnauthorized();
$this->deleteJson('/api/account')->assertUnauthorized();
}
}

View File

@@ -0,0 +1,24 @@
<?php
namespace Tests\Feature;
use Illuminate\Support\Facades\DB;
use Tests\TestCase;
/**
* Guardia di sicurezza: garantisce che la suite giri sul DB di test dedicato
* (servizio db-test), non sul database di sviluppo `dyncoll`. È volutamente
* read-only (non usa RefreshDatabase): qui assertiamo che l'host e il nome DB
* siano quelli di test. La protezione "dura" che aborta PRIMA di un eventuale
* migrate:fresh sta nel guard beforeRefreshingDatabase() di Tests\TestCase.
*/
class EnvironmentSafetyTest extends TestCase
{
public function test_suite_runs_against_the_dedicated_test_database(): void
{
$this->assertSame('testing', app()->environment());
$this->assertSame('mysql', config('database.default'));
$this->assertSame('db-test', config('database.connections.mysql.host'));
$this->assertSame('dyncoll_test', DB::connection()->getDatabaseName());
}
}

View File

@@ -1,19 +0,0 @@
<?php
namespace Tests\Feature;
// use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class ExampleTest extends TestCase
{
/**
* A basic test example.
*/
public function test_the_application_returns_a_successful_response(): void
{
$response = $this->get('/');
$response->assertStatus(200);
}
}

View File

@@ -0,0 +1,77 @@
<?php
namespace Tests\Feature;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class PurgeAnonymizableUsersCommandTest extends TestCase
{
use RefreshDatabase;
/** Crea un utente cestinato con deleted_at retrodatato di $days giorni. */
private function trashedDaysAgo(int $days): User
{
$user = $this->operationalUser();
$user->delete();
User::withTrashed()->whereKey($user->id)->update(['deleted_at' => now()->subDays($days)]);
return $user->fresh();
}
public function test_it_anonymizes_users_past_the_retention_window(): void
{
$this->systemUser();
$old = $this->trashedDaysAgo(40);
$this->artisan('users:purge', ['--days' => 30])->assertSuccessful();
$old->refresh();
$this->assertNotNull($old->anonymized_at);
$this->assertSame('Deleted user', $old->name);
}
public function test_it_skips_users_still_within_retention(): void
{
$this->systemUser();
$recent = $this->trashedDaysAgo(5);
$this->artisan('users:purge', ['--days' => 30])->assertSuccessful();
$recent->refresh();
$this->assertNull($recent->anonymized_at);
}
public function test_dry_run_does_not_modify_anyone(): void
{
$this->systemUser();
$old = $this->trashedDaysAgo(40);
$this->artisan('users:purge', ['--days' => 30, '--dry-run' => true])->assertSuccessful();
$old->refresh();
$this->assertNull($old->anonymized_at);
}
public function test_it_never_touches_the_system_user(): void
{
$system = $this->systemUser();
// Anche se (per assurdo) cestinato e vecchio, l'utente di sistema è escluso.
$system->delete();
User::withTrashed()->whereKey($system->id)->update(['deleted_at' => now()->subDays(99)]);
$this->artisan('users:purge', ['--days' => 30])->assertSuccessful();
$this->assertNull($system->fresh()->anonymized_at);
}
public function test_it_reports_when_there_is_nothing_to_do(): void
{
$this->systemUser();
$this->artisan('users:purge', ['--days' => 30])
->expectsOutputToContain('No users to anonymize.')
->assertSuccessful();
}
}

View File

@@ -0,0 +1,55 @@
<?php
namespace Tests\Feature;
use App\Actions\PurgeUserAction;
use App\Exceptions\CannotDeleteSystemUserException;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class PurgeUserActionTest extends TestCase
{
use RefreshDatabase;
private function purge(): PurgeUserAction
{
return app(PurgeUserAction::class);
}
public function test_it_anonymizes_personal_data_in_place(): void
{
$this->systemUser();
$user = $this->operationalUser(['name' => 'Mario Rossi']);
$this->purge()->execute($user);
$user->refresh();
$this->assertSame('Deleted user', $user->name);
$this->assertSame("deleted-{$user->id}@anonymized.invalid", $user->email);
$this->assertNotNull($user->anonymized_at);
$this->assertNull($user->email_verified_at);
$this->assertNull($user->two_factor_setup_completed_at);
$this->assertFalse($user->must_change_password);
}
public function test_it_is_idempotent_on_already_anonymized_users(): void
{
$this->systemUser();
$user = $this->operationalUser(['name' => 'Keep Me', 'anonymized_at' => now()]);
$this->purge()->execute($user);
$user->refresh();
// Già anonimizzato → uscita anticipata: nessuna riscrittura del nome.
$this->assertSame('Keep Me', $user->name);
}
public function test_it_refuses_to_anonymize_the_system_user(): void
{
$system = $this->systemUser();
$this->expectException(CannotDeleteSystemUserException::class);
$this->purge()->execute($system);
}
}

View File

@@ -0,0 +1,73 @@
<?php
namespace Tests\Feature;
use App\Models\Lists\UserRole;
use App\Models\User;
use Database\Seeders\DatabaseSeeder;
use Database\Seeders\SystemUserSeeder;
use Database\Seeders\UserRoleSeeder;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class SeederTest extends TestCase
{
use RefreshDatabase;
public function test_user_role_seeder_creates_the_base_roles(): void
{
$this->seed(UserRoleSeeder::class);
$this->assertSame(4, UserRole::count());
foreach ([UserRole::ADMIN, UserRole::SUPERVISOR, UserRole::USER, UserRole::GUEST] as $name) {
$this->assertDatabaseHas('user_roles', ['name' => $name]);
}
}
public function test_role_descriptions_are_in_english(): void
{
$this->seed(UserRoleSeeder::class);
$this->assertStringContainsString(
'Full access',
(string) UserRole::where('name', UserRole::ADMIN)->value('description')
);
}
public function test_user_role_seeder_is_idempotent(): void
{
$this->seed(UserRoleSeeder::class);
$this->seed(UserRoleSeeder::class);
$this->assertSame(4, UserRole::count());
}
public function test_system_user_seeder_creates_a_locked_admin_account(): void
{
$this->seed(UserRoleSeeder::class);
$this->seed(SystemUserSeeder::class);
$system = User::where('email', SystemUserSeeder::EMAIL)->firstOrFail();
$this->assertTrue($system->is_system);
$this->assertFalse($system->must_change_password);
$this->assertSame(UserRole::ADMIN, $system->role->name);
$this->assertSame('complete', $system->setup_status);
}
public function test_system_user_seeder_is_idempotent(): void
{
$this->seed(UserRoleSeeder::class);
$this->seed(SystemUserSeeder::class);
$this->seed(SystemUserSeeder::class);
$this->assertSame(1, User::where('email', SystemUserSeeder::EMAIL)->count());
}
public function test_database_seeder_runs_the_full_chain(): void
{
$this->seed(DatabaseSeeder::class);
$this->assertSame(4, UserRole::count());
$this->assertDatabaseHas('users', ['email' => SystemUserSeeder::EMAIL, 'is_system' => true]);
}
}

View File

@@ -0,0 +1,170 @@
<?php
namespace Tests\Feature;
use App\Models\Lists\UserRole;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Testing\TestResponse;
use Tests\TestCase;
class UserControllerTest extends TestCase
{
use RefreshDatabase;
/** @return list<int> id presenti in data della risposta. */
private function idsFrom(TestResponse $response): array
{
return collect($response->json('data'))->pluck('id')->all();
}
// --- Gating ---------------------------------------------------------------
public function test_index_requires_authentication(): void
{
$this->getJson('/api/users')->assertUnauthorized();
}
public function test_index_is_forbidden_for_non_admin(): void
{
$this->actingAs($this->operationalUser(), 'sanctum');
$this->getJson('/api/users')->assertForbidden();
}
// --- Index ----------------------------------------------------------------
public function test_index_returns_paginated_envelope(): void
{
$this->actingAs($this->adminUser(), 'sanctum');
$this->getJson('/api/users')
->assertOk()
->assertJsonStructure([
'message',
'data',
'meta' => ['current_page', 'last_page', 'per_page', 'total'],
]);
}
public function test_index_filters_by_search_term(): void
{
$this->actingAs($this->adminUser(), 'sanctum');
$match = $this->operationalUser(['name' => 'Zzz Unique Marker']);
$other = $this->operationalUser(['name' => 'Someone Else']);
$ids = $this->idsFrom($this->getJson('/api/users?search=Unique+Marker')->assertOk());
$this->assertContains($match->id, $ids);
$this->assertNotContains($other->id, $ids);
}
public function test_index_filters_by_role(): void
{
$this->actingAs($this->adminUser(), 'sanctum');
$role = UserRole::factory()->create();
$inRole = $this->operationalUser(['role_id' => $role->id]);
$outRole = $this->operationalUser();
$ids = $this->idsFrom($this->getJson("/api/users?role_id={$role->id}")->assertOk());
$this->assertContains($inRole->id, $ids);
$this->assertNotContains($outRole->id, $ids);
}
public function test_index_excludes_trashed_by_default(): void
{
$this->actingAs($this->adminUser(), 'sanctum');
$trashed = $this->operationalUser();
$trashed->delete();
$ids = $this->idsFrom($this->getJson('/api/users')->assertOk());
$this->assertNotContains($trashed->id, $ids);
}
public function test_index_can_include_trashed(): void
{
$this->actingAs($this->adminUser(), 'sanctum');
$active = $this->operationalUser();
$trashed = $this->operationalUser();
$trashed->delete();
$ids = $this->idsFrom($this->getJson('/api/users?trashed=with')->assertOk());
$this->assertContains($active->id, $ids);
$this->assertContains($trashed->id, $ids);
}
public function test_index_can_return_only_trashed(): void
{
$this->actingAs($this->adminUser(), 'sanctum');
$active = $this->operationalUser();
$trashed = $this->operationalUser();
$trashed->delete();
$ids = $this->idsFrom($this->getJson('/api/users?trashed=only')->assertOk());
$this->assertContains($trashed->id, $ids);
$this->assertNotContains($active->id, $ids);
}
public function test_index_respects_per_page(): void
{
$this->actingAs($this->adminUser(), 'sanctum');
$this->getJson('/api/users?per_page=5')
->assertOk()
->assertJsonPath('meta.per_page', 5);
}
public function test_index_rejects_an_excessive_per_page(): void
{
$this->actingAs($this->adminUser(), 'sanctum');
$this->getJson('/api/users?per_page=500')
->assertUnprocessable()
->assertJsonValidationErrors('per_page');
}
public function test_index_rejects_an_unknown_trashed_value(): void
{
$this->actingAs($this->adminUser(), 'sanctum');
$this->getJson('/api/users?trashed=garbage')
->assertUnprocessable()
->assertJsonValidationErrors('trashed');
}
// --- Show -----------------------------------------------------------------
public function test_show_returns_a_user_with_role(): void
{
$this->actingAs($this->adminUser(), 'sanctum');
$user = $this->operationalUser();
$this->getJson("/api/users/{$user->id}")
->assertOk()
->assertJsonPath('data.id', $user->id)
->assertJsonPath('data.setup_status', 'complete');
}
public function test_show_can_load_a_trashed_user(): void
{
$this->actingAs($this->adminUser(), 'sanctum');
$user = $this->operationalUser();
$user->delete();
$this->getJson("/api/users/{$user->id}")
->assertOk()
->assertJsonPath('data.id', $user->id);
}
public function test_show_is_forbidden_for_non_admin(): void
{
$this->actingAs($this->operationalUser(), 'sanctum');
$target = User::factory()->create();
$this->getJson("/api/users/{$target->id}")->assertForbidden();
}
}

View File

@@ -0,0 +1,163 @@
<?php
namespace Tests\Feature;
use App\Models\Lists\UserRole;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class UserRoleControllerTest extends TestCase
{
use RefreshDatabase;
// --- Lettura (tier.app) --------------------------------------------------
public function test_index_lists_roles_for_an_operational_user(): void
{
$this->actingAs($this->operationalUser(), 'sanctum');
UserRole::factory()->count(2)->create();
$this->getJson('/api/user-roles')
->assertOk()
->assertJsonStructure(['message', 'data' => [['id', 'name', 'description']]]);
}
public function test_show_returns_a_single_role(): void
{
$this->actingAs($this->operationalUser(), 'sanctum');
$role = UserRole::factory()->create();
$this->getJson("/api/user-roles/{$role->id}")
->assertOk()
->assertJsonPath('data.id', $role->id)
->assertJsonPath('data.name', $role->name);
}
public function test_usage_flags_system_role_as_locked(): void
{
$this->actingAs($this->operationalUser(), 'sanctum');
$role = UserRole::create(['name' => UserRole::SUPERVISOR]);
$this->getJson("/api/user-roles/{$role->id}/usage")
->assertOk()
->assertJsonPath('in_use', true);
}
public function test_usage_flags_free_custom_role_as_unlocked(): void
{
$this->actingAs($this->operationalUser(), 'sanctum');
$role = UserRole::factory()->create();
$this->getJson("/api/user-roles/{$role->id}/usage")
->assertOk()
->assertJsonPath('in_use', false);
}
// --- Scrittura (tier.admin) ---------------------------------------------
public function test_admin_can_create_a_role(): void
{
$this->actingAs($this->adminUser(), 'sanctum');
$this->postJson('/api/user-roles', ['name' => 'Editor', 'description' => 'Edits stuff'])
->assertCreated()
->assertJsonPath('data.name', 'Editor');
$this->assertDatabaseHas('user_roles', ['name' => 'Editor']);
}
public function test_create_requires_a_name(): void
{
$this->actingAs($this->adminUser(), 'sanctum');
$this->postJson('/api/user-roles', ['description' => 'no name'])
->assertUnprocessable()
->assertJsonValidationErrors('name');
}
public function test_create_rejects_a_duplicate_name(): void
{
$this->actingAs($this->adminUser(), 'sanctum');
UserRole::factory()->create(['name' => 'Editor']);
$this->postJson('/api/user-roles', ['name' => 'Editor'])
->assertUnprocessable()
->assertJsonValidationErrors('name');
}
public function test_admin_can_update_a_role(): void
{
$this->actingAs($this->adminUser(), 'sanctum');
$role = UserRole::factory()->create(['name' => 'Old']);
$this->putJson("/api/user-roles/{$role->id}", ['name' => 'New'])
->assertOk()
->assertJsonPath('data.name', 'New');
$this->assertDatabaseHas('user_roles', ['id' => $role->id, 'name' => 'New']);
}
public function test_update_can_keep_the_same_name(): void
{
$this->actingAs($this->adminUser(), 'sanctum');
$role = UserRole::factory()->create(['name' => 'Stable']);
$this->putJson("/api/user-roles/{$role->id}", ['name' => 'Stable'])
->assertOk();
}
public function test_update_rejects_a_name_taken_by_another_role(): void
{
$this->actingAs($this->adminUser(), 'sanctum');
UserRole::factory()->create(['name' => 'Taken']);
$role = UserRole::factory()->create(['name' => 'Mine']);
$this->putJson("/api/user-roles/{$role->id}", ['name' => 'Taken'])
->assertUnprocessable()
->assertJsonValidationErrors('name');
}
public function test_cannot_delete_a_system_role(): void
{
$this->actingAs($this->adminUser(), 'sanctum');
$role = UserRole::create(['name' => UserRole::GUEST]);
$this->deleteJson("/api/user-roles/{$role->id}")
->assertStatus(409)
->assertJsonPath('in_use', true);
$this->assertDatabaseHas('user_roles', ['id' => $role->id]);
}
public function test_cannot_delete_a_role_in_use(): void
{
$this->actingAs($this->adminUser(), 'sanctum');
$role = UserRole::factory()->create(['name' => 'Busy']);
$this->operationalUser(['role_id' => $role->id]);
$this->deleteJson("/api/user-roles/{$role->id}")
->assertStatus(409)
->assertJsonPath('in_use', true);
$this->assertDatabaseHas('user_roles', ['id' => $role->id]);
}
public function test_admin_can_delete_a_free_custom_role(): void
{
$this->actingAs($this->adminUser(), 'sanctum');
$role = UserRole::factory()->create(['name' => 'Disposable']);
$this->deleteJson("/api/user-roles/{$role->id}")->assertOk();
$this->assertDatabaseMissing('user_roles', ['id' => $role->id]);
}
// --- Gating ---------------------------------------------------------------
public function test_non_admin_cannot_write(): void
{
$this->actingAs($this->operationalUser(), 'sanctum');
$this->postJson('/api/user-roles', ['name' => 'Nope'])->assertForbidden();
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace Tests\Support;
use RuntimeException;
/**
* Sollevata dal guard del base TestCase quando la suite sta per girare su un
* database che non è quello di test dedicato (`dyncoll_test`): blocca il run
* prima che RefreshDatabase possa azzerare il database di sviluppo.
*/
class UnsafeTestDatabaseException extends RuntimeException
{
public static function for(string $database): self
{
return new self(
"Test isolation guard: connesso al DB '{$database}', atteso 'dyncoll_test'. "
.'Lancia i test con `make test` (punta al servizio db-test).'
);
}
}

View File

@@ -3,8 +3,30 @@
namespace Tests;
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
use Illuminate\Support\Facades\DB;
use Tests\Concerns\CreatesUsers;
use Tests\Support\UnsafeTestDatabaseException;
abstract class TestCase extends BaseTestCase
{
//
use CreatesUsers;
/**
* Cintura di sicurezza. refreshApplication() gira PRIMA di setUpTraits()
* (quindi prima dell'eventuale migrate:fresh di RefreshDatabase): se la
* connessione non punta al DB di test dedicato, abortiamo invece di azzerare
* il database di sviluppo `dyncoll`. (Non si può usare beforeRefreshingDatabase:
* nelle classi che usano il trait RefreshDatabase la sua versione vuota
* soppianta quella ereditata dal parent.) Esegui sempre con `make test`.
*/
protected function refreshApplication(): void
{
parent::refreshApplication();
$database = DB::connection()->getDatabaseName();
if ($database !== 'dyncoll_test') {
throw UnsafeTestDatabaseException::for($database);
}
}
}

View File

@@ -1,16 +0,0 @@
<?php
namespace Tests\Unit;
use PHPUnit\Framework\TestCase;
class ExampleTest extends TestCase
{
/**
* A basic test example.
*/
public function test_that_true_is_true(): void
{
$this->assertTrue(true);
}
}