From 809a87b8040e0bc159d1b09c7287fc21ffa1aefd Mon Sep 17 00:00:00 2001 From: Prem Palanisamy Date: Wed, 8 Apr 2026 17:56:12 +0100 Subject: [PATCH 1/3] Throw BulkWriteException with write results instead of generic Exception --- src/Client.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Client.php b/src/Client.php index 7481249..fb9b518 100644 --- a/src/Client.php +++ b/src/Client.php @@ -1634,8 +1634,12 @@ private function parseResponse(string $response, int $responseLength): stdClass| // Check for write errors (duplicate key, etc.) if (\property_exists($result, 'writeErrors') && !empty($result->writeErrors)) { - throw new Exception( + throw new BulkWriteException( $result->writeErrors[0]->errmsg, + [ + 'writeErrors' => $result->writeErrors, + 'nInserted' => $result->n ?? 0, + ], $result->writeErrors[0]->code ); } From f1f106c380cafa1936c01d6eff2730fba29058c7 Mon Sep 17 00:00:00 2001 From: Prem Palanisamy Date: Wed, 8 Apr 2026 17:58:46 +0100 Subject: [PATCH 2/3] Add test for BulkWriteException on duplicate insertMany --- tests/MongoTest.php | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/tests/MongoTest.php b/tests/MongoTest.php index 2d48716..94d23bd 100644 --- a/tests/MongoTest.php +++ b/tests/MongoTest.php @@ -5,6 +5,7 @@ use MongoDB\BSON\ObjectId; use PHPUnit\Framework\TestCase; use Utopia\Mongo\Client; +use Utopia\Mongo\BulkWriteException; use Utopia\Mongo\Exception; class MongoTest extends TestCase @@ -477,4 +478,44 @@ public function testCountMethod() $this->getDatabase()->dropCollection($collectionName); } } + + public function testInsertManyDuplicateThrowsBulkWriteException(): void + { + $collectionName = 'test_bulk_write_exception'; + $this->getDatabase()->createCollection($collectionName); + + try { + // Insert a doc with explicit _id + $this->getDatabase()->insert($collectionName, [ + '_id' => 'dup_id', + 'name' => 'Original', + ]); + + // insertMany with a duplicate _id should throw BulkWriteException + try { + $this->getDatabase()->insertMany($collectionName, [ + ['_id' => 'dup_id', 'name' => 'Duplicate'], + ['_id' => 'new_id', 'name' => 'New'], + ], ['ordered' => false]); + + self::fail('Expected BulkWriteException'); + } catch (BulkWriteException $e) { + // Should be a duplicate key error + self::assertSame(11000, $e->getCode()); + + $result = $e->getResult(); + self::assertArrayHasKey('nInserted', $result); + self::assertArrayHasKey('writeErrors', $result); + self::assertGreaterThanOrEqual(1, $result['nInserted']); + } + + // 'new_id' should have been inserted despite the duplicate (ordered: false) + $found = $this->getDatabase()->find($collectionName, ['_id' => 'new_id']); + $docs = $found->cursor->firstBatch ?? []; + self::assertCount(1, $docs); + self::assertSame('New', $docs[0]->name); + } finally { + $this->getDatabase()->dropCollection($collectionName); + } + } } From 56fe485885fe72cc6a99c94433a3ea3bd4f8c3d3 Mon Sep 17 00:00:00 2001 From: Prem Palanisamy Date: Wed, 8 Apr 2026 18:05:29 +0100 Subject: [PATCH 3/3] Populate parent writeErrors in BulkWriteException, tighten nInserted assertion --- src/Exception.php | 2 +- tests/MongoTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Exception.php b/src/Exception.php index 7c47d6a..cd0ecab 100644 --- a/src/Exception.php +++ b/src/Exception.php @@ -243,7 +243,7 @@ class BulkWriteException extends Exception public function __construct(string $message, array $result, int $code = 0, ?\Throwable $previous = null) { - parent::__construct($message, $code, $previous); + parent::__construct($message, $code, $previous, [], $result['writeErrors'] ?? null); $this->result = $result; } diff --git a/tests/MongoTest.php b/tests/MongoTest.php index 94d23bd..78404a7 100644 --- a/tests/MongoTest.php +++ b/tests/MongoTest.php @@ -506,7 +506,7 @@ public function testInsertManyDuplicateThrowsBulkWriteException(): void $result = $e->getResult(); self::assertArrayHasKey('nInserted', $result); self::assertArrayHasKey('writeErrors', $result); - self::assertGreaterThanOrEqual(1, $result['nInserted']); + self::assertSame(1, $result['nInserted']); } // 'new_id' should have been inserted despite the duplicate (ordered: false)