Skip to content

Commit 7d6f0a4

Browse files
committed
test(database): improve dto serialization coverage
1 parent 11f9327 commit 7d6f0a4

File tree

3 files changed

+735
-0
lines changed

3 files changed

+735
-0
lines changed
Lines changed: 308 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,308 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tests\Tempest\Integration\Database\DtoSerialization;
6+
7+
use Tempest\Database\DatabaseMigration;
8+
use Tempest\Database\Migrations\CreateMigrationsTable;
9+
use Tempest\Database\Query;
10+
use Tempest\Database\QueryStatement;
11+
use Tempest\Database\QueryStatements\CreateTableStatement;
12+
use Tempest\Mapper\MapperConfig;
13+
use Tempest\Mapper\SerializeAs;
14+
use Tests\Tempest\Integration\FrameworkIntegrationTestCase;
15+
16+
use function Tempest\Database\query;
17+
18+
final class SerializeAsTest extends FrameworkIntegrationTestCase
19+
{
20+
public function test_serialize_as_simple_object(): void
21+
{
22+
$config = $this->container->get(MapperConfig::class);
23+
$config->serializeAs(SimpleSpell::class, 'simple-spell');
24+
25+
$this->migrate(CreateMigrationsTable::class, new class implements DatabaseMigration {
26+
public string $name = '001_spell_library';
27+
28+
public function up(): QueryStatement
29+
{
30+
return new CreateTableStatement('spell_libraries')
31+
->primary()
32+
->text('name')
33+
->json('spell_data');
34+
}
35+
36+
public function down(): null
37+
{
38+
return null;
39+
}
40+
});
41+
42+
$spell = new SimpleSpell(name: 'Zoltraak', element: 'destruction');
43+
$library = new SpellLibrary(name: "Frieren's Collection", spell_data: $spell);
44+
45+
query(SpellLibrary::class)
46+
->insert($library)
47+
->execute();
48+
49+
$retrieved = query(SpellLibrary::class)
50+
->select()
51+
->first();
52+
53+
$this->assertSame("Frieren's Collection", $retrieved->name);
54+
$this->assertInstanceOf(SimpleSpell::class, $retrieved->spell_data);
55+
$this->assertSame('Zoltraak', $retrieved->spell_data->name);
56+
$this->assertSame('destruction', $retrieved->spell_data->element);
57+
58+
$raw = new Query('SELECT spell_data FROM spell_libraries WHERE id = 1')->fetchFirst();
59+
$json = json_decode($raw['spell_data'], associative: true);
60+
61+
$this->assertSame('simple-spell', $json['type']);
62+
$this->assertArrayHasKey('data', $json);
63+
$this->assertSame('Zoltraak', $json['data']['name']);
64+
$this->assertSame('destruction', $json['data']['element']);
65+
}
66+
67+
public function test_serialize_as_nested_objects(): void
68+
{
69+
$config = $this->container->get(MapperConfig::class);
70+
$config->serializeAs(MageProfile::class, 'mage-profile');
71+
$config->serializeAs(SimpleSpell::class, 'simple-spell');
72+
73+
$this->migrate(CreateMigrationsTable::class, new class implements DatabaseMigration {
74+
public string $name = '002_mage_profiles';
75+
76+
public function up(): QueryStatement
77+
{
78+
return new CreateTableStatement('mages')
79+
->primary()
80+
->text('name')
81+
->json('profile_data');
82+
}
83+
84+
public function down(): null
85+
{
86+
return null;
87+
}
88+
});
89+
90+
$profile = new MageProfile(
91+
age: 1000,
92+
favoriteSpell: new SimpleSpell(name: 'Zoltraak', element: 'destruction'),
93+
);
94+
95+
$mage = new Mage(name: 'Frieren', profile_data: $profile);
96+
97+
query(Mage::class)
98+
->insert($mage)
99+
->execute();
100+
101+
$retrieved = query(Mage::class)
102+
->select()
103+
->first();
104+
105+
$this->assertSame('Frieren', $retrieved->name);
106+
$this->assertInstanceOf(MageProfile::class, $retrieved->profile_data);
107+
$this->assertSame(1000, $retrieved->profile_data->age);
108+
$this->assertInstanceOf(SimpleSpell::class, $retrieved->profile_data->favoriteSpell);
109+
$this->assertSame('Zoltraak', $retrieved->profile_data->favoriteSpell->name);
110+
111+
$raw = new Query('SELECT profile_data FROM mages WHERE id = 1')->fetchFirst();
112+
$json = json_decode($raw['profile_data'], associative: true);
113+
114+
$this->assertSame('mage-profile', $json['type']);
115+
$this->assertSame(1000, $json['data']['age']);
116+
$this->assertSame('simple-spell', $json['data']['favoriteSpell']['type']);
117+
$this->assertSame('Zoltraak', $json['data']['favoriteSpell']['data']['name']);
118+
}
119+
120+
public function test_serialize_as_with_arrays(): void
121+
{
122+
$config = $this->container->get(MapperConfig::class);
123+
$config->serializeAs(SpellCollection::class, 'spell-collection');
124+
$config->serializeAs(SimpleSpell::class, 'simple-spell');
125+
126+
$this->migrate(CreateMigrationsTable::class, new class implements DatabaseMigration {
127+
public string $name = '003_collections';
128+
129+
public function up(): QueryStatement
130+
{
131+
return new CreateTableStatement('spell_containers')
132+
->primary()
133+
->text('title')
134+
->json('collection_data');
135+
}
136+
137+
public function down(): null
138+
{
139+
return null;
140+
}
141+
});
142+
143+
$collection = new SpellCollection(
144+
count: 3,
145+
spells: [
146+
new SimpleSpell(name: 'Zoltraak', element: 'destruction'),
147+
new SimpleSpell(name: 'Shield', element: 'protection'),
148+
new SimpleSpell(name: 'Heal', element: 'restoration'),
149+
],
150+
);
151+
152+
$container = new SpellContainer(title: 'Basic Spells', collection_data: $collection);
153+
154+
query(SpellContainer::class)
155+
->insert($container)
156+
->execute();
157+
158+
$retrieved = query(SpellContainer::class)
159+
->select()
160+
->first();
161+
162+
$this->assertSame('Basic Spells', $retrieved->title);
163+
$this->assertInstanceOf(SpellCollection::class, $retrieved->collection_data);
164+
$this->assertSame(3, $retrieved->collection_data->count);
165+
$this->assertCount(3, $retrieved->collection_data->spells);
166+
$this->assertInstanceOf(SimpleSpell::class, $retrieved->collection_data->spells[0]);
167+
$this->assertSame('Zoltraak', $retrieved->collection_data->spells[0]->name);
168+
169+
$raw = new Query('SELECT collection_data FROM spell_containers WHERE id = 1')->fetchFirst();
170+
$json = json_decode($raw['collection_data'], associative: true);
171+
172+
$this->assertSame('spell-collection', $json['type']);
173+
$this->assertSame(3, $json['data']['count']);
174+
$this->assertCount(3, $json['data']['spells']);
175+
$this->assertSame('simple-spell', $json['data']['spells'][0]['type']);
176+
$this->assertSame('Zoltraak', $json['data']['spells'][0]['data']['name']);
177+
}
178+
179+
public function test_serialize_as_without_explicit_casters(): void
180+
{
181+
$config = $this->container->get(MapperConfig::class);
182+
$config->serializeAs(MagicItem::class, 'magic-item');
183+
184+
$this->migrate(CreateMigrationsTable::class, new class implements DatabaseMigration {
185+
public string $name = '004_inventory';
186+
187+
public function up(): QueryStatement
188+
{
189+
return new CreateTableStatement('inventories')
190+
->primary()
191+
->text('owner')
192+
->json('item_data');
193+
}
194+
195+
public function down(): null
196+
{
197+
return null;
198+
}
199+
});
200+
201+
$item = new MagicItem(
202+
name: 'Staff of Power',
203+
type: ItemType::STAFF,
204+
enchanted: true,
205+
);
206+
207+
$inventory = new Inventory(owner: 'Frieren', item_data: $item);
208+
209+
query(Inventory::class)
210+
->insert($inventory)
211+
->execute();
212+
213+
$retrieved = query(Inventory::class)
214+
->select()
215+
->first();
216+
217+
$this->assertSame('Frieren', $retrieved->owner);
218+
$this->assertInstanceOf(MagicItem::class, $retrieved->item_data);
219+
$this->assertSame('Staff of Power', $retrieved->item_data->name);
220+
$this->assertSame(ItemType::STAFF, $retrieved->item_data->type);
221+
$this->assertTrue($retrieved->item_data->enchanted);
222+
223+
$raw = new Query('SELECT item_data FROM inventories WHERE id = 1')->fetchFirst();
224+
$json = json_decode($raw['item_data'], associative: true);
225+
226+
$this->assertSame('magic-item', $json['type']);
227+
$this->assertSame('Staff of Power', $json['data']['name']);
228+
$this->assertSame('staff', $json['data']['type']);
229+
$this->assertTrue($json['data']['enchanted']);
230+
}
231+
}
232+
233+
enum ItemType: string
234+
{
235+
case STAFF = 'staff';
236+
case WAND = 'wand';
237+
case RING = 'ring';
238+
}
239+
240+
final class SpellLibrary
241+
{
242+
public function __construct(
243+
public string $name,
244+
public SimpleSpell $spell_data,
245+
) {}
246+
}
247+
248+
#[SerializeAs('simple-spell')]
249+
final class SimpleSpell
250+
{
251+
public function __construct(
252+
public string $name,
253+
public string $element,
254+
) {}
255+
}
256+
257+
final class Mage
258+
{
259+
public function __construct(
260+
public string $name,
261+
public MageProfile $profile_data,
262+
) {}
263+
}
264+
265+
#[SerializeAs('mage-profile')]
266+
final class MageProfile
267+
{
268+
public function __construct(
269+
public int $age,
270+
public SimpleSpell $favoriteSpell,
271+
) {}
272+
}
273+
274+
final class SpellContainer
275+
{
276+
public function __construct(
277+
public string $title,
278+
public SpellCollection $collection_data,
279+
) {}
280+
}
281+
282+
#[SerializeAs('spell-collection')]
283+
final class SpellCollection
284+
{
285+
public function __construct(
286+
public int $count,
287+
/** @var \Tests\Tempest\Integration\Database\DtoSerialization\SimpleSpell[] */
288+
public array $spells,
289+
) {}
290+
}
291+
292+
final class Inventory
293+
{
294+
public function __construct(
295+
public string $owner,
296+
public MagicItem $item_data,
297+
) {}
298+
}
299+
300+
#[SerializeAs('magic-item')]
301+
final class MagicItem
302+
{
303+
public function __construct(
304+
public string $name,
305+
public ItemType $type,
306+
public bool $enchanted,
307+
) {}
308+
}

0 commit comments

Comments
 (0)