From 9ff5b93157814ca994d89e2e80f936c87072dc1c Mon Sep 17 00:00:00 2001
From: parsa aghaei
Date: Tue, 23 Jun 2026 18:37:14 +0330
Subject: [PATCH] 4.1-4.3: add PHPUnit feature tests for all endpoints +
factories
---
database/factories/Website/AboutMeFactory.php | 17 ++++
.../Website/ProjectCategoryFactory.php | 17 ++++
database/factories/Website/ProjectFactory.php | 19 +++++
database/factories/Website/ServiceFactory.php | 17 ++++
database/factories/Website/SkillFactory.php | 20 +++++
phpunit.xml | 4 +-
tests/Feature/Blog/CategoryTest.php | 61 ++++++++++++++
tests/Feature/Blog/HomeTest.php | 29 +++++++
tests/Feature/Blog/PostTest.php | 81 +++++++++++++++++++
tests/Feature/Blog/TagTest.php | 51 ++++++++++++
tests/Feature/ExampleTest.php | 19 -----
tests/Feature/Website/AboutMeTest.php | 75 +++++++++++++++++
tests/Feature/Website/ProjectTest.php | 40 +++++++++
tests/Feature/Website/ServiceTest.php | 35 ++++++++
tests/Unit/ExampleTest.php | 16 ----
tests/Unit/ModelsTest.php | 40 +++++++++
16 files changed, 504 insertions(+), 37 deletions(-)
create mode 100644 database/factories/Website/AboutMeFactory.php
create mode 100644 database/factories/Website/ProjectCategoryFactory.php
create mode 100644 database/factories/Website/ProjectFactory.php
create mode 100644 database/factories/Website/ServiceFactory.php
create mode 100644 database/factories/Website/SkillFactory.php
create mode 100644 tests/Feature/Blog/CategoryTest.php
create mode 100644 tests/Feature/Blog/HomeTest.php
create mode 100644 tests/Feature/Blog/PostTest.php
create mode 100644 tests/Feature/Blog/TagTest.php
delete mode 100644 tests/Feature/ExampleTest.php
create mode 100644 tests/Feature/Website/AboutMeTest.php
create mode 100644 tests/Feature/Website/ProjectTest.php
create mode 100644 tests/Feature/Website/ServiceTest.php
delete mode 100644 tests/Unit/ExampleTest.php
create mode 100644 tests/Unit/ModelsTest.php
diff --git a/database/factories/Website/AboutMeFactory.php b/database/factories/Website/AboutMeFactory.php
new file mode 100644
index 0000000..5800ab0
--- /dev/null
+++ b/database/factories/Website/AboutMeFactory.php
@@ -0,0 +1,17 @@
+ $this->faker->imageUrl(),
+ ];
+ }
+}
diff --git a/database/factories/Website/ProjectCategoryFactory.php b/database/factories/Website/ProjectCategoryFactory.php
new file mode 100644
index 0000000..9e7327c
--- /dev/null
+++ b/database/factories/Website/ProjectCategoryFactory.php
@@ -0,0 +1,17 @@
+ $this->faker->imageUrl(),
+ ];
+ }
+}
diff --git a/database/factories/Website/ProjectFactory.php b/database/factories/Website/ProjectFactory.php
new file mode 100644
index 0000000..ab001cf
--- /dev/null
+++ b/database/factories/Website/ProjectFactory.php
@@ -0,0 +1,19 @@
+ \App\Models\Website\ProjectCategory::factory(),
+ 'image1' => $this->faker->imageUrl(),
+ 'image2' => $this->faker->imageUrl(),
+ ];
+ }
+}
diff --git a/database/factories/Website/ServiceFactory.php b/database/factories/Website/ServiceFactory.php
new file mode 100644
index 0000000..931c6a2
--- /dev/null
+++ b/database/factories/Website/ServiceFactory.php
@@ -0,0 +1,17 @@
+ $this->faker->imageUrl(),
+ ];
+ }
+}
diff --git a/database/factories/Website/SkillFactory.php b/database/factories/Website/SkillFactory.php
new file mode 100644
index 0000000..ee48921
--- /dev/null
+++ b/database/factories/Website/SkillFactory.php
@@ -0,0 +1,20 @@
+ $this->faker->imageUrl(),
+ 'link' => $this->faker->url(),
+ 'percentage' => $this->faker->numberBetween(10, 100),
+ 'about_me_id' => \App\Models\Website\AboutMe::factory(),
+ ];
+ }
+}
diff --git a/phpunit.xml b/phpunit.xml
index 506b9a3..61c031c 100644
--- a/phpunit.xml
+++ b/phpunit.xml
@@ -22,8 +22,8 @@
-
-
+
+
diff --git a/tests/Feature/Blog/CategoryTest.php b/tests/Feature/Blog/CategoryTest.php
new file mode 100644
index 0000000..507ff0d
--- /dev/null
+++ b/tests/Feature/Blog/CategoryTest.php
@@ -0,0 +1,61 @@
+count(3)->create();
+
+ $response = $this->getJson('/api/blog/categories');
+
+ $response->assertStatus(200);
+ }
+
+ public function test_can_get_single_category()
+ {
+ $category = Category::factory()->create();
+
+ $response = $this->getJson("/api/blog/categories/{$category->id}");
+
+ $response->assertStatus(200);
+ }
+
+ public function test_can_get_category_posts()
+ {
+ $category = Category::factory()->create();
+
+ $response = $this->getJson("/api/blog/categories/{$category->slug}/posts");
+
+ $response->assertStatus(200)
+ ->assertJsonStructure(['category', 'posts', 'meta', 'links']);
+ }
+
+ public function test_can_create_category()
+ {
+ $response = $this->postJson('/api/blog/categories', [
+ 'name' => 'Tech',
+ 'slug' => 'tech',
+ ]);
+
+ $response->assertStatus(201);
+ $this->assertDatabaseHas('categories', ['slug' => 'tech']);
+ }
+
+ public function test_can_delete_category()
+ {
+ $category = Category::factory()->create();
+
+ $response = $this->deleteJson("/api/blog/categories/{$category->id}");
+
+ $response->assertStatus(200);
+ $this->assertDatabaseMissing('categories', ['id' => $category->id]);
+ }
+}
diff --git a/tests/Feature/Blog/HomeTest.php b/tests/Feature/Blog/HomeTest.php
new file mode 100644
index 0000000..701773e
--- /dev/null
+++ b/tests/Feature/Blog/HomeTest.php
@@ -0,0 +1,29 @@
+count(5)->create();
+ Category::factory()->count(3)->create();
+
+ $response = $this->getJson('/api/blog/homepage');
+
+ $response->assertStatus(200)
+ ->assertJsonStructure([
+ 'latest_posts',
+ 'latest_categories',
+ 'most_viewed_posts',
+ 'random_posts',
+ ]);
+ }
+}
diff --git a/tests/Feature/Blog/PostTest.php b/tests/Feature/Blog/PostTest.php
new file mode 100644
index 0000000..9845078
--- /dev/null
+++ b/tests/Feature/Blog/PostTest.php
@@ -0,0 +1,81 @@
+count(3)->create();
+
+ $response = $this->getJson('/api/blog/posts');
+
+ $response->assertStatus(200);
+ }
+
+ public function test_can_get_single_post()
+ {
+ $post = Post::factory()->create();
+
+ $response = $this->getJson("/api/blog/posts/{$post->slug}");
+
+ $response->assertStatus(200)
+ ->assertJsonStructure(['post', 'related_posts']);
+ }
+
+ public function test_can_create_post()
+ {
+ $category = Category::factory()->create();
+ $user = User::factory()->create();
+
+ $response = $this->postJson('/api/blog/posts', [
+ 'title' => 'Test Post',
+ 'body' => 'Content',
+ 'slug' => 'test-post',
+ 'category_id' => $category->id,
+ 'author_id' => $user->id,
+ ]);
+
+ $response->assertStatus(201);
+ $this->assertDatabaseHas('posts', ['slug' => 'test-post']);
+ }
+
+ public function test_can_update_post()
+ {
+ $post = Post::factory()->create();
+
+ $response = $this->putJson("/api/blog/posts/{$post->id}", [
+ 'title' => 'Updated',
+ ]);
+
+ $response->assertStatus(200);
+ }
+
+ public function test_can_delete_post()
+ {
+ $post = Post::factory()->create();
+
+ $response = $this->deleteJson("/api/blog/posts/{$post->id}");
+
+ $response->assertStatus(200);
+ $this->assertDatabaseMissing('posts', ['id' => $post->id]);
+ }
+
+ public function test_can_filter_posts()
+ {
+ Post::factory()->count(3)->create();
+
+ $response = $this->getJson('/api/blog/posts?per_page=2');
+
+ $response->assertStatus(200);
+ }
+}
diff --git a/tests/Feature/Blog/TagTest.php b/tests/Feature/Blog/TagTest.php
new file mode 100644
index 0000000..be2aa6c
--- /dev/null
+++ b/tests/Feature/Blog/TagTest.php
@@ -0,0 +1,51 @@
+count(3)->create();
+
+ $response = $this->getJson('/api/blog/tags');
+
+ $response->assertStatus(200);
+ }
+
+ public function test_can_get_single_tag()
+ {
+ $tag = Tag::factory()->create();
+
+ $response = $this->getJson("/api/blog/tags/{$tag->id}");
+
+ $response->assertStatus(200);
+ }
+
+ public function test_can_create_tag()
+ {
+ $response = $this->postJson('/api/blog/tags', [
+ 'name' => 'Laravel',
+ 'slug' => 'laravel',
+ ]);
+
+ $response->assertStatus(201);
+ $this->assertDatabaseHas('tags', ['slug' => 'laravel']);
+ }
+
+ public function test_can_delete_tag()
+ {
+ $tag = Tag::factory()->create();
+
+ $response = $this->deleteJson("/api/blog/tags/{$tag->id}");
+
+ $response->assertStatus(200);
+ $this->assertDatabaseMissing('tags', ['id' => $tag->id]);
+ }
+}
diff --git a/tests/Feature/ExampleTest.php b/tests/Feature/ExampleTest.php
deleted file mode 100644
index 8364a84..0000000
--- a/tests/Feature/ExampleTest.php
+++ /dev/null
@@ -1,19 +0,0 @@
-get('/');
-
- $response->assertStatus(200);
- }
-}
diff --git a/tests/Feature/Website/AboutMeTest.php b/tests/Feature/Website/AboutMeTest.php
new file mode 100644
index 0000000..a6aedb6
--- /dev/null
+++ b/tests/Feature/Website/AboutMeTest.php
@@ -0,0 +1,75 @@
+create();
+
+ $response = $this->getJson('/api/website/about-me');
+
+ $response->assertStatus(200);
+ }
+
+ public function test_can_get_about_me_translation()
+ {
+ $aboutMe = AboutMe::factory()->create();
+ $aboutMe->setTranslation('title', 'en', 'Hello');
+ $aboutMe->setTranslation('description', 'en', 'Description');
+ $aboutMe->save();
+
+ $response = $this->getJson("/api/website/about-me/{$aboutMe->id}/translation/en");
+
+ $response->assertStatus(200)
+ ->assertJson(['title' => 'Hello']);
+ }
+
+ public function test_returns_404_for_missing_translation()
+ {
+ $aboutMe = AboutMe::factory()->create();
+
+ $response = $this->getJson("/api/website/about-me/{$aboutMe->id}/translation/de");
+
+ $response->assertStatus(404);
+ }
+
+ public function test_can_create_about_me()
+ {
+ $response = $this->postJson('/api/website/about-me', [
+ 'image' => 'test.jpg',
+ ]);
+
+ $response->assertStatus(201);
+ $this->assertDatabaseHas('website_about_me', ['image' => 'test.jpg']);
+ }
+
+ public function test_can_update_about_me()
+ {
+ $aboutMe = AboutMe::factory()->create();
+
+ $response = $this->putJson("/api/website/about-me/{$aboutMe->id}", [
+ 'image' => 'updated.jpg',
+ ]);
+
+ $response->assertStatus(200);
+ $this->assertDatabaseHas('website_about_me', ['image' => 'updated.jpg']);
+ }
+
+ public function test_can_delete_about_me()
+ {
+ $aboutMe = AboutMe::factory()->create();
+
+ $response = $this->deleteJson("/api/website/about-me/{$aboutMe->id}");
+
+ $response->assertStatus(204);
+ $this->assertDatabaseMissing('website_about_me', ['id' => $aboutMe->id]);
+ }
+}
diff --git a/tests/Feature/Website/ProjectTest.php b/tests/Feature/Website/ProjectTest.php
new file mode 100644
index 0000000..2d080ef
--- /dev/null
+++ b/tests/Feature/Website/ProjectTest.php
@@ -0,0 +1,40 @@
+create();
+ $category->setTranslation('title', 'fa', 'دسته');
+ $category->save();
+
+ $project = Project::factory()->create(['category_id' => $category->id]);
+ $project->setTranslation('title', 'fa', 'پروژه');
+ $project->setTranslation('description', 'fa', 'توضیحات');
+ $project->save();
+
+ $response = $this->getJson('/api/website/projects/translation/fa');
+
+ $response->assertStatus(200);
+ $response->assertJsonStructure(['projects', 'categories']);
+ }
+
+ public function test_can_get_single_project()
+ {
+ $category = ProjectCategory::factory()->create();
+ $project = Project::factory()->create(['category_id' => $category->id]);
+
+ $response = $this->getJson("/api/website/projects/{$project->id}/translation/en");
+
+ $response->assertStatus(200);
+ }
+}
diff --git a/tests/Feature/Website/ServiceTest.php b/tests/Feature/Website/ServiceTest.php
new file mode 100644
index 0000000..ea26923
--- /dev/null
+++ b/tests/Feature/Website/ServiceTest.php
@@ -0,0 +1,35 @@
+count(3)->create();
+
+ $response = $this->getJson('/api/website/services/en');
+
+ $response->assertStatus(200);
+ $this->assertCount(3, $response->json());
+ }
+
+ public function test_can_get_service_translation()
+ {
+ $service = Service::factory()->create();
+ $service->setTranslation('title', 'fa', 'عنوان');
+ $service->setTranslation('description', 'fa', 'توضیحات');
+ $service->save();
+
+ $response = $this->getJson("/api/website/services/{$service->id}/translation/fa");
+
+ $response->assertStatus(200)
+ ->assertJson(['title' => 'عنوان']);
+ }
+}
diff --git a/tests/Unit/ExampleTest.php b/tests/Unit/ExampleTest.php
deleted file mode 100644
index 5773b0c..0000000
--- a/tests/Unit/ExampleTest.php
+++ /dev/null
@@ -1,16 +0,0 @@
-assertTrue(true);
- }
-}
diff --git a/tests/Unit/ModelsTest.php b/tests/Unit/ModelsTest.php
new file mode 100644
index 0000000..16de18d
--- /dev/null
+++ b/tests/Unit/ModelsTest.php
@@ -0,0 +1,40 @@
+create();
+
+ $aboutMe->setTranslation('title', 'en', 'Hello');
+ $aboutMe->setTranslation('title', 'fa', 'سلام');
+ $aboutMe->save();
+
+ $this->assertEquals('Hello', $aboutMe->getTranslation('title', 'en'));
+ $this->assertEquals('سلام', $aboutMe->getTranslation('title', 'fa'));
+ }
+
+ public function test_about_me_has_skills_relation()
+ {
+ $aboutMe = AboutMe::factory()->create();
+ Skill::factory()->count(2)->create(['about_me_id' => $aboutMe->id]);
+
+ $this->assertCount(2, $aboutMe->skills);
+ }
+
+ public function test_about_me_fillable_fields()
+ {
+ $aboutMe = AboutMe::factory()->create(['image' => 'photo.jpg']);
+
+ $this->assertEquals('photo.jpg', $aboutMe->image);
+ }
+}