packages/sql-catalog/tests/Unit/Reporter/Html/ReportSiteTest.php
1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Unit\Reporter\Html;
6
7use PHPUnit\Framework\Attributes\CoversClass;
8use PHPUnit\Framework\Attributes\DataProvider;
9use PHPUnit\Framework\Attributes\UsesClass;
10use PHPUnit\Framework\TestCase;
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\Reporter\Html\CatalogIndex;
18use SqlCatalog\Reporter\Html\CatalogStatistics;
19use SqlCatalog\Reporter\Html\HtmlText;
20use SqlCatalog\Reporter\Html\ReportSite;
21use SqlCatalog\Reporter\Html\Scope;
22
23#[CoversClass(ReportSite::class)]
24#[UsesClass(CallSite::class)]
25#[UsesClass(Catalog::class)]
26#[UsesClass(CatalogEntry::class)]
27#[UsesClass(CatalogIndex::class)]
28#[UsesClass(CatalogStatistics::class)]
29#[UsesClass(HtmlText::class)]
30#[UsesClass(Scope::class)]
31#[UsesClass(StatementKind::class)]
32#[UsesClass(LiteralText::class)]
33#[UsesClass(TextPattern::class)]
34final class ReportSiteTest extends TestCase
35{
36 public function testCatalogIsHeldInReportingOrder(): void
37 {
38 $catalog = new Catalog([
39 new CatalogEntry('b', StatementKind::Select, TextPattern::fromText('SELECT 2'), [], [], new CallSite('b.php', 1, 'f', 'pdo.query'), []),
40 new CatalogEntry('a', StatementKind::Select, TextPattern::fromText('SELECT 1'), [], [], new CallSite('a.php', 1, 'f', 'pdo.query'), []),
41 ]);
42
43 self::assertSame(['a', 'b'], array_column((new ReportSite($catalog, formatter: \SqlCatalog\Facade\Builtins::sqlFormatter()))->catalog()->entries(), 'id'));
44 }
45
46 public function testIndexGroupsTheCatalog(): void
47 {
48 $catalog = new Catalog([
49 new CatalogEntry('a', StatementKind::Select, TextPattern::fromText('SELECT 1'), ['users'], [], new CallSite('a.php', 1, 'f', 'pdo.query'), []),
50 ]);
51
52 self::assertSame(['users'], array_keys((new ReportSite($catalog, formatter: \SqlCatalog\Facade\Builtins::sqlFormatter()))->index()->byTable()));
53 }
54
55 public function testStatisticsCountTheCatalog(): void
56 {
57 $catalog = new Catalog([
58 new CatalogEntry('a', StatementKind::Select, TextPattern::fromText('SELECT 1'), [], [], new CallSite('a.php', 1, 'f', 'pdo.query'), []),
59 ]);
60
61 self::assertSame(1, (new ReportSite($catalog, formatter: \SqlCatalog\Facade\Builtins::sqlFormatter()))->statistics()->statements());
62 }
63
64 public function testPagesForKeepsNamesApartWhenTheySlugAlike(): void
65 {
66 self::assertSame(
67 ['wp_posts' => 'tables/wp-posts.html', 'wp-posts' => 'tables/wp-posts-2.html', '' => 'tables/unnamed.html'],
68 (new ReportSite(new Catalog(), formatter: \SqlCatalog\Facade\Builtins::sqlFormatter()))->pagesFor(['wp_posts', 'wp-posts', ''], 'tables/'),
69 );
70 }
71
72 public function testStatementPageIsNamedAfterTheIdentifier(): void
73 {
74 self::assertSame('statements/abc123.html', (new ReportSite(new Catalog(), formatter: \SqlCatalog\Facade\Builtins::sqlFormatter()))->statementPage('abc123'));
75 }
76
77 public function testTablePageFallsBackToTheListingForAnUnknownTable(): void
78 {
79 $catalog = new Catalog([
80 new CatalogEntry('a', StatementKind::Select, TextPattern::fromText('SELECT 1'), ['app.users'], [], new CallSite('a.php', 1, 'f', 'pdo.query'), []),
81 ]);
82 $site = new ReportSite($catalog, formatter: \SqlCatalog\Facade\Builtins::sqlFormatter());
83
84 self::assertSame('tables/app-users.html', $site->tablePage('app.users'));
85 self::assertSame('tables.html', $site->tablePage('none'));
86 }
87
88 public function testClassPageFallsBackToTheListingForAnUnknownClass(): void
89 {
90 $catalog = new Catalog([
91 new CatalogEntry('a', StatementKind::Select, TextPattern::fromText('SELECT 1'), [], [], new CallSite('a.php', 1, 'App\\Users::find', 'pdo.query'), []),
92 ]);
93 $site = new ReportSite($catalog, formatter: \SqlCatalog\Facade\Builtins::sqlFormatter());
94
95 self::assertSame('classes/app-users.html', $site->classPage('App\\Users'));
96 self::assertSame('namespaces.html', $site->classPage('None'));
97 }
98
99 public function testFilePageFallsBackToTheListingForAnUnknownFile(): void
100 {
101 $catalog = new Catalog([
102 new CatalogEntry('a', StatementKind::Select, TextPattern::fromText('SELECT 1'), [], [], new CallSite('src/a.php', 1, 'f', 'pdo.query'), []),
103 ]);
104 $site = new ReportSite($catalog, formatter: \SqlCatalog\Facade\Builtins::sqlFormatter());
105
106 self::assertSame('files/src-a-php.html', $site->filePage('src/a.php'));
107 self::assertSame('files.html', $site->filePage('none.php'));
108 }
109
110 public function testFunctionAnchorIsWhatAFunctionsStatementsAreGroupedUnder(): void
111 {
112 self::assertSame('fn-app-users-find', (new ReportSite(new Catalog(), formatter: \SqlCatalog\Facade\Builtins::sqlFormatter()))->functionAnchor('\\App\\Users::find'));
113 }
114
115 public function testFunctionUrlLeadsToTheClassPageForAMethodAndTheFilePageOtherwise(): void
116 {
117 $method = new CatalogEntry('a', StatementKind::Select, TextPattern::fromText('SELECT 1'), [], [], new CallSite('src/a.php', 1, 'App\\Users::find', 'pdo.query'), []);
118 $function = new CatalogEntry('b', StatementKind::Select, TextPattern::fromText('SELECT 2'), [], [], new CallSite('src/b.php', 1, 'helper', 'pdo.query'), []);
119 $site = new ReportSite(new Catalog([$method, $function]), formatter: \SqlCatalog\Facade\Builtins::sqlFormatter());
120
121 self::assertSame('classes/app-users.html#fn-app-users-find', $site->functionUrl($method));
122 self::assertSame('files/src-b-php.html#fn-helper', $site->functionUrl($function));
123 }
124
125 public function testTablesAreMostNamedFirst(): void
126 {
127 $catalog = new Catalog([
128 new CatalogEntry('a', StatementKind::Select, TextPattern::fromText('SELECT 1'), ['users', 'posts'], [], new CallSite('a.php', 1, 'f', 'pdo.query'), []),
129 new CatalogEntry('b', StatementKind::Select, TextPattern::fromText('SELECT 2'), ['posts'], [], new CallSite('a.php', 2, 'f', 'pdo.query'), []),
130 ]);
131
132 self::assertSame(['posts', 'users'], (new ReportSite($catalog, formatter: \SqlCatalog\Facade\Builtins::sqlFormatter()))->tables());
133 }
134
135 public function testClassesAreInNameOrder(): void
136 {
137 $catalog = new Catalog([
138 new CatalogEntry('a', StatementKind::Select, TextPattern::fromText('SELECT 1'), [], [], new CallSite('a.php', 1, 'B::f', 'pdo.query'), []),
139 new CatalogEntry('b', StatementKind::Select, TextPattern::fromText('SELECT 2'), [], [], new CallSite('a.php', 2, 'A::f', 'pdo.query'), []),
140 ]);
141
142 self::assertSame(['A', 'B'], (new ReportSite($catalog, formatter: \SqlCatalog\Facade\Builtins::sqlFormatter()))->classes());
143 }
144
145 public function testFilesAreInPathOrder(): void
146 {
147 $catalog = new Catalog([
148 new CatalogEntry('a', StatementKind::Select, TextPattern::fromText('SELECT 1'), [], [], new CallSite('b.php', 1, 'f', 'pdo.query'), []),
149 new CatalogEntry('b', StatementKind::Select, TextPattern::fromText('SELECT 2'), [], [], new CallSite('a.php', 1, 'f', 'pdo.query'), []),
150 ]);
151
152 self::assertSame(['a.php', 'b.php'], (new ReportSite($catalog, formatter: \SqlCatalog\Facade\Builtins::sqlFormatter()))->files());
153 }
154
155 /**
156 * @return list<array{string, string}>
157 */
158 public static function providerPrefixOf(): array
159 {
160 return [['index.html', ''], ['tables/users.html', '../']];
161 }
162
163 #[DataProvider('providerPrefixOf')]
164 public function testPrefixOfIsWhatALinkFromThatPageGoesThrough(string $page, string $expected): void
165 {
166 self::assertSame($expected, (new ReportSite(new Catalog(), formatter: \SqlCatalog\Facade\Builtins::sqlFormatter()))->prefixOf($page));
167 }
168}
169