packages/sql-fixture/tests/Unit/Plan/PlanSyntaxExceptionTest.php
1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Unit\Plan;
6
7use PHPUnit\Framework\Attributes\CoversClass;
8use PHPUnit\Framework\Attributes\Test;
9use PHPUnit\Framework\Attributes\UsesClass;
10use PHPUnit\Framework\TestCase;
11use SqlFixture\Plan\ColumnRef;
12use SqlFixture\Plan\PlanSyntaxException;
13
14#[CoversClass(PlanSyntaxException::class)]
15#[UsesClass(ColumnRef::class)]
16#[UsesClass(\SqlFixture\Plan\FixturePlan::class)]
17#[UsesClass(\SqlFixture\Plan\PlanParser::class)]
18#[UsesClass(\SqlFixture\Plan\PlanPrinter::class)]
19#[UsesClass(\SqlFixture\Plan\Relation::class)]
20#[UsesClass(\SqlFixture\Plan\RelationKind::class)]
21#[UsesClass(\SqlFixture\Plan\RelationSide::class)]
22#[UsesClass(\SqlFixture\Plan\Parsing\PlanStatements::class)]
23#[UsesClass(\SqlFixture\Plan\Parsing\RelationCursor::class)]
24#[UsesClass(\SqlFixture\Plan\Parsing\RelationReader::class)]
25#[UsesClass(\SqlFixture\Plan\PlanStructureException::class)]
26#[UsesClass(\SqlFixture\Plan\Printing\PlanTables::class)]
27#[UsesClass(\SqlFixture\Plan\Printing\RelationGroups::class)]
28#[UsesClass(\SqlFixture\Plan\Printing\StatementPrinter::class)]
29#[UsesClass(\SqlFixture\Plan\Validation\PlanValidation::class)]
30#[UsesClass(\SqlFixture\Plan\Validation\TableName::class)]
31#[UsesClass(\SqlFixture\Plan\Exception\EmptyPlanException::class)]
32#[UsesClass(\SqlFixture\Plan\Exception\EmptyTableNameException::class)]
33#[UsesClass(\SqlFixture\Plan\Exception\MissingEndpointColumnsException::class)]
34#[UsesClass(\SqlFixture\Plan\Exception\InvalidTableNameException::class)]
35#[UsesClass(\SqlFixture\Plan\Exception\UnbalancedBracketsException::class)]
36#[UsesClass(\SqlFixture\Plan\Exception\UnexpectedPlanTokenException::class)]
37#[UsesClass(\SqlFixture\Plan\Exception\UnsupportedManyToManyException::class)]
38#[UsesClass(\SqlFixture\Plan\Exception\CompositeArityMismatchException::class)]
39#[UsesClass(\SqlFixture\Plan\Choice\ChoiceValidation::class)]
40#[UsesClass(\SqlFixture\Plan\Choice\PlanContents::class)]
41final class PlanSyntaxExceptionTest extends TestCase
42{
43 #[Test]
44 public function testEmptyPlanAsksForATable(): void
45 {
46 self::assertSame(
47 'A fixture plan must name at least one table.',
48 (new \SqlFixture\Plan\Exception\EmptyPlanException())->getMessage()
49 );
50 }
51
52 #[Test]
53 public function testEmptyTableNameAsksForATable(): void
54 {
55 self::assertSame(
56 'A relation endpoint must name a table.',
57 (new \SqlFixture\Plan\Exception\EmptyTableNameException())->getMessage()
58 );
59 }
60
61 #[Test]
62 public function testNoColumnsNamesTheTable(): void
63 {
64 self::assertSame(
65 'The endpoint for table order names no columns.',
66 (new \SqlFixture\Plan\Exception\MissingEndpointColumnsException('order'))->getMessage()
67 );
68 }
69
70 #[Test]
71 public function testUnexpectedReportsTheOffsetAndWhatWasWanted(): void
72 {
73 $message = (new \SqlFixture\Plan\Exception\UnexpectedPlanTokenException('order.id ! x.y', 9, "one of '<', '>' or '-'"))->getMessage();
74
75 self::assertSame(
76 "Cannot parse the fixture plan at offset 9: expected one of '<', '>' or '-'. "
77 . 'Plan: order.id ! x.y',
78 $message
79 );
80 }
81
82 #[Test]
83 public function testManyToManyUnsupportedManyToManyPointsAtTheExplicitForm(): void
84 {
85 $message = (new \SqlFixture\Plan\Exception\UnsupportedManyToManyException('order.id <> product.id'))->getMessage();
86
87 self::assertSame(
88 'The <> operator is not supported, because a fixture has to put rows in the '
89 . 'junction table and so must name it. Write the two halves instead, for example '
90 . '"order.id < order_detail.order_id, order_detail.product_id > product.id". '
91 . 'Plan: order.id <> product.id',
92 $message
93 );
94 }
95
96 #[Test]
97 public function testCompositeArityMismatchReportsBothCounts(): void
98 {
99 $message = (new \SqlFixture\Plan\Exception\CompositeArityMismatchException(
100 ColumnRef::of('order', 'shop_id', 'no'),
101 ColumnRef::of('order_detail', 'order_no')
102 ))->getMessage();
103
104 self::assertSame(
105 'The relation order.(shop_id, no) ... order_detail.order_no names 2 columns on '
106 . 'one side and 1 on the other.',
107 $message
108 );
109 }
110
111 #[Test]
112 public function testNotATableNamePointsAtFrom(): void
113 {
114 $message = (new \SqlFixture\Plan\Exception\InvalidTableNameException('order.id < order_detail.order_id'))->getMessage();
115
116 self::assertSame(
117 'A FixturePlan part must be a Relation or a plain table name, but '
118 . '"order.id < order_detail.order_id" is neither. To build a plan from relation '
119 . 'syntax, use FixturePlan::from().',
120 $message
121 );
122 }
123
124 #[Test]
125 public function testUnbalancedBracketsNamesThePlan(): void
126 {
127 self::assertSame(
128 'The fixture plan closes a bracket it never opened. Plan: a.id < b.a_id]',
129 (new \SqlFixture\Plan\Exception\UnbalancedBracketsException('a.id < b.a_id]'))->getMessage()
130 );
131 }
132}
133