Skip to content

Commit 66a75fd

Browse files
committed
add tests
1 parent 62ded83 commit 66a75fd

10 files changed

Lines changed: 515 additions & 0 deletions

File tree

Lines changed: 266 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Cycle\ORM\Tests\Functional\Driver\Common\Integration\Case532;
6+
7+
use Cycle\ORM\Heap\Heap;
8+
use Cycle\ORM\Reference\ReferenceInterface;
9+
use Cycle\ORM\Relation\BulkLoader;
10+
use Cycle\ORM\Select;
11+
use Cycle\ORM\Tests\Functional\Driver\Common\BaseTest;
12+
use Cycle\ORM\Tests\Functional\Driver\Common\Integration\IntegrationTestTrait;
13+
use Cycle\ORM\Tests\Traits\TableTrait;
14+
15+
/**
16+
* Lazy-loaded embedded (same-row) relations: end-to-end coverage for `Select::load()`,
17+
* promise resolution on property access, and `BulkLoader::load()`.
18+
*/
19+
abstract class CaseTest extends BaseTest
20+
{
21+
use IntegrationTestTrait;
22+
use TableTrait;
23+
24+
public function testSelectLoadEmbedded(): void
25+
{
26+
/** @var Entity\User $user */
27+
$user = (new Select($this->orm, Entity\User::class))
28+
->load('credentials')
29+
->wherePK(1)
30+
->fetchOne();
31+
32+
$this->assertInstanceOf(Entity\UserCredentials::class, $user->credentials);
33+
$this->assertSame('user1', $user->credentials->username);
34+
$this->assertSame('pass1', $user->credentials->password);
35+
$this->assertSame(0, $user->credentials->numLogins);
36+
}
37+
38+
public function testSelectLoadEmbeddedMultiple(): void
39+
{
40+
/** @var array<Entity\User> $users */
41+
$users = (new Select($this->orm, Entity\User::class))
42+
->load('credentials')
43+
->orderBy('id')
44+
->fetchAll();
45+
46+
$this->assertCount(2, $users);
47+
$this->assertSame('user1', $users[0]->credentials->username);
48+
$this->assertSame('user2', $users[1]->credentials->username);
49+
$this->assertSame(1, $users[1]->credentials->numLogins);
50+
}
51+
52+
public function testSelectWithoutLoadProducesPromise(): void
53+
{
54+
/** @var Entity\User $user */
55+
$user = (new Select($this->orm, Entity\User::class))
56+
->wherePK(1)
57+
->fetchOne();
58+
59+
$relations = $this->extractEntity($user);
60+
$this->assertInstanceOf(ReferenceInterface::class, $relations['credentials']);
61+
}
62+
63+
public function testPromiseResolvesOnPropertyAccess(): void
64+
{
65+
/** @var Entity\User $user */
66+
$user = (new Select($this->orm, Entity\User::class))
67+
->wherePK(1)
68+
->fetchOne();
69+
70+
$this->assertInstanceOf(Entity\UserCredentials::class, $user->credentials);
71+
$this->assertSame('user1', $user->credentials->username);
72+
}
73+
74+
public function testBulkLoadLazyEmbeddedSingleEntity(): void
75+
{
76+
$this->orm = $this->orm->withHeap(new Heap());
77+
78+
/** @var Entity\User $user */
79+
$user = (new Select($this->orm, Entity\User::class))
80+
->wherePK(1)
81+
->fetchOne();
82+
83+
$rels = $this->extractEntity($user);
84+
$this->assertInstanceOf(ReferenceInterface::class, $rels['credentials']);
85+
86+
(new BulkLoader($this->orm))
87+
->collect($user)
88+
->load('credentials')
89+
->run();
90+
91+
// After BulkLoader the relation is hydrated; reading the fields must not query the DB.
92+
$this->captureReadQueries();
93+
$this->assertInstanceOf(Entity\UserCredentials::class, $user->credentials);
94+
$this->assertSame('user1', $user->credentials->username);
95+
$this->assertSame('pass1', $user->credentials->password);
96+
$this->assertSame(0, $user->credentials->numLogins);
97+
$this->assertNumReads(0);
98+
}
99+
100+
public function testBulkLoadLazyEmbeddedMultipleEntities(): void
101+
{
102+
$this->orm = $this->orm->withHeap(new Heap());
103+
104+
/** @var array<Entity\User> $users */
105+
$users = (new Select($this->orm, Entity\User::class))
106+
->orderBy('id')
107+
->fetchAll();
108+
109+
$this->assertCount(2, $users);
110+
111+
(new BulkLoader($this->orm))
112+
->collect(...$users)
113+
->load('credentials')
114+
->run();
115+
116+
$this->captureReadQueries();
117+
$this->assertSame('user1', $users[0]->credentials->username);
118+
$this->assertSame('user2', $users[1]->credentials->username);
119+
$this->assertSame(0, $users[0]->credentials->numLogins);
120+
$this->assertSame(1, $users[1]->credentials->numLogins);
121+
$this->assertNumReads(0);
122+
}
123+
124+
public function testBulkLoadEmbeddedAlongsideHasMany(): void
125+
{
126+
$this->orm = $this->orm->withHeap(new Heap());
127+
128+
/** @var array<Entity\User> $users */
129+
$users = (new Select($this->orm, Entity\User::class))
130+
->orderBy('id')
131+
->fetchAll();
132+
133+
(new BulkLoader($this->orm))
134+
->collect(...$users)
135+
->load('credentials')
136+
->load('comments')
137+
->run();
138+
139+
$this->captureReadQueries();
140+
$this->assertSame('user1', $users[0]->credentials->username);
141+
$this->assertSame('user2', $users[1]->credentials->username);
142+
$this->assertCount(2, $users[0]->comments);
143+
$this->assertCount(1, $users[1]->comments);
144+
$this->assertNumReads(0);
145+
}
146+
147+
public function testBulkLoadMultipleEmbeddedInSingleQuery(): void
148+
{
149+
$this->orm = $this->orm->withHeap(new Heap());
150+
151+
/** @var array<Entity\User> $users */
152+
$users = (new Select($this->orm, Entity\User::class))
153+
->orderBy('id')
154+
->fetchAll();
155+
156+
// All embedded relations share the parent table, so loading any number of them
157+
// must collapse into one SQL query.
158+
$this->captureReadQueries();
159+
(new BulkLoader($this->orm))
160+
->collect(...$users)
161+
->load('credentials')
162+
->load('profile')
163+
->run();
164+
$this->assertNumReads(1);
165+
166+
$this->captureReadQueries();
167+
$this->assertSame('user1', $users[0]->credentials->username);
168+
$this->assertSame('user2', $users[1]->credentials->username);
169+
$this->assertSame('bio 1', $users[0]->profile->bio);
170+
$this->assertSame('bio 2', $users[1]->profile->bio);
171+
$this->assertSame('image1.png', $users[0]->profile->image);
172+
$this->assertNumReads(0);
173+
}
174+
175+
public function testBulkLoadDoesNotRefetchParentFields(): void
176+
{
177+
/** @var Entity\User $user */
178+
$user = (new Select($this->orm, Entity\User::class))
179+
->wherePK(1)
180+
->fetchOne();
181+
182+
// Mutate the DB row out-of-band: if BulkLoader re-fetched parent columns,
183+
// $user->email would pick up the new DB value.
184+
$this->getDatabase()->table('user')->update(['email' => 'changed-in-db@test.com'], ['id' => 1])->run();
185+
186+
(new BulkLoader($this->orm))
187+
->collect($user)
188+
->load('credentials')
189+
->run();
190+
191+
$this->assertSame('hello@world.com', $user->email);
192+
$this->assertInstanceOf(Entity\UserCredentials::class, $user->credentials);
193+
}
194+
195+
public function testBulkLoadDoesNotOverwriteAlreadyLoadedEmbedded(): void
196+
{
197+
/** @var Entity\User $user */
198+
$user = (new Select($this->orm, Entity\User::class))
199+
->load('credentials')
200+
->wherePK(1)
201+
->fetchOne();
202+
203+
$originalCredentials = $user->credentials;
204+
$this->assertInstanceOf(Entity\UserCredentials::class, $originalCredentials);
205+
206+
// The relation is already a real entity (not a Reference), so BulkLoader must
207+
// keep the same instance instead of replacing it.
208+
(new BulkLoader($this->orm))
209+
->collect($user)
210+
->load('credentials')
211+
->run();
212+
213+
$this->assertSame($originalCredentials, $user->credentials);
214+
$this->assertSame('user1', $user->credentials->username);
215+
}
216+
217+
public function setUp(): void
218+
{
219+
parent::setUp();
220+
$this->makeTables();
221+
$this->fillData();
222+
223+
$this->loadSchema(__DIR__ . '/schema.php');
224+
}
225+
226+
private function makeTables(): void
227+
{
228+
$this->makeTable('user', [
229+
'id' => 'primary',
230+
'email' => 'string',
231+
'balance' => 'float',
232+
'creds_username' => 'string',
233+
'creds_password' => 'string',
234+
'creds_num_logins' => 'int',
235+
'profile_bio' => 'string',
236+
'profile_image' => 'string',
237+
]);
238+
239+
$this->makeTable('comment', [
240+
'id' => 'primary',
241+
'user_id' => 'integer,null',
242+
'message' => 'string',
243+
]);
244+
$this->makeFK('comment', 'user_id', 'user', 'id', 'NO ACTION', 'NO ACTION');
245+
}
246+
247+
private function fillData(): void
248+
{
249+
$this->getDatabase()->table('user')->insertMultiple(
250+
['email', 'balance', 'creds_username', 'creds_password', 'creds_num_logins', 'profile_bio', 'profile_image'],
251+
[
252+
['hello@world.com', 100, 'user1', 'pass1', 0, 'bio 1', 'image1.png'],
253+
['another@world.com', 200, 'user2', 'pass2', 1, 'bio 2', 'image2.png'],
254+
],
255+
);
256+
257+
$this->getDatabase()->table('comment')->insertMultiple(
258+
['user_id', 'message'],
259+
[
260+
[1, 'msg 1'],
261+
[1, 'msg 2'],
262+
[2, 'msg 3'],
263+
],
264+
);
265+
}
266+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Cycle\ORM\Tests\Functional\Driver\Common\Integration\Case532\Entity;
6+
7+
class Comment
8+
{
9+
public ?int $id = null;
10+
public ?int $user_id = null;
11+
public string $message = '';
12+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Cycle\ORM\Tests\Functional\Driver\Common\Integration\Case532\Entity;
6+
7+
class User
8+
{
9+
public const ROLE = 'user';
10+
11+
public ?int $id = null;
12+
public string $email = '';
13+
public float $balance = 0.0;
14+
15+
public ?UserCredentials $credentials = null;
16+
public ?UserProfile $profile = null;
17+
18+
/** @var iterable<Comment> */
19+
public iterable $comments = [];
20+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Cycle\ORM\Tests\Functional\Driver\Common\Integration\Case532\Entity;
6+
7+
class UserCredentials
8+
{
9+
public string $username = '';
10+
public string $password = '';
11+
public int $numLogins = 0;
12+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Cycle\ORM\Tests\Functional\Driver\Common\Integration\Case532\Entity;
6+
7+
class UserProfile
8+
{
9+
public string $bio = '';
10+
public string $image = '';
11+
}

0 commit comments

Comments
 (0)