feat: continue work on server
This commit is contained in:
1 parent
2a7fee541b
commit
449a952c3e
1 file changed
+157
+157
@@ -3284,3 +3284,160 @@ test "server table_export renders a real table to csv and json over TCP, matchin
|
|||||||
try std.testing.expect(std.mem.indexOf(u8, jsonResult.content, "krakow") != null);
|
try std.testing.expect(std.mem.indexOf(u8, jsonResult.content, "krakow") != null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "server table_import loads csv and json content sent over TCP into a real table" {
|
||||||
|
const allocator = std.testing.allocator;
|
||||||
|
var tmp = std.testing.tmpDir(.{});
|
||||||
|
defer tmp.cleanup();
|
||||||
|
const path = try tmp.dir.realpathAlloc(allocator, ".");
|
||||||
|
defer allocator.free(path);
|
||||||
|
|
||||||
|
var db = try testDb(allocator, path);
|
||||||
|
defer db.close();
|
||||||
|
|
||||||
|
const address = try std.net.Address.parseIp("127.0.0.1", 0);
|
||||||
|
var server = try Server.init(allocator, &db, address);
|
||||||
|
try server.start();
|
||||||
|
defer server.stop();
|
||||||
|
|
||||||
|
const client_mod = @import("client.zig");
|
||||||
|
var client = try client_mod.Client.connect(allocator, server.boundAddress());
|
||||||
|
defer client.close();
|
||||||
|
|
||||||
|
const csvImported = try client.tableImport("from_csv", .csv, "__pk,city\ni:1,s:warsaw\ni:2,s:krakow\n");
|
||||||
|
try std.testing.expectEqual(@as(u64, 2), csvImported);
|
||||||
|
|
||||||
|
const jsonImported = try client.tableImport("from_json", .json, "[{\"__pk\": \"i:1\", \"city\": \"s:gdansk\"}]");
|
||||||
|
try std.testing.expectEqual(@as(u64, 1), jsonImported);
|
||||||
|
|
||||||
|
const csvRows = try client.tableSelect(allocator, "from_csv", "", 0, 0, &.{});
|
||||||
|
defer protocol.freeRows(allocator, csvRows);
|
||||||
|
try std.testing.expectEqual(@as(usize, 2), csvRows.len);
|
||||||
|
|
||||||
|
const jsonRows = try client.tableSelect(allocator, "from_json", "city eq s:gdansk", 0, 0, &.{});
|
||||||
|
defer protocol.freeRows(allocator, jsonRows);
|
||||||
|
try std.testing.expectEqual(@as(usize, 1), jsonRows.len);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "server table_export then table_import round trips a real table over two TCP round trips" {
|
||||||
|
const allocator = std.testing.allocator;
|
||||||
|
var tmp = std.testing.tmpDir(.{});
|
||||||
|
defer tmp.cleanup();
|
||||||
|
const path = try tmp.dir.realpathAlloc(allocator, ".");
|
||||||
|
defer allocator.free(path);
|
||||||
|
|
||||||
|
var db = try testDb(allocator, path);
|
||||||
|
defer db.close();
|
||||||
|
|
||||||
|
{
|
||||||
|
var table = try querymod.Table.open(&db, allocator, "source");
|
||||||
|
defer table.deinit();
|
||||||
|
try table.put(.{ .int = 1 }, &.{ .{ .name = "city", .value = .{ .text = "warsaw" } }, .{ .name = "score", .value = .{ .float = 4.5 } } });
|
||||||
|
try table.put(.{ .int = 2 }, &.{.{ .name = "city", .value = .{ .text = "lodz" } }});
|
||||||
|
try table.put(.{ .int = 3 }, &.{});
|
||||||
|
}
|
||||||
|
|
||||||
|
const address = try std.net.Address.parseIp("127.0.0.1", 0);
|
||||||
|
var server = try Server.init(allocator, &db, address);
|
||||||
|
try server.start();
|
||||||
|
defer server.stop();
|
||||||
|
|
||||||
|
const client_mod = @import("client.zig");
|
||||||
|
var client = try client_mod.Client.connect(allocator, server.boundAddress());
|
||||||
|
defer client.close();
|
||||||
|
|
||||||
|
var exported = try client.tableExport(allocator, "source", .json);
|
||||||
|
defer exported.deinit(allocator);
|
||||||
|
try std.testing.expectEqual(@as(u64, 3), exported.rowCount);
|
||||||
|
|
||||||
|
const imported = try client.tableImport("dest", .json, exported.content);
|
||||||
|
try std.testing.expectEqual(@as(u64, 3), imported);
|
||||||
|
|
||||||
|
const warsawRows = try client.tableSelect(allocator, "dest", "city eq s:warsaw", 0, 0, &.{});
|
||||||
|
defer protocol.freeRows(allocator, warsawRows);
|
||||||
|
try std.testing.expectEqual(@as(usize, 1), warsawRows.len);
|
||||||
|
try std.testing.expectEqual(@as(f64, 4.5), warsawRows[0].get("score").?.float);
|
||||||
|
|
||||||
|
const untouched = try client.tableSelect(allocator, "dest", "city eq s:lodz", 0, 0, &.{});
|
||||||
|
defer protocol.freeRows(allocator, untouched);
|
||||||
|
try std.testing.expectEqual(@as(usize, 1), untouched.len);
|
||||||
|
|
||||||
|
const allRows = try client.tableSelect(allocator, "dest", "", 0, 0, &.{});
|
||||||
|
defer protocol.freeRows(allocator, allRows);
|
||||||
|
try std.testing.expectEqual(@as(usize, 3), allRows.len);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "server table_export succeeds on a never-written table (schemaless tables have no declare step), table_import rejects malformed content without crashing" {
|
||||||
|
const allocator = std.testing.allocator;
|
||||||
|
var tmp = std.testing.tmpDir(.{});
|
||||||
|
defer tmp.cleanup();
|
||||||
|
const path = try tmp.dir.realpathAlloc(allocator, ".");
|
||||||
|
defer allocator.free(path);
|
||||||
|
|
||||||
|
var db = try testDb(allocator, path);
|
||||||
|
defer db.close();
|
||||||
|
|
||||||
|
const address = try std.net.Address.parseIp("127.0.0.1", 0);
|
||||||
|
var server = try Server.init(allocator, &db, address);
|
||||||
|
try server.start();
|
||||||
|
defer server.stop();
|
||||||
|
|
||||||
|
const client_mod = @import("client.zig");
|
||||||
|
var client = try client_mod.Client.connect(allocator, server.boundAddress());
|
||||||
|
defer client.close();
|
||||||
|
|
||||||
|
// `table` is just a keyspace prefix, never declared ahead of time (see
|
||||||
|
// `query.Table.open`), so exporting one nothing has ever put a row into
|
||||||
|
// is a valid zero-row export, not an error.
|
||||||
|
var exported = try client.tableExport(allocator, "nosuch", .csv);
|
||||||
|
defer exported.deinit(allocator);
|
||||||
|
try std.testing.expectEqual(@as(u64, 0), exported.rowCount);
|
||||||
|
|
||||||
|
const badImport = client.tableImport("nosuch", .json, "not json");
|
||||||
|
try std.testing.expectError(client_mod.ClientError.ServerInternal, badImport);
|
||||||
|
|
||||||
|
// The connection survives an internal error and can still serve a
|
||||||
|
// good request right after.
|
||||||
|
const rowCount = try client.tableImport("nosuch", .json, "[]");
|
||||||
|
try std.testing.expectEqual(@as(u64, 0), rowCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "server rejects a malformed table_export frame (bad format byte) with bad_request without crashing" {
|
||||||
|
const allocator = std.testing.allocator;
|
||||||
|
var tmp = std.testing.tmpDir(.{});
|
||||||
|
defer tmp.cleanup();
|
||||||
|
const path = try tmp.dir.realpathAlloc(allocator, ".");
|
||||||
|
defer allocator.free(path);
|
||||||
|
|
||||||
|
var db = try testDb(allocator, path);
|
||||||
|
defer db.close();
|
||||||
|
|
||||||
|
{
|
||||||
|
var table = try querymod.Table.open(&db, allocator, "people");
|
||||||
|
defer table.deinit();
|
||||||
|
try table.put(.{ .int = 1 }, &.{});
|
||||||
|
}
|
||||||
|
|
||||||
|
const address = try std.net.Address.parseIp("127.0.0.1", 0);
|
||||||
|
var server = try Server.init(allocator, &db, address);
|
||||||
|
try server.start();
|
||||||
|
defer server.stop();
|
||||||
|
|
||||||
|
const stream = try std.net.tcpConnectToAddress(server.boundAddress());
|
||||||
|
defer stream.close();
|
||||||
|
|
||||||
|
// A well-formed table name ("people") followed by a format byte
|
||||||
|
// outside the DumpFormat enum (99) - `decodeTableExport` itself must
|
||||||
|
// reject this before `Table.open` is ever reached.
|
||||||
|
var payload = std.ArrayList(u8).init(allocator);
|
||||||
|
defer payload.deinit();
|
||||||
|
var lenBuf: [4]u8 = undefined;
|
||||||
|
std.mem.writeInt(u32, &lenBuf, 6, .little);
|
||||||
|
try payload.appendSlice(&lenBuf);
|
||||||
|
try payload.appendSlice("people");
|
||||||
|
try payload.append(99);
|
||||||
|
|
||||||
|
try protocol.writeFrame(stream.writer(), @intFromEnum(protocol.Op.table_export), payload.items);
|
||||||
|
var frame = try protocol.readFrame(allocator, stream.reader());
|
||||||
|
defer frame.deinit();
|
||||||
|
try std.testing.expectEqual(@intFromEnum(protocol.Status.bad_request), frame.tag);
|
||||||
|
|
||||||
Reference in new issue
Block a user