@@ -6229,4 +6229,138 @@ public function testBypassStructureWithSupportForAttributes(): void
62296229
62306230 $ database ->deleteCollection ($ collectionId );
62316231 }
6232+
6233+ public function testValidationGuardsWithNullRequired (): void
6234+ {
6235+ /** @var Database $database */
6236+ $ database = static ::getDatabase ();
6237+
6238+ if (!$ database ->getAdapter ()->getSupportForAttributes ()) {
6239+ $ this ->expectNotToPerformAssertions ();
6240+ return ;
6241+ }
6242+
6243+ // Base collection and attributes
6244+ $ collection = 'validation_guard_all ' ;
6245+ $ database ->createCollection ($ collection , permissions: [
6246+ Permission::read (Role::any ()),
6247+ Permission::create (Role::any ()),
6248+ Permission::update (Role::any ()),
6249+ Permission::delete (Role::any ()),
6250+ ], documentSecurity: true );
6251+ $ database ->createAttribute ($ collection , 'name ' , Database::VAR_STRING , 32 , true );
6252+ $ database ->createAttribute ($ collection , 'age ' , Database::VAR_INTEGER , 0 , true );
6253+ $ database ->createAttribute ($ collection , 'value ' , Database::VAR_INTEGER , 0 , false );
6254+
6255+ // 1) createDocument with null required should fail when validation enabled, pass when disabled
6256+ try {
6257+ $ database ->createDocument ($ collection , new Document ([
6258+ '$permissions ' => [Permission::read (Role::any ()), Permission::create (Role::any ())],
6259+ 'name ' => null ,
6260+ 'age ' => null ,
6261+ ]));
6262+ $ this ->fail ('Failed to throw exception ' );
6263+ } catch (Throwable $ e ) {
6264+ $ this ->assertInstanceOf (StructureException::class, $ e );
6265+ }
6266+
6267+ $ database ->disableValidation ();
6268+ $ doc = $ database ->createDocument ($ collection , new Document ([
6269+ '$id ' => 'created-null ' ,
6270+ '$permissions ' => [Permission::read (Role::any ()), Permission::create (Role::any ()), Permission::update (Role::any ())],
6271+ 'name ' => null ,
6272+ 'age ' => null ,
6273+ ]));
6274+ $ this ->assertEquals ('created-null ' , $ doc ->getId ());
6275+ $ database ->enableValidation ();
6276+
6277+ // Seed a valid document for updates
6278+ $ valid = $ database ->createDocument ($ collection , new Document ([
6279+ '$id ' => 'valid ' ,
6280+ '$permissions ' => [Permission::read (Role::any ()), Permission::update (Role::any ())],
6281+ 'name ' => 'ok ' ,
6282+ 'age ' => 10 ,
6283+ ]));
6284+ $ this ->assertEquals ('valid ' , $ valid ->getId ());
6285+
6286+ // 2) updateDocument set required to null should fail when validation enabled, pass when disabled
6287+ try {
6288+ $ database ->updateDocument ($ collection , 'valid ' , new Document ([
6289+ 'age ' => null ,
6290+ ]));
6291+ $ this ->fail ('Failed to throw exception ' );
6292+ } catch (Throwable $ e ) {
6293+ $ this ->assertInstanceOf (StructureException::class, $ e );
6294+ }
6295+
6296+ $ database ->disableValidation ();
6297+ $ updated = $ database ->updateDocument ($ collection , 'valid ' , new Document ([
6298+ 'age ' => null ,
6299+ ]));
6300+ $ this ->assertNull ($ updated ->getAttribute ('age ' ));
6301+ $ database ->enableValidation ();
6302+
6303+ // Seed a few valid docs for bulk update
6304+ for ($ i = 0 ; $ i < 2 ; $ i ++) {
6305+ $ database ->createDocument ($ collection , new Document ([
6306+ '$id ' => 'b ' . $ i ,
6307+ '$permissions ' => [Permission::read (Role::any ()), Permission::update (Role::any ())],
6308+ 'name ' => 'ok ' ,
6309+ 'age ' => 1 ,
6310+ ]));
6311+ }
6312+
6313+ // 3) updateDocuments setting required to null should fail when validation enabled, pass when disabled
6314+ if ($ database ->getAdapter ()->getSupportForBatchOperations ()) {
6315+ try {
6316+ $ database ->updateDocuments ($ collection , new Document ([
6317+ 'name ' => null ,
6318+ ]));
6319+ $ this ->fail ('Failed to throw exception ' );
6320+ } catch (Throwable $ e ) {
6321+ $ this ->assertInstanceOf (StructureException::class, $ e );
6322+ }
6323+
6324+ $ database ->disableValidation ();
6325+ $ count = $ database ->updateDocuments ($ collection , new Document ([
6326+ 'name ' => null ,
6327+ ]));
6328+ $ this ->assertGreaterThanOrEqual (3 , $ count ); // at least the seeded docs are updated
6329+ $ database ->enableValidation ();
6330+ }
6331+
6332+ // 4) upsertDocumentsWithIncrease with null required should fail when validation enabled, pass when disabled
6333+ if ($ database ->getAdapter ()->getSupportForUpserts ()) {
6334+ try {
6335+ $ database ->upsertDocumentsWithIncrease (
6336+ collection: $ collection ,
6337+ attribute: 'value ' ,
6338+ documents: [new Document ([
6339+ '$id ' => 'u1 ' ,
6340+ 'name ' => null , // required null
6341+ 'value ' => 1 ,
6342+ ])]
6343+ );
6344+ $ this ->fail ('Failed to throw exception ' );
6345+ } catch (Throwable $ e ) {
6346+ $ this ->assertInstanceOf (StructureException::class, $ e );
6347+ }
6348+
6349+ $ database ->disableValidation ();
6350+ $ ucount = $ database ->upsertDocumentsWithIncrease (
6351+ collection: $ collection ,
6352+ attribute: 'value ' ,
6353+ documents: [new Document ([
6354+ '$id ' => 'u1 ' ,
6355+ 'name ' => null ,
6356+ 'value ' => 1 ,
6357+ ])]
6358+ );
6359+ $ this ->assertEquals (1 , $ ucount );
6360+ $ database ->enableValidation ();
6361+ }
6362+
6363+ // Cleanup
6364+ $ database ->deleteCollection ($ collection );
6365+ }
62326366}
0 commit comments