Skip to content
This repository was archived by the owner on Feb 14, 2023. It is now read-only.

Commit e24d6e0

Browse files
committed
Fix assert testing utilities with hasMany relationships
1 parent 6d7df6d commit e24d6e0

File tree

5 files changed

+216
-28
lines changed

5 files changed

+216
-28
lines changed

src/Testing/Concerns/HasRelationships.php

Lines changed: 70 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -49,14 +49,16 @@ public function hasAnyRelationships($name, $withIncluded = false)
4949
{
5050
$type = JsonApi::getResourceType($name);
5151

52-
PHPUnit::assertTrue(count(array_filter($this->relationships, function ($relation) use ($type) {
53-
return $relation['data']['type'] === $type;
54-
})) > 0);
52+
PHPUnit::assertTrue(
53+
count($this->filterResources($this->relationships, $type)) > 0,
54+
sprintf('There is not any relationship with type "%s"', $type)
55+
);
5556

5657
if ($withIncluded) {
57-
PHPUnit::assertTrue(count(array_filter($this->includeds, function ($included) use ($type) {
58-
return $included['type'] === $type;
59-
})) > 0);
58+
PHPUnit::assertTrue(
59+
count($this->filterResources($this->includeds, $type)) > 0,
60+
sprintf('There is not any relationship with type "%s"', $type)
61+
);
6062
}
6163

6264
return $this;
@@ -74,14 +76,16 @@ public function hasNotAnyRelationships($name, $withIncluded = false)
7476
{
7577
$type = JsonApi::getResourceType($name);
7678

77-
PHPUnit::assertFalse(count(array_filter($this->relationships, function ($relation) use ($type) {
78-
return $relation['data']['type'] === $type;
79-
})) > 0, sprintf('There is a relationship with type "%s" for resource "%s"', $type, $this->getIdentifierMessageFor()));
79+
PHPUnit::assertFalse(
80+
count($this->filterResources($this->relationships, $type)) > 0,
81+
sprintf('There is a relationship with type "%s" for resource "%s"', $type, $this->getIdentifierMessageFor())
82+
);
8083

8184
if ($withIncluded) {
82-
PHPUnit::assertFalse(count(array_filter($this->includeds, function ($included) use ($type) {
83-
return $included['type'] === $type;
84-
})) > 0, sprintf('There is a included relationship with type "%s"', $type));
85+
PHPUnit::assertFalse(
86+
count($this->filterResources($this->includeds, $type)) > 0,
87+
sprintf('There is a included relationship with type "%s"', $type)
88+
);
8589
}
8690

8791
return $this;
@@ -99,14 +103,16 @@ public function hasRelationshipWith(Model $model, $withIncluded = false)
99103
{
100104
$type = JsonApi::getResourceType($model);
101105

102-
PHPUnit::assertTrue(count(array_filter($this->relationships, function ($relation) use ($type, $model) {
103-
return $relation['data']['type'] === $type && $relation['data']['id'] == $model->getKey();
104-
})) > 0, sprintf('There is no relationship "%s" for resource "%s"', $this->getIdentifierMessageFor($model->getKey(), $type), $this->getIdentifierMessageFor()));
106+
PHPUnit::assertTrue(
107+
count($this->filterResources($this->relationships, $type, $model->getKey())) > 0,
108+
sprintf('There is no relationship "%s" for resource "%s"', $this->getIdentifierMessageFor($model->getKey(), $type), $this->getIdentifierMessageFor())
109+
);
105110

106111
if ($withIncluded) {
107-
PHPUnit::assertTrue(count(array_filter($this->includeds, function ($included) use ($type, $model) {
108-
return $included['type'] === $type && $included['id'] == $model->getKey();
109-
})) > 0, sprintf('There is no included relationship "%s"', $this->getIdentifierMessageFor($model->getKey(), $type)));
112+
PHPUnit::assertTrue(
113+
count($this->filterResources($this->includeds, $type, $model->getKey())) > 0,
114+
sprintf('There is no included relationship "%s"', $this->getIdentifierMessageFor($model->getKey(), $type))
115+
);
110116
}
111117

112118
return $this;
@@ -124,16 +130,56 @@ public function hasNotRelationshipWith(Model $model, $withIncluded = false)
124130
{
125131
$type = JsonApi::getResourceType($model);
126132

127-
PHPUnit::assertFalse(count(array_filter($this->relationships, function ($relation) use ($type, $model) {
128-
return $relation['data']['type'] === $type && $relation['data']['id'] == $model->getKey();
129-
})) > 0, sprintf('There is a relationship "%s" for resource "%s"', $this->getIdentifierMessageFor($model->getKey(), $type), $this->getIdentifierMessageFor()));
133+
PHPUnit::assertFalse(
134+
count($this->filterResources($this->relationships, $type, $model->getKey())) > 0,
135+
sprintf('There is a relationship "%s" for resource "%s"', $this->getIdentifierMessageFor($model->getKey(), $type), $this->getIdentifierMessageFor())
136+
);
130137

131138
if ($withIncluded) {
132-
PHPUnit::assertFalse(count(array_filter($this->includeds, function ($included) use ($type, $model) {
133-
return $included['type'] === $type && $included['id'] == $model->getKey();
134-
})) > 0, sprintf('There is a included relationship "%s"', $this->getIdentifierMessageFor($model->getKey(), $type)));
139+
PHPUnit::assertFalse(
140+
count($this->filterResources($this->includeds, $type, $model->getKey())) > 0,
141+
sprintf('There is a included relationship "%s"', $this->getIdentifierMessageFor($model->getKey(), $type))
142+
);
135143
}
136144

137145
return $this;
138146
}
147+
148+
/**
149+
* Filter array of resources by a provided identifier.
150+
*
151+
* @param array $resources
152+
* @param string $type
153+
* @param mixed $id
154+
* @return array
155+
*/
156+
protected function filterResources(array $resources, string $type, $id = null)
157+
{
158+
return array_filter($resources, function ($resource) use ($type, $id) {
159+
return $this->filterResourceWithIdentifier($resource, $type, $id);
160+
});
161+
}
162+
163+
/**
164+
* Filter provided resource with given identifier.
165+
*
166+
* @param array $resource
167+
* @param string $type
168+
* @param mixed $id
169+
* @return bool
170+
*/
171+
protected function filterResourceWithIdentifier(array $resource, string $type, $id = null)
172+
{
173+
if (is_array($resource) && ! isset($resource['type'])) {
174+
return count($this->filterResources($resource, $type, $id)) > 0;
175+
}
176+
177+
$condition = $resource['type'] === $type;
178+
179+
if ($id) {
180+
$condition &= $resource['id'] == $id;
181+
}
182+
183+
return (bool) $condition;
184+
}
139185
}

tests/Fixtures/Post.php

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,17 @@ public function parent()
3737
*/
3838
public function author()
3939
{
40-
return $this->belongsTo(User::class);
40+
return $this->belongsTo(User::class, 'user_id');
41+
}
42+
43+
/**
44+
* Get its tags.
45+
*
46+
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
47+
*/
48+
public function tags()
49+
{
50+
return $this->belongsToMany(Tag::class);
4151
}
4252

4353
/**

tests/Fixtures/Tag.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
namespace SkoreLabs\JsonApi\Tests\Fixtures;
4+
5+
use Illuminate\Database\Eloquent\Model;
6+
7+
class Tag extends Model
8+
{
9+
/**
10+
* The attributes that should be visible in serialization.
11+
*
12+
* @var string[]
13+
*/
14+
protected $visible = ['name', 'slug'];
15+
16+
/**
17+
* The attributes that aren't mass assignable.
18+
*
19+
* @var string[]
20+
*/
21+
protected $guarded = [];
22+
}

tests/JsonApiRelationshipsTest.php

Lines changed: 70 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use SkoreLabs\JsonApi\Support\JsonApi;
77
use SkoreLabs\JsonApi\Testing\Assert;
88
use SkoreLabs\JsonApi\Tests\Fixtures\Post;
9+
use SkoreLabs\JsonApi\Tests\Fixtures\Tag;
910
use SkoreLabs\JsonApi\Tests\Fixtures\User;
1011

1112
class JsonApiRelationshipsTest extends TestCase
@@ -20,6 +21,16 @@ class JsonApiRelationshipsTest extends TestCase
2021
*/
2122
protected $lonelyPost;
2223

24+
/**
25+
* @var \SkoreLabs\JsonApi\Tests\Fixtures\Tag
26+
*/
27+
protected $postTag;
28+
29+
/**
30+
* @var \SkoreLabs\JsonApi\Tests\Fixtures\Tag
31+
*/
32+
protected $lonelyTag;
33+
2334
/**
2435
* Setup the test environment.
2536
*
@@ -30,7 +41,10 @@ protected function setUp(): void
3041
parent::setUp();
3142

3243
$this->bypassPolicies();
44+
}
3345

46+
public function testCollectionHasAnyClientAuthorRelationship()
47+
{
3448
Route::get('/', function () {
3549
$this->authoredPost = new Post([
3650
'id' => 5,
@@ -55,10 +69,7 @@ protected function setUp(): void
5569
$this->lonelyPost,
5670
]));
5771
});
58-
}
5972

60-
public function testCollectionHasAnyClientAuthorRelationship()
61-
{
6273
$response = $this->get('/', ['Accept' => 'application/json']);
6374

6475
$response->assertSuccessful();
@@ -70,4 +81,60 @@ public function testCollectionHasAnyClientAuthorRelationship()
7081
$jsonApi->at(0)->hasNotRelationshipWith($this->lonelyPost, true);
7182
});
7283
}
84+
85+
/**
86+
* @group requiresDatabase
87+
*/
88+
public function testResourceHasTagsRelationships()
89+
{
90+
// TODO: setRelation method doesn't work with hasMany relationships, so need migrations loaded
91+
$this->loadMigrationsFrom(__DIR__.'/database/migrations');
92+
93+
Route::get('/', function () {
94+
$this->authoredPost = Post::create([
95+
'title' => 'Test Title',
96+
]);
97+
98+
$this->lonelyTag = Tag::create([
99+
'name' => 'Lifestyle',
100+
'slug' => 'lifestyle',
101+
]);
102+
103+
$this->postTag = Tag::create([
104+
'name' => 'News',
105+
'slug' => 'news',
106+
]);
107+
108+
$anotherTag = Tag::create([
109+
'name' => 'International',
110+
'slug' => 'international',
111+
]);
112+
113+
$this->authoredPost->tags()->attach([
114+
$this->postTag->id,
115+
$anotherTag->id,
116+
]);
117+
118+
$this->authoredPost->author()->associate(
119+
User::create([
120+
'name' => 'Myself',
121+
'email' => 'me@internet.org',
122+
'password' => '1234',
123+
])->id
124+
);
125+
126+
$this->authoredPost->save();
127+
128+
return JsonApi::format($this->authoredPost->refresh()->loadMissing('author', 'tags'));
129+
});
130+
131+
$response = $this->get('/', ['Accept' => 'application/json']);
132+
133+
$response->assertSuccessful();
134+
135+
$response->assertJsonApi(function (Assert $jsonApi) {
136+
$jsonApi->hasRelationshipWith($this->postTag, true)
137+
->hasNotRelationshipWith($this->lonelyTag, true);
138+
});
139+
}
73140
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
use Illuminate\Database\Migrations\Migration;
4+
use Illuminate\Database\Schema\Blueprint;
5+
use Illuminate\Support\Facades\Schema;
6+
7+
class CreateTagsTable extends Migration
8+
{
9+
/**
10+
* Run the migrations.
11+
*
12+
* @return void
13+
*/
14+
public function up()
15+
{
16+
Schema::create('tags', static function (Blueprint $table) {
17+
$table->bigInteger('id')->primary();
18+
$table->string('name');
19+
$table->string('slug')->unique();
20+
$table->timestamps();
21+
});
22+
23+
Schema::create('post_tag', static function (Blueprint $table) {
24+
$table->bigInteger('id')->primary();
25+
$table->unsignedBigInteger('post_id')->nullable();
26+
$table->unsignedBigInteger('tag_id')->nullable();
27+
28+
$table->foreign('post_id')->references('id')->on('posts');
29+
$table->foreign('tag_id')->references('id')->on('tags');
30+
});
31+
}
32+
33+
/**
34+
* Reverse the migrations.
35+
*
36+
* @return void
37+
*/
38+
public function down()
39+
{
40+
Schema::dropIfExists('post_tag');
41+
Schema::dropIfExists('tags');
42+
}
43+
}

0 commit comments

Comments
 (0)