fix: DockerSecretsResolver - don't normalize absolute paths like /var/www/html/...
Some checks failed
Deploy Application / deploy (push) Has been cancelled
Some checks failed
Deploy Application / deploy (push) Has been cancelled
This commit is contained in:
125
tests/Framework/Discovery/Storage/DebugCacheManagerTest.php
Normal file
125
tests/Framework/Discovery/Storage/DebugCacheManagerTest.php
Normal file
@@ -0,0 +1,125 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Framework\Discovery\Storage;
|
||||
|
||||
use App\Framework\Cache\Cache;
|
||||
use App\Framework\Cache\CacheItem;
|
||||
use App\Framework\Cache\Driver\InMemoryCache;
|
||||
use App\Framework\Cache\GeneralCache;
|
||||
use App\Framework\DateTime\SystemClock;
|
||||
use App\Framework\Discovery\Results\AttributeRegistry;
|
||||
use App\Framework\Discovery\Results\DiscoveryRegistry;
|
||||
use App\Framework\Discovery\Results\InterfaceRegistry;
|
||||
use App\Framework\Discovery\Results\TemplateRegistry;
|
||||
use App\Framework\Discovery\Storage\DiscoveryCacheManager;
|
||||
use App\Framework\Discovery\Storage\Services\CacheEntrySerializer;
|
||||
use App\Framework\Discovery\Storage\Services\CacheEntryUpgrader;
|
||||
use App\Framework\Discovery\Storage\Services\CacheEntryValidator;
|
||||
use App\Framework\Discovery\Storage\Services\StalenessChecker;
|
||||
use App\Framework\Core\ValueObjects\Timestamp;
|
||||
use App\Framework\Discovery\Storage\ValueObjects\CacheEntry;
|
||||
use App\Framework\Discovery\ValueObjects\CacheLevel;
|
||||
use App\Framework\Discovery\ValueObjects\CacheTier;
|
||||
use App\Framework\Discovery\ValueObjects\DiscoveryContext;
|
||||
use App\Framework\Discovery\ValueObjects\DiscoveryOptions;
|
||||
use App\Framework\Discovery\ValueObjects\ScanType;
|
||||
use App\Framework\Filesystem\FileSystemService;
|
||||
use App\Framework\Serializer\Php\PhpSerializer;
|
||||
use App\Framework\Serializer\Php\PhpSerializerConfig;
|
||||
|
||||
describe('Debug: DiscoveryCacheManager Integration', function () {
|
||||
beforeEach(function () {
|
||||
$cacheDriver = new InMemoryCache();
|
||||
$serializer = new PhpSerializer(PhpSerializerConfig::safe());
|
||||
$this->cache = new GeneralCache($cacheDriver, $serializer);
|
||||
$this->clock = new SystemClock();
|
||||
$this->fileSystemService = new FileSystemService();
|
||||
|
||||
// Use a real existing path with future time
|
||||
$basePath = file_exists('/var/www/html/src') ? '/var/www/html/src' : __DIR__ . '/../../../../src';
|
||||
|
||||
// Use a future time to avoid stale detection
|
||||
$futureTime = new \DateTimeImmutable('2099-01-01 00:00:00');
|
||||
$this->testContext = new DiscoveryContext(
|
||||
paths: [$basePath],
|
||||
scanType: ScanType::FULL,
|
||||
options: new DiscoveryOptions(),
|
||||
startTime: $futureTime
|
||||
);
|
||||
|
||||
$this->testRegistry = new DiscoveryRegistry(
|
||||
attributes: new AttributeRegistry(),
|
||||
interfaces: new InterfaceRegistry(),
|
||||
templates: new TemplateRegistry()
|
||||
);
|
||||
|
||||
// Create cache manager with new services
|
||||
$this->cacheManager = new DiscoveryCacheManager(
|
||||
cache: $this->cache,
|
||||
clock: $this->clock,
|
||||
fileSystemService: $this->fileSystemService,
|
||||
serializer: new CacheEntrySerializer(),
|
||||
stalenessChecker: new StalenessChecker($this->fileSystemService),
|
||||
validator: new CacheEntryValidator(),
|
||||
upgrader: new CacheEntryUpgrader()
|
||||
);
|
||||
});
|
||||
|
||||
it('debugs cache storage and retrieval flow', function () {
|
||||
// Step 1: Store
|
||||
$success = $this->cacheManager->store($this->testContext, $this->testRegistry);
|
||||
expect($success)->toBeTrue('Store should succeed');
|
||||
|
||||
// Step 2: Check cache directly
|
||||
$key = $this->testContext->getCacheKey();
|
||||
$result = $this->cache->get($key);
|
||||
$item = $result->getItem($key);
|
||||
|
||||
expect($item->isHit)->toBeTrue('Cache item should be hit');
|
||||
|
||||
$cacheData = $item->value;
|
||||
expect(is_array($cacheData))->toBeTrue('Cache data should be array');
|
||||
expect(isset($cacheData['registry']))->toBeTrue('Cache should have registry');
|
||||
expect(isset($cacheData['startTime']))->toBeTrue('Cache should have startTime');
|
||||
expect(isset($cacheData['version']))->toBeTrue('Cache should have version');
|
||||
|
||||
// Step 3: Test serializer supports
|
||||
$serializer = new CacheEntrySerializer();
|
||||
$supports = $serializer->supports($cacheData);
|
||||
expect($supports)->toBeTrue('Serializer should support cache data');
|
||||
|
||||
// Step 4: Debug cache data structure
|
||||
expect($cacheData['startTime'])->not->toBeNull('startTime should not be null');
|
||||
expect(is_int($cacheData['startTime']))->toBeTrue('startTime should be int timestamp');
|
||||
|
||||
// Step 5: Test deserialization
|
||||
try {
|
||||
$entry = $serializer->deserialize($cacheData);
|
||||
expect($entry)->toBeInstanceOf(CacheEntry::class, 'Deserialization should return CacheEntry');
|
||||
expect($entry->registry)->toBeInstanceOf(DiscoveryRegistry::class, 'Registry should be DiscoveryRegistry');
|
||||
} catch (\Throwable $e) {
|
||||
$this->fail("Deserialization failed: {$e->getMessage()}\nCache data keys: " . implode(', ', array_keys($cacheData)) . "\nstartTime type: " . gettype($cacheData['startTime'] ?? 'NOT SET'));
|
||||
}
|
||||
|
||||
// Step 5: Test validator
|
||||
$validator = new CacheEntryValidator();
|
||||
$valid = $validator->validate($cacheData);
|
||||
expect($valid)->toBeTrue('Cache data should be valid');
|
||||
|
||||
// Step 6: Test staleness checker
|
||||
$stalenessChecker = new StalenessChecker($this->fileSystemService);
|
||||
$stalenessCheck = $stalenessChecker->check($this->testContext, $entry);
|
||||
|
||||
// Step 7: Finally test retrieval
|
||||
$cached = $this->cacheManager->get($this->testContext);
|
||||
|
||||
if ($cached === null) {
|
||||
$this->fail("Retrieval returned null. Staleness check: " . ($stalenessCheck->isStale ? 'STALE' : 'FRESH'));
|
||||
}
|
||||
|
||||
expect($cached)->toBeInstanceOf(DiscoveryRegistry::class);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,209 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Framework\Discovery\Storage;
|
||||
|
||||
use App\Framework\Cache\Cache;
|
||||
use App\Framework\Cache\CacheItem;
|
||||
use App\Framework\Cache\CacheKey;
|
||||
use App\Framework\Cache\Driver\InMemoryCache;
|
||||
use App\Framework\Cache\GeneralCache;
|
||||
use App\Framework\Core\ValueObjects\Byte;
|
||||
use App\Framework\DateTime\Clock;
|
||||
use App\Framework\DateTime\SystemClock;
|
||||
use App\Framework\Discovery\Results\AttributeRegistry;
|
||||
use App\Framework\Discovery\Results\DiscoveryRegistry;
|
||||
use App\Framework\Discovery\Results\InterfaceRegistry;
|
||||
use App\Framework\Discovery\Results\TemplateRegistry;
|
||||
use App\Framework\Discovery\Storage\DiscoveryCacheManager;
|
||||
use App\Framework\Discovery\Storage\Services\CacheEntrySerializer;
|
||||
use App\Framework\Discovery\Storage\Services\CacheEntryUpgrader;
|
||||
use App\Framework\Discovery\Storage\Services\CacheEntryValidator;
|
||||
use App\Framework\Discovery\Storage\Services\StalenessChecker;
|
||||
use App\Framework\Core\ValueObjects\Timestamp;
|
||||
use App\Framework\Discovery\Storage\ValueObjects\CacheEntry;
|
||||
use App\Framework\Discovery\Storage\ValueObjects\CacheRetrievalResult;
|
||||
use App\Framework\Discovery\Storage\ValueObjects\CacheStorageResult;
|
||||
use App\Framework\Discovery\ValueObjects\CacheLevel;
|
||||
use App\Framework\Discovery\ValueObjects\CacheTier;
|
||||
use App\Framework\Discovery\ValueObjects\DiscoveryContext;
|
||||
use App\Framework\Discovery\ValueObjects\DiscoveryOptions;
|
||||
use App\Framework\Discovery\ValueObjects\ScanType;
|
||||
use App\Framework\Filesystem\FileSystemService;
|
||||
use App\Framework\Serializer\Php\PhpSerializer;
|
||||
use App\Framework\Serializer\Php\PhpSerializerConfig;
|
||||
|
||||
describe('DiscoveryCacheManager - Refactored Implementation', function () {
|
||||
beforeEach(function () {
|
||||
$cacheDriver = new InMemoryCache();
|
||||
$serializer = new PhpSerializer(PhpSerializerConfig::safe());
|
||||
$this->cache = new GeneralCache($cacheDriver, $serializer);
|
||||
$this->clock = new SystemClock();
|
||||
$this->fileSystemService = new FileSystemService();
|
||||
|
||||
// Use a real existing path but with future time to avoid stale detection
|
||||
// This prevents the test from triggering actual discovery while allowing staleness checks
|
||||
$basePath = file_exists('/var/www/html/src') ? '/var/www/html/src' : __DIR__ . '/../../../../src';
|
||||
$testPath = $basePath;
|
||||
|
||||
// Use a future time to avoid stale detection issues
|
||||
$futureTime = new \DateTimeImmutable('2099-01-01 00:00:00');
|
||||
$this->testContext = new DiscoveryContext(
|
||||
paths: [$testPath],
|
||||
scanType: ScanType::FULL,
|
||||
options: new DiscoveryOptions(),
|
||||
startTime: $futureTime
|
||||
);
|
||||
|
||||
$this->testRegistry = new DiscoveryRegistry(
|
||||
attributes: new AttributeRegistry(),
|
||||
interfaces: new InterfaceRegistry(),
|
||||
templates: new TemplateRegistry()
|
||||
);
|
||||
|
||||
// Create cache manager with new services
|
||||
$this->cacheManager = new DiscoveryCacheManager(
|
||||
cache: $this->cache,
|
||||
clock: $this->clock,
|
||||
fileSystemService: $this->fileSystemService,
|
||||
serializer: new CacheEntrySerializer(),
|
||||
stalenessChecker: new StalenessChecker($this->fileSystemService),
|
||||
validator: new CacheEntryValidator(),
|
||||
upgrader: new CacheEntryUpgrader()
|
||||
);
|
||||
});
|
||||
|
||||
it('stores registry using new CacheEntry structure', function () {
|
||||
$success = $this->cacheManager->store($this->testContext, $this->testRegistry);
|
||||
|
||||
expect($success)->toBeTrue();
|
||||
|
||||
// Debug: Check what's in cache
|
||||
$key = $this->testContext->getCacheKey();
|
||||
$result = $this->cache->get($key);
|
||||
$item = $result->getItem($key);
|
||||
|
||||
expect($item->isHit)->toBeTrue('Cache item should be hit');
|
||||
|
||||
// Verify cache structure
|
||||
$cacheData = $item->value;
|
||||
expect(is_array($cacheData))->toBeTrue('Cache data should be array');
|
||||
expect(isset($cacheData['registry']))->toBeTrue('Cache should have registry');
|
||||
expect(isset($cacheData['startTime']))->toBeTrue('Cache should have startTime');
|
||||
expect(isset($cacheData['version']))->toBeTrue('Cache should have version');
|
||||
|
||||
// Verify cache contains the data
|
||||
$cached = $this->cacheManager->get($this->testContext);
|
||||
expect($cached)->not->toBeNull('Cached registry should not be null');
|
||||
expect($cached)->toBeInstanceOf(DiscoveryRegistry::class);
|
||||
expect($cached->isEmpty())->toBe($this->testRegistry->isEmpty());
|
||||
});
|
||||
|
||||
it('retrieves cached registry using new services', function () {
|
||||
// Store first
|
||||
$this->cacheManager->store($this->testContext, $this->testRegistry);
|
||||
|
||||
// Retrieve
|
||||
$cached = $this->cacheManager->get($this->testContext);
|
||||
|
||||
expect($cached)->toBeInstanceOf(DiscoveryRegistry::class);
|
||||
expect($cached->isEmpty())->toBe($this->testRegistry->isEmpty());
|
||||
});
|
||||
|
||||
it('handles cache miss correctly', function () {
|
||||
// Don't store anything
|
||||
$cached = $this->cacheManager->get($this->testContext);
|
||||
|
||||
expect($cached)->toBeNull();
|
||||
});
|
||||
|
||||
it('stores and retrieves with version information', function () {
|
||||
$success = $this->cacheManager->store($this->testContext, $this->testRegistry);
|
||||
expect($success)->toBeTrue();
|
||||
|
||||
// Retrieve should work
|
||||
$cached = $this->cacheManager->get($this->testContext);
|
||||
expect($cached)->toBeInstanceOf(DiscoveryRegistry::class);
|
||||
});
|
||||
|
||||
it('handles cache invalidation', function () {
|
||||
// Store
|
||||
$this->cacheManager->store($this->testContext, $this->testRegistry);
|
||||
|
||||
// Invalidate
|
||||
$invalidated = $this->cacheManager->invalidate($this->testContext);
|
||||
expect($invalidated)->toBeTrue();
|
||||
|
||||
// Should not be retrievable
|
||||
$cached = $this->cacheManager->get($this->testContext);
|
||||
expect($cached)->toBeNull();
|
||||
});
|
||||
|
||||
it('uses CacheEntrySerializer for serialization', function () {
|
||||
$success = $this->cacheManager->store($this->testContext, $this->testRegistry);
|
||||
expect($success)->toBeTrue();
|
||||
|
||||
// Verify cache contains serialized CacheEntry structure
|
||||
$key = $this->testContext->getCacheKey();
|
||||
$result = $this->cache->get($key);
|
||||
$item = $result->getItem($key);
|
||||
|
||||
expect($item->isHit)->toBeTrue();
|
||||
|
||||
// Data should be an array (serialized CacheEntry)
|
||||
$data = $item->value;
|
||||
expect(is_array($data))->toBeTrue();
|
||||
expect(isset($data['registry']))->toBeTrue();
|
||||
expect(isset($data['version']))->toBeTrue();
|
||||
});
|
||||
|
||||
it('uses StalenessChecker for staleness detection', function () {
|
||||
// Store with future time
|
||||
$this->cacheManager->store($this->testContext, $this->testRegistry);
|
||||
|
||||
// Retrieve should work (cache is fresh)
|
||||
$cached = $this->cacheManager->get($this->testContext);
|
||||
expect($cached)->toBeInstanceOf(DiscoveryRegistry::class);
|
||||
});
|
||||
|
||||
it('handles incremental scans as stale', function () {
|
||||
// Store first
|
||||
$this->cacheManager->store($this->testContext, $this->testRegistry);
|
||||
|
||||
// Create incremental context
|
||||
$incrementalContext = new DiscoveryContext(
|
||||
paths: $this->testContext->paths,
|
||||
scanType: ScanType::INCREMENTAL,
|
||||
options: new DiscoveryOptions(),
|
||||
startTime: $this->testContext->startTime
|
||||
);
|
||||
|
||||
// Should return null (stale)
|
||||
$cached = $this->cacheManager->get($incrementalContext);
|
||||
expect($cached)->toBeNull();
|
||||
});
|
||||
|
||||
it('uses CacheEntryValidator for validation', function () {
|
||||
// Store valid data
|
||||
$this->cacheManager->store($this->testContext, $this->testRegistry);
|
||||
|
||||
// Should retrieve successfully
|
||||
$cached = $this->cacheManager->get($this->testContext);
|
||||
expect($cached)->toBeInstanceOf(DiscoveryRegistry::class);
|
||||
});
|
||||
|
||||
it('uses CacheEntryUpgrader for old format migration', function () {
|
||||
// Store old format directly (just DiscoveryRegistry)
|
||||
$key = $this->testContext->getCacheKey();
|
||||
$oldFormatItem = CacheItem::forSet($key, $this->testRegistry);
|
||||
$this->cache->set($oldFormatItem);
|
||||
|
||||
// Retrieve should upgrade automatically
|
||||
$cached = $this->cacheManager->get($this->testContext);
|
||||
|
||||
// Should work (upgraded)
|
||||
expect($cached)->toBeInstanceOf(DiscoveryRegistry::class);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Framework\Discovery\Storage\Services;
|
||||
|
||||
use App\Framework\Discovery\Results\DiscoveryRegistry;
|
||||
use App\Framework\Discovery\Results\AttributeRegistry;
|
||||
use App\Framework\Discovery\Results\InterfaceRegistry;
|
||||
use App\Framework\Discovery\Results\TemplateRegistry;
|
||||
use App\Framework\Discovery\Storage\Services\CacheEntrySerializer;
|
||||
use App\Framework\Core\ValueObjects\Timestamp;
|
||||
use App\Framework\Discovery\Storage\ValueObjects\CacheEntry;
|
||||
use App\Framework\Discovery\ValueObjects\CacheLevel;
|
||||
use App\Framework\Discovery\ValueObjects\CacheTier;
|
||||
|
||||
describe('CacheEntrySerializer', function () {
|
||||
beforeEach(function () {
|
||||
$this->serializer = new CacheEntrySerializer();
|
||||
});
|
||||
|
||||
it('serializes CacheEntry to array', function () {
|
||||
$registry = new DiscoveryRegistry(
|
||||
attributes: new AttributeRegistry(),
|
||||
interfaces: new InterfaceRegistry(),
|
||||
templates: new TemplateRegistry()
|
||||
);
|
||||
|
||||
$entry = new CacheEntry(
|
||||
registry: $registry,
|
||||
createdAt: Timestamp::fromDateTime(new \DateTimeImmutable('2024-01-01 12:00:00')),
|
||||
version: 'v1-abc12345',
|
||||
cacheLevel: CacheLevel::NORMAL,
|
||||
cacheTier: CacheTier::HOT
|
||||
);
|
||||
|
||||
$serialized = $this->serializer->serialize($entry);
|
||||
|
||||
expect($serialized)->toBeArray();
|
||||
// Registry should be serialized as string when explicitly serialized
|
||||
expect(is_string($serialized['registry']))->toBeTrue('Registry should be serialized as string');
|
||||
expect(isset($serialized['__registry_serialized__']))->toBeTrue('Should have serialization flag');
|
||||
expect($serialized['__registry_serialized__'])->toBeTrue('Serialization flag should be true');
|
||||
expect($serialized['version'])->toBe('v1-abc12345');
|
||||
});
|
||||
|
||||
it('deserializes DiscoveryRegistry (old format)', function () {
|
||||
$registry = new DiscoveryRegistry(
|
||||
attributes: new AttributeRegistry(),
|
||||
interfaces: new InterfaceRegistry(),
|
||||
templates: new TemplateRegistry()
|
||||
);
|
||||
|
||||
$entry = $this->serializer->deserialize($registry);
|
||||
|
||||
expect($entry)->toBeInstanceOf(CacheEntry::class);
|
||||
expect($entry->registry)->toBe($registry);
|
||||
expect($entry->isCompressed())->toBeFalse();
|
||||
});
|
||||
|
||||
it('deserializes array (new format)', function () {
|
||||
$registry = new DiscoveryRegistry(
|
||||
attributes: new AttributeRegistry(),
|
||||
interfaces: new InterfaceRegistry(),
|
||||
templates: new TemplateRegistry()
|
||||
);
|
||||
|
||||
$timestamp = Timestamp::fromDateTime(new \DateTimeImmutable('2024-01-01 12:00:00'));
|
||||
$data = [
|
||||
'registry' => $registry,
|
||||
'startTime' => $timestamp->toTimestamp(), // Serialize as int timestamp
|
||||
'version' => 'v1-abc12345',
|
||||
'cacheLevel' => CacheLevel::NORMAL->value,
|
||||
'cacheTier' => CacheTier::HOT->value,
|
||||
];
|
||||
|
||||
$entry = $this->serializer->deserialize($data);
|
||||
|
||||
expect($entry)->toBeInstanceOf(CacheEntry::class);
|
||||
expect($entry->registry)->toBe($registry);
|
||||
expect($entry->version)->toBe('v1-abc12345');
|
||||
});
|
||||
|
||||
it('supports DiscoveryRegistry format', function () {
|
||||
$registry = new DiscoveryRegistry(
|
||||
attributes: new AttributeRegistry(),
|
||||
interfaces: new InterfaceRegistry(),
|
||||
templates: new TemplateRegistry()
|
||||
);
|
||||
|
||||
expect($this->serializer->supports($registry))->toBeTrue();
|
||||
});
|
||||
|
||||
it('supports array format', function () {
|
||||
$data = [
|
||||
'registry' => new DiscoveryRegistry(
|
||||
attributes: new AttributeRegistry(),
|
||||
interfaces: new InterfaceRegistry(),
|
||||
templates: new TemplateRegistry()
|
||||
),
|
||||
];
|
||||
|
||||
expect($this->serializer->supports($data))->toBeTrue();
|
||||
});
|
||||
|
||||
it('does not support invalid format', function () {
|
||||
expect($this->serializer->supports('invalid'))->toBeFalse();
|
||||
expect($this->serializer->supports(123))->toBeFalse();
|
||||
expect($this->serializer->supports(null))->toBeFalse();
|
||||
});
|
||||
|
||||
it('throws exception when deserializing unsupported format', function () {
|
||||
expect(fn() => $this->serializer->deserialize('invalid'))
|
||||
->toThrow(\InvalidArgumentException::class);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Framework\Discovery\Storage\Services;
|
||||
|
||||
use App\Framework\Discovery\Storage\Services\StalenessChecker;
|
||||
use App\Framework\Core\ValueObjects\Timestamp;
|
||||
use App\Framework\Discovery\Storage\ValueObjects\CacheEntry;
|
||||
use App\Framework\Discovery\Storage\ValueObjects\StalenessCheckResult;
|
||||
use App\Framework\Discovery\ValueObjects\DiscoveryContext;
|
||||
use App\Framework\Discovery\ValueObjects\DiscoveryOptions;
|
||||
use App\Framework\Discovery\ValueObjects\ScanType;
|
||||
use App\Framework\Discovery\Results\DiscoveryRegistry;
|
||||
use App\Framework\Discovery\Results\AttributeRegistry;
|
||||
use App\Framework\Discovery\Results\InterfaceRegistry;
|
||||
use App\Framework\Discovery\Results\TemplateRegistry;
|
||||
use App\Framework\Discovery\ValueObjects\CacheLevel;
|
||||
use App\Framework\Discovery\ValueObjects\CacheTier;
|
||||
use App\Framework\Filesystem\FileSystemService;
|
||||
use App\Framework\Filesystem\ValueObjects\FilePath;
|
||||
use App\Framework\Filesystem\ValueObjects\FileMetadata;
|
||||
|
||||
use Mockery;
|
||||
|
||||
describe('StalenessChecker', function () {
|
||||
beforeEach(function () {
|
||||
// Use real FileSystemService since it's final
|
||||
$this->fileSystemService = new FileSystemService();
|
||||
$this->checker = new StalenessChecker($this->fileSystemService);
|
||||
});
|
||||
|
||||
it('returns stale for incremental scan', function () {
|
||||
$registry = new DiscoveryRegistry(
|
||||
attributes: new AttributeRegistry(),
|
||||
interfaces: new InterfaceRegistry(),
|
||||
templates: new TemplateRegistry()
|
||||
);
|
||||
|
||||
$entry = new CacheEntry(
|
||||
registry: $registry,
|
||||
createdAt: Timestamp::fromDateTime(new \DateTimeImmutable()),
|
||||
version: 'v1-abc12345',
|
||||
cacheLevel: CacheLevel::NORMAL,
|
||||
cacheTier: CacheTier::HOT
|
||||
);
|
||||
|
||||
$context = new DiscoveryContext(
|
||||
paths: ['/test/path'],
|
||||
scanType: ScanType::INCREMENTAL,
|
||||
options: new DiscoveryOptions(),
|
||||
startTime: new \DateTimeImmutable()
|
||||
);
|
||||
|
||||
$result = $this->checker->check($context, $entry);
|
||||
|
||||
expect($result->isStale)->toBeTrue();
|
||||
expect($result->reason)->toBe('incremental_scan');
|
||||
});
|
||||
|
||||
it('checks staleness with real filesystem', function () {
|
||||
$registry = new DiscoveryRegistry(
|
||||
attributes: new AttributeRegistry(),
|
||||
interfaces: new InterfaceRegistry(),
|
||||
templates: new TemplateRegistry()
|
||||
);
|
||||
|
||||
// Use a past time to ensure cache is stale (directory was modified after cache creation)
|
||||
$createdAt = Timestamp::fromDateTime(new \DateTimeImmutable('2020-01-01 12:00:00'));
|
||||
$entry = new CacheEntry(
|
||||
registry: $registry,
|
||||
createdAt: $createdAt,
|
||||
version: 'v1-abc12345',
|
||||
cacheLevel: CacheLevel::NORMAL,
|
||||
cacheTier: CacheTier::HOT
|
||||
);
|
||||
|
||||
// Use real existing path
|
||||
$basePath = file_exists('/var/www/html/src') ? '/var/www/html/src' : __DIR__ . '/../../../../src';
|
||||
$context = new DiscoveryContext(
|
||||
paths: [$basePath],
|
||||
scanType: ScanType::FULL,
|
||||
options: new DiscoveryOptions(),
|
||||
startTime: new \DateTimeImmutable('2020-01-01 13:00:00')
|
||||
);
|
||||
|
||||
$result = $this->checker->check($context, $entry);
|
||||
|
||||
// Should be stale (directory modified after cache creation)
|
||||
expect($result)->toBeInstanceOf(StalenessCheckResult::class);
|
||||
expect($result->isStale)->toBeTrue();
|
||||
});
|
||||
|
||||
it('handles non-existent paths gracefully', function () {
|
||||
$registry = new DiscoveryRegistry(
|
||||
attributes: new AttributeRegistry(),
|
||||
interfaces: new InterfaceRegistry(),
|
||||
templates: new TemplateRegistry()
|
||||
);
|
||||
|
||||
$entry = new CacheEntry(
|
||||
registry: $registry,
|
||||
createdAt: Timestamp::fromDateTime(new \DateTimeImmutable()),
|
||||
version: 'v1-abc12345',
|
||||
cacheLevel: CacheLevel::NORMAL,
|
||||
cacheTier: CacheTier::HOT
|
||||
);
|
||||
|
||||
$context = new DiscoveryContext(
|
||||
paths: ['/non/existent/path'],
|
||||
scanType: ScanType::FULL,
|
||||
options: new DiscoveryOptions(),
|
||||
startTime: new \DateTimeImmutable()
|
||||
);
|
||||
|
||||
$result = $this->checker->check($context, $entry);
|
||||
|
||||
// Should assume stale on error (conservative)
|
||||
expect($result->isStale)->toBeTrue();
|
||||
});
|
||||
});
|
||||
|
||||
42
tests/Framework/Discovery/Storage/StructuredTestPlan.md
Normal file
42
tests/Framework/Discovery/Storage/StructuredTestPlan.md
Normal file
@@ -0,0 +1,42 @@
|
||||
# Strukturierter Test-Plan für DiscoveryCacheManager Refactoring
|
||||
|
||||
## Problem
|
||||
- Discovery läuft in Timeout (>10 Sekunden)
|
||||
- Vollständige Discovery ist zu langsam für Unit-Tests
|
||||
- Tests sollten isoliert und schnell sein
|
||||
|
||||
## Test-Strategie
|
||||
|
||||
### 1. Unit-Tests (Isoliert, ohne Discovery)
|
||||
- ✅ Value Objects Tests (bereits vorhanden)
|
||||
- ✅ Service Tests (bereits vorhanden)
|
||||
- ⚠️ CacheManager Tests mit Mock-Daten
|
||||
|
||||
### 2. Integration-Tests (Schnell, mit Mock-Registry)
|
||||
- Cache Storage/Retrieval mit vorgefertigten Registry-Objekten
|
||||
- Keine echte Discovery-Performance
|
||||
|
||||
### 3. Performance-Tests (Optional, separat)
|
||||
- Nur wenn nötig, mit Timeout-Schutz
|
||||
|
||||
## Test-Kategorien
|
||||
|
||||
### Kategorie 1: Value Objects (✅ Fertig)
|
||||
- CacheEntry
|
||||
- CacheEntryMetadata
|
||||
- StalenessCheckResult
|
||||
- CacheRetrievalResult
|
||||
- CacheStorageResult
|
||||
|
||||
### Kategorie 2: Services (✅ Fertig)
|
||||
- CacheEntrySerializer
|
||||
- StalenessChecker
|
||||
- CacheEntryValidator
|
||||
- CacheEntryUpgrader
|
||||
|
||||
### Kategorie 3: CacheManager (⚠️ Zu testen)
|
||||
- Store/Retrieve ohne Discovery
|
||||
- Serialization/Deserialization
|
||||
- Upgrade von altem Format
|
||||
- Staleness Detection
|
||||
|
||||
69
tests/Framework/Discovery/Storage/TEST_SUMMARY.md
Normal file
69
tests/Framework/Discovery/Storage/TEST_SUMMARY.md
Normal file
@@ -0,0 +1,69 @@
|
||||
# Test-Zusammenfassung: DiscoveryCacheManager Refactoring
|
||||
|
||||
## ✅ Erfolgreich getestet (24 Tests, 69 Assertions)
|
||||
|
||||
### Value Objects Tests (5 Tests)
|
||||
- ✅ CacheEntry - Erstellung, Serialisierung, Kompression
|
||||
- ✅ CacheEntryMetadata - Metadaten-Struktur
|
||||
- ✅ StalenessCheckResult - Fresh/Stale Logik
|
||||
- ✅ CacheRetrievalResult - Retrieval-Ergebnisse
|
||||
- ✅ CacheStorageResult - Storage-Ergebnisse
|
||||
|
||||
### Service Tests (19 Tests)
|
||||
- ✅ CacheEntrySerializer - Serialisierung/Deserialisierung
|
||||
- ✅ StalenessChecker - Staleness-Detection
|
||||
- ✅ CacheEntryValidator - Validierung
|
||||
- ✅ CacheEntryUpgrader - Format-Upgrade
|
||||
|
||||
## ⚠️ Bekannte Probleme
|
||||
|
||||
### 1. Discovery Timeout
|
||||
- **Problem**: Vollständige Discovery dauert >10 Sekunden
|
||||
- **Ursache**: Verarbeitung von 3000+ Dateien
|
||||
- **Lösung**: Tests verwenden isolierte Mock-Daten, keine echte Discovery
|
||||
|
||||
### 2. CacheManager Integration Test
|
||||
- **Problem**: `get()` gibt null zurück trotz vorhandener Cache-Daten
|
||||
- **Mögliche Ursachen**:
|
||||
- Staleness-Prüfung schlägt fehl (Pfad wurde modifiziert)
|
||||
- Deserialisierung schlägt fehl
|
||||
- Validierung schlägt fehl
|
||||
- **Status**: In Bearbeitung
|
||||
|
||||
## Test-Strategie
|
||||
|
||||
### ✅ Unit-Tests (Isoliert)
|
||||
- Value Objects: Alle Tests bestehen
|
||||
- Services: Alle Tests bestehen
|
||||
- Keine Discovery-Performance-Probleme
|
||||
|
||||
### ⚠️ Integration-Tests
|
||||
- CacheManager: Teilweise funktional
|
||||
- Benötigt weitere Debugging
|
||||
|
||||
### 📝 Empfehlungen
|
||||
|
||||
1. **Für schnelle Tests**: Nur Unit-Tests ausführen
|
||||
```bash
|
||||
./vendor/bin/pest tests/Framework/Discovery/Storage/ValueObjects tests/Framework/Discovery/Storage/Services
|
||||
```
|
||||
|
||||
2. **Für vollständige Tests**: Mit Timeout-Schutz
|
||||
```bash
|
||||
timeout 30 ./vendor/bin/pest tests/Framework/Discovery/Storage
|
||||
```
|
||||
|
||||
3. **Für Production-Tests**: Separate Performance-Tests mit Mock-Daten
|
||||
|
||||
## Refactoring-Status
|
||||
|
||||
### ✅ Abgeschlossen
|
||||
- Phase 1: Value Objects (5 VOs)
|
||||
- Phase 2: Services (4 Services)
|
||||
- Phase 3: DiscoveryCacheManager Refactoring
|
||||
- Phase 4: Tests (24 Tests)
|
||||
|
||||
### ⚠️ Offen
|
||||
- CacheManager Integration-Tests debuggen
|
||||
- Performance-Optimierung für Discovery
|
||||
|
||||
41
tests/Framework/Discovery/Storage/TIMESTAMP_MIGRATION.md
Normal file
41
tests/Framework/Discovery/Storage/TIMESTAMP_MIGRATION.md
Normal file
@@ -0,0 +1,41 @@
|
||||
# Timestamp Migration - Status
|
||||
|
||||
## ✅ Abgeschlossen
|
||||
|
||||
### CacheEntry
|
||||
- ✅ Verwendet jetzt `Timestamp` statt `DateTimeInterface`
|
||||
- ✅ `toArray()` serialisiert als `int` timestamp
|
||||
- ✅ `fromArray()` deserialisiert von `int`, `float`, `string` oder `DateTimeInterface`
|
||||
|
||||
### Services
|
||||
- ✅ `StalenessChecker` konvertiert `Timestamp` zu `DateTimeInterface` für Vergleich
|
||||
- ✅ `CacheEntryUpgrader` konvertiert `DateTimeInterface` zu `Timestamp`
|
||||
- ✅ `CacheEntrySerializer` verwendet `Timestamp::now()` für Fallbacks
|
||||
|
||||
### DiscoveryCacheManager
|
||||
- ✅ Konvertiert `DiscoveryContext->startTime` (DateTimeInterface) zu `Timestamp` beim Erstellen von `CacheEntry`
|
||||
- ✅ Konvertiert `Timestamp` zu `DateTimeInterface` für `CacheEntryMetadata`
|
||||
|
||||
## ⚠️ Bekanntes Problem
|
||||
|
||||
### Registry Deserialization
|
||||
**Problem**: Registry wird als `__PHP_Incomplete_Class` deserialisiert
|
||||
|
||||
**Ursache**:
|
||||
- Cache serialisiert das Array mit Registry-Objekt
|
||||
- Beim Deserialisieren wird Registry zu `__PHP_Incomplete_Class`, wenn die Klasse nicht geladen ist
|
||||
- Dies passiert, wenn `unserialize()` aufgerufen wird, bevor die Klasse geladen ist
|
||||
|
||||
**Mögliche Lösungen**:
|
||||
1. Registry explizit serialisieren, bevor es in Array eingefügt wird
|
||||
2. Cache-Serializer so konfigurieren, dass er Klassen automatisch lädt
|
||||
3. Registry-Objekt separat serialisieren und als String im Array speichern
|
||||
|
||||
**Status**: In Bearbeitung
|
||||
|
||||
## Nächste Schritte
|
||||
|
||||
1. Registry-Deserialisierung Problem lösen
|
||||
2. Integration-Tests debuggen
|
||||
3. Performance-Tests mit Timestamp
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Framework\Discovery\Storage\ValueObjects;
|
||||
|
||||
use App\Framework\Core\ValueObjects\Timestamp;
|
||||
use App\Framework\Discovery\Results\AttributeRegistry;
|
||||
use App\Framework\Discovery\Results\DiscoveryRegistry;
|
||||
use App\Framework\Discovery\Results\InterfaceRegistry;
|
||||
use App\Framework\Discovery\Results\TemplateRegistry;
|
||||
use App\Framework\Discovery\Storage\ValueObjects\CacheEntry;
|
||||
use App\Framework\Discovery\ValueObjects\CacheLevel;
|
||||
use App\Framework\Discovery\ValueObjects\CacheTier;
|
||||
|
||||
use function Pest\Faker\faker;
|
||||
|
||||
describe('CacheEntry', function () {
|
||||
it('creates entry with DiscoveryRegistry', function () {
|
||||
$registry = new DiscoveryRegistry(
|
||||
attributes: new AttributeRegistry(),
|
||||
interfaces: new InterfaceRegistry(),
|
||||
templates: new TemplateRegistry()
|
||||
);
|
||||
|
||||
$createdAt = Timestamp::fromDateTime(new \DateTimeImmutable('2024-01-01 12:00:00'));
|
||||
$entry = new CacheEntry(
|
||||
registry: $registry,
|
||||
createdAt: $createdAt,
|
||||
version: 'v1-abc12345',
|
||||
cacheLevel: CacheLevel::NORMAL,
|
||||
cacheTier: CacheTier::HOT
|
||||
);
|
||||
|
||||
expect($entry->registry)->toBe($registry);
|
||||
expect($entry->createdAt)->toBe($createdAt);
|
||||
expect($entry->version)->toBe('v1-abc12345');
|
||||
expect($entry->cacheLevel)->toBe(CacheLevel::NORMAL);
|
||||
expect($entry->cacheTier)->toBe(CacheTier::HOT);
|
||||
expect($entry->isCompressed())->toBeFalse();
|
||||
});
|
||||
|
||||
it('creates entry with compressed array', function () {
|
||||
$compressedData = ['__discovery_compressed__' => true, 'data' => 'compressed'];
|
||||
|
||||
$createdAt = new \DateTimeImmutable('2024-01-01 12:00:00');
|
||||
$entry = new CacheEntry(
|
||||
registry: $compressedData,
|
||||
createdAt: $createdAt,
|
||||
version: 'v1-abc12345',
|
||||
cacheLevel: CacheLevel::EXTENDED,
|
||||
cacheTier: CacheTier::ARCHIVE
|
||||
);
|
||||
|
||||
expect($entry->isCompressed())->toBeTrue();
|
||||
expect($entry->registry)->toBe($compressedData);
|
||||
});
|
||||
|
||||
it('creates from array', function () {
|
||||
$registry = new DiscoveryRegistry(
|
||||
attributes: new AttributeRegistry(),
|
||||
interfaces: new InterfaceRegistry(),
|
||||
templates: new TemplateRegistry()
|
||||
);
|
||||
|
||||
$createdAt = Timestamp::fromDateTime(new \DateTimeImmutable('2024-01-01 12:00:00'));
|
||||
$data = [
|
||||
'registry' => $registry,
|
||||
'startTime' => $createdAt->toTimestamp(),
|
||||
'version' => 'v1-abc12345',
|
||||
'cacheLevel' => CacheLevel::NORMAL->value,
|
||||
'cacheTier' => CacheTier::HOT->value,
|
||||
];
|
||||
|
||||
$entry = CacheEntry::fromArray($data);
|
||||
|
||||
expect($entry->registry)->toBe($registry);
|
||||
expect($entry->createdAt->toTimestamp())->toBe($createdAt->toTimestamp());
|
||||
expect($entry->version)->toBe('v1-abc12345');
|
||||
expect($entry->cacheLevel)->toBe(CacheLevel::NORMAL);
|
||||
expect($entry->cacheTier)->toBe(CacheTier::HOT);
|
||||
});
|
||||
|
||||
it('converts to array', function () {
|
||||
$registry = new DiscoveryRegistry(
|
||||
attributes: new AttributeRegistry(),
|
||||
interfaces: new InterfaceRegistry(),
|
||||
templates: new TemplateRegistry()
|
||||
);
|
||||
|
||||
$createdAt = Timestamp::fromDateTime(new \DateTimeImmutable('2024-01-01 12:00:00'));
|
||||
$entry = new CacheEntry(
|
||||
registry: $registry,
|
||||
createdAt: $createdAt,
|
||||
version: 'v1-abc12345',
|
||||
cacheLevel: CacheLevel::NORMAL,
|
||||
cacheTier: CacheTier::HOT
|
||||
);
|
||||
|
||||
$array = $entry->toArray();
|
||||
|
||||
expect($array)->toBeArray();
|
||||
expect($array['registry'])->toBe($registry);
|
||||
expect($array['startTime'])->toBe($createdAt->toTimestamp());
|
||||
expect($array['version'])->toBe('v1-abc12345');
|
||||
expect($array['cacheLevel'])->toBe(CacheLevel::NORMAL->value);
|
||||
expect($array['cacheTier'])->toBe(CacheTier::HOT->value);
|
||||
});
|
||||
|
||||
it('throws exception when getting registry from compressed entry', function () {
|
||||
$compressedData = ['__discovery_compressed__' => true, 'data' => 'compressed'];
|
||||
|
||||
$entry = new CacheEntry(
|
||||
registry: $compressedData,
|
||||
createdAt: Timestamp::now(),
|
||||
version: 'v1-abc12345',
|
||||
cacheLevel: CacheLevel::NORMAL,
|
||||
cacheTier: CacheTier::HOT
|
||||
);
|
||||
|
||||
expect(fn() => $entry->getRegistry())->toThrow(\RuntimeException::class);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Framework\Discovery\Storage\ValueObjects;
|
||||
|
||||
use App\Framework\Discovery\Results\DiscoveryRegistry;
|
||||
use App\Framework\Discovery\Results\AttributeRegistry;
|
||||
use App\Framework\Discovery\Results\InterfaceRegistry;
|
||||
use App\Framework\Discovery\Results\TemplateRegistry;
|
||||
use App\Framework\Core\ValueObjects\Timestamp;
|
||||
use App\Framework\Discovery\Storage\ValueObjects\CacheEntry;
|
||||
use App\Framework\Discovery\Storage\ValueObjects\CacheRetrievalResult;
|
||||
use App\Framework\Discovery\Storage\ValueObjects\StalenessCheckResult;
|
||||
use App\Framework\Discovery\ValueObjects\CacheLevel;
|
||||
use App\Framework\Discovery\ValueObjects\CacheTier;
|
||||
|
||||
describe('CacheRetrievalResult', function () {
|
||||
it('creates found result', function () {
|
||||
$registry = new DiscoveryRegistry(
|
||||
attributes: new AttributeRegistry(),
|
||||
interfaces: new InterfaceRegistry(),
|
||||
templates: new TemplateRegistry()
|
||||
);
|
||||
|
||||
$entry = new CacheEntry(
|
||||
registry: $registry,
|
||||
createdAt: Timestamp::fromDateTime(new \DateTimeImmutable()),
|
||||
version: 'v1-abc12345',
|
||||
cacheLevel: CacheLevel::NORMAL,
|
||||
cacheTier: CacheTier::HOT
|
||||
);
|
||||
|
||||
$result = CacheRetrievalResult::found($entry);
|
||||
|
||||
expect($result->found)->toBeTrue();
|
||||
expect($result->entry)->toBe($entry);
|
||||
expect($result->reason)->toBeNull();
|
||||
expect($result->isUsable())->toBeTrue();
|
||||
});
|
||||
|
||||
it('creates found result with staleness check', function () {
|
||||
$registry = new DiscoveryRegistry(
|
||||
attributes: new AttributeRegistry(),
|
||||
interfaces: new InterfaceRegistry(),
|
||||
templates: new TemplateRegistry()
|
||||
);
|
||||
|
||||
$entry = new CacheEntry(
|
||||
registry: $registry,
|
||||
createdAt: Timestamp::fromDateTime(new \DateTimeImmutable()),
|
||||
version: 'v1-abc12345',
|
||||
cacheLevel: CacheLevel::NORMAL,
|
||||
cacheTier: CacheTier::HOT
|
||||
);
|
||||
|
||||
$stalenessCheck = StalenessCheckResult::fresh();
|
||||
$result = CacheRetrievalResult::found($entry, $stalenessCheck);
|
||||
|
||||
expect($result->found)->toBeTrue();
|
||||
expect($result->stalenessCheck)->toBe($stalenessCheck);
|
||||
expect($result->isUsable())->toBeTrue();
|
||||
});
|
||||
|
||||
it('creates not found result', function () {
|
||||
$result = CacheRetrievalResult::notFound('not_found');
|
||||
|
||||
expect($result->found)->toBeFalse();
|
||||
expect($result->entry)->toBeNull();
|
||||
expect($result->reason)->toBe('not_found');
|
||||
expect($result->isUsable())->toBeFalse();
|
||||
});
|
||||
|
||||
it('creates stale result', function () {
|
||||
$registry = new DiscoveryRegistry(
|
||||
attributes: new AttributeRegistry(),
|
||||
interfaces: new InterfaceRegistry(),
|
||||
templates: new TemplateRegistry()
|
||||
);
|
||||
|
||||
$entry = new CacheEntry(
|
||||
registry: $registry,
|
||||
createdAt: Timestamp::fromDateTime(new \DateTimeImmutable()),
|
||||
version: 'v1-abc12345',
|
||||
cacheLevel: CacheLevel::NORMAL,
|
||||
cacheTier: CacheTier::HOT
|
||||
);
|
||||
|
||||
$stalenessCheck = StalenessCheckResult::stale('directory_modified');
|
||||
$result = CacheRetrievalResult::stale($entry, $stalenessCheck);
|
||||
|
||||
expect($result->found)->toBeTrue();
|
||||
expect($result->entry)->toBe($entry);
|
||||
expect($result->reason)->toBe('stale');
|
||||
expect($result->stalenessCheck)->toBe($stalenessCheck);
|
||||
expect($result->isUsable())->toBeFalse();
|
||||
});
|
||||
|
||||
it('throws exception when found without entry', function () {
|
||||
expect(fn() => new CacheRetrievalResult(
|
||||
found: true,
|
||||
entry: null
|
||||
))->toThrow(\InvalidArgumentException::class);
|
||||
});
|
||||
|
||||
it('throws exception when not found with entry', function () {
|
||||
$registry = new DiscoveryRegistry(
|
||||
attributes: new AttributeRegistry(),
|
||||
interfaces: new InterfaceRegistry(),
|
||||
templates: new TemplateRegistry()
|
||||
);
|
||||
|
||||
$entry = new CacheEntry(
|
||||
registry: $registry,
|
||||
createdAt: Timestamp::fromDateTime(new \DateTimeImmutable()),
|
||||
version: 'v1-abc12345',
|
||||
cacheLevel: CacheLevel::NORMAL,
|
||||
cacheTier: CacheTier::HOT
|
||||
);
|
||||
|
||||
expect(fn() => new CacheRetrievalResult(
|
||||
found: false,
|
||||
entry: $entry
|
||||
))->toThrow(\InvalidArgumentException::class);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Framework\Discovery\Storage\ValueObjects;
|
||||
|
||||
use App\Framework\Discovery\Storage\ValueObjects\StalenessCheckResult;
|
||||
|
||||
describe('StalenessCheckResult', function () {
|
||||
it('creates fresh result', function () {
|
||||
$result = StalenessCheckResult::fresh();
|
||||
|
||||
expect($result->isStale)->toBeFalse();
|
||||
expect($result->isFresh())->toBeTrue();
|
||||
expect($result->reason)->toBeNull();
|
||||
expect($result->modifiedPaths)->toBe([]);
|
||||
});
|
||||
|
||||
it('creates stale result with reason', function () {
|
||||
$result = StalenessCheckResult::stale('directory_modified', ['/path/to/src']);
|
||||
|
||||
expect($result->isStale)->toBeTrue();
|
||||
expect($result->isFresh())->toBeFalse();
|
||||
expect($result->reason)->toBe('directory_modified');
|
||||
expect($result->modifiedPaths)->toBe(['/path/to/src']);
|
||||
});
|
||||
|
||||
it('creates stale result without paths', function () {
|
||||
$result = StalenessCheckResult::stale('incremental_scan');
|
||||
|
||||
expect($result->isStale)->toBeTrue();
|
||||
expect($result->reason)->toBe('incremental_scan');
|
||||
expect($result->modifiedPaths)->toBe([]);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user