Skip to content

Commit e743a8b

Browse files
committed
test(article): added tests coverage for article APIs
1 parent 3d8ea96 commit e743a8b

File tree

2 files changed

+341
-0
lines changed

2 files changed

+341
-0
lines changed
Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use App\Models\Article;
6+
use App\Models\Category;
7+
use App\Models\Tag;
8+
use App\Models\User;
9+
10+
describe('API/V1/Article/GetArticlesController', function () {
11+
it('can get articles with basic pagination', function () {
12+
// Create test data
13+
$user = User::factory()->create();
14+
$articles = Article::factory()
15+
->count(25)
16+
->for($user, 'author')
17+
->for($user, 'approver')
18+
->published()
19+
->create();
20+
21+
$response = $this->getJson('/api/v1/articles');
22+
23+
$response->assertStatus(200)
24+
->assertJsonStructure([
25+
'status',
26+
'message',
27+
'data' => [
28+
'articles' => [
29+
'*' => [
30+
'id',
31+
'slug',
32+
'title',
33+
'subtitle',
34+
'excerpt',
35+
'featured_image',
36+
'status',
37+
'published_at',
38+
'created_at',
39+
'updated_at',
40+
'author' => [
41+
'id',
42+
'name',
43+
'avatar_url',
44+
],
45+
'categories',
46+
'tags',
47+
'comments_count',
48+
],
49+
],
50+
'meta',
51+
],
52+
]);
53+
54+
// Should return 15 articles per page by default
55+
expect($response->json('data.articles'))->toHaveCount(15);
56+
});
57+
58+
it('can filter articles by category', function () {
59+
$user = User::factory()->create();
60+
$category = Category::factory()->create();
61+
62+
// Create article with category
63+
$article = Article::factory()
64+
->for($user, 'author')
65+
->for($user, 'approver')
66+
->published()
67+
->create();
68+
69+
$article->categories()->attach($category->id);
70+
71+
// Create article without category
72+
Article::factory()
73+
->for($user, 'author')
74+
->for($user, 'approver')
75+
->published()
76+
->create();
77+
78+
$response = $this->getJson("/api/v1/articles?category_slug={$category->slug}");
79+
80+
$response->assertStatus(200);
81+
expect($response->json('data.articles'))->toHaveCount(1);
82+
expect($response->json('data.articles.0.id'))->toBe($article->id);
83+
});
84+
85+
it('can filter articles by tag', function () {
86+
$user = User::factory()->create();
87+
$tag = Tag::factory()->create();
88+
89+
// Create article with tag
90+
$article = Article::factory()
91+
->for($user, 'author')
92+
->for($user, 'approver')
93+
->published()
94+
->create();
95+
96+
$article->tags()->attach($tag->id);
97+
98+
// Create article without tag
99+
Article::factory()
100+
->for($user, 'author')
101+
->for($user, 'approver')
102+
->published()
103+
->create();
104+
105+
$response = $this->getJson("/api/v1/articles?tag_slug={$tag->slug}");
106+
107+
$response->assertStatus(200);
108+
expect($response->json('data.articles'))->toHaveCount(1);
109+
expect($response->json('data.articles.0.id'))->toBe($article->id);
110+
});
111+
112+
it('can search articles', function () {
113+
$user = User::factory()->create();
114+
115+
$article = Article::factory()
116+
->for($user, 'author')
117+
->for($user, 'approver')
118+
->published()
119+
->create(['title' => 'Laravel Testing Guide']);
120+
121+
Article::factory()
122+
->for($user, 'author')
123+
->for($user, 'approver')
124+
->published()
125+
->create(['title' => 'PHP Best Practices']);
126+
127+
$response = $this->getJson('/api/v1/articles?search=Laravel');
128+
129+
$response->assertStatus(200);
130+
expect($response->json('data.articles'))->toHaveCount(1);
131+
expect($response->json('data.articles.0.id'))->toBe($article->id);
132+
});
133+
134+
it('can filter articles by author', function () {
135+
$author1 = User::factory()->create();
136+
$author2 = User::factory()->create();
137+
138+
$article1 = Article::factory()
139+
->for($author1, 'author')
140+
->for($author1, 'approver')
141+
->published()
142+
->create();
143+
144+
Article::factory()
145+
->for($author2, 'author')
146+
->for($author2, 'approver')
147+
->published()
148+
->create();
149+
150+
$response = $this->getJson("/api/v1/articles?created_by={$author1->id}");
151+
152+
$response->assertStatus(200);
153+
expect($response->json('data.articles'))->toHaveCount(1);
154+
expect($response->json('data.articles.0.id'))->toBe($article1->id);
155+
});
156+
157+
it('can filter articles by status', function () {
158+
$user = User::factory()->create();
159+
160+
$publishedArticle = Article::factory()
161+
->for($user, 'author')
162+
->for($user, 'approver')
163+
->published()
164+
->create();
165+
166+
Article::factory()
167+
->for($user, 'author')
168+
->for($user, 'approver')
169+
->draft()
170+
->create();
171+
172+
$response = $this->getJson('/api/v1/articles?status=published');
173+
174+
$response->assertStatus(200);
175+
expect($response->json('data.articles'))->toHaveCount(1);
176+
expect($response->json('data.articles.0.id'))->toBe($publishedArticle->id);
177+
});
178+
179+
it('can customize pagination', function () {
180+
$user = User::factory()->create();
181+
Article::factory()
182+
->count(30)
183+
->for($user, 'author')
184+
->for($user, 'approver')
185+
->published()
186+
->create();
187+
188+
$response = $this->getJson('/api/v1/articles?per_page=5&page=2');
189+
190+
$response->assertStatus(200);
191+
expect($response->json('data.articles'))->toHaveCount(5);
192+
expect($response->json('data.meta.current_page'))->toBe(2);
193+
expect($response->json('data.meta.per_page'))->toBe(5);
194+
});
195+
196+
it('can sort articles', function () {
197+
$user = User::factory()->create();
198+
199+
$article1 = Article::factory()
200+
->for($user, 'author')
201+
->for($user, 'approver')
202+
->published()
203+
->create(['title' => 'A Article']);
204+
205+
$article2 = Article::factory()
206+
->for($user, 'author')
207+
->for($user, 'approver')
208+
->published()
209+
->create(['title' => 'Z Article']);
210+
211+
$response = $this->getJson('/api/v1/articles?sort_by=title&sort_direction=asc');
212+
213+
$response->assertStatus(200);
214+
expect($response->json('data.articles.0.id'))->toBe($article1->id);
215+
expect($response->json('data.articles.1.id'))->toBe($article2->id);
216+
});
217+
218+
it('returns 500 when getting articles fails with exception', function () {
219+
// Mock ArticleService to throw an exception
220+
$this->mock(\App\Services\ArticleService::class, function ($mock) {
221+
$mock->shouldReceive('getArticles')
222+
->andThrow(new \Exception('Database connection failed'));
223+
});
224+
225+
$response = $this->getJson('/api/v1/articles');
226+
227+
$response->assertStatus(500)
228+
->assertJson([
229+
'status' => false,
230+
'message' => __('common.something_went_wrong'),
231+
'data' => null,
232+
'error' => null,
233+
]);
234+
});
235+
});
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use App\Models\Article;
6+
use App\Models\Category;
7+
use App\Models\Tag;
8+
use App\Models\User;
9+
10+
describe('API/V1/Article/ShowArticleController', function () {
11+
it('can get single article by slug', function () {
12+
$user = User::factory()->create();
13+
$category = Category::factory()->create();
14+
$tag = Tag::factory()->create();
15+
16+
$article = Article::factory()
17+
->for($user, 'author')
18+
->for($user, 'approver')
19+
->published()
20+
->create();
21+
22+
$article->categories()->attach($category->id);
23+
$article->tags()->attach($tag->id);
24+
25+
$response = $this->getJson("/api/v1/articles/{$article->slug}");
26+
27+
$response->assertStatus(200)
28+
->assertJsonStructure([
29+
'status',
30+
'message',
31+
'data' => [
32+
'id',
33+
'slug',
34+
'title',
35+
'subtitle',
36+
'excerpt',
37+
'content_html',
38+
'content_markdown',
39+
'featured_image',
40+
'status',
41+
'published_at',
42+
'meta_title',
43+
'meta_description',
44+
'created_at',
45+
'updated_at',
46+
'author' => [
47+
'id',
48+
'name',
49+
'email',
50+
'avatar_url',
51+
'bio',
52+
],
53+
'categories' => [
54+
'*' => [
55+
'id',
56+
'name',
57+
'slug',
58+
],
59+
],
60+
'tags' => [
61+
'*' => [
62+
'id',
63+
'name',
64+
'slug',
65+
],
66+
],
67+
'authors',
68+
'comments_count',
69+
],
70+
]);
71+
72+
expect($response->json('data.id'))->toBe($article->id);
73+
expect($response->json('data.slug'))->toBe($article->slug);
74+
});
75+
76+
it('returns 404 when article not found by slug', function () {
77+
$response = $this->getJson('/api/v1/articles/non-existent-slug');
78+
79+
$response->assertStatus(404)
80+
->assertJson([
81+
'status' => false,
82+
'message' => __('common.not_found'),
83+
'data' => null,
84+
'error' => null,
85+
]);
86+
});
87+
88+
it('returns 500 when showing article fails with exception', function () {
89+
// Mock ArticleService to throw an exception
90+
$this->mock(\App\Services\ArticleService::class, function ($mock) {
91+
$mock->shouldReceive('getArticleBySlug')
92+
->with('test-slug')
93+
->andThrow(new \Exception('Database connection failed'));
94+
});
95+
96+
$response = $this->getJson('/api/v1/articles/test-slug');
97+
98+
$response->assertStatus(500)
99+
->assertJson([
100+
'status' => false,
101+
'message' => __('common.something_went_wrong'),
102+
'data' => null,
103+
'error' => null,
104+
]);
105+
});
106+
});

0 commit comments

Comments
 (0)