packages/sql-catalog/tests/Unit/Core/Evaluation/ArrayTermTest.php

1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Unit\Core\Evaluation;
6
7use PHPUnit\Framework\Attributes\CoversClass;
8use PHPUnit\Framework\Attributes\UsesClass;
9use PHPUnit\Framework\TestCase;
10use SqlCatalog\Core\Evaluation\ArrayEntry;
11use SqlCatalog\Core\Evaluation\ArrayTerm;
12use SqlCatalog\Core\Evaluation\Domain;
13use SqlCatalog\Core\Evaluation\LiteralTerm;
14use SqlCatalog\Core\Evaluation\OpaqueTerm;
15use SqlCatalog\Core\Text\Origin;
16use SqlCatalog\Core\Text\TextHole;
17use SqlCatalog\Core\Text\TextPattern;
18use SqlCatalog\Core\Type\TypeShape;
19
20#[CoversClass(ArrayTerm::class)]
21#[UsesClass(ArrayEntry::class)]
22#[UsesClass(Domain::class)]
23#[UsesClass(LiteralTerm::class)]
24#[UsesClass(OpaqueTerm::class)]
25#[UsesClass(TextHole::class)]
26#[UsesClass(TextPattern::class)]
27#[UsesClass(TypeShape::class)]
28final class ArrayTermTest extends TestCase
29{
30    public function testPositionalKeepsUnkeyedAndIntegerKeyedElements(): void
31    {
32        $term = new ArrayTerm([
33            new ArrayEntry(null, Domain::literal('a')),
34            new ArrayEntry(Domain::literal(1), Domain::literal('b')),
35            new ArrayEntry(Domain::literal(':x'), Domain::literal('c')),
36        ]);
37        self::assertCount(2, $term->positional());
38        self::assertSame('a', $term->positional()[0]->soleLiteral()?->value);
39    }
40
41    public function testNamedKeepsStringKeyedElements(): void
42    {
43        $term = new ArrayTerm([
44            new ArrayEntry(Domain::literal(':x'), Domain::literal('c')),
45            new ArrayEntry(null, Domain::literal('a')),
46        ]);
47        self::assertSame([':x'], array_keys($term->named()));
48    }
49
50    public function testElementFindsAnElementByItsKey(): void
51    {
52        $term = new ArrayTerm([
53            new ArrayEntry(null, Domain::literal('first')),
54            new ArrayEntry(Domain::literal('k'), Domain::literal('keyed')),
55            new ArrayEntry(null, Domain::literal('second')),
56        ]);
57        self::assertSame('first', $term->element(0)?->soleLiteral()?->value);
58        self::assertSame('first', $term->element('0')?->soleLiteral()?->value);
59        self::assertSame('second', $term->element(1)?->soleLiteral()?->value);
60        self::assertSame('keyed', $term->element('k')?->soleLiteral()?->value);
61        self::assertNull($term->element('missing'));
62    }
63
64    public function testElementNeverReadsAnElementUnderABooleanOrANullKey(): void
65    {
66        $term = new ArrayTerm([
67            new ArrayEntry(null, Domain::literal('first')),
68            new ArrayEntry(null, Domain::literal('second')),
69            new ArrayEntry(Domain::literal(''), Domain::literal('empty')),
70        ]);
71        self::assertNull($term->element(true));
72        self::assertNull($term->element(false));
73        self::assertNull($term->element(null));
74    }
75
76    public function testAnyValueIsTheUnionOfEveryElement(): void
77    {
78        $term = new ArrayTerm([
79            new ArrayEntry(Domain::literal('u'), Domain::literal('users')),
80            new ArrayEntry(Domain::literal('a'), Domain::literal('admins')),
81            new ArrayEntry(null, Domain::literal('users')),
82        ]);
83        self::assertSame('literal:string:admins|literal:string:users', $term->anyValue(Origin::Unresolved)?->signature());
84    }
85
86    public function testAnyValueOfAnArrayWithNoElementsIsNothing(): void
87    {
88        self::assertNull((new ArrayTerm([]))->anyValue(Origin::Unresolved));
89        self::assertNull((new ArrayTerm([], false))->anyValue(Origin::Unresolved));
90    }
91
92    public function testAnyValueOfAnArrayKnownOnlyInPartKeepsAGapWhereItWasRead(): void
93    {
94        $term = new ArrayTerm([new ArrayEntry(null, Domain::literal('users'))], false);
95        $values = $term->anyValue(Origin::Loop, 'iterated value');
96
97        self::assertSame('literal:string:users|opaque:mixed:loop', $values?->signature());
98        self::assertEquals(new OpaqueTerm(TypeShape::unknown(), Origin::Loop, 'iterated value'), $values->terms[1]);
99    }
100
101    public function testToPatternLeavesAGap(): void
102    {
103        self::assertSame('{$}', (new ArrayTerm([]))->toPattern()->display());
104    }
105
106    public function testTypeIsArray(): void
107    {
108        self::assertSame('array', (new ArrayTerm([]))->type()->display());
109    }
110
111    public function testSignatureRecordsWhetherEveryElementIsKnown(): void
112    {
113        self::assertNotSame((new ArrayTerm([], true))->signature(), (new ArrayTerm([], false))->signature());
114    }
115}
116