packages/sql-fixture/tests/Unit/Plan/Parsing/RelationCursorTest.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\Parsing\RelationCursor as Subject;
10use SqlFixture\Plan\RelationKind;
11
12#[CoversClass(Subject::class)]
13#[\PHPUnit\Framework\Attributes\UsesClass(\SqlFixture\Plan\ColumnRef::class)]
14#[\PHPUnit\Framework\Attributes\UsesClass(\SqlFixture\Plan\FixturePlan::class)]
15#[\PHPUnit\Framework\Attributes\UsesClass(\SqlFixture\Plan\PlanParser::class)]
16#[\PHPUnit\Framework\Attributes\UsesClass(\SqlFixture\Plan\PlanPrinter::class)]
17#[\PHPUnit\Framework\Attributes\UsesClass(\SqlFixture\Plan\PlanSyntaxException::class)]
18#[\PHPUnit\Framework\Attributes\UsesClass(\SqlFixture\Plan\Relation::class)]
19#[\PHPUnit\Framework\Attributes\UsesClass(RelationKind::class)]
20#[\PHPUnit\Framework\Attributes\UsesClass(\SqlFixture\Plan\RelationSide::class)]
21#[\PHPUnit\Framework\Attributes\UsesClass(\SqlFixture\Plan\Parsing\PlanStatements::class)]
22#[\PHPUnit\Framework\Attributes\UsesClass(\SqlFixture\Plan\Parsing\RelationReader::class)]
23#[\PHPUnit\Framework\Attributes\UsesClass(\SqlFixture\Plan\PlanStructureException::class)]
24#[\PHPUnit\Framework\Attributes\UsesClass(\SqlFixture\Plan\Printing\PlanTables::class)]
25#[\PHPUnit\Framework\Attributes\UsesClass(\SqlFixture\Plan\Printing\RelationGroups::class)]
26#[\PHPUnit\Framework\Attributes\UsesClass(\SqlFixture\Plan\Printing\StatementPrinter::class)]
27#[\PHPUnit\Framework\Attributes\UsesClass(\SqlFixture\Plan\Validation\PlanValidation::class)]
28#[\PHPUnit\Framework\Attributes\UsesClass(\SqlFixture\Plan\Validation\TableName::class)]
29#[\PHPUnit\Framework\Attributes\UsesClass(\SqlFixture\Plan\Exception\EmptyPlanException::class)]
30#[\PHPUnit\Framework\Attributes\UsesClass(\SqlFixture\Plan\Exception\EmptyTableNameException::class)]
31#[\PHPUnit\Framework\Attributes\UsesClass(\SqlFixture\Plan\Exception\MissingEndpointColumnsException::class)]
32#[\PHPUnit\Framework\Attributes\UsesClass(\SqlFixture\Plan\Exception\InvalidTableNameException::class)]
33#[\PHPUnit\Framework\Attributes\UsesClass(\SqlFixture\Plan\Exception\UnbalancedBracketsException::class)]
34#[\PHPUnit\Framework\Attributes\UsesClass(\SqlFixture\Plan\Exception\UnexpectedPlanTokenException::class)]
35#[\PHPUnit\Framework\Attributes\UsesClass(\SqlFixture\Plan\Exception\UnsupportedManyToManyException::class)]
36#[\PHPUnit\Framework\Attributes\UsesClass(\SqlFixture\Plan\Exception\CompositeArityMismatchException::class)]
37#[\PHPUnit\Framework\Attributes\UsesClass(\SqlFixture\Plan\Exception\DuplicateColumnBindingException::class)]
38#[\PHPUnit\Framework\Attributes\UsesClass(\SqlFixture\Plan\Exception\CyclicDependencyException::class)]
39#[\PHPUnit\Framework\Attributes\UsesClass(\SqlFixture\Plan\Exception\UnboundedSelfReferenceException::class)]
40#[\PHPUnit\Framework\Attributes\UsesClass(\SqlFixture\Plan\Choice\ChoiceValidation::class)]
41#[\PHPUnit\Framework\Attributes\UsesClass(\SqlFixture\Plan\Choice\PlanContents::class)]
42final class RelationCursorTest extends TestCase
43{
44    public function testReadIdentifierConsumesQuotedNames(): void
45    {
46        $cursor = new Subject('`order items`.id');
47        self::assertSame('order items', $cursor->readIdentifier('table'));
48        self::assertSame('.', $cursor->peek());
49    }
50
51    public function testReadOperatorConsumesDirection(): void
52    {
53        $cursor = new Subject('>?');
54        self::assertSame(RelationKind::ManyToOne, $cursor->readOperator());
55        self::assertSame('?', $cursor->peek());
56    }
57
58    public function testReadOptionalMarkerLeavesUnmarkedInputAlone(): void
59    {
60        $cursor = new Subject('?a');
61        self::assertTrue($cursor->readOptionalMarker());
62        self::assertFalse($cursor->readOptionalMarker());
63        self::assertSame('a', $cursor->peek());
64    }
65
66    public function testSkipWhitespaceStopsBeforeIdentifier(): void
67    {
68        $cursor = new Subject(" \t\n        a");
69        $cursor->skipWhitespace();
70        self::assertSame('a', $cursor->peek());
71    }
72
73    public function testExpectEndAcceptsAnExhaustedStatement(): void
74    {
75        $cursor = new Subject('a');
76        self::assertSame('a', $cursor->readIdentifier('table'));
77        $cursor->expectEnd();
78        self::assertNull($cursor->peek());
79    }
80
81    public function testPeekDoesNotAdvance(): void
82    {
83        $cursor = new Subject('ab');
84        self::assertSame('a', $cursor->peek());
85        self::assertSame(0, $cursor->offset);
86    }
87}
88