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)]
52final class GenerationRunTest extends TestCase
53{
54    #[Test]
55    public function testClaimingATableStopsASecondWalkStarting(): void
56    {
57        $run = new GenerationRun([]);
58        $run->claim(['order', 'customer']);
59
60        self::assertTrue($run->hasVisited('order'));
61        self::assertTrue($run->hasVisited('customer'));
62        self::assertFalse($run->hasVisited('product'));
63    }
64
65    #[Test]
66    public function testReachedReachingATableAsAListSticks(): void
67    {
68        $run = new GenerationRun([]);
69        $run->claim(['order_detail']);
70        $run->reached('order_detail', true);
71        $run->reached('order_detail', false);
72
73        $plan = FixturePlan::table('order_detail');
74        self::assertSame([], $run->toSet($plan)['order_detail']);
75    }
76
77    #[Test]
78    public function testToSetATableReachedOnlyAsASingleRowReadsBackAsOne(): void
79    {
80        $schema = new TableSchema('order', [
81            'id' => new ColumnDefinition('id', 'INT', autoIncrement: true),
82            'status' => new ColumnDefinition('status', 'VARCHAR', length: 20),
83        ], ['id']);
84        $run = new GenerationRun([]);
85        $run->reached('order', false);
86        $run->record($schema, ['status' => 'paid']);
87
88        self::assertSame(
89            ['status' => 'paid'],
90            $run->toSet(FixturePlan::table('order'))['order']
91        );
92    }
93
94    #[Test]
95    public function testAKeyNothingReadsIsLeftToTheDatabase(): void
96    {
97        $schema = new TableSchema('order', [
98            'id' => new ColumnDefinition('id', 'INT', autoIncrement: true),
99            'status' => new ColumnDefinition('status', 'VARCHAR', length: 20),
100        ], ['id']);
101        $run = new GenerationRun([]);
102
103        self::assertArrayNotHasKey('id', $run->record($schema, ['status' => 'paid']));
104    }
105
106    #[Test]
107    public function testAKeyARelationReadsIsStoodInFor(): void
108    {
109        $schema = new TableSchema('order', [
110            'id' => new ColumnDefinition('id', 'INT', autoIncrement: true),
111            'status' => new ColumnDefinition('status', 'VARCHAR', length: 20),
112        ], ['id']);
113        $run = new GenerationRun([]);
114
115        self::assertSame(1, $run->record($schema, [], ['id'])['id']);
116        self::assertSame(2, $run->record($schema, [], ['id'])['id']);
117    }
118
119    #[Test]
120    public function testRecordKeepsAKeyTheCallerSupplied(): void
121    {
122        $schema = new TableSchema('order', [
123            'id' => new ColumnDefinition('id', 'INT', autoIncrement: true),
124            'status' => new ColumnDefinition('status', 'VARCHAR', length: 20),
125        ], ['id']);
126        $run = new GenerationRun([]);
127
128        self::assertSame(100, $run->record($schema, ['id' => 100], ['id'])['id']);
129    }
130
131    #[Test]
132    public function testOnlyAutoIncrementColumnsAreStoodInFor(): void
133    {
134        $schema = new TableSchema('order', [
135            'id' => new ColumnDefinition('id', 'INT', autoIncrement: true),
136            'status' => new ColumnDefinition('status', 'VARCHAR', length: 20),
137        ], ['id']);
138        $run = new GenerationRun([]);
139
140        self::assertArrayNotHasKey('status', $run->record($schema, [], ['status']));
141    }
142
143    #[Test]
144    public function testWasAskedForReportsWhatTheCallerMentioned(): void
145    {
146        $run = new GenerationRun(['order' => RowSpec::from('order', 2)]);
147
148        self::assertTrue($run->wasAskedFor('order'));
149        self::assertFalse($run->wasAskedFor('customer'));
150    }
151
152    #[Test]
153    public function testSpecForFallsBackToUnspecified(): void
154    {
155        $run = new GenerationRun(['order' => RowSpec::from('order', 2)]);
156
157        self::assertSame(2, $run->specFor('order')->count);
158        self::assertNull($run->specFor('customer')->count);
159    }
160
161    #[Test]
162    public function testATableThatGeneratedNothingReadsBackAsNull(): void
163    {
164        $run = new GenerationRun([]);
165
166        self::assertNull($run->toSet(FixturePlan::table('order'))['order']);
167    }
168
169    #[Test]
170    public function testClaimingDoesNotUndoWhatTheWalkAlreadyLearnt(): void
171    {
172        $schema = new TableSchema('order', [
173            'id' => new ColumnDefinition('id', 'INT', autoIncrement: true),
174            'status' => new ColumnDefinition('status', 'VARCHAR', length: 20),
175        ], ['id']);
176        $run = new GenerationRun([]);
177        $run->reached('order', true);
178        $run->claim(['order']);
179        $run->record($schema, ['status' => 'paid']);
180
181        self::assertSame(
182            [['status' => 'paid']],
183            $run->toSet(FixturePlan::table('order'))['order']
184        );
185    }
186
187    #[Test]
188    public function testAClaimedTableThatWasNeverReachedIsNotAList(): void
189    {
190        $run = new GenerationRun([]);
191        $run->claim(['order']);
192
193        self::assertNull($run->toSet(FixturePlan::table('order'))['order']);
194    }
195
196    #[Test]
197    public function testEveryReferencedColumnIsConsideredNotJustTheFirst(): void
198    {
199        $schema = new TableSchema('order', [
200            'id' => new ColumnDefinition('id', 'INT', autoIncrement: true),
201            'status' => new ColumnDefinition('status', 'VARCHAR', length: 20),
202        ], ['id']);
203        $run = new GenerationRun([]);
204
205        $row = $run->record($schema, [], ['status', 'id']);
206
207        self::assertSame(1, $row['id']);
208    }
209
210    #[Test]
211    public function testRecordReturnsTheWholeRow(): void
212    {
213        $schema = new TableSchema('order', [
214            'id' => new ColumnDefinition('id', 'INT', autoIncrement: true),
215            'status' => new ColumnDefinition('status', 'VARCHAR', length: 20),
216        ], ['id']);
217        $run = new GenerationRun([]);
218
219        self::assertSame(
220            ['status' => 'paid', 'id' => 1],
221            $run->record($schema, ['status' => 'paid'], ['id'])
222        );
223    }
224    public function testLastRowReturnsTheMostRecentRecordedValues(): void
225    {
226        $schema = new TableSchema('order', [
227            'id' => new ColumnDefinition('id', 'INT', autoIncrement: true),
228            'status' => new ColumnDefinition('status', 'VARCHAR', length: 20),
229        ], ['id']);
230        $run = new GenerationRun([]);
231        $run->record($schema, ['status' => 'new']);
232        $run->record($schema, ['status' => 'paid']);
233        self::assertSame(['status' => 'paid'], $run->lastRow('order'));
234        self::assertSame([], $run->lastRow('unknown'));
235    }
236    public function testHasVisitedDistinguishesNewAndClaimedTables(): void
237    {
238        $run = new GenerationRun([]);
239        self::assertFalse($run->hasVisited('order'));
240        $run->claim(['order']);
241        self::assertTrue($run->hasVisited('order'));
242    }
243}
244