@@ -322,7 +322,7 @@ public function testCreateDocumentsWithDifferentAttributes(): void
322322 static ::getDatabase ()->deleteCollection ($ collection );
323323 }
324324
325- public function testCreateOrUpdateDocuments (): void
325+ public function testUpsertDocuments (): void
326326 {
327327 if (!static ::getDatabase ()->getAdapter ()->getSupportForUpserts ()) {
328328 $ this ->expectNotToPerformAssertions ();
@@ -362,9 +362,13 @@ public function testCreateOrUpdateDocuments(): void
362362 ];
363363
364364 $ results = [];
365- $ count = static ::getDatabase ()->createOrUpdateDocuments (__FUNCTION__ , $ documents , onNext: function ($ doc ) use (&$ results ) {
366- $ results [] = $ doc ;
367- });
365+ $ count = static ::getDatabase ()->createOrUpdateDocuments (
366+ __FUNCTION__ ,
367+ $ documents ,
368+ onNext: function ($ doc ) use (&$ results ) {
369+ $ results [] = $ doc ;
370+ }
371+ );
368372
369373 $ this ->assertEquals (2 , $ count );
370374
@@ -432,7 +436,7 @@ public function testCreateOrUpdateDocuments(): void
432436 }
433437 }
434438
435- public function testCreateOrUpdateDocumentsInc (): void
439+ public function testUpsertDocumentsInc (): void
436440 {
437441 if (!static ::getDatabase ()->getAdapter ()->getSupportForUpserts ()) {
438442 $ this ->expectNotToPerformAssertions ();
@@ -501,7 +505,7 @@ public function testCreateOrUpdateDocumentsInc(): void
501505 }
502506 }
503507
504- public function testCreateOrUpdateDocumentsPermissions (): void
508+ public function testUpsertDocumentsPermissions (): void
505509 {
506510 if (!static ::getDatabase ()->getAdapter ()->getSupportForUpserts ()) {
507511 $ this ->expectNotToPerformAssertions ();
@@ -587,6 +591,118 @@ public function testCreateOrUpdateDocumentsPermissions(): void
587591 $ this ->assertEquals ($ newPermissions , $ document ->getPermissions ());
588592 }
589593
594+ public function testUpsertDocumentsAttributeMismatch (): void
595+ {
596+ /** @var Database $database */
597+ $ database = static ::getDatabase ();
598+
599+ if (!$ database ->getAdapter ()->getSupportForUpserts ()) {
600+ $ this ->expectNotToPerformAssertions ();
601+ return ;
602+ }
603+
604+ $ database ->createCollection (__FUNCTION__ , permissions: [
605+ Permission::create (Role::any ()),
606+ Permission::read (Role::any ()),
607+ Permission::update (Role::any ()),
608+ Permission::delete (Role::any ()),
609+ ], documentSecurity: false );
610+ $ database ->createAttribute (__FUNCTION__ , 'first ' , Database::VAR_STRING , 128 , true );
611+ $ database ->createAttribute (__FUNCTION__ , 'last ' , Database::VAR_STRING , 128 , false );
612+
613+ $ existingDocument = $ database ->createDocument (__FUNCTION__ , new Document ([
614+ '$id ' => 'first ' ,
615+ 'first ' => 'first ' ,
616+ 'last ' => 'last ' ,
617+ ]));
618+
619+ $ newDocument = new Document ([
620+ '$id ' => 'second ' ,
621+ 'first ' => 'second ' ,
622+ ]);
623+
624+ // Ensure missing optionals on new document is allowed
625+ $ docs = $ database ->createOrUpdateDocuments (__FUNCTION__ , [
626+ $ existingDocument ->setAttribute ('first ' , 'updated ' ),
627+ $ newDocument ,
628+ ]);
629+
630+ $ this ->assertEquals (2 , $ docs );
631+ $ this ->assertEquals ('updated ' , $ existingDocument ->getAttribute ('first ' ));
632+ $ this ->assertEquals ('last ' , $ existingDocument ->getAttribute ('last ' ));
633+ $ this ->assertEquals ('second ' , $ newDocument ->getAttribute ('first ' ));
634+ $ this ->assertEquals ('' , $ newDocument ->getAttribute ('last ' ));
635+
636+ try {
637+ $ database ->createOrUpdateDocuments (__FUNCTION__ , [
638+ $ existingDocument ->removeAttribute ('first ' ),
639+ $ newDocument
640+ ]);
641+ $ this ->fail ('Failed to throw exception ' );
642+ } catch (Throwable $ e ) {
643+ $ this ->assertTrue ($ e instanceof StructureException, $ e ->getMessage ());
644+ }
645+
646+ // Ensure missing optionals on existing document is allowed
647+ $ docs = $ database ->createOrUpdateDocuments (__FUNCTION__ , [
648+ $ existingDocument
649+ ->setAttribute ('first ' , 'first ' )
650+ ->removeAttribute ('last ' ),
651+ $ newDocument
652+ ->setAttribute ('last ' , 'last ' )
653+ ]);
654+
655+ $ this ->assertEquals (2 , $ docs );
656+ $ this ->assertEquals ('first ' , $ existingDocument ->getAttribute ('first ' ));
657+ $ this ->assertEquals ('last ' , $ existingDocument ->getAttribute ('last ' ));
658+ $ this ->assertEquals ('second ' , $ newDocument ->getAttribute ('first ' ));
659+ $ this ->assertEquals ('last ' , $ newDocument ->getAttribute ('last ' ));
660+
661+ // Ensure set null on existing document is allowed
662+ $ docs = $ database ->createOrUpdateDocuments (__FUNCTION__ , [
663+ $ existingDocument
664+ ->setAttribute ('first ' , 'first ' )
665+ ->setAttribute ('last ' , null ),
666+ $ newDocument
667+ ->setAttribute ('last ' , 'last ' )
668+ ]);
669+
670+ $ this ->assertEquals (1 , $ docs );
671+ $ this ->assertEquals ('first ' , $ existingDocument ->getAttribute ('first ' ));
672+ $ this ->assertEquals (null , $ existingDocument ->getAttribute ('last ' ));
673+ $ this ->assertEquals ('second ' , $ newDocument ->getAttribute ('first ' ));
674+ $ this ->assertEquals ('last ' , $ newDocument ->getAttribute ('last ' ));
675+ }
676+
677+ public function testUpsertDocumentsNoop (): void
678+ {
679+ if (!static ::getDatabase ()->getAdapter ()->getSupportForUpserts ()) {
680+ $ this ->expectNotToPerformAssertions ();
681+ return ;
682+ }
683+
684+ static ::getDatabase ()->createCollection (__FUNCTION__ );
685+ static ::getDatabase ()->createAttribute (__FUNCTION__ , 'string ' , Database::VAR_STRING , 128 , true );
686+
687+ $ document = new Document ([
688+ '$id ' => 'first ' ,
689+ 'string ' => 'text📝 ' ,
690+ '$permissions ' => [
691+ Permission::read (Role::any ()),
692+ Permission::create (Role::any ()),
693+ Permission::update (Role::any ()),
694+ Permission::delete (Role::any ()),
695+ ],
696+ ]);
697+
698+ $ count = static ::getDatabase ()->createOrUpdateDocuments (__FUNCTION__ , [$ document ]);
699+ $ this ->assertEquals (1 , $ count );
700+
701+ // No changes, should return 0
702+ $ count = static ::getDatabase ()->createOrUpdateDocuments (__FUNCTION__ , [$ document ]);
703+ $ this ->assertEquals (0 , $ count );
704+ }
705+
590706 public function testRespectNulls (): Document
591707 {
592708 static ::getDatabase ()->createCollection ('documents_nulls ' );
0 commit comments