Files
crewli/api/tests/Feature/Person/PersonSoftDeleteTest.php
bert.hausmans fe2202d835 test(persons): verify SoftDeletes behaviour per WS-1 finding D-05
Pre-flight audit verified the implementation was already in place,
so this commit is test-only (as the task prompt allowed):
- api/database/migrations/2026_04_08_220000_create_persons_table.php:27
  already carries $table->softDeletes().
- api/app/Models/Person.php lines 18 + 24 already use the SoftDeletes trait.

No production code change required.

tests/Feature/Person/PersonSoftDeleteTest.php pins the documented
behaviour so WS-6 (FormBindingApplicator + ARTIST_ADVANCE /
EVENT_REGISTRATION workflows) can rely on it without a second
verification pass:
- delete() sets deleted_at and excludes the row from default queries
- Person::withTrashed()->find($id) returns the soft-deleted row
- Person::onlyTrashed()->count() reports accurately
- restore() clears deleted_at
- forceDelete() removes the row permanently

PersonPolicy was spot-checked alongside the audit; existing view/update/
delete methods do not invoke withTrashed() on controller resolution.
That's acceptable today — no caller relies on editing a trashed Person —
so AUTH_ARCHITECTURE.md is left unchanged. If a restore-workflow lands
in a future sprint, the controller binding will need to switch to
->withTrashed() and the behaviour deserves a dedicated doc paragraph
then.

Minor path note: the task prompt specified
tests/Feature/Persons/PersonSoftDeleteTest.php (plural), but the
existing convention in this repo is tests/Feature/Person/ (singular).
Matched the existing directory rather than introducing a singular/plural
split.

Also carries the BACKLOG DOC-04 entry Bert asked for during WS-4 review:
scripts/install-claude-sync-hooks.sh belongs in SETUP.md / onboarding
so new clones don't miss the post-commit sync hook.

5 tests / 10 new assertions. Full suite: 1005 passed (2716 assertions).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 17:10:37 +02:00

101 lines
2.8 KiB
PHP

<?php
declare(strict_types=1);
namespace Tests\Feature\Person;
use App\Models\CrowdType;
use App\Models\Event;
use App\Models\Organisation;
use App\Models\Person;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
/**
* WS-4 Commit 4 — WS-1 finding D-05 verification.
*
* SCHEMA.md §3.5.11 Rule 3 lists `persons` under "Soft delete YES".
* This test pins the documented behaviour — delete/restore/forceDelete,
* withTrashed and onlyTrashed visibility — so the ARTIST_ADVANCE and
* EVENT_REGISTRATION workflows (WS-6) can rely on it without a second
* verification pass.
*
* Pre-flight found the implementation already in place
* (2026_04_08_220000_create_persons_table.php line 27 has
* `$table->softDeletes()`; `app/Models/Person.php` lines 18 + 24 carry
* the `SoftDeletes` trait). No code change required — this file is the
* behavioural contract.
*/
final class PersonSoftDeleteTest extends TestCase
{
use RefreshDatabase;
private Person $person;
protected function setUp(): void
{
parent::setUp();
$organisation = Organisation::factory()->create();
$event = Event::factory()->for($organisation)->create();
$crowdType = CrowdType::factory()->for($organisation)->create();
$this->person = Person::factory()
->for($event)
->for($crowdType)
->create();
}
public function test_delete_sets_deleted_at_and_hides_row_from_default_queries(): void
{
$id = $this->person->id;
$this->person->delete();
$this->assertNotNull($this->person->fresh()?->deleted_at);
$this->assertNull(
Person::query()->find($id),
'Soft-deleted person must not appear in default queries'
);
}
public function test_with_trashed_returns_deleted_row(): void
{
$id = $this->person->id;
$this->person->delete();
$trashed = Person::withTrashed()->find($id);
$this->assertNotNull($trashed);
$this->assertSame($id, $trashed->id);
}
public function test_only_trashed_counts_soft_deleted_rows(): void
{
$this->assertSame(0, Person::onlyTrashed()->count());
$this->person->delete();
$this->assertSame(1, Person::onlyTrashed()->count());
}
public function test_restore_clears_deleted_at(): void
{
$this->person->delete();
$this->assertNotNull($this->person->fresh()?->deleted_at);
$this->person->restore();
$this->assertNull($this->person->fresh()?->deleted_at);
$this->assertNotNull(Person::query()->find($this->person->id));
}
public function test_force_delete_removes_row_permanently(): void
{
$id = $this->person->id;
$this->person->forceDelete();
$this->assertNull(Person::withTrashed()->find($id));
}
}