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