packages/sql-catalog/tests/Unit/Core/Catalog/CatalogTest.php

1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Unit\Core\Catalog;
6
7use PHPUnit\Framework\Attributes\CoversClass;
8use PHPUnit\Framework\Attributes\UsesClass;
9use PHPUnit\Framework\TestCase;
10use SqlCatalog\Core\Catalog\AnalysisProblem;
11use SqlCatalog\Core\Catalog\CallSite;
12use SqlCatalog\Core\Catalog\Catalog;
13use SqlCatalog\Core\Catalog\CatalogEntry;
14use SqlCatalog\Core\Sql\StatementKind;
15use SqlCatalog\Core\Text\LiteralText;
16use SqlCatalog\Core\Text\TextPattern;
17use SqlCatalog\Facade\AnalysisOptions;
18use SqlCatalog\Facade\Analyzer;
19
20#[CoversClass(Catalog::class)]
21#[UsesClass(AnalysisOptions::class)]
22#[UsesClass(Analyzer::class)]
23#[UsesClass(AnalysisProblem::class)]
24#[UsesClass(CallSite::class)]
25#[UsesClass(CatalogEntry::class)]
26#[UsesClass(LiteralText::class)]
27#[UsesClass(TextPattern::class)]
28#[UsesClass(\SqlCatalog\Core\Analysis\FunctionModel\Registry::class)]
29#[UsesClass(\SqlCatalog\Core\Analysis\BuiltinCallModel::class)]
30final class CatalogTest extends TestCase
31{
32    public function testEntriesAreReturnedInTheOrderTheyWereGiven(): void
33    {
34        $first = new CatalogEntry('a', StatementKind::Select, TextPattern::fromText('SELECT 1'), [], [], new CallSite('b.php', 2, 'f', 's'), []);
35        $second = new CatalogEntry('b', StatementKind::Select, TextPattern::fromText('SELECT 2'), [], [], new CallSite('a.php', 1, 'f', 's'), []);
36        self::assertSame([$first, $second], (new Catalog([$first, $second]))->entries());
37    }
38
39    public function testProblemsAreKept(): void
40    {
41        $problem = new AnalysisProblem('a.php', 'broken');
42        self::assertSame([$problem], (new Catalog([], [$problem]))->problems());
43    }
44
45    public function testFindLooksUpAStatementByItsIdentifier(): void
46    {
47        $entry = new CatalogEntry('abc', StatementKind::Select, TextPattern::fromText('SELECT 1'), [], [], new CallSite('a.php', 1, 'f', 's'), []);
48        $catalog = new Catalog([$entry]);
49        self::assertSame($entry, $catalog->find('abc'));
50        self::assertNull($catalog->find('missing'));
51    }
52
53    public function testFilterKeepsOnlyWhatTheTestAccepts(): void
54    {
55        $kept = new CatalogEntry('a', StatementKind::Select, TextPattern::fromText('SELECT 1'), [], [], new CallSite('a.php', 1, 'f', 's'), []);
56        $dropped = new CatalogEntry('b', StatementKind::Insert, TextPattern::fromText('INSERT INTO t VALUES (1)'), [], [], new CallSite('a.php', 2, 'f', 's'), []);
57        $filtered = (new Catalog([$dropped, $kept]))->filter(
58            static fn (CatalogEntry $entry): bool => $entry->kind === StatementKind::Select,
59        );
60        self::assertSame([$kept], $filtered->entries());
61    }
62
63    public function testMergeJoinsStatementsAndProblems(): void
64    {
65        $left = new Catalog([], [new AnalysisProblem('a.php', 'x')]);
66        $right = new Catalog([], [new AnalysisProblem('b.php', 'y')]);
67        self::assertCount(2, $left->merge($right)->problems());
68    }
69
70    public function testSortedOrdersByWhereTheStatementIsIssued(): void
71    {
72        $later = new CatalogEntry('a', StatementKind::Select, TextPattern::fromText('SELECT 1'), [], [], new CallSite('b.php', 2, 'f', 's'), []);
73        $earlier = new CatalogEntry('b', StatementKind::Select, TextPattern::fromText('SELECT 2'), [], [], new CallSite('a.php', 1, 'f', 's'), []);
74        $sorted = (new Catalog([$later, $earlier], [new AnalysisProblem('z.php', 'x'), new AnalysisProblem('a.php', 'y')]))->sorted();
75        self::assertSame([$earlier, $later], $sorted->entries());
76        self::assertSame('a.php', $sorted->problems()[0]->file);
77    }
78
79    public function testCountIsTheNumberOfStatements(): void
80    {
81        self::assertCount(0, new Catalog());
82    }
83
84    public function testGetIteratorWalksTheStatements(): void
85    {
86        $entry = new CatalogEntry('a', StatementKind::Select, TextPattern::fromText('SELECT 1'), [], [], new CallSite('a.php', 1, 'f', 's'), []);
87        self::assertSame([$entry], iterator_to_array(new Catalog([$entry])));
88    }
89
90    public function testSourceSurvivesFilteringSortingAndMerging(): void
91    {
92        $source = '<?php $db->query($sql);';
93        $left = new Catalog([], [], ['a.php' => $source, 'empty.php' => '']);
94        $right = new Catalog([], [], ['b.php' => '<?php function {']);
95        $catalog = $left->filter(static fn (CatalogEntry $entry): bool => false)->sorted()->merge($right);
96
97        self::assertSame($source, $catalog->source('a.php'));
98        self::assertSame('<?php function {', $catalog->source('b.php'));
99        self::assertSame('', $catalog->source('empty.php'));
100        self::assertNull($catalog->source('missing.php'));
101        self::assertSame('updated', $left->merge(new Catalog([], [], ['a.php' => 'updated']))->source('a.php'));
102        self::assertSame($source, $left->source('a.php'));
103    }
104
105}
106