packages/ztd-query-core/tests/Unit/Schema/TableDefinitionTest.php

1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Unit\Schema;
6
7use PHPUnit\Framework\Attributes\CoversClass;
8use PHPUnit\Framework\Attributes\UsesClass;
9use PHPUnit\Framework\TestCase;
10use ZtdQuery\Schema\ColumnDeclaration;
11use ZtdQuery\Schema\ColumnTypeFamily;
12use ZtdQuery\Schema\Key\CandidateKeySet;
13use ZtdQuery\Schema\Key\ForeignKeyDefinition;
14use ZtdQuery\Schema\Key\IdentityGenerationStrategy;
15use ZtdQuery\Schema\Key\PartialUniqueIndex;
16use ZtdQuery\Schema\Partition\TablePartitioning;
17use ZtdQuery\Schema\Partition\TablePartitionKey;
18use ZtdQuery\Schema\Partition\TablePartitionRelation;
19use ZtdQuery\Schema\Partition\TablePartitionStrategy;
20use ZtdQuery\Schema\TableDefinition;
21
22#[UsesClass(ColumnDeclaration::class)]
23#[UsesClass(CandidateKeySet::class)]
24#[UsesClass(ForeignKeyDefinition::class)]
25#[UsesClass(PartialUniqueIndex::class)]
26#[UsesClass(TablePartitioning::class)]
27#[UsesClass(TablePartitionKey::class)]
28#[UsesClass(TablePartitionRelation::class)]
29#[CoversClass(TableDefinition::class)]
30final class TableDefinitionTest extends TestCase
31{
32    public function testKeepsEverythingATableDeclaresAboutItself(): void
33    {
34        $typedColumns = ['id' => new ColumnDeclaration(ColumnTypeFamily::INTEGER, 'INT')];
35
36        $definition = new TableDefinition(
37            ['id', 'name'],
38            ['id' => 'INT', 'name' => 'VARCHAR(255)'],
39            ['id'],
40            ['id'],
41            ['unique_name' => ['name']],
42            $typedColumns,
43            ['name' => "'anonymous'"],
44            ['id' => IdentityGenerationStrategy::MaxValue],
45            ['name' => "CONCAT('user-', id)"],
46            ['fk_parent' => new ForeignKeyDefinition(['id'], 'parents', ['id'])],
47            new TablePartitioning(['p0' => 'id < 10']),
48        );
49
50        self::assertSame(['id', 'name'], $definition->columns);
51        self::assertSame(['id' => 'INT', 'name' => 'VARCHAR(255)'], $definition->columnTypes);
52        self::assertSame(['id'], $definition->primaryKeys);
53        self::assertSame(['id'], $definition->notNullColumns);
54        self::assertSame(['unique_name' => ['name']], $definition->uniqueConstraints);
55        self::assertSame($typedColumns, $definition->typedColumns);
56        self::assertSame(['name' => "'anonymous'"], $definition->columnDefaults);
57        self::assertSame(['id' => IdentityGenerationStrategy::MaxValue], $definition->identityStrategies);
58        self::assertSame(['name' => "CONCAT('user-', id)"], $definition->generatedExpressions);
59        self::assertSame(['fk_parent'], array_keys($definition->foreignKeys));
60        self::assertSame(['id < 10'], $definition->partitioning?->predicatesFor(['p0']));
61    }
62
63    public function testTypedColumnsDefaultsToEmpty(): void
64    {
65        $definition = new TableDefinition(
66            ['id'],
67            ['id' => 'INT'],
68            [],
69            [],
70            [],
71        );
72
73        self::assertSame([], $definition->typedColumns);
74        self::assertSame([], $definition->columnDefaults);
75        self::assertSame([], $definition->identityStrategies);
76        self::assertSame([], $definition->generatedExpressions);
77        self::assertSame([], $definition->foreignKeys);
78        self::assertNull($definition->partitioning);
79        self::assertNull($definition->partitionKey);
80        self::assertNull($definition->partitionRelation);
81        self::assertSame([], $definition->partialUniqueIndexes);
82    }
83
84    public function testWithPartitioningPreservesSchemaAndReturnsNewDefinition(): void
85    {
86        $definition = new TableDefinition(['id'], ['id' => 'INT'], ['id'], ['id'], []);
87        $partitioning = new TablePartitioning(['p0' => 'id < 10']);
88
89        $partitioned = $definition->withPartitioning($partitioning);
90
91        self::assertNotSame($definition, $partitioned);
92        self::assertSame($definition->columns, $partitioned->columns);
93        self::assertSame($definition->primaryKeys, $partitioned->primaryKeys);
94        self::assertSame($partitioning, $partitioned->partitioning);
95    }
96
97    public function testCandidateKeysWithPartitionKeyCandidateKeysPartitionMetadataCopiesPreserveOtherSchemaState(): void
98    {
99        $definition = new TableDefinition(['id'], ['id' => 'INT'], ['id'], ['id'], []);
100        $key = new TablePartitionKey(TablePartitionStrategy::Range, ['id']);
101        $relation = new TablePartitionRelation('events', 'id >= 10');
102
103        $partition = $definition->withPartitionKey($key)->withPartitionRelation($relation);
104
105        self::assertSame($key, $partition->partitionKey);
106        self::assertSame($relation, $partition->partitionRelation);
107        self::assertSame($definition->columns, $partition->columns);
108        self::assertSame($definition->candidateKeys()->keys(), $partition->candidateKeys()->keys());
109    }
110
111    public function testWithPartialUniqueIndexPreservesSchemaAndIndexesByName(): void
112    {
113        $definition = new TableDefinition(['email', 'status'], ['email' => 'TEXT', 'status' => 'TEXT'], [], [], []);
114        $index = new PartialUniqueIndex('users_active_email', ['email'], "status = 'active'");
115
116        $indexed = $definition->withPartialUniqueIndex($index);
117
118        self::assertNotSame($definition, $indexed);
119        self::assertSame([], $definition->partialUniqueIndexes);
120        self::assertSame($index, $indexed->partialUniqueIndexes['users_active_email']);
121        self::assertSame($definition->columns, $indexed->columns);
122    }
123
124    public function testWithPartitionKeyAnswersATableDividedByThatKey(): void
125    {
126        $definition = new TableDefinition(['id'], [], ['id'], [], []);
127        $key = new TablePartitionKey(TablePartitionStrategy::Range, ['id']);
128
129        $divided = $definition->withPartitionKey($key);
130
131        self::assertSame($key, $divided->partitionKey);
132        self::assertNull($definition->partitionKey);
133    }
134
135    public function testWithPartitionRelationAnswersATableThatIsPartOfAnother(): void
136    {
137        $definition = new TableDefinition(['id'], [], ['id'], [], []);
138        $relation = new TablePartitionRelation('parent', 'id < 10');
139
140        $part = $definition->withPartitionRelation($relation);
141
142        self::assertSame($relation, $part->partitionRelation);
143        self::assertNull($definition->partitionRelation);
144    }
145}
146