@@ -1260,6 +1260,69 @@ public function testSchemalessRemoveAttributesByUpdate(): void
12601260 // verify docs is still preserved(untouched)
12611261 $ docS = $ database ->getDocument ($ col , 'docS ' );
12621262 $ this ->assertEquals ('single2 ' , $ docS ->getAttribute ('key ' ));
1263+
1264+ // 1) Null Value Preservation vs Removal
1265+ $ docNull = $ database ->createDocument ($ col , new Document ([
1266+ '$id ' => 'docNull ' ,
1267+ '$permissions ' => $ permissions ,
1268+ 'keepMe ' => 'k1 ' ,
1269+ 'removeMe ' => 'r1 ' ,
1270+ 'nullable ' => 'n1 '
1271+ ]));
1272+ $ this ->assertEquals ('docNull ' , $ docNull ->getId ());
1273+
1274+ $ docNullUpdated = $ database ->updateDocument ($ col , 'docNull ' , new Document ([
1275+ 'keepMe ' => 'k2 ' ,
1276+ 'nullable ' => null // explicitly set null should be preserved as key with null value
1277+ ]));
1278+ $ this ->assertEquals ('k2 ' , $ docNullUpdated ->getAttribute ('keepMe ' ));
1279+ $ this ->assertArrayHasKey ('nullable ' , $ docNullUpdated ->getAttributes ());
1280+ $ this ->assertNull ($ docNullUpdated ->getAttribute ('nullable ' ));
1281+ $ this ->assertArrayNotHasKey ('removeMe ' , $ docNullUpdated ->getAttributes ());
1282+
1283+ $ docNullRefetch = $ database ->getDocument ($ col , 'docNull ' );
1284+ $ this ->assertEquals ('k2 ' , $ docNullRefetch ->getAttribute ('keepMe ' ));
1285+ $ this ->assertArrayHasKey ('nullable ' , $ docNullRefetch ->getAttributes ());
1286+ $ this ->assertNull ($ docNullRefetch ->getAttribute ('nullable ' ));
1287+ $ this ->assertArrayNotHasKey ('removeMe ' , $ docNullRefetch ->getAttributes ());
1288+
1289+ // 2) Internal attributes preservation ($id, $sequence, $permissions)
1290+ $ before = $ database ->getDocument ($ col , 'docS ' );
1291+ $ this ->assertEquals ('docS ' , $ before ->getId ());
1292+ $ beforeSequence = $ before ->getSequence ();
1293+ $ beforePermissions = $ before ->getPermissions ();
1294+ $ this ->assertNotEmpty ($ beforeSequence );
1295+ $ this ->assertGreaterThanOrEqual (1 , count ($ beforePermissions ));
1296+
1297+ $ after = $ database ->updateDocument ($ col , 'docS ' , new Document (['key ' => 'single3 ' ]));
1298+ $ this ->assertEquals ('docS ' , $ after ->getId ());
1299+ $ this ->assertEquals ('single3 ' , $ after ->getAttribute ('key ' ));
1300+ $ this ->assertEquals ($ beforeSequence , $ after ->getSequence ());
1301+ foreach ($ beforePermissions as $ perm ) {
1302+ $ this ->assertContains ($ perm , $ after ->getPermissions ());
1303+ }
1304+
1305+ $ afterRefetch = $ database ->getDocument ($ col , 'docS ' );
1306+ $ this ->assertEquals ('docS ' , $ afterRefetch ->getId ());
1307+ $ this ->assertEquals ($ beforeSequence , $ afterRefetch ->getSequence ());
1308+ foreach ($ beforePermissions as $ perm ) {
1309+ $ this ->assertContains ($ perm , $ afterRefetch ->getPermissions ());
1310+ }
1311+
1312+ // 3) Update with empty document (removes all non-internal attributes)
1313+ $ noOp = $ database ->updateDocument ($ col , 'docS ' , new Document ([]));
1314+ $ this ->assertEquals ('docS ' , $ noOp ->getId ());
1315+ $ this ->assertArrayNotHasKey ('key ' , $ noOp ->getAttributes ());
1316+
1317+ $ noOpRefetch = $ database ->getDocument ($ col , 'docS ' );
1318+ $ this ->assertArrayNotHasKey ('key ' , $ noOpRefetch ->getAttributes ());
1319+ // Internal attributes should still be present
1320+ $ this ->assertEquals ('docS ' , $ noOpRefetch ->getId ());
1321+ $ this ->assertNotEmpty ($ noOpRefetch ->getSequence ());
1322+ $ this ->assertEquals ($ col , $ noOpRefetch ->getCollection ());
1323+ $ this ->assertTrue (is_string ($ noOpRefetch ->getAttribute ('$createdAt ' )));
1324+ $ this ->assertTrue (is_string ($ noOpRefetch ->getAttribute ('$updatedAt ' )));
1325+ $ this ->assertGreaterThanOrEqual (1 , count ($ noOpRefetch ->getPermissions ()));
12631326 $ database ->deleteCollection ($ col );
12641327 }
12651328}
0 commit comments