@@ -1179,6 +1179,144 @@ public function testUpsertMixedPermissionDelta(): void
11791179 ], $ db ->getDocument (__FUNCTION__ , 'b ' )->getPermissions ());
11801180 }
11811181
1182+ public function testPreserveSequenceUpsert (): void
1183+ {
1184+ /** @var Database $database */
1185+ $ database = $ this ->getDatabase ();
1186+
1187+ if (!$ database ->getAdapter ()->getSupportForUpserts ()) {
1188+ $ this ->expectNotToPerformAssertions ();
1189+ return ;
1190+ }
1191+
1192+ $ collectionName = 'preserve_sequence_upsert ' ;
1193+
1194+ $ database ->createCollection ($ collectionName );
1195+
1196+ if ($ database ->getAdapter ()->getSupportForAttributes ()) {
1197+ $ database ->createAttribute ($ collectionName , 'name ' , Database::VAR_STRING , 128 , true );
1198+ }
1199+
1200+ // Create initial documents
1201+ $ doc1 = $ database ->createDocument ($ collectionName , new Document ([
1202+ '$id ' => 'doc1 ' ,
1203+ '$permissions ' => [
1204+ Permission::read (Role::any ()),
1205+ Permission::update (Role::any ()),
1206+ ],
1207+ 'name ' => 'Alice ' ,
1208+ ]));
1209+
1210+ $ doc2 = $ database ->createDocument ($ collectionName , new Document ([
1211+ '$id ' => 'doc2 ' ,
1212+ '$permissions ' => [
1213+ Permission::read (Role::any ()),
1214+ Permission::update (Role::any ()),
1215+ ],
1216+ 'name ' => 'Bob ' ,
1217+ ]));
1218+
1219+ $ originalSeq1 = $ doc1 ->getSequence ();
1220+ $ originalSeq2 = $ doc2 ->getSequence ();
1221+
1222+ $ this ->assertNotEmpty ($ originalSeq1 );
1223+ $ this ->assertNotEmpty ($ originalSeq2 );
1224+
1225+ // Test: Without preserveSequence (default), $sequence should be ignored
1226+ $ database ->setPreserveSequence (false );
1227+
1228+ $ database ->upsertDocuments ($ collectionName , [
1229+ new Document ([
1230+ '$id ' => 'doc1 ' ,
1231+ '$sequence ' => 999 , // Try to set a different sequence
1232+ '$permissions ' => [
1233+ Permission::read (Role::any ()),
1234+ Permission::update (Role::any ()),
1235+ ],
1236+ 'name ' => 'Alice Updated ' ,
1237+ ]),
1238+ ]);
1239+
1240+ $ doc1Updated = $ database ->getDocument ($ collectionName , 'doc1 ' );
1241+ $ this ->assertEquals ('Alice Updated ' , $ doc1Updated ->getAttribute ('name ' ));
1242+ $ this ->assertEquals ($ originalSeq1 , $ doc1Updated ->getSequence ()); // Sequence unchanged
1243+
1244+ // Test: With preserveSequence=true, $sequence from document should be used
1245+ $ database ->setPreserveSequence (true );
1246+
1247+ $ database ->upsertDocuments ($ collectionName , [
1248+ new Document ([
1249+ '$id ' => 'doc2 ' ,
1250+ '$sequence ' => $ originalSeq2 , // Keep original sequence
1251+ '$permissions ' => [
1252+ Permission::read (Role::any ()),
1253+ Permission::update (Role::any ()),
1254+ ],
1255+ 'name ' => 'Bob Updated ' ,
1256+ ]),
1257+ ]);
1258+
1259+ $ doc2Updated = $ database ->getDocument ($ collectionName , 'doc2 ' );
1260+ $ this ->assertEquals ('Bob Updated ' , $ doc2Updated ->getAttribute ('name ' ));
1261+ $ this ->assertEquals ($ originalSeq2 , $ doc2Updated ->getSequence ()); // Sequence preserved
1262+
1263+ // Test: withPreserveSequence helper
1264+ $ database ->setPreserveSequence (false );
1265+
1266+ $ doc1 = $ database ->getDocument ($ collectionName , 'doc1 ' );
1267+ $ currentSeq1 = $ doc1 ->getSequence ();
1268+
1269+ $ database ->withPreserveSequence (function () use ($ database , $ collectionName , $ currentSeq1 ) {
1270+ $ database ->upsertDocuments ($ collectionName , [
1271+ new Document ([
1272+ '$id ' => 'doc1 ' ,
1273+ '$sequence ' => $ currentSeq1 ,
1274+ '$permissions ' => [
1275+ Permission::read (Role::any ()),
1276+ Permission::update (Role::any ()),
1277+ ],
1278+ 'name ' => 'Alice Final ' ,
1279+ ]),
1280+ ]);
1281+ });
1282+
1283+ $ doc1Final = $ database ->getDocument ($ collectionName , 'doc1 ' );
1284+ $ this ->assertEquals ('Alice Final ' , $ doc1Final ->getAttribute ('name ' ));
1285+ $ this ->assertEquals ($ currentSeq1 , $ doc1Final ->getSequence ());
1286+
1287+ // Verify flag was reset after withPreserveSequence
1288+ $ this ->assertFalse ($ database ->getPreserveSequence ());
1289+
1290+ // Test: With preserveSequence=true, invalid $sequence should throw error (SQL adapters only)
1291+ $ database ->setPreserveSequence (true );
1292+
1293+ try {
1294+ $ database ->upsertDocuments ($ collectionName , [
1295+ new Document ([
1296+ '$id ' => 'doc1 ' ,
1297+ '$sequence ' => 'abc ' , // Invalid sequence value
1298+ '$permissions ' => [
1299+ Permission::read (Role::any ()),
1300+ Permission::update (Role::any ()),
1301+ ],
1302+ 'name ' => 'Alice Invalid ' ,
1303+ ]),
1304+ ]);
1305+ // Schemaless adapters may not validate sequence type, so only fail for schemaful
1306+ if ($ database ->getAdapter ()->getSupportForAttributes ()) {
1307+ $ this ->fail ('Expected StructureException for invalid sequence ' );
1308+ }
1309+ } catch (Throwable $ e ) {
1310+ if ($ database ->getAdapter ()->getSupportForAttributes ()) {
1311+ $ this ->assertInstanceOf (StructureException::class, $ e );
1312+ $ this ->assertStringContainsString ('sequence ' , $ e ->getMessage ());
1313+ }
1314+ }
1315+
1316+ $ database ->setPreserveSequence (false );
1317+ $ database ->deleteCollection ($ collectionName );
1318+ }
1319+
11821320 public function testRespectNulls (): Document
11831321 {
11841322 /** @var Database $database */
0 commit comments