packages/sql-faker/tests/Unit/Generation/Plan/LexemeConstraintTest.php

1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Unit\Generation\Plan;
6
7use PHPUnit\Framework\Attributes\CoversClass;
8use PHPUnit\Framework\Attributes\UsesClass;
9use PHPUnit\Framework\TestCase;
10use SqlFaker\Generation\Plan\LexemeConstraint;
11use SqlFaker\Generation\Value\IntegerDomain;
12
13#[CoversClass(LexemeConstraint::class)]
14#[UsesClass(IntegerDomain::class)]
15final class LexemeConstraintTest extends TestCase
16{
17    public function testOneOfChoicesRemainReachableAndIntersectionRemovesOnlyForbiddenSpellings(): void
18    {
19        $names = LexemeConstraint::oneOf('users', 'orders', 'users');
20        self::assertSame('users', $names->choose(static fn (int $count): int => 0));
21        self::assertSame('orders', $names->choose(static fn (int $count): int => $count - 1));
22        $intersection = $names->intersect(LexemeConstraint::oneOf('orders', 'products'));
23        self::assertSame('orders', $intersection->choose(static fn (int $count): int => 0));
24        self::assertFalse($intersection->accepts('users'));
25        self::assertTrue($names->accepts('users'));
26    }
27
28    public function testIntegersBoundsAndFiniteSetsIntersectWithoutSampling(): void
29    {
30        $range = LexemeConstraint::integers(10, 99)->intersect(LexemeConstraint::integers(20, 30));
31        self::assertSame('20', $range->choose(static fn (int $count): int => 0));
32        self::assertSame('30', $range->choose(static fn (int $count): int => $count - 1));
33        self::assertFalse($range->accepts('31'));
34        self::assertFalse($range->accepts("'20'"));
35        $finite = $range->intersect(LexemeConstraint::oneOf('19', '20', '31'));
36        self::assertSame('20', $finite->choose(static fn (int $count): int => 0));
37    }
38
39
40    public function testChooseRetainsTheWholeIntegerIntervalIncludingItsBounds(): void
41    {
42        $range = LexemeConstraint::integers(0, PHP_INT_MAX);
43        self::assertSame('0', $range->choose(static fn (int $count): int => 0));
44        self::assertSame((string) PHP_INT_MAX, $range->choose(static fn (int $count): int => $count - 1));
45    }
46
47    public function testAcceptsUsesLexicalSpellingsRatherThanNumericCoercion(): void
48    {
49        $range = LexemeConstraint::integers(10, 20);
50        self::assertTrue($range->accepts('10'));
51        self::assertTrue($range->accepts('20'));
52        self::assertFalse($range->accepts('9'));
53        self::assertFalse($range->accepts('21'));
54        self::assertFalse($range->accepts('-10'));
55        self::assertFalse($range->accepts('+10'));
56        self::assertFalse($range->accepts("'10'"));
57        self::assertFalse($range->accepts('1e1'));
58        self::assertFalse($range->accepts('10.0'));
59        self::assertFalse($range->accepts(' 10'));
60
61    }
62
63    public function testIntersectIsIndependentOfWhichSideHoldsTheFiniteSet(): void
64    {
65        $range = LexemeConstraint::integers(10, 20);
66        $set = LexemeConstraint::oneOf('9', '10', '15', '21');
67        self::assertEquals($range->intersect($set), $set->intersect($range));
68        self::assertFalse($range->intersect($set)->accepts('11'));
69        self::assertTrue($range->intersect($set)->accepts('15'));
70    }
71
72
73    public function testOneOfDeduplicatesValuesWithoutLeavingChoiceIndexGaps(): void
74    {
75        $names = LexemeConstraint::oneOf('users', 'users', 'orders');
76        self::assertSame('orders', $names->choose(static fn (int $count): int => $count - 1));
77    }
78
79    public function testIntegersSupportsSingletonIntervalsIncludingZero(): void
80    {
81        $zero = LexemeConstraint::integers(0, 0);
82        self::assertSame('0', $zero->choose(static fn (int $count): int => $count - 1));
83        self::assertTrue($zero->accepts('0'));
84        self::assertSame('42', LexemeConstraint::integers(42, 42)->choose(static fn (int $count): int => 0));
85    }
86
87    public function testChooseDefaultIntegerDomainIncludesZero(): void
88    {
89        $domain = new LexemeConstraint([]);
90        self::assertSame('0', $domain->choose(static fn (int $count): int => 0));
91        self::assertSame((string) PHP_INT_MAX, $domain->choose(static fn (int $count): int => $count - 1));
92    }
93
94}
95