packages/ztd-query-sqlite/tests/Unit/Schema/Key/ForeignKeyEntryParserTest.php
1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Unit\Schema\Key;
6
7use PHPUnit\Framework\Attributes\CoversClass;
8use PHPUnit\Framework\Attributes\UsesClass;
9use PHPUnit\Framework\TestCase;
10use ZtdQuery\Platform\Sqlite\Schema\Key\ForeignKeyEntryParser;
11use ZtdQuery\Platform\Sqlite\Sql\SqliteLexerProfile;
12use ZtdQuery\Sql\SqlTokenStream;
13
14#[CoversClass(ForeignKeyEntryParser::class)]
15#[UsesClass(\ZtdQuery\Platform\Sqlite\Schema\Key\ForeignKeyTokens::class)]
16#[UsesClass(SqliteLexerProfile::class)]
17final class ForeignKeyEntryParserTest extends TestCase
18{
19 public function testParseEntryReadsNamedConstraintAndActions(): void
20 {
21 $stream = SqlTokenStream::tokenize('CONSTRAINT fk FOREIGN KEY(user_id) REFERENCES main.users(id) ON DELETE CASCADE ON UPDATE SET NULL', SqliteLexerProfile::create());
22 $entry = (new ForeignKeyEntryParser())->parseEntry($stream, 'fallback', null);
23 self::assertNotNull($entry);
24 self::assertSame('fk', $entry['name']);
25 self::assertSame(['user_id'], $entry['foreignKey']->columns);
26 self::assertSame('users', $entry['foreignKey']->referencedTable);
27 self::assertSame(['id'], $entry['foreignKey']->referencedColumns);
28 self::assertSame(\ZtdQuery\Schema\Key\ReferentialAction::Cascade, $entry['foreignKey']->onDelete);
29 self::assertSame(\ZtdQuery\Schema\Key\ReferentialAction::SetNull, $entry['foreignKey']->onUpdate);
30 }
31
32 public function testParseEntryAcceptsInlineReferencesAndRejectsMissingRelations(): void
33 {
34 $parser = new ForeignKeyEntryParser();
35 $inline = $parser->parseEntry(SqlTokenStream::tokenize('user_id INTEGER REFERENCES users', SqliteLexerProfile::create()), 'inline', 'user_id');
36 self::assertNotNull($inline);
37 self::assertSame(['user_id'], $inline['foreignKey']->columns);
38 self::assertNull($parser->parseEntry(SqlTokenStream::tokenize('user_id INTEGER', SqliteLexerProfile::create()), 'inline', 'user_id'));
39 self::assertNull($parser->parseEntry(SqlTokenStream::tokenize('user_id REFERENCES', SqliteLexerProfile::create()), 'inline', 'user_id'));
40 }
41
42 public function testForeignKeyColumnsExtractsLocalCompositeKey(): void
43 {
44 $stream = SqlTokenStream::tokenize('FOREIGN KEY(a, b) REFERENCES parent(a, b)', SqliteLexerProfile::create());
45 $parser = new ForeignKeyEntryParser();
46 self::assertSame(['a', 'b'], $parser->foreignKeyColumns($stream, $stream->significantTokens(), 7));
47 self::assertSame([], $parser->foreignKeyColumns($stream, [], 0));
48 }
49
50 public function testReferencedRelationUnqualifiesTableAndReadsColumns(): void
51 {
52 $stream = SqlTokenStream::tokenize('main.parent(a, b)', SqliteLexerProfile::create());
53 self::assertSame(['table' => 'parent', 'columns' => ['a', 'b']], (new ForeignKeyEntryParser())->referencedRelation($stream, $stream->significantTokens(), 0));
54 }
55
56 public function testIdentifierListRequiresAClosingGroup(): void
57 {
58 $parser = new ForeignKeyEntryParser();
59 $stream = SqlTokenStream::tokenize('(a, "b name")', SqliteLexerProfile::create());
60 self::assertSame(['a', 'b name'], $parser->identifierList($stream, $stream->significantTokens(), 0));
61 $incomplete = SqlTokenStream::tokenize('(a, b', SqliteLexerProfile::create());
62 self::assertSame([], $parser->identifierList($incomplete, $incomplete->significantTokens(), 0));
63 }
64
65 public function testActionDistinguishesSupportedReferentialActions(): void
66 {
67 $parser = new ForeignKeyEntryParser();
68 self::assertSame(\ZtdQuery\Schema\Key\ReferentialAction::Restrict, $parser->action(SqlTokenStream::tokenize('ON DELETE RESTRICT', SqliteLexerProfile::create())->significantTokens(), 'DELETE'));
69 self::assertSame(\ZtdQuery\Schema\Key\ReferentialAction::SetDefault, $parser->action(SqlTokenStream::tokenize('ON UPDATE SET DEFAULT', SqliteLexerProfile::create())->significantTokens(), 'UPDATE'));
70 self::assertSame(\ZtdQuery\Schema\Key\ReferentialAction::NoAction, $parser->action(SqlTokenStream::tokenize('ON UPDATE SET', SqliteLexerProfile::create())->significantTokens(), 'UPDATE'));
71 self::assertSame(\ZtdQuery\Schema\Key\ReferentialAction::NoAction, $parser->action([], 'DELETE'));
72 }
73
74}
75