packages/sql-fixture/tests/Unit/Fixture/GenerationRunTest.php

1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Unit\Fixture;
6
7use PHPUnit\Framework\Attributes\CoversClass;
8use PHPUnit\Framework\Attributes\Test;
9use PHPUnit\Framework\Attributes\UsesClass;
10use PHPUnit\Framework\TestCase;
11use SqlFixture\Fixture\GenerationRun;
12use SqlFixture\Fixture\RowSpec;
13use SqlFixture\Plan\FixturePlan;
14use SqlFixture\Schema\ColumnDefinition;
15use SqlFixture\Schema\TableSchema;
16
17#[CoversClass(GenerationRun::class)]
18#[UsesClass(RowSpec::class)]
19#[UsesClass(FixturePlan::class)]
20#[UsesClass(\SqlFixture\Fixture\FixtureSet::class)]
21#[UsesClass(TableSchema::class)]
22#[UsesClass(ColumnDefinition::class)]
23#[UsesClass(\SqlFixture\Fixture\OverrideRows::class)]
24#[UsesClass(\SqlFixture\Fixture\TableOverrides::class)]
25#[UsesClass(\SqlFixture\Plan\ColumnRef::class)]
26#[UsesClass(\SqlFixture\Plan\PlanParser::class)]
27#[UsesClass(\SqlFixture\Plan\PlanPrinter::class)]
28#[UsesClass(\SqlFixture\Plan\PlanSyntaxException::class)]
29#[UsesClass(\SqlFixture\Plan\Relation::class)]
30#[UsesClass(\SqlFixture\Plan\RelationKind::class)]
31#[UsesClass(\SqlFixture\Plan\RelationSide::class)]
32#[UsesClass(\SqlFixture\Plan\Parsing\PlanStatements::class)]
33#[UsesClass(\SqlFixture\Plan\Parsing\RelationCursor::class)]
34#[UsesClass(\SqlFixture\Plan\Parsing\RelationReader::class)]
35#[UsesClass(\SqlFixture\Plan\PlanStructureException::class)]
36#[UsesClass(\SqlFixture\Plan\Printing\PlanTables::class)]
37#[UsesClass(\SqlFixture\Plan\Printing\RelationGroups::class)]
38#[UsesClass(\SqlFixture\Plan\Printing\StatementPrinter::class)]
39#[UsesClass(\SqlFixture\Plan\Validation\PlanValidation::class)]
40#[UsesClass(\SqlFixture\Plan\Validation\TableName::class)]
41#[UsesClass(\SqlFixture\Plan\Exception\EmptyPlanException::class)]
42#[UsesClass(\SqlFixture\Plan\Exception\EmptyTableNameException::class)]
43#[UsesClass(\SqlFixture\Plan\Exception\MissingEndpointColumnsException::class)]
44#[UsesClass(\SqlFixture\Plan\Exception\InvalidTableNameException::class)]
45#[UsesClass(\SqlFixture\Plan\Exception\UnbalancedBracketsException::class)]
46#[UsesClass(\SqlFixture\Plan\Exception\UnexpectedPlanTokenException::class)]
47#[UsesClass(\SqlFixture\Plan\Exception\UnsupportedManyToManyException::class)]
48#[UsesClass(\SqlFixture\Plan\Exception\CompositeArityMismatchException::class)]
49#[UsesClass(\SqlFixture\Plan\Exception\DuplicateColumnBindingException::class)]
50#[UsesClass(\SqlFixture\Plan\Exception\CyclicDependencyException::class)]
51#[UsesClass(\SqlFixture\Plan\Exception\UnboundedSelfReferenceException::class)]
52#[UsesClass(\SqlFixture\Plan\Choice\ChoiceValidation::class)]
53#[UsesClass(\SqlFixture\Plan\Choice\PlanContents::class)]
54final class GenerationRunTest extends TestCase
55{
56    #[Test]
57    public function testClaimingATableStopsASecondWalkStarting(): void
58    {
59        $run = new GenerationRun([]);
60        $run->claim(['order', 'customer']);
61
62        self::assertTrue($run->hasVisited('order'));
63        self::assertTrue($run->hasVisited('customer'));
64        self::assertFalse($run->hasVisited('product'));
65    }
66
67    #[Test]
68    public function testReachedReachingATableAsAListSticks(): void
69    {
70        $run = new GenerationRun([]);
71        $run->claim(['order_detail']);
72        $run->reached('order_detail', true);
73        $run->reached('order_detail', false);
74
75        $plan = FixturePlan::table('order_detail');
76        self::assertSame([], $run->toSet($plan)['order_detail']);
77    }
78
79    #[Test]
80    public function testToSetATableReachedOnlyAsASingleRowReadsBackAsOne(): void
81    {
82        $schema = new TableSchema('order', [
83            'id' => new ColumnDefinition('id', 'INT', autoIncrement: true),
84            'status' => new ColumnDefinition('status', 'VARCHAR', length: 20),
85        ], ['id']);
86        $run = new GenerationRun([]);
87        $run->reached('order', false);
88        $run->record($schema, ['status' => 'paid']);
89
90        self::assertSame(
91            ['status' => 'paid'],
92            $run->toSet(FixturePlan::table('order'))['order']
93        );
94    }
95
96    #[Test]
97    public function testAKeyNothingReadsIsLeftToTheDatabase(): void
98    {
99        $schema = new TableSchema('order', [
100            'id' => new ColumnDefinition('id', 'INT', autoIncrement: true),
101            'status' => new ColumnDefinition('status', 'VARCHAR', length: 20),
102        ], ['id']);
103        $run = new GenerationRun([]);
104
105        self::assertArrayNotHasKey('id', $run->record($schema, ['status' => 'paid']));
106    }
107
108    #[Test]
109    public function testAKeyARelationReadsIsStoodInFor(): void
110    {
111        $schema = new TableSchema('order', [
112            'id' => new ColumnDefinition('id', 'INT', autoIncrement: true),
113            'status' => new ColumnDefinition('status', 'VARCHAR', length: 20),
114        ], ['id']);
115        $run = new GenerationRun([]);
116
117        self::assertSame(1, $run->record($schema, [], ['id'])['id']);
118        self::assertSame(2, $run->record($schema, [], ['id'])['id']);
119    }
120
121    #[Test]
122    public function testRecordKeepsAKeyTheCallerSupplied(): void
123    {
124        $schema = new TableSchema('order', [
125            'id' => new ColumnDefinition('id', 'INT', autoIncrement: true),
126            'status' => new ColumnDefinition('status', 'VARCHAR', length: 20),
127        ], ['id']);
128        $run = new GenerationRun([]);
129
130        self::assertSame(100, $run->record($schema, ['id' => 100], ['id'])['id']);
131    }
132
133    #[Test]
134    public function testOnlyAutoIncrementColumnsAreStoodInFor(): void
135    {
136        $schema = new TableSchema('order', [
137            'id' => new ColumnDefinition('id', 'INT', autoIncrement: true),
138            'status' => new ColumnDefinition('status', 'VARCHAR', length: 20),
139        ], ['id']);
140        $run = new GenerationRun([]);
141
142        self::assertArrayNotHasKey('status', $run->record($schema, [], ['status']));
143    }
144
145    #[Test]
146    public function testWasAskedForReportsWhatTheCallerMentioned(): void
147    {
148        $run = new GenerationRun(['order' => RowSpec::from('order', 2)]);
149
150        self::assertTrue($run->wasAskedFor('order'));
151        self::assertFalse($run->wasAskedFor('customer'));
152    }
153
154    #[Test]
155    public function testSpecForFallsBackToUnspecified(): void
156    {
157        $run = new GenerationRun(['order' => RowSpec::from('order', 2)]);
158
159        self::assertSame(2, $run->specFor('order')->count);
160        self::assertNull($run->specFor('customer')->count);
161    }
162
163    #[Test]
164    public function testATableThatGeneratedNothingReadsBackAsNull(): void
165    {
166        $run = new GenerationRun([]);
167
168        self::assertNull($run->toSet(FixturePlan::table('order'))['order']);
169    }
170
171    #[Test]
172    public function testClaimingDoesNotUndoWhatTheWalkAlreadyLearnt(): void
173    {
174        $schema = new TableSchema('order', [
175            'id' => new ColumnDefinition('id', 'INT', autoIncrement: true),
176            'status' => new ColumnDefinition('status', 'VARCHAR', length: 20),
177        ], ['id']);
178        $run = new GenerationRun([]);
179        $run->reached('order', true);
180        $run->claim(['order']);
181        $run->record($schema, ['status' => 'paid']);
182
183        self::assertSame(
184            [['status' => 'paid']],
185            $run->toSet(FixturePlan::table('order'))['order']
186        );
187    }
188
189    #[Test]
190    public function testAClaimedTableThatWasNeverReachedIsNotAList(): void
191    {
192        $run = new GenerationRun([]);
193        $run->claim(['order']);
194
195        self::assertNull($run->toSet(FixturePlan::table('order'))['order']);
196    }
197
198    #[Test]
199    public function testEveryReferencedColumnIsConsideredNotJustTheFirst(): void
200    {
201        $schema = new TableSchema('order', [
202            'id' => new ColumnDefinition('id', 'INT', autoIncrement: true),
203            'status' => new ColumnDefinition('status', 'VARCHAR', length: 20),
204        ], ['id']);
205        $run = new GenerationRun([]);
206
207        $row = $run->record($schema, [], ['status', 'id']);
208
209        self::assertSame(1, $row['id']);
210    }
211
212    #[Test]
213    public function testRecordReturnsTheWholeRow(): void
214    {
215        $schema = new TableSchema('order', [
216            'id' => new ColumnDefinition('id', 'INT', autoIncrement: true),
217            'status' => new ColumnDefinition('status', 'VARCHAR', length: 20),
218        ], ['id']);
219        $run = new GenerationRun([]);
220
221        self::assertSame(
222            ['status' => 'paid', 'id' => 1],
223            $run->record($schema, ['status' => 'paid'], ['id'])
224        );
225    }
226    public function testLastRowReturnsTheMostRecentRecordedValues(): void
227    {
228        $schema = new TableSchema('order', [
229            'id' => new ColumnDefinition('id', 'INT', autoIncrement: true),
230            'status' => new ColumnDefinition('status', 'VARCHAR', length: 20),
231        ], ['id']);
232        $run = new GenerationRun([]);
233        $run->record($schema, ['status' => 'new']);
234        $run->record($schema, ['status' => 'paid']);
235        self::assertSame(['status' => 'paid'], $run->lastRow('order'));
236        self::assertSame([], $run->lastRow('unknown'));
237    }
238    public function testHasVisitedDistinguishesNewAndClaimedTables(): void
239    {
240        $run = new GenerationRun([]);
241        self::assertFalse($run->hasVisited('order'));
242        $run->claim(['order']);
243        self::assertTrue($run->hasVisited('order'));
244    }
245}
246