@@ -6185,4 +6185,182 @@ public function testCreateUpdateDocumentsMismatch(): void
61856185 $ database ->deleteCollection ($ colName );
61866186 }
61876187
6188+ public function testBypassStructureWithSupportForAttributes (): void
6189+ {
6190+ /** @var Database $database */
6191+ $ database = static ::getDatabase ();
6192+ // for schemaless the validation will be automatically skipped
6193+ if (!$ database ->getAdapter ()->getSupportForAttributes ()) {
6194+ $ this ->expectNotToPerformAssertions ();
6195+ return ;
6196+ }
6197+
6198+ $ collectionId = 'successive_update_single ' ;
6199+
6200+ $ database ->createCollection ($ collectionId );
6201+ $ database ->createAttribute ($ collectionId , 'attrA ' , Database::VAR_STRING , 50 , true );
6202+ $ database ->createAttribute ($ collectionId , 'attrB ' , Database::VAR_STRING , 50 , true );
6203+
6204+ // bypass required
6205+ $ database ->disableValidation ();
6206+
6207+ $ permissions = [Permission::read (Role::any ()), Permission::write (Role::any ()), Permission::update (Role::any ()), Permission::delete (Role::any ())];
6208+ $ docs = $ database ->createDocuments ($ collectionId , [
6209+ new Document (['attrA ' => null ,'attrB ' => 'B ' ,'$permissions ' => $ permissions ])
6210+ ]);
6211+
6212+ $ docs = $ database ->find ($ collectionId );
6213+ foreach ($ docs as $ doc ) {
6214+ $ this ->assertArrayHasKey ('attrA ' , $ doc ->getAttributes ());
6215+ $ this ->assertNull ($ doc ->getAttribute ('attrA ' ));
6216+ $ this ->assertEquals ('B ' , $ doc ->getAttribute ('attrB ' ));
6217+ }
6218+ // reset
6219+ $ database ->enableValidation ();
6220+
6221+ try {
6222+ $ database ->createDocuments ($ collectionId , [
6223+ new Document (['attrA ' => null ,'attrB ' => 'B ' ,'$permissions ' => $ permissions ])
6224+ ]);
6225+ $ this ->fail ('Failed to throw exception ' );
6226+ } catch (Exception $ e ) {
6227+ $ this ->assertInstanceOf (StructureException::class, $ e );
6228+ }
6229+
6230+ $ database ->deleteCollection ($ collectionId );
6231+ }
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+ }
61886366}
0 commit comments