create(); $artwork = Artwork::factory()->create(); $response = $this->actingAs($user)->postJson('/api/discovery/events', [ 'event_type' => 'view', 'artwork_id' => $artwork->id, 'meta' => ['source' => 'artwork_show'], ]); $response ->assertStatus(202) ->assertJsonPath('queued', true) ->assertJsonPath('algo_version', (string) config('discovery.algo_version')); Queue::assertPushed(IngestUserDiscoveryEventJob::class, function (IngestUserDiscoveryEventJob $job) use ($user, $artwork): bool { return $job->userId === $user->id && $job->artworkId === $artwork->id && $job->eventType === 'view'; }); }); it('validates discovery event payload', function () { $user = User::factory()->create(); $artwork = Artwork::factory()->create(); $response = $this->actingAs($user)->postJson('/api/discovery/events', [ 'event_type' => 'impression', 'artwork_id' => $artwork->id, ]); $response->assertStatus(422); $response->assertJsonValidationErrors(['event_type']); }); it('accepts session-oriented discovery events', function () { Queue::fake(); $user = User::factory()->create(); $artwork = Artwork::factory()->create(); $response = $this->actingAs($user)->postJson('/api/discovery/events', [ 'event_type' => 'dwell', 'artwork_id' => $artwork->id, 'meta' => ['duration_ms' => 4500], ]); $response->assertStatus(202); Queue::assertPushed(IngestUserDiscoveryEventJob::class, function (IngestUserDiscoveryEventJob $job): bool { return $job->eventType === 'dwell'; }); });