feat: build out server

This commit is contained in:
dualfroz committed 2026-09-11 22:45:40 +02:00
1 parent 50b9f57522
commit 555977be6b
1 file changed
+160
+160
View File
@@ -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);