Files
michaelschiemer/tests/Framework/Context/ExecutionContextCacheTest.php
Michael Schiemer 9b74ade5b0 feat: Fix discovery system critical issues
Resolved multiple critical discovery system issues:

## Discovery System Fixes
- Fixed console commands not being discovered on first run
- Implemented fallback discovery for empty caches
- Added context-aware caching with separate cache keys
- Fixed object serialization preventing __PHP_Incomplete_Class

## Cache System Improvements
- Smart caching that only caches meaningful results
- Separate caches for different execution contexts (console, web, test)
- Proper array serialization/deserialization for cache compatibility
- Cache hit logging for debugging and monitoring

## Object Serialization Fixes
- Fixed DiscoveredAttribute serialization with proper string conversion
- Sanitized additional data to prevent object reference issues
- Added fallback for corrupted cache entries

## Performance & Reliability
- All 69 console commands properly discovered and cached
- 534 total discovery items successfully cached and restored
- No more __PHP_Incomplete_Class cache corruption
- Improved error handling and graceful fallbacks

## Testing & Quality
- Fixed code style issues across discovery components
- Enhanced logging for better debugging capabilities
- Improved cache validation and error recovery

Ready for production deployment with stable discovery system.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-13 12:04:17 +02:00

237 lines
8.9 KiB
PHP

