feat: extend repl

This commit is contained in:
dualfroz committed 2026-09-10 14:06:33 +02:00
1 parent dce1077708
commit aead324484
1 file changed
+156
+156
View File
@@ -2512,3 +2512,159 @@ test "table writes made before commit are not visible to an independent Table ha
try std.testing.expectEqualStrings("alice", row.get("name").?.text); try std.testing.expectEqualStrings("alice", row.get("name").?.text);
} }
test "table put/get/del outside a transaction autocommit immediately like plain Table" {
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,
"table put widgets 7 sku=s:ABC qty=i:3\ntable get widgets 7\ntable del widgets 7\ntable get widgets 7\n",
);
defer allocator.free(out);
try std.testing.expect(std.mem.startsWith(u8, out, "OK\n"));
try std.testing.expect(std.mem.indexOf(u8, out, "sku=ABC") != null);
try std.testing.expect(std.mem.indexOf(u8, out, "qty=3") != null);
try std.testing.expect(std.mem.endsWith(u8, out, "(nil)\n"));
}
test "malformed field is rejected before staging, transaction stays 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\ntable put accounts 1 badfieldnoequals\ntable put accounts 1 name=bob\ncommit\ntable get accounts 1\n",
);
defer allocator.free(out);
try std.testing.expect(std.mem.startsWith(u8, out, "OK\nerror: InvalidField\nOK\nOK\n"));
try std.testing.expect(std.mem.indexOf(u8, out, "name=bob") != null);
}
test "a real external conflict surfaces error.Conflict on commit and applies nothing" {
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 repl = Repl.init(allocator, &db);
defer repl.deinit();
var stream = std.io.fixedBufferStream("begin\nget balance\n");
var out = std.ArrayList(u8).init(allocator);
defer out.deinit();
try repl.runFromReader(stream.reader(), out.writer(), false);
try db.put("balance", "external");
var stream2 = std.io.fixedBufferStream("put balance 150\ncommit\n");
try repl.runFromReader(stream2.reader(), out.writer(), false);
try std.testing.expect(std.mem.endsWith(u8, out.items, "OK\nerror: Conflict\n"));
const current = (try db.get(allocator, "balance")).?;
defer allocator.free(current);
try std.testing.expectEqualStrings("external", current);
try std.testing.expect(repl.inTransaction());
var stream3 = std.io.fixedBufferStream("rollback\n");
try repl.runFromReader(stream3.reader(), out.writer(), false);
try std.testing.expect(!repl.inTransaction());
}
test "scan always reflects committed Db state, not an in-flight transaction's staged writes" {
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, "put k1 v1\nbegin\nput k2 v2\nscan k\n");
defer allocator.free(out);
try std.testing.expectEqualStrings("OK\nOK\nOK\nk1\tv1\n", out);
}
test "abandoning an open transaction on deinit frees staged writes and pending TableTxn state" {
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);
const out = try runScript(allocator, &repl, "begin\nput a 1\ntable put accounts 1 name=alice\n");
defer allocator.free(out);
try std.testing.expectEqualStrings("OK\nOK\nOK\n", out);
repl.deinit();
try std.testing.expectEqual(@as(?[]u8, null), try db.get(allocator, "a"));
var table = try Table.open(&db, allocator, "accounts");
defer table.deinit();
try std.testing.expectEqual(@as(?Row, null), try table.get(.{ .text = "1" }));
}
test "flush and compact run against the live Db and stats reflects it" {
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, "put a 1\nflush\ncompact\nstats\n");
defer allocator.free(out);
try std.testing.expect(std.mem.startsWith(u8, out, "OK\nOK\nOK\n"));
try std.testing.expect(out.len > "OK\nOK\nOK\n".len);
}
test "help lists commands without touching db state" {
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, "help\n");
defer allocator.free(out);
try std.testing.expect(std.mem.indexOf(u8, out, "begin / commit / rollback") != null);
}
test "prompt is written before each read only when requested" {
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\nput a 1\n");
var out = std.ArrayList(u8).init(allocator);
defer out.deinit();
try repl.runFromReader(stream.reader(), out.writer(), true);
try std.testing.expectEqualStrings("cobalt> OK\ncobalt(txn)> OK\ncobalt(txn)> ", out.items);
}