get(route('w.index')); $response->assertOk(); } public function test_index_lists_writings(): void { $writing = Writing::factory()->create(); $response = $this->get(route('w.index')); $response->assertSee($writing->title); } // ─── Show ───────────────────────────────────────────────────────────────── public function test_guests_can_view_a_writing(): void { $writing = Writing::factory()->create(); $response = $this->get(route('w.show', $writing)); $response->assertOk()->assertSee($writing->title); } public function test_show_404s_for_missing_writing(): void { $response = $this->get(route('w.show', 99999)); $response->assertNotFound(); } // ─── Create ─────────────────────────────────────────────────────────────── public function test_guest_is_redirected_from_create(): void { $response = $this->get(route('w.create')); $response->assertRedirect(route('login')); } public function test_authenticated_user_can_see_create_form(): void { $user = User::factory()->create(); $response = $this->actingAs($user)->get(route('w.create')); $response->assertOk(); } // ─── Store ──────────────────────────────────────────────────────────────── public function test_guest_cannot_store_writing(): void { $response = $this->post(route('w.store'), [ 'title' => 'Guest Writing', 'content' => 'Should not be stored at all.', ]); $response->assertRedirect(route('login')); $this->assertDatabaseMissing('writings', ['title' => 'Guest Writing']); } public function test_user_can_store_writing(): void { $user = User::factory()->create(); $response = $this->actingAs($user)->post(route('w.store'), [ 'title' => 'My New Writing', 'content' => 'This is content that is long enough to pass validation.', ]); $response->assertRedirect(); $this->assertDatabaseHas('writings', [ 'title' => 'My New Writing', 'user_id' => $user->id, ]); } public function test_store_requires_title(): void { $user = User::factory()->create(); $response = $this->actingAs($user)->post(route('w.store'), [ 'title' => '', 'content' => 'Content long enough to pass.', ]); $response->assertSessionHasErrors('title'); } public function test_store_requires_title_min_3_chars(): void { $user = User::factory()->create(); $response = $this->actingAs($user)->post(route('w.store'), [ 'title' => 'AB', 'content' => 'Content long enough to pass.', ]); $response->assertSessionHasErrors('title'); } public function test_store_requires_content(): void { $user = User::factory()->create(); $response = $this->actingAs($user)->post(route('w.store'), [ 'title' => 'Valid Title', 'content' => '', ]); $response->assertSessionHasErrors('content'); } public function test_store_requires_content_min_10_chars(): void { $user = User::factory()->create(); $response = $this->actingAs($user)->post(route('w.store'), [ 'title' => 'Valid Title', 'content' => 'Too short', ]); $response->assertSessionHasErrors('content'); } // ─── Edit ───────────────────────────────────────────────────────────────── public function test_guest_is_redirected_from_edit(): void { $writing = Writing::factory()->create(); $response = $this->get(route('w.edit', $writing)); $response->assertRedirect(route('login')); } public function test_owner_can_access_edit_form(): void { $user = User::factory()->create(); $writing = Writing::factory()->create(['user_id' => $user->id]); $response = $this->actingAs($user)->get(route('w.edit', $writing)); $response->assertOk(); } public function test_non_owner_cannot_access_edit_form(): void { $owner = User::factory()->create(); $other = User::factory()->create(); $writing = Writing::factory()->create(['user_id' => $owner->id]); $response = $this->actingAs($other)->get(route('w.edit', $writing)); $response->assertForbidden(); } public function test_admin_can_access_any_edit_form(): void { $owner = User::factory()->create(); $admin = User::factory()->create(['role' => 0]); $writing = Writing::factory()->create(['user_id' => $owner->id]); $response = $this->actingAs($admin)->get(route('w.edit', $writing)); $response->assertOk(); } // ─── Update ─────────────────────────────────────────────────────────────── public function test_owner_can_update_writing(): void { $user = User::factory()->create(); $writing = Writing::factory()->create(['user_id' => $user->id]); $response = $this->actingAs($user)->put(route('w.update', $writing), [ 'title' => 'Updated Title', 'content' => 'Updated content that is long enough to pass validation.', ]); $response->assertRedirect(route('w.show', $writing)); $this->assertDatabaseHas('writings', [ 'id' => $writing->id, 'title' => 'Updated Title', ]); } public function test_non_owner_cannot_update_writing(): void { $owner = User::factory()->create(); $other = User::factory()->create(); $writing = Writing::factory()->create(['user_id' => $owner->id, 'title' => 'Original Title']); $response = $this->actingAs($other)->put(route('w.update', $writing), [ 'title' => 'Hijacked Title', 'content' => 'Hijacked content that is long enough.', ]); $response->assertForbidden(); $this->assertDatabaseHas('writings', ['id' => $writing->id, 'title' => 'Original Title']); } public function test_admin_can_update_any_writing(): void { $owner = User::factory()->create(); $admin = User::factory()->create(['role' => 0]); $writing = Writing::factory()->create(['user_id' => $owner->id]); $response = $this->actingAs($admin)->put(route('w.update', $writing), [ 'title' => 'Admin Updated Title', 'content' => 'Admin updated content that is long enough.', ]); $response->assertRedirect(route('w.show', $writing)); $this->assertDatabaseHas('writings', ['id' => $writing->id, 'title' => 'Admin Updated Title']); } public function test_update_validates_title(): void { $user = User::factory()->create(); $writing = Writing::factory()->create(['user_id' => $user->id]); $response = $this->actingAs($user)->put(route('w.update', $writing), [ 'title' => 'AB', 'content' => 'Content long enough to pass.', ]); $response->assertSessionHasErrors('title'); } // ─── Destroy ───────────────────────────────────────────────────────────── public function test_guest_cannot_delete_writing(): void { $writing = Writing::factory()->create(); $response = $this->delete(route('w.destroy', $writing)); $response->assertRedirect(route('login')); $this->assertDatabaseHas('writings', ['id' => $writing->id]); } public function test_owner_can_delete_writing(): void { $user = User::factory()->create(); $writing = Writing::factory()->create(['user_id' => $user->id]); $response = $this->actingAs($user)->delete(route('w.destroy', $writing)); $response->assertRedirect(route('w.index')); $this->assertDatabaseMissing('writings', ['id' => $writing->id]); } public function test_non_owner_cannot_delete_writing(): void { $owner = User::factory()->create(); $other = User::factory()->create(); $writing = Writing::factory()->create(['user_id' => $owner->id]); $response = $this->actingAs($other)->delete(route('w.destroy', $writing)); $response->assertForbidden(); $this->assertDatabaseHas('writings', ['id' => $writing->id]); } public function test_admin_can_delete_any_writing(): void { $owner = User::factory()->create(); $admin = User::factory()->create(['role' => 0]); $writing = Writing::factory()->create(['user_id' => $owner->id]); $response = $this->actingAs($admin)->delete(route('w.destroy', $writing)); $response->assertRedirect(route('w.index')); $this->assertDatabaseMissing('writings', ['id' => $writing->id]); } }