packages/sql-fixture/tests/Unit/Plan/Parsing/RelationReaderTest.php
1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Unit\Plan\Parsing;
6
7use PHPUnit\Framework\Attributes\CoversClass;
8use PHPUnit\Framework\TestCase;
9use SqlFixture\Plan\ColumnRef;
10use SqlFixture\Plan\Parsing\RelationReader as Subject;
11use SqlFixture\Plan\Relation;
12use SqlFixture\Plan\RelationKind;
13
14#[CoversClass(Subject::class)]
15#[\PHPUnit\Framework\Attributes\UsesClass(ColumnRef::class)]
16#[\PHPUnit\Framework\Attributes\UsesClass(\SqlFixture\Plan\FixturePlan::class)]
17#[\PHPUnit\Framework\Attributes\UsesClass(\SqlFixture\Plan\Parsing\RelationCursor::class)]
18#[\PHPUnit\Framework\Attributes\UsesClass(\SqlFixture\Plan\PlanParser::class)]
19#[\PHPUnit\Framework\Attributes\UsesClass(\SqlFixture\Plan\PlanPrinter::class)]
20#[\PHPUnit\Framework\Attributes\UsesClass(\SqlFixture\Plan\PlanSyntaxException::class)]
21#[\PHPUnit\Framework\Attributes\UsesClass(Relation::class)]
22#[\PHPUnit\Framework\Attributes\UsesClass(RelationKind::class)]
23#[\PHPUnit\Framework\Attributes\UsesClass(\SqlFixture\Plan\RelationSide::class)]
24#[\PHPUnit\Framework\Attributes\UsesClass(\SqlFixture\Plan\Parsing\PlanStatements::class)]
25#[\PHPUnit\Framework\Attributes\UsesClass(\SqlFixture\Plan\PlanStructureException::class)]
26#[\PHPUnit\Framework\Attributes\UsesClass(\SqlFixture\Plan\Printing\PlanTables::class)]
27#[\PHPUnit\Framework\Attributes\UsesClass(\SqlFixture\Plan\Printing\RelationGroups::class)]
28#[\PHPUnit\Framework\Attributes\UsesClass(\SqlFixture\Plan\Printing\StatementPrinter::class)]
29#[\PHPUnit\Framework\Attributes\UsesClass(\SqlFixture\Plan\Validation\PlanValidation::class)]
30#[\PHPUnit\Framework\Attributes\UsesClass(\SqlFixture\Plan\Validation\TableName::class)]
31#[\PHPUnit\Framework\Attributes\UsesClass(\SqlFixture\Plan\Exception\EmptyPlanException::class)]
32#[\PHPUnit\Framework\Attributes\UsesClass(\SqlFixture\Plan\Exception\EmptyTableNameException::class)]
33#[\PHPUnit\Framework\Attributes\UsesClass(\SqlFixture\Plan\Exception\MissingEndpointColumnsException::class)]
34#[\PHPUnit\Framework\Attributes\UsesClass(\SqlFixture\Plan\Exception\InvalidTableNameException::class)]
35#[\PHPUnit\Framework\Attributes\UsesClass(\SqlFixture\Plan\Exception\UnbalancedBracketsException::class)]
36#[\PHPUnit\Framework\Attributes\UsesClass(\SqlFixture\Plan\Exception\UnexpectedPlanTokenException::class)]
37#[\PHPUnit\Framework\Attributes\UsesClass(\SqlFixture\Plan\Exception\UnsupportedManyToManyException::class)]
38#[\PHPUnit\Framework\Attributes\UsesClass(\SqlFixture\Plan\Exception\CompositeArityMismatchException::class)]
39#[\PHPUnit\Framework\Attributes\UsesClass(\SqlFixture\Plan\Exception\DuplicateColumnBindingException::class)]
40#[\PHPUnit\Framework\Attributes\UsesClass(\SqlFixture\Plan\Exception\CyclicDependencyException::class)]
41#[\PHPUnit\Framework\Attributes\UsesClass(\SqlFixture\Plan\Exception\UnboundedSelfReferenceException::class)]
42#[\PHPUnit\Framework\Attributes\UsesClass(\SqlFixture\Plan\Choice\ChoiceValidation::class)]
43#[\PHPUnit\Framework\Attributes\UsesClass(\SqlFixture\Plan\Choice\PlanContents::class)]
44final class RelationReaderTest extends TestCase
45{
46 public function testParseStatementExpandsGroupedTargets(): void
47 {
48 $reader = new Subject(new \SqlFixture\Plan\Parsing\RelationCursor('a.id < [b.a_id, c.a_id]'));
49 $relations = $reader->parseStatement();
50 self::assertIsArray($relations);
51 self::assertSame(['b', 'c'], array_map(static fn (Relation $relation): string => $relation->child()->table, $relations));
52 }
53
54 public function testReadTargetsReadsACompositeEndpoint(): void
55 {
56 $targets = (new Subject(new \SqlFixture\Plan\Parsing\RelationCursor('[b.(a_id, tenant), c.id]')))->readTargets();
57 self::assertSame(['a_id', 'tenant'], $targets[0]->columns);
58 self::assertSame('c', $targets[1]->table);
59 }
60
61 public function testReadEndpointPreservesQuotedIdentifiers(): void
62 {
63 $endpoint = (new Subject(new \SqlFixture\Plan\Parsing\RelationCursor('`orders`.`customer_id`')))->readEndpoint();
64 self::assertSame('orders', $endpoint->table);
65 self::assertSame(['customer_id'], $endpoint->columns);
66 }
67
68 public function testBuildRelationsCarriesOptionalityToEachTarget(): void
69 {
70 $relations = (new Subject(new \SqlFixture\Plan\Parsing\RelationCursor('')))->buildRelations(new ColumnRef('a', ['id']), RelationKind::OneToMany, [new ColumnRef('b', ['a_id']), new ColumnRef('c', ['a_id'])], false, true);
71 self::assertCount(2, $relations);
72 self::assertFalse($relations[0]->parentIsOptional());
73 self::assertTrue($relations[1]->childIsOptional());
74 }
75}
76