Artwork::factory()->create($attrs)); } beforeEach(function () { // Register GREATEST() polyfill for SQLite (used by observers on user_statistics) if (DB::connection()->getDriverName() === 'sqlite') { DB::connection()->getPdo()->sqliteCreateFunction('GREATEST', function (...$args) { return max($args); }, -1); } $this->user = User::factory()->create(); $this->actingAs($this->user); }); // ── Route Auth Tests ────────────────────────────────────────────────────────── test('studio routes require authentication', function () { auth()->logout(); $routes = [ '/studio', '/studio/artworks', '/studio/artworks/drafts', '/studio/artworks/archived', ]; foreach ($routes as $route) { $this->get($route)->assertRedirect('/login'); } }); test('studio dashboard loads for authenticated user', function () { $this->get('/studio') ->assertStatus(200); }); test('studio artworks page loads', function () { $this->get('/studio/artworks') ->assertStatus(200); }); test('studio drafts page loads', function () { $this->get('/studio/artworks/drafts') ->assertStatus(200); }); test('studio archived page loads', function () { $this->get('/studio/artworks/archived') ->assertStatus(200); }); // ── API Tests ───────────────────────────────────────────────────────────────── test('studio api requires authentication', function () { auth()->logout(); $this->getJson('/api/studio/artworks') ->assertStatus(401); }); test('studio api returns artworks for authenticated user', function () { // Create artworks for this user $artwork = studioArtwork([ 'user_id' => $this->user->id, 'is_public' => true, 'is_approved' => true, ]); ArtworkStats::create([ 'artwork_id' => $artwork->id, 'views' => 100, 'downloads' => 10, 'favorites' => 5, ]); $this->getJson('/api/studio/artworks') ->assertStatus(200) ->assertJsonStructure([ 'data' => [['id', 'title', 'slug', 'views', 'favourites']], 'meta' => ['current_page', 'last_page', 'per_page', 'total'], ]); }); test('studio api does not return other users artworks', function () { $otherUser = User::factory()->create(); studioArtwork([ 'user_id' => $otherUser->id, 'is_public' => true, 'is_approved' => true, ]); $this->getJson('/api/studio/artworks') ->assertStatus(200) ->assertJsonCount(0, 'data'); }); // ── Bulk Action Tests ───────────────────────────────────────────────────────── test('bulk archive works on owned artworks', function () { $artwork = studioArtwork([ 'user_id' => $this->user->id, 'is_public' => true, ]); $this->postJson('/api/studio/artworks/bulk', [ 'action' => 'archive', 'artwork_ids' => [$artwork->id], ]) ->assertStatus(200) ->assertJsonPath('success', 1); expect($artwork->fresh()->trashed())->toBeTrue(); }); test('bulk delete requires confirmation', function () { $artwork = studioArtwork(['user_id' => $this->user->id]); $this->postJson('/api/studio/artworks/bulk', [ 'action' => 'delete', 'artwork_ids' => [$artwork->id], ]) ->assertStatus(422); }); test('bulk delete with confirmation works', function () { $artwork = studioArtwork(['user_id' => $this->user->id]); $this->postJson('/api/studio/artworks/bulk', [ 'action' => 'delete', 'artwork_ids' => [$artwork->id], 'confirm' => 'DELETE', ]) ->assertStatus(200) ->assertJsonPath('success', 1); }); test('bulk publish on owned artworks', function () { $artwork = studioArtwork([ 'user_id' => $this->user->id, 'is_public' => false, ]); $this->postJson('/api/studio/artworks/bulk', [ 'action' => 'publish', 'artwork_ids' => [$artwork->id], ]) ->assertStatus(200) ->assertJsonPath('success', 1); expect($artwork->fresh()->is_public)->toBeTrue(); }); test('bulk action cannot modify other users artworks', function () { $otherUser = User::factory()->create(); $artwork = studioArtwork(['user_id' => $otherUser->id]); $this->postJson('/api/studio/artworks/bulk', [ 'action' => 'archive', 'artwork_ids' => [$artwork->id], ]) ->assertStatus(422) ->assertJsonPath('success', 0) ->assertJsonPath('failed', 1); }); // ── Toggle Tests ────────────────────────────────────────────────────────────── test('toggle publish on single artwork', function () { $artwork = studioArtwork([ 'user_id' => $this->user->id, 'is_public' => false, ]); $this->postJson("/api/studio/artworks/{$artwork->id}/toggle", [ 'action' => 'publish', ]) ->assertStatus(200) ->assertJsonPath('success', true); expect($artwork->fresh()->is_public)->toBeTrue(); }); test('toggle on non-owned artwork returns 404', function () { $otherUser = User::factory()->create(); $artwork = studioArtwork(['user_id' => $otherUser->id]); $this->postJson("/api/studio/artworks/{$artwork->id}/toggle", [ 'action' => 'archive', ]) ->assertStatus(404); }); // ── Analytics API Tests ─────────────────────────────────────────────────────── test('analytics api returns artwork stats', function () { $artwork = studioArtwork(['user_id' => $this->user->id]); ArtworkStats::create([ 'artwork_id' => $artwork->id, 'views' => 500, 'downloads' => 20, 'favorites' => 30, 'shares_count' => 10, 'comments_count' => 5, 'ranking_score' => 42.5, 'heat_score' => 8.3, ]); $this->getJson("/api/studio/artworks/{$artwork->id}/analytics") ->assertStatus(200) ->assertJsonStructure([ 'artwork' => ['id', 'title', 'slug'], 'analytics' => ['views', 'favourites', 'shares', 'comments', 'downloads', 'ranking_score', 'heat_score'], ]); }); test('analytics api denies access to other users artwork', function () { $otherUser = User::factory()->create(); $artwork = studioArtwork(['user_id' => $otherUser->id]); $this->getJson("/api/studio/artworks/{$artwork->id}/analytics") ->assertStatus(404); });