Add extensive controller and model tests
All checks were successful
CI/CD Pipeline / test (push) Successful in 10s
CI/CD Pipeline / deploy (push) Successful in 25s

This commit is contained in:
2026-02-07 22:14:42 +01:00
parent 9c60a8944e
commit 160430e128
39 changed files with 3941 additions and 1 deletions

View File

@@ -0,0 +1,248 @@
<?php
use App\Models\Forum;
use App\Models\Role;
use App\Models\Thread;
use App\Models\User;
use Laravel\Sanctum\Sanctum;
function makeForum(): Forum
{
$category = Forum::create([
'name' => 'Category',
'description' => null,
'type' => 'category',
'parent_id' => null,
'position' => 1,
]);
return Forum::create([
'name' => 'Forum',
'description' => null,
'type' => 'forum',
'parent_id' => $category->id,
'position' => 1,
]);
}
it('creates a thread inside a forum', function (): void {
$user = User::factory()->create();
Sanctum::actingAs($user);
$forum = makeForum();
$response = $this->postJson('/api/threads', [
'title' => 'First Thread',
'body' => 'Hello world',
'forum' => "/api/forums/{$forum->id}",
]);
$response->assertStatus(201);
$response->assertJsonFragment([
'title' => 'First Thread',
'forum' => "/api/forums/{$forum->id}",
]);
$this->assertDatabaseHas('threads', [
'forum_id' => $forum->id,
'user_id' => $user->id,
'title' => 'First Thread',
]);
});
it('rejects creating threads in a category', function (): void {
$user = User::factory()->create();
Sanctum::actingAs($user);
$category = Forum::create([
'name' => 'Category Only',
'description' => null,
'type' => 'category',
'parent_id' => null,
'position' => 1,
]);
$response = $this->postJson('/api/threads', [
'title' => 'Nope',
'body' => 'Not allowed',
'forum' => "/api/forums/{$category->id}",
]);
$response->assertStatus(422);
$response->assertJsonFragment(['message' => 'Threads can only be created inside forums.']);
});
it('requires authentication to update a thread', function (): void {
$forum = makeForum();
$owner = User::factory()->create();
$thread = Thread::create([
'forum_id' => $forum->id,
'user_id' => $owner->id,
'title' => 'Original',
'body' => 'Body',
]);
$response = $this->patchJson("/api/threads/{$thread->id}", [
'title' => 'Updated',
'body' => 'Updated body',
]);
$response->assertStatus(401);
});
it('enforces thread update permissions', function (): void {
$forum = makeForum();
$owner = User::factory()->create();
$other = User::factory()->create();
$thread = Thread::create([
'forum_id' => $forum->id,
'user_id' => $owner->id,
'title' => 'Original',
'body' => 'Body',
]);
Sanctum::actingAs($other);
$response = $this->patchJson("/api/threads/{$thread->id}", [
'title' => 'Updated',
'body' => 'Updated body',
]);
$response->assertStatus(403);
Sanctum::actingAs($owner);
$response = $this->patchJson("/api/threads/{$thread->id}", [
'title' => 'Owner Update',
'body' => 'Owner body',
]);
$response->assertOk();
$this->assertDatabaseHas('threads', [
'id' => $thread->id,
'title' => 'Owner Update',
'body' => 'Owner body',
]);
$admin = User::factory()->create();
$role = Role::create(['name' => 'ROLE_ADMIN', 'color' => '#111111']);
$admin->roles()->attach($role);
Sanctum::actingAs($admin);
$response = $this->patchJson("/api/threads/{$thread->id}", [
'title' => 'Admin Update',
'body' => 'Admin body',
]);
$response->assertOk();
$this->assertDatabaseHas('threads', [
'id' => $thread->id,
'title' => 'Admin Update',
'body' => 'Admin body',
]);
});
it('enforces solved status permissions', function (): void {
$forum = makeForum();
$owner = User::factory()->create();
$other = User::factory()->create();
$thread = Thread::create([
'forum_id' => $forum->id,
'user_id' => $owner->id,
'title' => 'Original',
'body' => 'Body',
'solved' => false,
]);
Sanctum::actingAs($other);
$response = $this->patchJson("/api/threads/{$thread->id}/solved", [
'solved' => true,
]);
$response->assertStatus(403);
Sanctum::actingAs($owner);
$response = $this->patchJson("/api/threads/{$thread->id}/solved", [
'solved' => true,
]);
$response->assertOk();
$this->assertDatabaseHas('threads', [
'id' => $thread->id,
'solved' => 1,
]);
});
it('filters threads by forum', function (): void {
$forumA = makeForum();
$forumB = makeForum();
$threadA = Thread::create([
'forum_id' => $forumA->id,
'user_id' => null,
'title' => 'Thread A',
'body' => 'Body A',
]);
Thread::create([
'forum_id' => $forumB->id,
'user_id' => null,
'title' => 'Thread B',
'body' => 'Body B',
]);
$response = $this->getJson("/api/threads?forum=/api/forums/{$forumA->id}");
$response->assertOk();
$response->assertJsonCount(1);
$response->assertJsonFragment([
'id' => $threadA->id,
'title' => 'Thread A',
]);
});
it('increments views count when showing a thread', function (): void {
$forum = makeForum();
$thread = Thread::create([
'forum_id' => $forum->id,
'user_id' => null,
'title' => 'Viewed Thread',
'body' => 'Body',
'views_count' => 0,
]);
$response = $this->getJson("/api/threads/{$thread->id}");
$response->assertOk();
$response->assertJsonFragment([
'id' => $thread->id,
'views_count' => 1,
]);
$thread->refresh();
expect($thread->views_count)->toBe(1);
});
it('soft deletes a thread and tracks deleted_by', function (): void {
$forum = makeForum();
$user = User::factory()->create();
$thread = Thread::create([
'forum_id' => $forum->id,
'user_id' => $user->id,
'title' => 'Delete Me',
'body' => 'Body',
]);
Sanctum::actingAs($user);
$response = $this->deleteJson("/api/threads/{$thread->id}");
$response->assertStatus(204);
$this->assertSoftDeleted('threads', [
'id' => $thread->id,
]);
$this->assertDatabaseHas('threads', [
'id' => $thread->id,
'deleted_by' => $user->id,
]);
});