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)]
42final class RelationReaderTest extends TestCase
43{
44    public function testParseStatementExpandsGroupedTargets(): void
45    {
46        $reader = new Subject(new \SqlFixture\Plan\Parsing\RelationCursor('a.id < [b.a_id, c.a_id]'));
47        $relations = $reader->parseStatement();
48        self::assertIsArray($relations);
49        self::assertSame(['b', 'c'], array_map(static fn (Relation $relation): string => $relation->child()->table, $relations));
50    }
51
52    public function testReadTargetsReadsACompositeEndpoint(): void
53    {
54        $targets = (new Subject(new \SqlFixture\Plan\Parsing\RelationCursor('[b.(a_id, tenant), c.id]')))->readTargets();
55        self::assertSame(['a_id', 'tenant'], $targets[0]->columns);
56        self::assertSame('c', $targets[1]->table);
57    }
58
59    public function testReadEndpointPreservesQuotedIdentifiers(): void
60    {
61        $endpoint = (new Subject(new \SqlFixture\Plan\Parsing\RelationCursor('`orders`.`customer_id`')))->readEndpoint();
62        self::assertSame('orders', $endpoint->table);
63        self::assertSame(['customer_id'], $endpoint->columns);
64    }
65
66    public function testBuildRelationsCarriesOptionalityToEachTarget(): void
67    {
68        $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);
69        self::assertCount(2, $relations);
70        self::assertFalse($relations[0]->parentIsOptional());
71        self::assertTrue($relations[1]->childIsOptional());
72    }
73}
74