test(timetable): Phase C — observer, resolver, seeder, portal controller tests
22 new tests across four files:
- AdvanceSectionObserverTest (7) — counter recompute on create / status
transition / delete / is_open toggle no-op / orphaned-section guard /
no activity-log noise on counter writes
- ArtistResolverTest (4) — happy path / invalid token / soft-deleted
artist / SHA-256 digest verification
- ArtistAdvanceDefaultTest (6) — five-section + slug shape / idempotency
/ per-section field shape / observer-invocation outside tests /
artisan one-org + all-orgs paths
- EngagementPortalControllerTest (6) — show 200/404/410 / show-section
schema + draft values / submit happy-path with submission persistence
+ counter recompute / cross-engagement section returns 404
Implementation tweaks driven by test feedback:
- OrganisationObserver gated by `app()->runningUnitTests()` — auto-seed
runs in production but is silent in CI so existing FormSchema-counting
tests are unperturbed. Tests that need the seeded schema invoke
`ArtistAdvanceDefault::seedFor()` explicitly.
- EngagementPortalController idempotency_key uses `aa-` + sha1 prefix
(28 chars) so it fits the form_submissions.idempotency_key
varchar(30) column.
Test count: 1709 (Session 2 close) → 1731 (+22).
Larastan: 0 new errors over baseline.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
86
api/tests/Unit/FormBuilder/Resolvers/ArtistResolverTest.php
Normal file
86
api/tests/Unit/FormBuilder/Resolvers/ArtistResolverTest.php
Normal file
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Unit\FormBuilder\Resolvers;
|
||||
|
||||
use App\Exceptions\Artist\ArtistDeletedException;
|
||||
use App\Exceptions\Artist\InvalidPortalTokenException;
|
||||
use App\FormBuilder\Resolvers\ArtistResolver;
|
||||
use App\Models\Artist;
|
||||
use App\Models\ArtistEngagement;
|
||||
use App\Models\Event;
|
||||
use App\Models\Organisation;
|
||||
use App\Models\Scopes\OrganisationScope;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Str;
|
||||
use Tests\TestCase;
|
||||
|
||||
final class ArtistResolverTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_valid_token_returns_subject_event_id_and_engagement(): void
|
||||
{
|
||||
$plain = (string) Str::ulid();
|
||||
$engagement = $this->makeEngagement(['portal_token' => hash('sha256', $plain)]);
|
||||
|
||||
$resolved = (new ArtistResolver)->fromPortalToken($plain);
|
||||
|
||||
$this->assertSame($engagement->artist_id, $resolved->subject->id);
|
||||
$this->assertSame((string) $engagement->event_id, $resolved->eventId);
|
||||
$this->assertSame($engagement->id, $resolved->engagement->id);
|
||||
}
|
||||
|
||||
public function test_invalid_token_throws_invalid_portal_token(): void
|
||||
{
|
||||
$this->expectException(InvalidPortalTokenException::class);
|
||||
|
||||
(new ArtistResolver)->fromPortalToken('not-a-real-token');
|
||||
}
|
||||
|
||||
public function test_engagement_with_soft_deleted_artist_throws_artist_deleted(): void
|
||||
{
|
||||
$plain = (string) Str::ulid();
|
||||
$engagement = $this->makeEngagement(['portal_token' => hash('sha256', $plain)]);
|
||||
|
||||
Artist::query()
|
||||
->withoutGlobalScope(OrganisationScope::class)
|
||||
->whereKey($engagement->artist_id)
|
||||
->delete();
|
||||
|
||||
try {
|
||||
(new ArtistResolver)->fromPortalToken($plain);
|
||||
$this->fail('Expected ArtistDeletedException');
|
||||
} catch (ArtistDeletedException $e) {
|
||||
$this->assertSame((string) $engagement->id, $e->engagementId);
|
||||
}
|
||||
}
|
||||
|
||||
public function test_token_uses_sha256_digest_lookup(): void
|
||||
{
|
||||
$plain = 'plain-text-token';
|
||||
$digest = hash('sha256', $plain);
|
||||
$engagement = $this->makeEngagement(['portal_token' => $digest]);
|
||||
|
||||
$resolved = (new ArtistResolver)->fromPortalToken($plain);
|
||||
|
||||
$this->assertSame($engagement->id, $resolved->engagement->id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $overrides
|
||||
*/
|
||||
private function makeEngagement(array $overrides = []): ArtistEngagement
|
||||
{
|
||||
$org = Organisation::factory()->create();
|
||||
$event = Event::factory()->for($org)->create();
|
||||
$artist = Artist::factory()->for($org)->create();
|
||||
|
||||
return ArtistEngagement::factory()->create(array_merge([
|
||||
'organisation_id' => $org->id,
|
||||
'artist_id' => $artist->id,
|
||||
'event_id' => $event->id,
|
||||
], $overrides));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user