From 6bb904de2ce7fae0ea7fb8a9db48c1c54543ab05 Mon Sep 17 00:00:00 2001 From: DualFroz Date: Fri, 28 Aug 2026 15:50:00 +0200 Subject: [PATCH] test(txn): add commit and conflict tests --- src/txn.zig | 84 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/src/txn.zig b/src/txn.zig index dd0152f..0c06b77 100644 --- a/src/txn.zig +++ b/src/txn.zig @@ -102,3 +102,87 @@ pub const Transaction = struct { self.finished = true; } }; + +fn openTemp(tmp: *std.testing.TmpDir, allocator: Allocator) !Db { + var pathBuf: [std.fs.max_path_bytes]u8 = undefined; + const base = try tmp.dir.realpath(".", &pathBuf); + var dbPathBuf: [std.fs.max_path_bytes]u8 = undefined; + const dbPath = try std.fmt.bufPrint(&dbPathBuf, "{s}/store", .{base}); + return Db.open(allocator, dbPath, .{}); +} + +test "transaction commits buffered writes atomically" { + const allocator = std.testing.allocator; + var tmp = std.testing.tmpDir(.{}); + defer tmp.cleanup(); + + var db = try openTemp(&tmp, allocator); + defer db.close(); + + var txn = Transaction.begin(&db); + defer txn.deinit(); + try txn.put("alpha", "1"); + try txn.put("bravo", "2"); + try txn.delete("alpha"); + + try std.testing.expectEqual(@as(?[]u8, null), try db.get(allocator, "bravo")); + + try txn.commit(); + + const bravo = (try db.get(allocator, "bravo")).?; + defer allocator.free(bravo); + try std.testing.expectEqualStrings("2", bravo); + try std.testing.expectEqual(@as(?[]u8, null), try db.get(allocator, "alpha")); +} + +test "transaction reads its own writes" { + const allocator = std.testing.allocator; + var tmp = std.testing.tmpDir(.{}); + defer tmp.cleanup(); + + var db = try openTemp(&tmp, allocator); + defer db.close(); + + var txn = Transaction.begin(&db); + defer txn.deinit(); + try txn.put("k", "staged"); + const staged = (try txn.get(allocator, "k")).?; + defer allocator.free(staged); + try std.testing.expectEqualStrings("staged", staged); + + try txn.delete("k"); + try std.testing.expectEqual(@as(?[]u8, null), try txn.get(allocator, "k")); +} + +test "conflicting commit is rejected but clean commit succeeds" { + const allocator = std.testing.allocator; + var tmp = std.testing.tmpDir(.{}); + defer tmp.cleanup(); + + var db = try openTemp(&tmp, allocator); + defer db.close(); + + try db.put("balance", "100"); + + var txn = Transaction.begin(&db); + defer txn.deinit(); + const seen = (try txn.get(allocator, "balance")).?; + defer allocator.free(seen); + try std.testing.expectEqualStrings("100", seen); + + try db.put("balance", "external"); + + try txn.put("balance", "150"); + try std.testing.expectError(error.Conflict, txn.commit()); + + var clean = Transaction.begin(&db); + defer clean.deinit(); + const current = (try clean.get(allocator, "balance")).?; + defer allocator.free(current); + try clean.put("note", "ok"); + try clean.commit(); + + const note = (try db.get(allocator, "note")).?; + defer allocator.free(note); + try std.testing.expectEqualStrings("ok", note); +}