@@ -1110,4 +1110,308 @@ mod tests {
11101110 let ( txn4, _) = registry. begin_transaction ( ) ;
11111111 assert ! ( registry. is_visible( txn3, txn4) ) ;
11121112 }
1113+
1114+ // === commit_transaction: line 371 override_count > 0 ===
1115+
1116+ #[ test]
1117+ fn test_commit_read_committed_skips_snapshot_seqs ( ) {
1118+ let registry = TransactionRegistry :: new ( ) ;
1119+ registry. set_global_isolation_level ( IsolationLevel :: ReadCommitted ) ;
1120+
1121+ let ( txn_id, _) = registry. begin_transaction ( ) ;
1122+ registry. commit_transaction ( txn_id) ;
1123+
1124+ // override_count == 0, global != snapshot => snapshot_seqs NOT populated
1125+ assert ! ( registry. snapshot_seqs. lock( ) . is_empty( ) ) ;
1126+ }
1127+
1128+ // === recover_committed_transaction: lines 416, 432 ===
1129+
1130+ #[ test]
1131+ fn test_recover_committed_advances_next_txn_id ( ) {
1132+ let registry = TransactionRegistry :: new ( ) ;
1133+
1134+ // Recover txn 100 with commit_seq 50
1135+ registry. recover_committed_transaction ( 100 , 50 ) ;
1136+ // next_txn_id must advance past 100
1137+ let ( new_id, _) = registry. begin_transaction ( ) ;
1138+ assert ! ( new_id > 100 ) ;
1139+ }
1140+
1141+ #[ test]
1142+ fn test_recover_committed_advances_next_sequence ( ) {
1143+ let registry = TransactionRegistry :: new ( ) ;
1144+
1145+ registry. recover_committed_transaction ( 10 , 200 ) ;
1146+ // next_sequence must advance past 200
1147+ let ( _, begin_seq) = registry. begin_transaction ( ) ;
1148+ assert ! ( begin_seq > 200 ) ;
1149+ }
1150+
1151+ #[ test]
1152+ fn test_recover_committed_descending_order ( ) {
1153+ let registry = TransactionRegistry :: new ( ) ;
1154+
1155+ // Recover in descending order — next_txn_id must still track the max
1156+ registry. recover_committed_transaction ( 100 , 50 ) ;
1157+ registry. recover_committed_transaction ( 50 , 30 ) ;
1158+
1159+ let ( new_id, _) = registry. begin_transaction ( ) ;
1160+ assert ! ( new_id > 100 , "next_txn_id should be >= 100, got {}" , new_id) ;
1161+ }
1162+
1163+ // === recover_aborted_transaction: lines 448, 455 ===
1164+
1165+ #[ test]
1166+ fn test_recover_aborted_marks_aborted ( ) {
1167+ let registry = TransactionRegistry :: new ( ) ;
1168+
1169+ registry. recover_aborted_transaction ( 42 ) ;
1170+
1171+ // Must be aborted, not committed, not active
1172+ assert ! ( !registry. is_committed( 42 ) ) ;
1173+ assert ! ( !registry. is_active( 42 ) ) ;
1174+ let state = registry. transactions . lock ( ) . get ( 42 ) . copied ( ) ;
1175+ assert ! ( state. is_some( ) ) ;
1176+ assert ! ( state. unwrap( ) . is_aborted( ) ) ;
1177+ }
1178+
1179+ #[ test]
1180+ fn test_recover_aborted_advances_next_txn_id ( ) {
1181+ let registry = TransactionRegistry :: new ( ) ;
1182+
1183+ registry. recover_aborted_transaction ( 100 ) ;
1184+ // next_txn_id must advance past 100
1185+ let ( new_id, _) = registry. begin_transaction ( ) ;
1186+ assert ! ( new_id > 100 ) ;
1187+ }
1188+
1189+ #[ test]
1190+ fn test_recover_aborted_descending_order ( ) {
1191+ let registry = TransactionRegistry :: new ( ) ;
1192+
1193+ registry. recover_aborted_transaction ( 200 ) ;
1194+ registry. recover_aborted_transaction ( 100 ) ;
1195+
1196+ // next_txn_id must still be >= 200
1197+ let ( new_id, _) = registry. begin_transaction ( ) ;
1198+ assert ! ( new_id > 200 , "next_txn_id should be > 200, got {}" , new_id) ;
1199+ }
1200+
1201+ #[ test]
1202+ fn test_recover_aborted_not_visible ( ) {
1203+ let registry = TransactionRegistry :: new ( ) ;
1204+
1205+ registry. recover_aborted_transaction ( 5 ) ;
1206+
1207+ let ( viewer, _) = registry. begin_transaction ( ) ;
1208+ // Aborted txn must never be visible
1209+ assert ! ( !registry. is_visible( 5 , viewer) ) ;
1210+ }
1211+
1212+ // === check_committed: line 512 ===
1213+
1214+ #[ test]
1215+ fn test_check_committed_negative_txn_id_not_committed ( ) {
1216+ let registry = TransactionRegistry :: new ( ) ;
1217+
1218+ // Start a real transaction so next_txn_id > 0
1219+ let ( txn_id, _) = registry. begin_transaction ( ) ;
1220+ registry. commit_transaction ( txn_id) ;
1221+
1222+ // Negative txn_id must NOT be committed.
1223+ // Catches && -> || mutation: (-5 > 0 || -5 <= next) would wrongly be true
1224+ assert ! ( !registry. check_committed( -5 ) ) ;
1225+ assert ! ( !registry. check_committed( -100 ) ) ;
1226+ }
1227+
1228+ #[ test]
1229+ fn test_check_committed_future_txn_id ( ) {
1230+ let registry = TransactionRegistry :: new ( ) ;
1231+
1232+ // No transactions started yet, so txn_id 1 is beyond next_txn_id
1233+ assert ! ( !registry. check_committed( 1 ) ) ;
1234+ }
1235+
1236+ #[ test]
1237+ fn test_check_committed_valid_committed ( ) {
1238+ let registry = TransactionRegistry :: new ( ) ;
1239+ registry. set_global_isolation_level ( IsolationLevel :: SnapshotIsolation ) ;
1240+
1241+ let ( txn_id, _) = registry. begin_transaction ( ) ;
1242+ registry. commit_transaction ( txn_id) ;
1243+
1244+ assert ! ( registry. check_committed( txn_id) ) ;
1245+ }
1246+
1247+ #[ test]
1248+ fn test_check_committed_boundary_next_txn_id ( ) {
1249+ let registry = TransactionRegistry :: new ( ) ;
1250+
1251+ let ( txn_id, _) = registry. begin_transaction ( ) ;
1252+ registry. commit_transaction ( txn_id) ;
1253+
1254+ // txn_id == next_txn_id should be committed (boundary: <= next)
1255+ assert ! ( registry. check_committed( txn_id) ) ;
1256+ // txn_id + 1 > next_txn_id should NOT be committed
1257+ assert ! ( !registry. check_committed( txn_id + 1 ) ) ;
1258+ }
1259+
1260+ // === is_directly_visible: line 523 ===
1261+
1262+ #[ test]
1263+ fn test_is_directly_visible_recovery ( ) {
1264+ let registry = TransactionRegistry :: new ( ) ;
1265+
1266+ // RECOVERY_TRANSACTION_ID is always directly visible
1267+ assert ! ( registry. is_directly_visible( RECOVERY_TRANSACTION_ID ) ) ;
1268+ }
1269+
1270+ #[ test]
1271+ fn test_is_directly_visible_normal_txn ( ) {
1272+ let registry = TransactionRegistry :: new ( ) ;
1273+ registry. set_global_isolation_level ( IsolationLevel :: SnapshotIsolation ) ;
1274+
1275+ let ( txn_id, _) = registry. begin_transaction ( ) ;
1276+ // Active txn is NOT directly visible
1277+ assert ! ( !registry. is_directly_visible( txn_id) ) ;
1278+
1279+ registry. commit_transaction ( txn_id) ;
1280+ // Committed txn IS directly visible
1281+ assert ! ( registry. is_directly_visible( txn_id) ) ;
1282+ }
1283+
1284+ #[ test]
1285+ fn test_is_directly_visible_non_recovery_negative ( ) {
1286+ let registry = TransactionRegistry :: new ( ) ;
1287+
1288+ // A negative txn_id that is NOT RECOVERY_TRANSACTION_ID should not be visible
1289+ assert ! ( !registry. is_directly_visible( -99 ) ) ;
1290+ }
1291+
1292+ // === is_visible_snapshot: lines 541, 570 ===
1293+
1294+ #[ test]
1295+ fn test_snapshot_committed_viewer_fallback ( ) {
1296+ // When the viewer has already committed, the match guard
1297+ // `is_active_or_committing()` fails → falls back to check_committed.
1298+ let registry = TransactionRegistry :: new ( ) ;
1299+ registry. set_global_isolation_level ( IsolationLevel :: SnapshotIsolation ) ;
1300+
1301+ let ( txn1, _) = registry. begin_transaction ( ) ;
1302+ registry. commit_transaction ( txn1) ;
1303+
1304+ let ( txn2, _) = registry. begin_transaction ( ) ;
1305+ registry. commit_transaction ( txn2) ;
1306+
1307+ // txn1 is committed → visible to committed viewer txn2
1308+ assert ! ( registry. is_visible( txn1, txn2) ) ;
1309+ }
1310+
1311+ #[ test]
1312+ fn test_snapshot_invalid_version_txn_zero ( ) {
1313+ let registry = TransactionRegistry :: new ( ) ;
1314+ registry. set_global_isolation_level ( IsolationLevel :: SnapshotIsolation ) ;
1315+
1316+ let ( viewer, _) = registry. begin_transaction ( ) ;
1317+
1318+ // version_txn_id 0 is invalid — not visible
1319+ // Catches || → && mutation: (0 <= 0 && 0 > next) = (true && false) = false
1320+ // but || → && would never return false for valid-looking IDs
1321+ assert ! ( !registry. is_visible( 0 , viewer) ) ;
1322+ }
1323+
1324+ #[ test]
1325+ fn test_snapshot_invalid_version_txn_negative ( ) {
1326+ let registry = TransactionRegistry :: new ( ) ;
1327+ registry. set_global_isolation_level ( IsolationLevel :: SnapshotIsolation ) ;
1328+
1329+ let ( viewer, _) = registry. begin_transaction ( ) ;
1330+
1331+ // Negative txn_id (not RECOVERY_TRANSACTION_ID) is invalid
1332+ assert ! ( !registry. is_visible( -50 , viewer) ) ;
1333+ }
1334+
1335+ #[ test]
1336+ fn test_snapshot_future_version_txn_not_visible ( ) {
1337+ let registry = TransactionRegistry :: new ( ) ;
1338+ registry. set_global_isolation_level ( IsolationLevel :: SnapshotIsolation ) ;
1339+
1340+ let ( viewer, _) = registry. begin_transaction ( ) ;
1341+
1342+ // version_txn_id beyond next_txn_id is invalid
1343+ // Catches > → == mutation: 9999 == next would be false (not caught),
1344+ // but next+1 is immediately beyond
1345+ let next = registry. next_txn_id . load ( Ordering :: Acquire ) ;
1346+ assert ! ( !registry. is_visible( next + 1 , viewer) ) ;
1347+ assert ! ( !registry. is_visible( next + 100 , viewer) ) ;
1348+ }
1349+
1350+ #[ test]
1351+ fn test_snapshot_boundary_version_equals_next ( ) {
1352+ // version_txn_id == next_txn_id should be valid (it's an assigned ID)
1353+ // Catches > → >= mutation at line 570
1354+ let registry = TransactionRegistry :: new ( ) ;
1355+ registry. set_global_isolation_level ( IsolationLevel :: SnapshotIsolation ) ;
1356+
1357+ let ( txn1, _) = registry. begin_transaction ( ) ;
1358+ registry. commit_transaction ( txn1) ;
1359+
1360+ // txn1 == next_txn_id at this point
1361+ let ( viewer, _) = registry. begin_transaction ( ) ;
1362+ // txn1 committed before viewer — should be visible
1363+ assert ! ( registry. is_visible( txn1, viewer) ) ;
1364+ }
1365+
1366+ // === get_commit_sequence: line 608 ===
1367+
1368+ #[ test]
1369+ fn test_get_commit_sequence_invalid_txn_id ( ) {
1370+ let registry = TransactionRegistry :: new ( ) ;
1371+
1372+ // txn_id 0 is invalid
1373+ assert_eq ! ( registry. get_commit_sequence( 0 ) , None ) ;
1374+ // Negative is invalid
1375+ assert_eq ! ( registry. get_commit_sequence( -1 ) , None ) ;
1376+ }
1377+
1378+ #[ test]
1379+ fn test_get_commit_sequence_future_txn_id ( ) {
1380+ let registry = TransactionRegistry :: new ( ) ;
1381+
1382+ // No transactions started, txn_id 1 is beyond next_txn_id
1383+ assert_eq ! ( registry. get_commit_sequence( 1 ) , None ) ;
1384+ }
1385+
1386+ #[ test]
1387+ fn test_get_commit_sequence_active ( ) {
1388+ let registry = TransactionRegistry :: new ( ) ;
1389+
1390+ let ( txn_id, _) = registry. begin_transaction ( ) ;
1391+ // Active transaction has no commit_seq
1392+ assert_eq ! ( registry. get_commit_sequence( txn_id) , None ) ;
1393+ }
1394+
1395+ #[ test]
1396+ fn test_get_commit_sequence_aborted ( ) {
1397+ let registry = TransactionRegistry :: new ( ) ;
1398+
1399+ let ( txn_id, _) = registry. begin_transaction ( ) ;
1400+ registry. abort_transaction ( txn_id) ;
1401+ // Aborted transaction has no commit_seq
1402+ assert_eq ! ( registry. get_commit_sequence( txn_id) , None ) ;
1403+ }
1404+
1405+ #[ test]
1406+ fn test_get_commit_sequence_committed_with_snapshot ( ) {
1407+ let registry = TransactionRegistry :: new ( ) ;
1408+ registry. set_global_isolation_level ( IsolationLevel :: SnapshotIsolation ) ;
1409+
1410+ let ( txn_id, _) = registry. begin_transaction ( ) ;
1411+ let commit_seq = registry. commit_transaction ( txn_id) ;
1412+
1413+ // With snapshot isolation, exact commit_seq is stored
1414+ assert_eq ! ( registry. get_commit_sequence( txn_id) , Some ( commit_seq) ) ;
1415+ assert ! ( commit_seq > 0 ) ;
1416+ }
11131417}
0 commit comments