feat: build out protocol

This commit is contained in:
dualfroz committed 2026-09-08 11:50:29 +02:00
1 parent 6f0790ed87
commit 6ff5e832eb
1 file changed
+156
+156
View File
@@ -2514,3 +2514,159 @@ test "decodeTableSelect rejects an order-by count claiming more columns than wer
try std.testing.expectError(error.Malformed, decodeTableSelect(allocator, corrupted));
}
test "rows round trip through encode and decode" {
const allocator = std.testing.allocator;
var rows = [_]rowmod.Row{ rowmod.Row.init(allocator), rowmod.Row.init(allocator) };
defer for (&rows) |*r| r.deinit();
try rows[0].set("name", .{ .text = "ada" });
try rows[0].set("age", .{ .int = 36 });
try rows[1].set("name", .{ .text = "bob" });
try rows[1].set("age", .{ .int = 24 });
const payload = try encodeRows(allocator, &rows);
defer allocator.free(payload);
const decoded = try decodeRows(allocator, payload);
defer freeRows(allocator, decoded);
try std.testing.expectEqual(@as(usize, 2), decoded.len);
try std.testing.expectEqualStrings("ada", decoded[0].get("name").?.text);
try std.testing.expectEqual(@as(i64, 36), decoded[0].get("age").?.int);
try std.testing.expectEqualStrings("bob", decoded[1].get("name").?.text);
try std.testing.expectEqual(@as(i64, 24), decoded[1].get("age").?.int);
}
test "empty row list round trips" {
const allocator = std.testing.allocator;
const payload = try encodeRows(allocator, &[_]rowmod.Row{});
defer allocator.free(payload);
const decoded = try decodeRows(allocator, payload);
defer freeRows(allocator, decoded);
try std.testing.expectEqual(@as(usize, 0), decoded.len);
}
test "JoinMode.fromByte rejects an unknown mode" {
try std.testing.expectError(error.UnknownJoinMode, JoinMode.fromByte(9));
}
test "table_join args round trip mode, tables, fields, and both expr texts" {
const allocator = std.testing.allocator;
const payload = try encodeTableJoin(allocator, .left, "customers", "__pk", "orders", "customer_id", 0, 0, "age gt i:10", "total gt i:0");
defer allocator.free(payload);
const args = try decodeTableJoin(payload);
try std.testing.expectEqual(JoinMode.left, args.mode);
try std.testing.expectEqualStrings("customers", args.leftTable);
try std.testing.expectEqualStrings("__pk", args.leftField);
try std.testing.expectEqualStrings("orders", args.rightTable);
try std.testing.expectEqualStrings("customer_id", args.rightField);
try std.testing.expectEqual(@as(u32, 0), args.offset);
try std.testing.expectEqual(@as(u32, 0), args.limit);
try std.testing.expectEqualStrings("age gt i:10", args.leftExprText);
try std.testing.expectEqualStrings("total gt i:0", args.rightExprText);
}
test "table_join args round trip with both expr texts empty" {
const allocator = std.testing.allocator;
const payload = try encodeTableJoin(allocator, .inner, "a", "x", "b", "y", 0, 0, "", "");
defer allocator.free(payload);
const args = try decodeTableJoin(payload);
try std.testing.expectEqual(JoinMode.inner, args.mode);
try std.testing.expectEqualStrings("", args.leftExprText);
try std.testing.expectEqualStrings("", args.rightExprText);
}
test "table_join args round trip a nonzero offset and limit" {
const allocator = std.testing.allocator;
const payload = try encodeTableJoin(allocator, .full, "a", "x", "b", "y", 3, 7, "", "");
defer allocator.free(payload);
const args = try decodeTableJoin(payload);
try std.testing.expectEqual(@as(u32, 3), args.offset);
try std.testing.expectEqual(@as(u32, 7), args.limit);
}
test "join pairs round trip a fully matched pair (inner-join shape)" {
const allocator = std.testing.allocator;
var left = rowmod.Row.init(allocator);
defer left.deinit();
try left.set("name", .{ .text = "ada" });
var right = rowmod.Row.init(allocator);
defer right.deinit();
try right.set("total", .{ .int = 42 });
const pairs = [_]JoinPair{.{ .left = left, .right = right }};
const payload = try encodeJoinedRows(allocator, &pairs);
defer allocator.free(payload);
const decoded = try decodeJoinedRows(allocator, payload);
defer freeJoinPairs(allocator, decoded);
try std.testing.expectEqual(@as(usize, 1), decoded.len);
try std.testing.expectEqualStrings("ada", decoded[0].left.?.get("name").?.text);
try std.testing.expectEqual(@as(i64, 42), decoded[0].right.?.get("total").?.int);
}
test "join pairs round trip a nil left side and a nil right side" {
const allocator = std.testing.allocator;
var right = rowmod.Row.init(allocator);
defer right.deinit();
try right.set("total", .{ .int = 7 });
var left = rowmod.Row.init(allocator);
defer left.deinit();
try left.set("name", .{ .text = "carol" });
const pairs = [_]JoinPair{
.{ .left = null, .right = right },
.{ .left = left, .right = null },
};
const payload = try encodeJoinedRows(allocator, &pairs);
defer allocator.free(payload);
const decoded = try decodeJoinedRows(allocator, payload);
defer freeJoinPairs(allocator, decoded);
try std.testing.expectEqual(@as(usize, 2), decoded.len);
try std.testing.expect(decoded[0].left == null);
try std.testing.expectEqual(@as(i64, 7), decoded[0].right.?.get("total").?.int);
try std.testing.expectEqualStrings("carol", decoded[1].left.?.get("name").?.text);
try std.testing.expect(decoded[1].right == null);
}
test "empty join pair list round trips" {
const allocator = std.testing.allocator;
const payload = try encodeJoinedRows(allocator, &[_]JoinPair{});
defer allocator.free(payload);
const decoded = try decodeJoinedRows(allocator, payload);
defer freeJoinPairs(allocator, decoded);
try std.testing.expectEqual(@as(usize, 0), decoded.len);
}
test "table_aggregate args round trip a groupField, several specs, and an expr" {
const allocator = std.testing.allocator;
const specs = [_]aggregatemod.AggSpec{
.{ .field = "", .kind = .count },
.{ .field = "total", .kind = .sum },
.{ .field = "total", .kind = .max },
.{ .field = "city", .kind = .count_distinct },
};
const payload = try encodeTableAggregate(allocator, "orders", "city", &specs, 0, 0, &.{}, "", "total gt i:10");
defer allocator.free(payload);
const args = try decodeTableAggregate(allocator, payload);
defer allocator.free(args.specs);
defer allocator.free(args.orderBy);
try std.testing.expectEqualStrings("orders", args.table);
try std.testing.expectEqualStrings("city", args.groupField);
try std.testing.expectEqual(@as(u32, 0), args.offset);
try std.testing.expectEqual(@as(u32, 0), args.limit);
try std.testing.expectEqualStrings("", args.havingText);
try std.testing.expectEqualStrings("total gt i:10", args.exprText);
try std.testing.expectEqual(@as(usize, 4), args.specs.len);
try std.testing.expectEqual(aggregatemod.AggKind.count, args.specs[0].kind);
try std.testing.expectEqualStrings("", args.specs[0].field);
try std.testing.expectEqual(aggregatemod.AggKind.sum, args.specs[1].kind);
try std.testing.expectEqualStrings("total", args.specs[1].field);
try std.testing.expectEqual(aggregatemod.AggKind.max, args.specs[2].kind);
try std.testing.expectEqual(aggregatemod.AggKind.count_distinct, args.specs[3].kind);
try std.testing.expectEqualStrings("city", args.specs[3].field);
}