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