packages/ztd-query-sqlite/tests/Unit/Schema/Create/TableDefinitionBuilderTest.php
1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Unit\Schema\Create;
6
7use PHPUnit\Framework\Attributes\CoversClass;
8use PHPUnit\Framework\Attributes\UsesClass;
9use PHPUnit\Framework\TestCase;
10use ZtdQuery\Platform\Sqlite\Schema\Create\TableDefinitionBuilder;
11
12#[CoversClass(TableDefinitionBuilder::class)]
13#[UsesClass(\ZtdQuery\Platform\Sqlite\Sql\Dml\Expression\AssignmentColumnParser::class)]
14#[UsesClass(\ZtdQuery\Platform\Sqlite\Sql\Dml\Expression\AssignmentParser::class)]
15#[UsesClass(\ZtdQuery\Platform\Sqlite\Sql\Dml\Expression\ValueListParser::class)]
16#[UsesClass(\ZtdQuery\Platform\Sqlite\Sql\Dml\Insert\InsertClauseParser::class)]
17#[UsesClass(\ZtdQuery\Platform\Sqlite\Sql\Lexing\ExpressionSpan::class)]
18#[UsesClass(\ZtdQuery\Platform\Sqlite\Sql\Lexing\IdentifierDecoder::class)]
19#[UsesClass(\ZtdQuery\Platform\Sqlite\Sql\Lexing\LiteralMasker::class)]
20#[UsesClass(\ZtdQuery\Platform\Sqlite\Sql\Lexing\OpaqueSqlSpan::class)]
21#[UsesClass(\ZtdQuery\Platform\Sqlite\Sql\Lexing\QuotedSpan::class)]
22#[UsesClass(\ZtdQuery\Platform\Sqlite\Sql\Lexing\TopLevelKeywordScanner::class)]
23#[UsesClass(\ZtdQuery\Platform\Sqlite\Sql\Relation\RelationSourceParser::class)]
24#[UsesClass(\ZtdQuery\Platform\Sqlite\Sql\Statement\StatementClassifier::class)]
25#[UsesClass(\ZtdQuery\Platform\Sqlite\Sql\Statement\StatementStructure::class)]
26#[UsesClass(\ZtdQuery\Platform\Sqlite\Sql\Statement\TargetTableParser::class)]
27#[UsesClass(\ZtdQuery\Platform\Sqlite\Sql\Dml\Update\UpdateClauseParser::class)]
28#[UsesClass(\ZtdQuery\Platform\Sqlite\Schema\Create\ColumnDefinitionParser::class)]
29#[UsesClass(\ZtdQuery\Platform\Sqlite\Schema\Create\TableBodyParser::class)]
30#[UsesClass(\ZtdQuery\Platform\Sqlite\Schema\Key\ForeignKeyEntryParser::class)]
31#[UsesClass(\ZtdQuery\Platform\Sqlite\Schema\Key\ForeignKeyTokens::class)]
32#[UsesClass(\ZtdQuery\Platform\Sqlite\Schema\SqliteColumnTypeMapper::class)]
33#[UsesClass(\ZtdQuery\Platform\Sqlite\Schema\Key\SqliteForeignKeyDefinitionParser::class)]
34#[UsesClass(\ZtdQuery\Platform\Sqlite\Sql\SqliteLexerProfile::class)]
35#[UsesClass(\ZtdQuery\Platform\Sqlite\Sql\SqliteLexicalMasker::class)]
36#[UsesClass(\ZtdQuery\Platform\Sqlite\Sql\SqliteParser::class)]
37#[UsesClass(\ZtdQuery\Platform\Sqlite\Sql\Relation\SqliteSelectRelationParser::class)]
38final class TableDefinitionBuilderTest extends TestCase
39{
40 public function testAddDefinitionCollectsTypedColumnsAndPrimaryKeys(): void
41 {
42 $builder = new TableDefinitionBuilder();
43 $builder->addDefinition('id INTEGER PRIMARY KEY');
44 $builder->addDefinition('name TEXT NOT NULL');
45 $builder->addDefinition('');
46 $definition = $builder->build('CREATE TABLE users(id INTEGER PRIMARY KEY, name TEXT NOT NULL)');
47 self::assertNotNull($definition);
48 self::assertSame(['id', 'name'], $definition->columns);
49 self::assertSame(['id'], $definition->primaryKeys);
50 self::assertSame(['id', 'name'], $definition->notNullColumns);
51 self::assertSame(\ZtdQuery\Schema\Key\IdentityGenerationStrategy::MaxValue, $definition->identityStrategies['id']);
52 }
53
54 public function testAddConstraintPreservesCompositeAndUniqueKeys(): void
55 {
56 $builder = new TableDefinitionBuilder();
57 $builder->addDefinition('a INTEGER');
58 $builder->addDefinition('b INTEGER');
59 $builder->addConstraint('CONSTRAINT pk PRIMARY KEY(a, b)');
60 $builder->addConstraint('UNIQUE(b)');
61 $definition = $builder->build('CREATE TABLE t(a INTEGER, b INTEGER, PRIMARY KEY(a, b))');
62 self::assertNotNull($definition);
63 self::assertSame(['a', 'b'], $definition->primaryKeys);
64 self::assertSame(['unique_0' => ['b']], $definition->uniqueConstraints);
65 self::assertSame([], $definition->identityStrategies);
66 }
67
68 public function testAddColumnPreservesDefaultsAndGeneratedExpressions(): void
69 {
70 $builder = new TableDefinitionBuilder();
71 $builder->addColumn([
72 'name' => 'total', 'type' => 'INTEGER', 'notNull' => false, 'primaryKey' => false,
73 'unique' => true, 'default' => '0', 'generatedExpression' => '(2 + 3)',
74 ]);
75 $definition = $builder->build('CREATE TABLE t(total INTEGER)');
76 self::assertNotNull($definition);
77 self::assertSame(['total_UNIQUE' => ['total']], $definition->uniqueConstraints);
78 self::assertSame(['total' => '0'], $definition->columnDefaults);
79 self::assertSame(['total' => '(2 + 3)'], $definition->generatedExpressions);
80 }
81
82 public function testBuildRejectsMissingColumnsAndUnknownUniqueColumns(): void
83 {
84 $builder = new TableDefinitionBuilder();
85 self::assertNull($builder->build('CREATE TABLE t()'));
86 $builder->addDefinition('id INTEGER');
87 $builder->addConstraint('UNIQUE(missing)');
88 self::assertNull($builder->build('CREATE TABLE t(id INTEGER, UNIQUE(missing))'));
89 }
90
91 public function testBuildDisablesIdentityForWithoutRowid(): void
92 {
93 $builder = new TableDefinitionBuilder();
94 $builder->addDefinition('id INTEGER PRIMARY KEY');
95 self::assertSame([], $builder->build('CREATE TABLE t(id INTEGER PRIMARY KEY) WITHOUT ROWID')?->identityStrategies);
96 }
97
98}
99