From 15c006f22916047770e72237f88423519771cb1a Mon Sep 17 00:00:00 2001 From: Nick Manning Date: Sat, 11 Jul 2026 08:16:34 +0200 Subject: [PATCH] fix(files): report new files as new in the files:scan summary The addToCache listener decides between NodeAddedToCache and FileCacheUpdated by checking `if ($fileId)`, but Cache\Scanner emits $fileId = -1 for newly inserted entries, which is truthy. As a result the NodeAddedToCache branch was unreachable and `occ files:scan` has reported every new file as "Updated" (never "New") since the summary was introduced in 292c0e53f8a. Check for the actual -1 sentinel instead, and add a regression test asserting that a first scan dispatches NodeAddedToCache and a re-scan of a modified file dispatches FileCacheUpdated. Assisted-by: ClaudeCode:claude-fable-5 Signed-off-by: Nick Manning --- lib/private/Files/Utils/Scanner.php | 2 +- tests/lib/Files/Utils/ScannerTest.php | 49 +++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/lib/private/Files/Utils/Scanner.php b/lib/private/Files/Utils/Scanner.php index 2831e4f4d8299..718877f00a12f 100644 --- a/lib/private/Files/Utils/Scanner.php +++ b/lib/private/Files/Utils/Scanner.php @@ -208,7 +208,7 @@ public function scan(string $dir = '', $recursive = \OC\Files\Cache\Scanner::SCA }); $scanner->listen('\OC\Files\Cache\Scanner', 'addToCache', function ($path, $storageId, $data, $fileId) use ($storage): void { $this->triggerPropagator($storage, $path); - if ($fileId) { + if ($fileId !== -1) { $this->eventDispatcher->dispatchTyped(new FileCacheUpdated($storage, $path)); } else { $this->eventDispatcher->dispatchTyped(new NodeAddedToCache($storage, $path)); diff --git a/tests/lib/Files/Utils/ScannerTest.php b/tests/lib/Files/Utils/ScannerTest.php index 435e7b38ee908..643d3d37e87cf 100644 --- a/tests/lib/Files/Utils/ScannerTest.php +++ b/tests/lib/Files/Utils/ScannerTest.php @@ -17,6 +17,8 @@ use OC\Files\Utils\Scanner; use OCP\EventDispatcher\IEventDispatcher; use OCP\Files\Config\IMountProvider; +use OCP\Files\Events\FileCacheUpdated; +use OCP\Files\Events\NodeAddedToCache; use OCP\Files\Config\IMountProviderCollection; use OCP\Files\Storage\IStorageFactory; use OCP\IDBConnection; @@ -209,6 +211,53 @@ public function testPropagateEtag(): void { $this->assertNotEquals($oldRoot->getEtag(), $newRoot->getEtag()); } + public function testScanDispatchesAddedForNewAndUpdatedForChangedEntries(): void { + $storage = new Temporary([]); + $mount = new MountPoint($storage, ''); + Filesystem::getMountManager()->addMount($mount); + + $storage->mkdir('folder'); + $storage->file_put_contents('folder/bar.txt', 'qwerty'); + $storage->touch('folder/bar.txt', time() - 200); + + /** @var list $events */ + $events = []; + $dispatcher = $this->createMock(IEventDispatcher::class); + $dispatcher->method('dispatchTyped') + ->willReturnCallback(function (object $event) use (&$events): void { + $events[] = $event; + }); + + $scanner = new TestScanner( + Server::get(IUserManager::class)->get(''), + Server::get(IDBConnection::class), + $dispatcher, + Server::get(LoggerInterface::class), + Server::get(SetupManager::class), + ); + $scanner->addMount($mount); + + $pathsForEvents = function (string $class) use (&$events): array { + return array_values(array_map( + static fn (object $event): string => $event->getPath(), + array_filter($events, static fn (object $event): bool => $event instanceof $class), + )); + }; + + // first scan: everything is new, nothing is updated + $scanner->scan(''); + $this->assertContains('folder/bar.txt', $pathsForEvents(NodeAddedToCache::class)); + $this->assertContains('folder', $pathsForEvents(NodeAddedToCache::class)); + $this->assertSame([], $pathsForEvents(FileCacheUpdated::class)); + + // re-scan after modifying the file: updated, not new + $events = []; + $storage->file_put_contents('folder/bar.txt', 'qwerty asdf'); + $scanner->scan(''); + $this->assertContains('folder/bar.txt', $pathsForEvents(FileCacheUpdated::class)); + $this->assertSame([], $pathsForEvents(NodeAddedToCache::class)); + } + public function testShallow(): void { $storage = new Temporary([]); $mount = new MountPoint($storage, '');