- Add DISCOVERY_LOG_LEVEL=debug - Add DISCOVERY_SHOW_PROGRESS=true - Temporary changes for debugging InitializerProcessor fixes on production
265 lines
9.8 KiB
PHP
265 lines
9.8 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Framework\Core\ValueObjects\ClassName;
|
|
|
|
describe('ClassName::exists()', function () {
|
|
it('returns false for empty strings', function () {
|
|
// This should be caught by constructor validation, but let's test the edge case
|
|
expect(function () {
|
|
ClassName::create('');
|
|
})->toThrow(InvalidArgumentException::class, 'Class name cannot be empty');
|
|
});
|
|
|
|
it('returns false for whitespace-only strings', function () {
|
|
expect(function () {
|
|
ClassName::create(' ');
|
|
})->toThrow(InvalidArgumentException::class, 'Invalid class name: ');
|
|
|
|
expect(function () {
|
|
ClassName::create("\t");
|
|
})->toThrow(InvalidArgumentException::class, 'Invalid class name: ');
|
|
|
|
expect(function () {
|
|
ClassName::create("\n");
|
|
})->toThrow(InvalidArgumentException::class, 'Invalid class name:
|
|
');
|
|
});
|
|
|
|
it('returns true for existing classes', function () {
|
|
$className = ClassName::create('stdClass');
|
|
expect($className->exists())->toBeTrue();
|
|
|
|
$className = ClassName::create('Exception');
|
|
expect($className->exists())->toBeTrue();
|
|
|
|
$className = ClassName::create('DateTime');
|
|
expect($className->exists())->toBeTrue();
|
|
});
|
|
|
|
it('returns true for existing interfaces', function () {
|
|
$className = ClassName::create('Countable');
|
|
expect($className->exists())->toBeTrue();
|
|
|
|
$className = ClassName::create('Traversable');
|
|
expect($className->exists())->toBeTrue();
|
|
|
|
$className = ClassName::create('JsonSerializable');
|
|
expect($className->exists())->toBeTrue();
|
|
});
|
|
|
|
it('returns true for existing traits', function () {
|
|
// PHP doesn't have many built-in traits, but let's test with a custom one
|
|
// We'll create a simple trait for testing
|
|
eval('trait TestTrait { public function testMethod() { return "test"; } }');
|
|
|
|
$className = ClassName::create('TestTrait');
|
|
expect($className->exists())->toBeTrue();
|
|
});
|
|
|
|
it('returns false for non-existent classes', function () {
|
|
$className = ClassName::create('NonExistentClass');
|
|
expect($className->exists())->toBeFalse();
|
|
|
|
$className = ClassName::create('App\\NonExistent\\Class\\Name');
|
|
expect($className->exists())->toBeFalse();
|
|
|
|
$className = ClassName::create('Some\\Random\\ClassName');
|
|
expect($className->exists())->toBeFalse();
|
|
});
|
|
|
|
it('handles fully qualified class names correctly', function () {
|
|
$className = ClassName::create('\\stdClass');
|
|
expect($className->exists())->toBeTrue();
|
|
|
|
$className = ClassName::create('\\Exception');
|
|
expect($className->exists())->toBeTrue();
|
|
|
|
$className = ClassName::create('\\NonExistentClass');
|
|
expect($className->exists())->toBeFalse();
|
|
});
|
|
|
|
it('handles framework classes correctly', function () {
|
|
$className = ClassName::create('App\\Framework\\Core\\ValueObjects\\ClassName');
|
|
expect($className->exists())->toBeTrue();
|
|
|
|
$className = ClassName::create('App\\Framework\\DI\\Container');
|
|
expect($className->exists())->toBeTrue();
|
|
|
|
$className = ClassName::create('App\\Framework\\NonExistent\\Class');
|
|
expect($className->exists())->toBeFalse();
|
|
});
|
|
|
|
it('handles different case variations (PHP classes are case-insensitive)', function () {
|
|
$className = ClassName::create('stdclass'); // lowercase
|
|
expect($className->exists())->toBeTrue(); // PHP classes are case-insensitive
|
|
|
|
$className = ClassName::create('STDCLASS'); // uppercase
|
|
expect($className->exists())->toBeTrue(); // PHP classes are case-insensitive
|
|
|
|
$className = ClassName::create('stdClass'); // correct case
|
|
expect($className->exists())->toBeTrue();
|
|
});
|
|
|
|
it('handles invalid class name formats gracefully', function () {
|
|
// These should be caught by constructor validation
|
|
expect(function () {
|
|
ClassName::create('123InvalidName');
|
|
})->toThrow(InvalidArgumentException::class);
|
|
|
|
expect(function () {
|
|
ClassName::create('Invalid-Name');
|
|
})->toThrow(InvalidArgumentException::class);
|
|
|
|
expect(function () {
|
|
ClassName::create('Invalid Name');
|
|
})->toThrow(InvalidArgumentException::class);
|
|
});
|
|
|
|
it('performance test - does not cause memory leaks with many calls', function () {
|
|
$memoryBefore = memory_get_usage();
|
|
|
|
// Test with many exists() calls
|
|
for ($i = 0; $i < 1000; $i++) {
|
|
$className = ClassName::create('stdClass');
|
|
$className->exists();
|
|
|
|
$className = ClassName::create('NonExistentClass' . $i);
|
|
$className->exists();
|
|
}
|
|
|
|
$memoryAfter = memory_get_usage();
|
|
$memoryIncrease = $memoryAfter - $memoryBefore;
|
|
|
|
// Memory increase should be reasonable (less than 1MB for 2000 calls)
|
|
expect($memoryIncrease)->toBeLessThan(1024 * 1024);
|
|
});
|
|
|
|
it('works correctly with autoloader edge cases', function () {
|
|
// Test that exists() doesn't trigger warnings or errors with edge cases
|
|
$className = ClassName::create('App\\NonExistent\\VeryLongClassNameThatDefinitelyDoesNotExist\\WithMultipleNamespaces\\AndMore\\Classes');
|
|
expect($className->exists())->toBeFalse();
|
|
|
|
// Test with special characters that are valid in namespaces
|
|
$className = ClassName::create('App\\Test\\ClassName123');
|
|
expect($className->exists())->toBeFalse();
|
|
});
|
|
|
|
it('handles concurrent access correctly', function () {
|
|
// Simulate concurrent access to exists() method
|
|
$results = [];
|
|
|
|
for ($i = 0; $i < 10; $i++) {
|
|
$className = ClassName::create('stdClass');
|
|
$results[] = $className->exists();
|
|
}
|
|
|
|
// All results should be true
|
|
foreach ($results as $result) {
|
|
expect($result)->toBeTrue();
|
|
}
|
|
|
|
// Test with non-existent class
|
|
$results = [];
|
|
for ($i = 0; $i < 10; $i++) {
|
|
$className = ClassName::create('NonExistentClass');
|
|
$results[] = $className->exists();
|
|
}
|
|
|
|
// All results should be false
|
|
foreach ($results as $result) {
|
|
expect($result)->toBeFalse();
|
|
}
|
|
});
|
|
});
|
|
|
|
describe('ClassName::exists() integration with container', function () {
|
|
it('prevents empty string issues in DI container context', function () {
|
|
// This test ensures that ClassName::exists() works correctly when used
|
|
// in the context that was causing the original "Uninitialized string offset 0" issue
|
|
|
|
// Test the exact scenario that was failing
|
|
$testCases = [
|
|
'stdClass', // Should exist
|
|
'Exception', // Should exist
|
|
'NonExistentClass', // Should not exist
|
|
'App\\Framework\\DI\\Container', // Should exist (interface)
|
|
'App\\NonExistent\\Interface', // Should not exist
|
|
];
|
|
|
|
foreach ($testCases as $testClass) {
|
|
$className = ClassName::create($testClass);
|
|
$result = $className->exists();
|
|
|
|
// The important thing is that no warnings or errors are generated
|
|
expect($result)->toBeIn([true, false]);
|
|
}
|
|
});
|
|
|
|
it('handles the AtomicStorage interface case that was failing', function () {
|
|
// Test the specific case that was causing issues
|
|
$className = ClassName::create('App\\Framework\\Filesystem\\AtomicStorage');
|
|
expect($className->exists())->toBeTrue(); // Interface should exist
|
|
|
|
$className = ClassName::create('App\\Framework\\Filesystem\\Storage');
|
|
expect($className->exists())->toBeTrue(); // Interface should exist
|
|
|
|
$className = ClassName::create('App\\Framework\\Filesystem\\FileStorage');
|
|
expect($className->exists())->toBeTrue(); // Implementation should exist
|
|
});
|
|
|
|
it('regression test - ensures no "Uninitialized string offset 0" warnings', function () {
|
|
// This is a regression test for the specific bug we fixed
|
|
// The bug occurred when empty strings were passed to class_exists()
|
|
|
|
// Capture any warnings or errors
|
|
$errorLevel = error_reporting(E_ALL);
|
|
$errors = [];
|
|
|
|
set_error_handler(function ($severity, $message, $file, $line) use (&$errors) {
|
|
$errors[] = [
|
|
'severity' => $severity,
|
|
'message' => $message,
|
|
'file' => $file,
|
|
'line' => $line,
|
|
];
|
|
});
|
|
|
|
try {
|
|
// Test various scenarios that could potentially cause the issue
|
|
$testCases = [
|
|
'stdClass',
|
|
'Exception',
|
|
'NonExistentClass',
|
|
'App\\Framework\\Filesystem\\AtomicStorage',
|
|
'App\\Framework\\Filesystem\\Storage',
|
|
'App\\Framework\\DI\\Container',
|
|
'App\\NonExistent\\Class\\Name',
|
|
'Very\\Long\\NonExistent\\Namespace\\ClassName',
|
|
];
|
|
|
|
foreach ($testCases as $testCase) {
|
|
$className = ClassName::create($testCase);
|
|
$result = $className->exists();
|
|
|
|
// Result should be boolean, no errors should occur
|
|
expect($result)->toBeBool();
|
|
}
|
|
|
|
// Ensure no "Uninitialized string offset" or similar warnings occurred
|
|
foreach ($errors as $error) {
|
|
expect($error['message'])->not->toContain('Uninitialized string offset');
|
|
expect($error['message'])->not->toContain('ClassLoader.php');
|
|
}
|
|
|
|
// Should have no errors at all
|
|
expect($errors)->toBeEmpty();
|
|
|
|
} finally {
|
|
restore_error_handler();
|
|
error_reporting($errorLevel);
|
|
}
|
|
});
|
|
});
|