makeEngagementWithSection(); $response = $this->getJson("/api/v1/p/artist/{$plain}"); $response->assertOk(); $response->assertJsonPath('data.engagement_id', $engagement->id); $response->assertJsonStructure([ 'data' => ['engagement_id', 'artist', 'event', 'sections'], ]); } public function test_show_returns_404_for_invalid_token(): void { $this->getJson('/api/v1/p/artist/not-a-real-token')->assertNotFound(); } public function test_show_returns_410_when_master_artist_soft_deleted(): void { [$plain, $engagement] = $this->makeEngagementWithSection(); Artist::query() ->withoutGlobalScope(OrganisationScope::class) ->whereKey($engagement->artist_id) ->delete(); $this->getJson("/api/v1/p/artist/{$plain}")->assertStatus(410); } public function test_show_section_returns_schema_and_existing_values(): void { [$plain, $engagement, $section] = $this->makeEngagementWithSection(); $response = $this->getJson("/api/v1/p/artist/{$plain}/sections/{$section->id}"); $response->assertOk(); $response->assertJsonPath('data.section.id', $section->id); $response->assertJsonStructure([ 'data' => ['section', 'fields', 'values'], ]); } public function test_submit_section_creates_submission_and_updates_status(): void { [$plain, $engagement, $section] = $this->makeEngagementWithSection(); $response = $this->postJson("/api/v1/p/artist/{$plain}/sections/{$section->id}", [ 'values' => [ 'general-notes' => 'Hello, world', ], ]); $response->assertOk(); $section->refresh(); $this->assertSame(AdvanceSectionSubmissionStatus::Submitted, $section->submission_status); $this->assertNotNull($section->last_submitted_at); // FormSubmission persisted with master artist as subject + event_id from engagement $submission = FormSubmission::query() ->withoutGlobalScope(OrganisationScope::class) ->where('subject_id', $engagement->artist_id) ->first(); $this->assertNotNull($submission); $this->assertSame('artist', $submission->subject_type); $this->assertSame((string) $engagement->event_id, (string) $submission->event_id); $this->assertSame($engagement->organisation_id, $submission->organisation_id); // Counter recompute fires (Submitted is not Approved, so completed stays 0) $this->assertSame(1, (int) $engagement->fresh()->advancing_total_count); } public function test_submit_with_section_from_different_engagement_returns_404(): void { [$plain, $engagement] = $this->makeEngagementWithSection(); $otherOrg = Organisation::factory()->create(); $otherEvent = Event::factory()->for($otherOrg)->create(); $otherArtist = Artist::factory()->for($otherOrg)->create(); $other = ArtistEngagement::factory()->create([ 'organisation_id' => $otherOrg->id, 'artist_id' => $otherArtist->id, 'event_id' => $otherEvent->id, ]); $stranger = AdvanceSection::factory()->create(['engagement_id' => $other->id, 'name' => 'Algemeen']); $this->postJson("/api/v1/p/artist/{$plain}/sections/{$stranger->id}", [ 'values' => ['general-notes' => 'x'], ])->assertNotFound(); } /** * @return array{0: string, 1: ArtistEngagement, 2: AdvanceSection} */ private function makeEngagementWithSection(): array { $org = Organisation::factory()->create(); $event = Event::factory()->for($org)->create(); $artist = Artist::factory()->for($org)->create(); // OrganisationObserver skips auto-seed in tests; seed explicitly. ArtistAdvanceDefault::seedFor($org); $schema = FormSchema::query() ->withoutGlobalScope(OrganisationScope::class) ->where('organisation_id', $org->id) ->where('purpose', \App\Enums\FormBuilder\FormPurpose::ARTIST_ADVANCE->value) ->firstOrFail(); $schemaSection = FormSchemaSection::query() ->withoutGlobalScope(OrganisationScope::class) ->where('form_schema_id', $schema->id) ->where('slug', 'general-info') ->firstOrFail(); $plain = (string) Str::ulid(); $engagement = ArtistEngagement::factory()->create([ 'organisation_id' => $org->id, 'artist_id' => $artist->id, 'event_id' => $event->id, 'portal_token' => hash('sha256', $plain), ]); $section = AdvanceSection::factory()->create([ 'engagement_id' => $engagement->id, 'name' => $schemaSection->name, 'type' => AdvanceSectionType::Custom, 'is_open' => true, ]); return [$plain, $engagement, $section]; } }