feat: build out server
This commit is contained in:
1 parent
50b9f57522
commit
555977be6b
1 file changed
+160
+160
@@ -4375,3 +4375,163 @@ test "server table_window RANGE frame groups tied rows by day, ROWS gives each r
|
|||||||
const path = try tmp.dir.realpathAlloc(allocator, ".");
|
const path = try tmp.dir.realpathAlloc(allocator, ".");
|
||||||
defer allocator.free(path);
|
defer allocator.free(path);
|
||||||
|
|
||||||
|
var db = try testDb(allocator, path);
|
||||||
|
defer db.close();
|
||||||
|
{
|
||||||
|
var orders = try querymod.Table.open(&db, allocator, "orders");
|
||||||
|
defer orders.deinit();
|
||||||
|
// Two rows tied at day=1 (10, 20), then one row at day=2 (5).
|
||||||
|
try orders.put(.{ .int = 1 }, &.{
|
||||||
|
.{ .name = "day", .value = .{ .int = 1 } },
|
||||||
|
.{ .name = "amount", .value = .{ .int = 10 } },
|
||||||
|
});
|
||||||
|
try orders.put(.{ .int = 2 }, &.{
|
||||||
|
.{ .name = "day", .value = .{ .int = 1 } },
|
||||||
|
.{ .name = "amount", .value = .{ .int = 20 } },
|
||||||
|
});
|
||||||
|
try orders.put(.{ .int = 3 }, &.{
|
||||||
|
.{ .name = "day", .value = .{ .int = 2 } },
|
||||||
|
.{ .name = "amount", .value = .{ .int = 5 } },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
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 orderBy = [_]querymod.OrderBy{.{ .field = "day", .descending = false }};
|
||||||
|
const specs = [_]windowmod.WindowSpec{.{ .field = "amount", .kind = .sum }};
|
||||||
|
|
||||||
|
const rangeResults = try client.tableWindow(allocator, "orders", &specs, &.{}, &orderBy, 0, 0, "", .range);
|
||||||
|
defer protocol.freeWindowRows(allocator, rangeResults);
|
||||||
|
try std.testing.expectEqual(@as(usize, 3), rangeResults.len);
|
||||||
|
// Both day=1 rows share the tie group's combined total (10+20=30).
|
||||||
|
try std.testing.expectEqual(@as(i64, 30), rangeResults[0].values[0].int);
|
||||||
|
try std.testing.expectEqual(@as(i64, 30), rangeResults[1].values[0].int);
|
||||||
|
try std.testing.expectEqual(@as(i64, 35), rangeResults[2].values[0].int);
|
||||||
|
|
||||||
|
const rowsResults = try client.tableWindow(allocator, "orders", &specs, &.{}, &orderBy, 0, 0, "", .rows);
|
||||||
|
defer protocol.freeWindowRows(allocator, rowsResults);
|
||||||
|
try std.testing.expectEqual(@as(usize, 3), rowsResults.len);
|
||||||
|
// ROWS: each physical row gets its own running total, even tied ones.
|
||||||
|
try std.testing.expectEqual(@as(i64, 10), rowsResults[0].values[0].int);
|
||||||
|
try std.testing.expectEqual(@as(i64, 30), rowsResults[1].values[0].int);
|
||||||
|
try std.testing.expectEqual(@as(i64, 35), rowsResults[2].values[0].int);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "server table_window rejects a RANGE frame combined with row_number as bad_request, not a crash" {
|
||||||
|
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();
|
||||||
|
try seedAggregateOrders(allocator, &db);
|
||||||
|
|
||||||
|
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 orderBy = [_]querymod.OrderBy{.{ .field = "total", .descending = false }};
|
||||||
|
const specs = [_]windowmod.WindowSpec{.{ .field = "", .kind = .row_number }};
|
||||||
|
const err = client.tableWindow(allocator, "orders", &specs, &.{}, &orderBy, 0, 0, "", .range);
|
||||||
|
try std.testing.expectError(client_mod.ClientError.ServerBadRequest, err);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "server table_window lag/lead read the adjacent row's value along order, NULL at the sequence edge, over real TCP" {
|
||||||
|
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();
|
||||||
|
try seedAggregateOrders(allocator, &db);
|
||||||
|
|
||||||
|
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 orderBy = [_]querymod.OrderBy{.{ .field = "total", .descending = false }};
|
||||||
|
const specs = [_]windowmod.WindowSpec{
|
||||||
|
.{ .field = "total", .kind = .lag },
|
||||||
|
.{ .field = "total", .kind = .lead },
|
||||||
|
};
|
||||||
|
const results = try client.tableWindow(allocator, "orders", &specs, &.{}, &orderBy, 0, 0, "", .rows);
|
||||||
|
defer protocol.freeWindowRows(allocator, results);
|
||||||
|
|
||||||
|
// Sorted by total ascending: krakow/40, warsaw/100, warsaw/250.
|
||||||
|
try std.testing.expectEqual(@as(usize, 3), results.len);
|
||||||
|
try std.testing.expectEqual(@as(i64, 40), results[0].row.get("total").?.int);
|
||||||
|
try std.testing.expectEqual(rowmod.ValueTag.none, @as(rowmod.ValueTag, results[0].values[0]));
|
||||||
|
try std.testing.expectEqual(@as(i64, 100), results[0].values[1].int);
|
||||||
|
try std.testing.expectEqual(@as(i64, 100), results[1].row.get("total").?.int);
|
||||||
|
try std.testing.expectEqual(@as(i64, 40), results[1].values[0].int);
|
||||||
|
try std.testing.expectEqual(@as(i64, 250), results[1].values[1].int);
|
||||||
|
try std.testing.expectEqual(@as(i64, 250), results[2].row.get("total").?.int);
|
||||||
|
try std.testing.expectEqual(@as(i64, 100), results[2].values[0].int);
|
||||||
|
try std.testing.expectEqual(rowmod.ValueTag.none, @as(rowmod.ValueTag, results[2].values[1]));
|
||||||
|
}
|
||||||
|
|
||||||
|
test "server table_window lag with an explicit offset and a caller-supplied default, over real TCP" {
|
||||||
|
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();
|
||||||
|
try seedAggregateOrders(allocator, &db);
|
||||||
|
|
||||||
|
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 orderBy = [_]querymod.OrderBy{.{ .field = "total", .descending = false }};
|
||||||
|
const specs = [_]windowmod.WindowSpec{.{ .field = "total", .kind = .lag, .offset = 2, .default = .{ .int = -1 } }};
|
||||||
|
const results = try client.tableWindow(allocator, "orders", &specs, &.{}, &orderBy, 0, 0, "", .rows);
|
||||||
|
defer protocol.freeWindowRows(allocator, results);
|
||||||
|
|
||||||
|
// Sorted: krakow/40, warsaw/100, warsaw/250 - lag offset 2 reaches back
|
||||||
|
// two physical positions, so only the LAST row has a real source (40);
|
||||||
|
// the first two fall outside the sequence and get the -1 default.
|
||||||
|
try std.testing.expectEqual(@as(i64, -1), results[0].values[0].int);
|
||||||
|
try std.testing.expectEqual(@as(i64, -1), results[1].values[0].int);
|
||||||
|
try std.testing.expectEqual(@as(i64, 40), results[2].values[0].int);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "server table_window rejects a RANGE frame combined with lag as bad_request, not a crash" {
|
||||||
|
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();
|
||||||
|
try seedAggregateOrders(allocator, &db);
|
||||||
|
|
||||||
Reference in new issue
Block a user