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>
237 lines
8.9 KiB
PHP
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);
|
|
}
|
|
});
|
|
});
|