packages/sql-faker/tests/Unit/Generation/Plan/ProductionPatternTest.php
1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Unit\SqlFaker\Generation\Plan;
6
7use PHPUnit\Framework\Attributes\CoversClass;
8use PHPUnit\Framework\TestCase;
9use SqlFaker\Generation\Plan\ProductionPattern;
10
11#[CoversClass(ProductionPattern::class)]
12#[\PHPUnit\Framework\Attributes\UsesClass(\SqlFaker\Generation\Derivation\CompletionCosts::class)]
13#[\PHPUnit\Framework\Attributes\UsesClass(\SqlFaker\Generation\Derivation\DerivationNode::class)]
14final class ProductionPatternTest extends TestCase
15{
16 public function testContainingRequiresEveryNamedSymbol(): void
17 {
18 $pattern = ProductionPattern::containing('FOREIGN', 'KEY');
19
20 self::assertTrue($pattern->matches(['CONSTRAINT', 'FOREIGN', 'KEY', 'REFERENCES']));
21 self::assertFalse($pattern->matches(['CONSTRAINT', 'FOREIGN', 'REFERENCES']));
22 }
23
24 public function testExactlyMatchesOrderAndCardinality(): void
25 {
26 $pattern = ProductionPattern::exactly(first: 'CONSTRAINT', second: 'name');
27
28 self::assertTrue($pattern->matches(['CONSTRAINT', 'name']));
29 self::assertFalse($pattern->matches(['name', 'CONSTRAINT']));
30 self::assertFalse($pattern->matches(['CONSTRAINT', 'name', 'FOREIGN']));
31 }
32
33 public function testExactlyCanSelectAnEmptyProduction(): void
34 {
35 $pattern = ProductionPattern::exactly();
36
37 self::assertTrue($pattern->matches([]));
38 self::assertFalse($pattern->matches(['COMMA']));
39 }
40
41 public function testNonEmptyRejectsOnlyAnEmptyProduction(): void
42 {
43 $pattern = ProductionPattern::nonEmpty();
44
45 self::assertTrue($pattern->matches(['name']));
46 self::assertFalse($pattern->matches([]));
47 }
48
49 public function testMatchesAcceptsAnAlternativeContainingEveryNamedSymbol(): void
50 {
51 self::assertTrue(ProductionPattern::containing('FOREIGN', 'KEY')->matches(['CONSTRAINT', 'FOREIGN', 'KEY']));
52 }
53
54 public function testMatchesRejectsAnAlternativeMissingANamedSymbol(): void
55 {
56 self::assertFalse(ProductionPattern::containing('FOREIGN', 'KEY')->matches(['FOREIGN']));
57 }
58
59 public function testMatchesAcceptsOnlyTheAlternativeWrittenExactly(): void
60 {
61 self::assertTrue(ProductionPattern::exactly('cmdlist', 'ecmd')->matches(['cmdlist', 'ecmd']));
62 self::assertFalse(ProductionPattern::exactly('cmdlist', 'ecmd')->matches(['ecmd', 'cmdlist']));
63 }
64
65 public function testMatchesRefusesOnlyTheEmptyAlternative(): void
66 {
67 self::assertTrue(ProductionPattern::nonEmpty()->matches(['ecmd']));
68 self::assertFalse(ProductionPattern::nonEmpty()->matches([]));
69 }
70 public function testAtSelectsAnOrdinalIndependentlyOfItsSymbols(): void
71 {
72 $pattern = ProductionPattern::at(2);
73 self::assertTrue($pattern->matches(['T'], 2));
74 self::assertFalse($pattern->matches(['T'], 1));
75 self::assertFalse($pattern->matches(['T']));
76 }
77}
78