Skip to content

Commit d46891e

Browse files
committed
Add more edge cases test in vendor isolation tests suite
1 parent ac3f4ba commit d46891e

1 file changed

Lines changed: 144 additions & 6 deletions

File tree

tests/Contract/VendorIsolationPathTest.php

Lines changed: 144 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,6 @@
22

33
declare(strict_types=1);
44

5-
namespace TypePHP\Tests\Contract;
6-
7-
use ReflectionMethod;
85
use TypePHP\Contract\FileFilter;
96
use TypePHP\Internal\Config;
107
use TypePHP\Internal\StreamWrapper;
@@ -36,6 +33,7 @@
3633
StreamWrapper::register();
3734

3835
$projectRoot = Config::getProjectRoot();
36+
3937
$vendorDoctrineFile = str_replace('\\', '/', $projectRoot . '/vendor/doctrine/dbal/src/Schema/AbstractNamedObject.php');
4038
$appFile = str_replace('\\', '/', $projectRoot . '/app/Services/UserService.php');
4139

@@ -53,7 +51,7 @@
5351
Config::set([
5452
'include' => [
5553
'src/**',
56-
'vendor/my-org/whitelisted-package/**',
54+
'vendor/my-org/whitelisted-package/**',
5755
],
5856
'exclude' => [
5957
'vendor/**',
@@ -80,7 +78,7 @@
8078
Config::set([
8179
'include' => [
8280
'src/**',
83-
'src/Core/Framework/**',
81+
'src/Core/Framework/**',
8482
'src/Core/Content/**',
8583
],
8684
'exclude' => [
@@ -106,10 +104,12 @@
106104
]);
107105

108106
StreamWrapper::register();
107+
109108
$projectRoot = Config::getProjectRoot();
110109

111110
$appLibFile = str_replace('\\', '/', $projectRoot . '/lib/Services/PaymentProcessor.php');
112111
$vendorLibFile = str_replace('\\', '/', $projectRoot . '/vendor/dompdf/php-font-lib/lib/Font.php');
112+
113113
expect(FileFilter::isFileExcluded($appLibFile))->toBeFalse();
114114
expect(FileFilter::isFileExcluded($vendorLibFile))->toBeTrue();
115115

@@ -118,4 +118,142 @@
118118
->and($refMethod->invoke(null, $vendorLibFile, $vendorLibFile))->toBeFalse()
119119
;
120120
});
121-
});
121+
122+
test('allows whitelisting a single specific file inside a vendor package while excluding its siblings', function () {
123+
Config::set([
124+
'include' => [
125+
'src/**',
126+
'vendor/monolog/monolog/src/Monolog/Logger.php',
127+
],
128+
'exclude' => [
129+
'vendor/**',
130+
],
131+
]);
132+
133+
StreamWrapper::register();
134+
135+
$projectRoot = Config::getProjectRoot();
136+
137+
$whitelistedSingleFile = str_replace('\\', '/', $projectRoot . '/vendor/monolog/monolog/src/Monolog/Logger.php');
138+
$siblingVendorFile = str_replace('\\', '/', $projectRoot . '/vendor/monolog/monolog/src/Monolog/Formatter/LineFormatter.php');
139+
140+
expect(FileFilter::isFileExcluded($whitelistedSingleFile))->toBeFalse();
141+
expect(FileFilter::isFileExcluded($siblingVendorFile))->toBeTrue();
142+
143+
$refMethod = new ReflectionMethod(StreamWrapper::class, 'isApplicationFile');
144+
expect($refMethod->invoke(null, $whitelistedSingleFile, $whitelistedSingleFile))->toBeTrue()
145+
->and($refMethod->invoke(null, $siblingVendorFile, $siblingVendorFile))->toBeFalse()
146+
;
147+
});
148+
149+
test('allows blacklisting a specific legacy file inside an otherwise whitelisted vendor package', function () {
150+
Config::set([
151+
'include' => [
152+
'src/**',
153+
'vendor/acme/custom-package/**',
154+
],
155+
'exclude' => [
156+
'vendor/**',
157+
'vendor/acme/custom-package/src/Legacy/UnsafeFile.php',
158+
],
159+
]);
160+
161+
StreamWrapper::register();
162+
163+
$projectRoot = Config::getProjectRoot();
164+
165+
$safeFile = str_replace('\\', '/', $projectRoot . '/vendor/acme/custom-package/src/SafeService.php');
166+
$unsafeFile = str_replace('\\', '/', $projectRoot . '/vendor/acme/custom-package/src/Legacy/UnsafeFile.php');
167+
168+
expect(FileFilter::isFileExcluded($safeFile))->toBeFalse();
169+
expect(FileFilter::isFileExcluded($unsafeFile))->toBeTrue();
170+
171+
$refMethod = new ReflectionMethod(StreamWrapper::class, 'isApplicationFile');
172+
expect($refMethod->invoke(null, $safeFile, $safeFile))->toBeTrue()
173+
->and($refMethod->invoke(null, $unsafeFile, $unsafeFile))->toBeFalse()
174+
;
175+
});
176+
177+
test('does not falsely classify application directories like vendor-tools/ or vendor_custom/ as vendor directories', function () {
178+
Config::set([
179+
'include' => [
180+
'vendor-tools/**',
181+
'vendor_custom/**',
182+
'src/**',
183+
],
184+
'exclude' => [
185+
'vendor/**',
186+
],
187+
]);
188+
189+
StreamWrapper::register();
190+
191+
$projectRoot = Config::getProjectRoot();
192+
193+
$appToolsFile = str_replace('\\', '/', $projectRoot . '/vendor-tools/DeployScript.php');
194+
$appCustomFile = str_replace('\\', '/', $projectRoot . '/vendor_custom/Helper.php');
195+
$realVendorFile = str_replace('\\', '/', $projectRoot . '/vendor/symfony/console/Application.php');
196+
197+
expect(FileFilter::isFileExcluded($appToolsFile))->toBeFalse();
198+
expect(FileFilter::isFileExcluded($appCustomFile))->toBeFalse();
199+
expect(FileFilter::isFileExcluded($realVendorFile))->toBeTrue();
200+
201+
$refMethod = new ReflectionMethod(StreamWrapper::class, 'isApplicationFile');
202+
expect($refMethod->invoke(null, $appToolsFile, $appToolsFile))->toBeTrue()
203+
->and($refMethod->invoke(null, $appCustomFile, $appCustomFile))->toBeTrue()
204+
->and($refMethod->invoke(null, $realVendorFile, $realVendorFile))->toBeFalse()
205+
;
206+
});
207+
208+
test('handles vendor package names containing hyphens, dots, numbers, and scoped prefixes', function () {
209+
Config::set([
210+
'include' => [
211+
'src/**',
212+
'vendor/symfony/polyfill-php83/**',
213+
'vendor/2amigos/qrcode-library/**',
214+
],
215+
'exclude' => [
216+
'vendor/**',
217+
],
218+
]);
219+
220+
StreamWrapper::register();
221+
222+
$projectRoot = Config::getProjectRoot();
223+
224+
$scopedVendorFile = str_replace('\\', '/', $projectRoot . '/vendor/symfony/polyfill-php83/bootstrap.php');
225+
$numericVendorFile = str_replace('\\', '/', $projectRoot . '/vendor/2amigos/qrcode-library/src/QrCode.php');
226+
$unwhitelistedVendor = str_replace('\\', '/', $projectRoot . '/vendor/guzzlehttp/guzzle/src/Client.php');
227+
228+
expect(FileFilter::isFileExcluded($scopedVendorFile))->toBeFalse();
229+
expect(FileFilter::isFileExcluded($numericVendorFile))->toBeFalse();
230+
expect(FileFilter::isFileExcluded($unwhitelistedVendor))->toBeTrue();
231+
});
232+
233+
test('handles mixed Windows backslashes and Unix forward slashes in vendor paths seamlessly', function () {
234+
Config::set([
235+
'include' => [
236+
'src/**',
237+
],
238+
'exclude' => [
239+
'vendor/**',
240+
],
241+
]);
242+
243+
StreamWrapper::register();
244+
245+
$projectRoot = Config::getProjectRoot();
246+
247+
$windowsVendorPath = $projectRoot . '\\vendor\\doctrine\\dbal\\src\\Schema\\Column.php';
248+
$windowsAppPath = $projectRoot . '\\app\\Services\\OrderService.php';
249+
250+
expect(FileFilter::isFileExcluded($windowsVendorPath))->toBeTrue()
251+
->and(FileFilter::isFileExcluded($windowsAppPath))->toBeFalse()
252+
;
253+
254+
$refMethod = new ReflectionMethod(StreamWrapper::class, 'isApplicationFile');
255+
expect($refMethod->invoke(null, $windowsVendorPath, $windowsVendorPath))->toBeFalse()
256+
->and($refMethod->invoke(null, $windowsAppPath, $windowsAppPath))->toBeTrue()
257+
;
258+
});
259+
});

0 commit comments

Comments
 (0)