<?php
declare(strict_types=1);
namespace Tests\Framework\Context;
use App\Framework\Context\ContextType;
use App\Framework\Context\ExecutionContext;
use App\Framework\Discovery\Cache\DiscoveryCacheIdentifiers;
describe('ExecutionContext Cache Key Generation', function () {
beforeEach(function () {
// Clear any environment variables that might affect detection
unset($_ENV['APP_ENV']);
unset($_SERVER['REQUEST_METHOD']);
unset($_SERVER['HTTP_HOST']);
unset($_SERVER['SERVER_NAME']);
});
it('generates different cache keys for different execution contexts', function () {
$paths = ['/test/src'];
// Create different execution contexts
$consoleContext = ExecutionContext::forConsole();
$webContext = ExecutionContext::forWeb();
$workerContext = ExecutionContext::forWorker();
$testContext = ExecutionContext::forTest();
// Generate cache keys for each context
$consoleKey = DiscoveryCacheIdentifiers::fullDiscoveryKey($paths, $consoleContext->getType()->value);
$webKey = DiscoveryCacheIdentifiers::fullDiscoveryKey($paths, $webContext->getType()->value);
$workerKey = DiscoveryCacheIdentifiers::fullDiscoveryKey($paths, $workerContext->getType()->value);
$testKey = DiscoveryCacheIdentifiers::fullDiscoveryKey($paths, $testContext->getType()->value);
// All keys should be different
$keys = [$consoleKey, $webKey, $workerKey, $testKey];
$uniqueKeys = array_unique(array_map(fn ($key) => $key->toString(), $keys));
expect(count($uniqueKeys))->toBe(4);
});
it('console and cli-script contexts have separate cache keys', function () {
$paths = ['/test/src'];
$consoleKey = DiscoveryCacheIdentifiers::fullDiscoveryKey($paths, 'console');
$cliScriptKey = DiscoveryCacheIdentifiers::fullDiscoveryKey($paths, 'cli-script');
expect($consoleKey->toString())->not->toBe($cliScriptKey->toString());
// Both should contain their respective context type
expect($consoleKey->toString())->toContain('console');
expect($cliScriptKey->toString())->toContain('cli-script');
});
it('cache keys are consistent for the same context', function () {
$paths = ['/test/src'];
$context = 'console';
$key1 = DiscoveryCacheIdentifiers::fullDiscoveryKey($paths, $context);
$key2 = DiscoveryCacheIdentifiers::fullDiscoveryKey($paths, $context);
expect($key1->toString())->toBe($key2->toString());
});
it('cache keys change with different paths', function () {
$paths1 = ['/test/src'];
$paths2 = ['/test/src', '/test/additional'];
$context = 'console';
$key1 = DiscoveryCacheIdentifiers::fullDiscoveryKey($paths1, $context);
$key2 = DiscoveryCacheIdentifiers::fullDiscoveryKey($paths2, $context);
expect($key1->toString())->not->toBe($key2->toString());
});
it('validates cache key format is safe for storage', function () {
$paths = ['/test/src'];
$contexts = ['console', 'cli-script', 'web', 'worker', 'test'];
foreach ($contexts as $context) {
$key = DiscoveryCacheIdentifiers::fullDiscoveryKey($paths, $context);
// Cache key should only contain safe characters
expect($key->toString())->toMatch('/^[a-zA-Z0-9_\-:\.]+$/');
// Should not be empty
expect($key->toString())->not->toBeEmpty();
// Should have reasonable length (not too short or too long)
expect(strlen($key->toString()))->toBeGreaterThan(10);
expect(strlen($key->toString()))->toBeLessThan(200);
}
});
});
describe('ExecutionContext Detection and Metadata', function () {
it('can detect different execution contexts', function () {
// Test forced contexts
$consoleContext = ExecutionContext::forConsole();
expect($consoleContext->isConsole())->toBeTrue();
expect($consoleContext->isCli())->toBeTrue();
expect($consoleContext->isWeb())->toBeFalse();
$webContext = ExecutionContext::forWeb();
expect($webContext->isWeb())->toBeTrue();
expect($webContext->isCli())->toBeFalse();
expect($webContext->isConsole())->toBeFalse();
$workerContext = ExecutionContext::forWorker();
expect($workerContext->isWorker())->toBeTrue();
expect($workerContext->isCli())->toBeTrue();
expect($workerContext->isWeb())->toBeFalse();
$testContext = ExecutionContext::forTest();
expect($testContext->isTest())->toBeTrue();
expect($testContext->isCli())->toBeFalse(); // Test context is separate from CLI
});
it('provides consistent metadata for cache key generation', function () {
$context = ExecutionContext::forConsole();
$metadata1 = $context->getMetadata();
$metadata2 = $context->getMetadata();
// Metadata should be consistent
expect($metadata1['type'])->toBe($metadata2['type']);
expect($metadata1['sapi'])->toBe($metadata2['sapi']);
// Type should match context
expect($metadata1['type'])->toBe('console');
});
it('includes execution context type in metadata', function () {
$contexts = [
ExecutionContext::forConsole(),
ExecutionContext::forWeb(),
ExecutionContext::forWorker(),
ExecutionContext::forTest(),
];
foreach ($contexts as $context) {
$metadata = $context->getMetadata();
expect($metadata)->toHaveKey('type');
expect($metadata['type'])->toBe($context->getType()->value);
expect($metadata)->toHaveKey('sapi');
expect($metadata)->toHaveKey('pid');
}
});
});
describe('ExecutionContext ContextType Enum', function () {
it('has all required context types', function () {
$types = [
ContextType::CONSOLE,
ContextType::WEB,
ContextType::WORKER,
ContextType::TEST,
ContextType::CLI_SCRIPT,
];
foreach ($types as $type) {
expect($type)->toBeInstanceOf(ContextType::class);
expect($type->value)->toBeString();
expect($type->value)->not->toBeEmpty();
}
});
it('context type values are suitable for cache keys', function () {
$types = [
ContextType::CONSOLE,
ContextType::WEB,
ContextType::WORKER,
ContextType::TEST,
ContextType::CLI_SCRIPT,
];
foreach ($types as $type) {
// Values should be valid for cache keys (no spaces, special chars)
expect($type->value)->toMatch('/^[a-z\-]+$/');
expect($type->value)->not->toContain(' ');
expect($type->value)->not->toContain('/');
expect($type->value)->not->toContain('\\');
}
});
it('console and cli-script are different types', function () {
expect(ContextType::CONSOLE->value)->not->toBe(ContextType::CLI_SCRIPT->value);
expect(ContextType::CONSOLE->value)->toBe('console');
expect(ContextType::CLI_SCRIPT->value)->toBe('cli-script');
});
});
describe('Cache Key Collision Prevention', function () {
it('prevents cache collisions between console.php and other CLI scripts', function () {
$paths = ['/test/src'];
// Before the fix, console.php was detected as CLI_SCRIPT
// Now it should be handled separately as CONSOLE context
$consoleKey = DiscoveryCacheIdentifiers::fullDiscoveryKey($paths, ContextType::CONSOLE->value);
$cliScriptKey = DiscoveryCacheIdentifiers::fullDiscoveryKey($paths, ContextType::CLI_SCRIPT->value);
expect($consoleKey->toString())->not->toBe($cliScriptKey->toString());
// Verify the fix: different contexts should have distinct cache keys
$keyDifferences = array_diff_assoc(
str_split($consoleKey->toString()),
str_split($cliScriptKey->toString())
);
// There should be differences between the keys
expect(count($keyDifferences))->toBeGreaterThan(0);
});
it('ensures discovery cache separation between execution contexts', function () {
$paths = ['/test/src'];
// All different contexts should produce different cache keys
$contexts = ['console', 'cli-script', 'web', 'worker', 'test'];
$keys = [];
foreach ($contexts as $context) {
$key = DiscoveryCacheIdentifiers::fullDiscoveryKey($paths, $context);
$keys[$context] = $key->toString();
}
// All keys should be unique
$uniqueKeys = array_unique($keys);
expect(count($uniqueKeys))->toBe(count($contexts));
// Each key should be identifiable by its context
foreach ($contexts as $context) {
expect($keys[$context])->toContain($context);
}
});
});