feat: fill in repl

This commit is contained in:
dualfroz committed 2026-09-10 14:02:22 +02:00
1 parent 755c0ece36
commit dce1077708
1 file changed
+157
+157
View File
@@ -2355,3 +2355,160 @@ test "unknown command and wrong arg counts report errors without crashing" {
); );
} }
test "staged writes inside a transaction are invisible until commit, then durable" {
const allocator = std.testing.allocator;
var tmp = std.testing.tmpDir(.{});
defer tmp.cleanup();
var db = try openTemp(&tmp, allocator);
defer db.close();
var repl = Repl.init(allocator, &db);
defer repl.deinit();
const out = try runScript(allocator, &repl, "begin\nput bal 100\nget bal\ncommit\nget bal\n");
defer allocator.free(out);
try std.testing.expectEqualStrings("OK\nOK\n100\nOK\n100\n", out);
}
test "get mid-transaction steers the next put, applied atomically on commit" {
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("bal", "100");
var repl = Repl.init(allocator, &db);
defer repl.deinit();
const out = try runScript(allocator, &repl, "begin\nget bal\nput bal 150\ncommit\nget bal\n");
defer allocator.free(out);
try std.testing.expectEqualStrings("OK\n100\nOK\nOK\n150\n", out);
const stored = (try db.get(allocator, "bal")).?;
defer allocator.free(stored);
try std.testing.expectEqualStrings("150", stored);
}
test "rollback discards staged writes entirely" {
const allocator = std.testing.allocator;
var tmp = std.testing.tmpDir(.{});
defer tmp.cleanup();
var db = try openTemp(&tmp, allocator);
defer db.close();
var repl = Repl.init(allocator, &db);
defer repl.deinit();
const out = try runScript(allocator, &repl, "begin\nput x 1\nrollback\nget x\n");
defer allocator.free(out);
try std.testing.expectEqualStrings("OK\nOK\nOK\n(nil)\n", out);
try std.testing.expectEqual(@as(?[]u8, null), try db.get(allocator, "x"));
}
test "commit/rollback without an active transaction report NoActiveTransaction" {
const allocator = std.testing.allocator;
var tmp = std.testing.tmpDir(.{});
defer tmp.cleanup();
var db = try openTemp(&tmp, allocator);
defer db.close();
var repl = Repl.init(allocator, &db);
defer repl.deinit();
const out = try runScript(allocator, &repl, "commit\nrollback\nput a 1\nget a\n");
defer allocator.free(out);
try std.testing.expectEqualStrings(
"error: NoActiveTransaction\nerror: NoActiveTransaction\nOK\n1\n",
out,
);
}
test "double begin is rejected and leaves the first transaction usable" {
const allocator = std.testing.allocator;
var tmp = std.testing.tmpDir(.{});
defer tmp.cleanup();
var db = try openTemp(&tmp, allocator);
defer db.close();
var repl = Repl.init(allocator, &db);
defer repl.deinit();
const out = try runScript(allocator, &repl, "begin\nput a 1\nbegin\ncommit\nget a\n");
defer allocator.free(out);
try std.testing.expectEqualStrings("OK\nOK\nerror: TransactionAlreadyActive\nOK\n1\n", out);
}
test "table put/get/del inside a transaction stage through TableTxn atomically across two tables" {
const allocator = std.testing.allocator;
var tmp = std.testing.tmpDir(.{});
defer tmp.cleanup();
var db = try openTemp(&tmp, allocator);
defer db.close();
var repl = Repl.init(allocator, &db);
defer repl.deinit();
const out = try runScript(
allocator,
&repl,
"begin\n" ++
"table put accounts 1 name=alice balance=i:100\n" ++
"table put ledger 1 note=opening\n" ++
"table get accounts 1\n" ++
"commit\n" ++
"table get accounts 1\n" ++
"table get ledger 1\n",
);
defer allocator.free(out);
var lines = std.mem.splitScalar(u8, std.mem.trimRight(u8, out, "\n"), '\n');
try std.testing.expectEqualStrings("OK", lines.next().?);
try std.testing.expectEqualStrings("OK", lines.next().?);
try std.testing.expectEqualStrings("OK", lines.next().?);
const preCommitGet = lines.next().?;
try std.testing.expect(std.mem.indexOf(u8, preCommitGet, "name=alice") != null);
try std.testing.expectEqualStrings("OK", lines.next().?);
const postAccounts = lines.next().?;
try std.testing.expect(std.mem.indexOf(u8, postAccounts, "balance=100") != null);
const postLedger = lines.next().?;
try std.testing.expect(std.mem.indexOf(u8, postLedger, "note=opening") != null);
try std.testing.expectEqual(@as(?[]const u8, null), lines.next());
var directTable = try Table.open(&db, allocator, "accounts");
defer directTable.deinit();
var row = (try directTable.get(.{ .text = "1" })).?;
defer row.deinit();
try std.testing.expectEqual(@as(i64, 100), row.get("balance").?.int);
}
test "table writes made before commit are not visible to an independent Table handle" {
const allocator = std.testing.allocator;
var tmp = std.testing.tmpDir(.{});
defer tmp.cleanup();
var db = try openTemp(&tmp, allocator);
defer db.close();
var repl = Repl.init(allocator, &db);
defer repl.deinit();
var stream = std.io.fixedBufferStream("begin\ntable put accounts 1 name=alice\n");
var out = std.ArrayList(u8).init(allocator);
defer out.deinit();
try repl.runFromReader(stream.reader(), out.writer(), false);
try std.testing.expectEqualStrings("OK\nOK\n", out.items);
var outsider = try Table.open(&db, allocator, "accounts");
defer outsider.deinit();
try std.testing.expectEqual(@as(?Row, null), try outsider.get(.{ .text = "1" }));
try repl.txn.?.commit();
repl.discardTxnState();
var again = try Table.open(&db, allocator, "accounts");
defer again.deinit();
var row = (try again.get(.{ .text = "1" })).?;
defer row.deinit();
try std.testing.expectEqualStrings("alice", row.get("name").?.text);
}