From 9b5cf6d873de6117f2deb3e673b380e9bea1fc6b Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Thu, 4 Jun 2026 16:03:01 +1200 Subject: [PATCH] fix: ignore unrequested row/document types in status counters getStatusCounters() seeds a status bucket only for requested resource types and guards the non-row branch with isset(). The row/document branch had no such guard: a row/document count aggregated into the cache for a type that was not requested would auto-vivify $status[$type], read an unseeded 'pending' key (emitting an "Undefined array key" warning) and leave a phantom, non-empty entry that survives the empty-bucket prune. Downstream this surfaced as a flaky assertEmpty($statusCounters) after an otherwise clean migration. Add the same isset() guard the non-row branch already uses so unrequested row/document counts are ignored. Co-Authored-By: Claude Opus 4.8 --- src/Migration/Transfer.php | 9 +++++++ tests/Migration/Unit/General/TransferTest.php | 27 +++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/src/Migration/Transfer.php b/src/Migration/Transfer.php index 84750c06..c40b17ec 100644 --- a/src/Migration/Transfer.php +++ b/src/Migration/Transfer.php @@ -251,6 +251,15 @@ public function getStatusCounters(): array foreach ($this->cache->getAll() as $resourceType => $resources) { foreach ($resources as $k => $resource) { if (($resourceType === Resource::TYPE_ROW || $resourceType === Resource::TYPE_DOCUMENT) && is_string($resource)) { + // Only report status for resource types that were requested, + // mirroring the isset() guard below. Row/document counts can be + // aggregated into the cache for an unrequested type, which would + // otherwise read an unseeded 'pending' key and leave a phantom, + // non-empty counter. + if (!isset($status[$resourceType])) { + continue; + } + $resource = intval($resource); $status[$resourceType][$k] = $resource; diff --git a/tests/Migration/Unit/General/TransferTest.php b/tests/Migration/Unit/General/TransferTest.php index bf21f849..e4d95c1a 100644 --- a/tests/Migration/Unit/General/TransferTest.php +++ b/tests/Migration/Unit/General/TransferTest.php @@ -5,6 +5,8 @@ use PHPUnit\Framework\TestCase; use Utopia\Migration\Resource; use Utopia\Migration\Resources\Database\Database; +use Utopia\Migration\Resources\Database\Row; +use Utopia\Migration\Resources\Database\Table; use Utopia\Migration\Transfer; use Utopia\Tests\Unit\Adapters\MockDestination; use Utopia\Tests\Unit\Adapters\MockSource; @@ -64,4 +66,29 @@ function () { $this->assertSame('test', $database->getDatabaseName()); $this->assertSame('test', $database->getId()); } + + /** + * Row and document counts are aggregated into the cache by status. When such + * a count exists for a resource type that was not part of the migration + * request, getStatusCounters() must ignore it, exactly as it already does for + * non-row resources via the isset() guard. Otherwise it reads an unseeded + * 'pending' key (triggering an "Undefined array key" warning) and reports a + * phantom, non-empty counter for a type the caller never asked to migrate. + */ + public function testStatusCountersIgnoreUnrequestedRowCounts(): void + { + // No resource types were requested, so 'row'/'document' are unrequested. + // A row count still leaks into the cache: the destination tallies row and + // document counts by status as it imports them. + $table = new Table(new Database('db', 'db'), 'table', 'table'); + $row = new Row('row-1', $table); + $row->setStatus(Resource::STATUS_SUCCESS); + + $this->transfer->getCache()->add($row); + + $counters = $this->transfer->getStatusCounters(); + + $this->assertArrayNotHasKey(Resource::TYPE_ROW, $counters); + $this->assertSame([], $counters); + } }