feat: build out protocol

This commit is contained in:
dualfroz committed 2026-09-08 16:14:04 +02:00
1 parent 96ab73e9e3
commit e0d6e2b8f9
1 file changed
+162
+162
View File
@@ -3613,3 +3613,165 @@ test "table_window args round trip a RANGE frame, and default to ROWS when the b
try std.testing.expectEqual(windowmod.FrameMode.rows, rowsArgs.frame); try std.testing.expectEqual(windowmod.FrameMode.rows, rowsArgs.frame);
} }
test "decodeTableWindow rejects an unknown frame byte instead of silently defaulting to ROWS" {
const allocator = std.testing.allocator;
var buf = std.ArrayList(u8).init(allocator);
defer buf.deinit();
const w = encode.ByteWriter.init(&buf);
try putLenPrefixed(w, "orders");
try w.byte(1); // specs count
try w.byte(@intFromEnum(windowmod.WindowKind.sum));
try putLenPrefixed(w, "total");
try w.byte(0); // partitionBy count
try w.byte(0); // orderBy count
try w.putU32(0);
try w.putU32(0);
try w.byte(200); // not a valid FrameMode tag
try std.testing.expectError(error.Malformed, decodeTableWindow(allocator, buf.items));
}
test "table_window args round trip a multi-column partition and multi-column descending order-by" {
const allocator = std.testing.allocator;
const partitionBy = [_][]const u8{ "city", "region" };
const orderBy = [_]querymod.OrderBy{
.{ .field = "day", .descending = true },
.{ .field = "amount", .descending = false },
};
const specs = [_]windowmod.WindowSpec{.{ .field = "amount", .kind = .max }};
const payload = try encodeTableWindow(allocator, "orders", &specs, &partitionBy, &orderBy, 0, 0, "", .rows);
defer allocator.free(payload);
const args = try decodeTableWindow(allocator, payload);
defer allocator.free(args.specs);
defer allocator.free(args.partitionBy);
defer allocator.free(args.orderBy);
try std.testing.expectEqual(@as(usize, 2), args.partitionBy.len);
try std.testing.expectEqualStrings("city", args.partitionBy[0]);
try std.testing.expectEqualStrings("region", args.partitionBy[1]);
try std.testing.expectEqual(@as(usize, 2), args.orderBy.len);
try std.testing.expect(args.orderBy[0].descending);
try std.testing.expect(!args.orderBy[1].descending);
}
test "decodeTableWindow rejects an unknown window kind byte instead of corrupting a spec" {
const allocator = std.testing.allocator;
var buf = std.ArrayList(u8).init(allocator);
defer buf.deinit();
const w = encode.ByteWriter.init(&buf);
try putLenPrefixed(w, "orders");
try w.byte(1); // specs count
try w.byte(200); // not a valid WindowKind tag
try putLenPrefixed(w, "total");
try w.byte(0); // partitionBy count
try w.byte(0); // orderBy count
try w.putU32(0);
try w.putU32(0);
try std.testing.expectError(error.Malformed, decodeTableWindow(allocator, buf.items));
}
test "window 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("city", .{ .text = "warsaw" });
try rows[1].set("city", .{ .text = "krakow" });
var values0 = [_]rowmod.Value{.{ .int = 1 }};
var values1 = [_]rowmod.Value{.{ .int = 2 }};
const windowRows = [_]WindowRow{
.{ .row = rows[0], .values = &values0 },
.{ .row = rows[1], .values = &values1 },
};
const payload = try encodeWindowRows(allocator, &windowRows);
defer allocator.free(payload);
const decoded = try decodeWindowRows(allocator, payload);
defer freeWindowRows(allocator, decoded);
try std.testing.expectEqual(@as(usize, 2), decoded.len);
try std.testing.expectEqualStrings("warsaw", decoded[0].row.get("city").?.text);
try std.testing.expectEqual(@as(usize, 1), decoded[0].values.len);
try std.testing.expectEqual(@as(i64, 1), decoded[0].values[0].int);
try std.testing.expectEqualStrings("krakow", decoded[1].row.get("city").?.text);
try std.testing.expectEqual(@as(i64, 2), decoded[1].values[0].int);
}
test "window rows round trip MULTIPLE values per row, same order as the request's specs" {
const allocator = std.testing.allocator;
var rows = [_]rowmod.Row{rowmod.Row.init(allocator)};
defer for (&rows) |*r| r.deinit();
try rows[0].set("city", .{ .text = "warsaw" });
var values0 = [_]rowmod.Value{ .{ .int = 30 }, .{ .float = 15.0 }, .{ .int = 1 } };
const windowRows = [_]WindowRow{.{ .row = rows[0], .values = &values0 }};
const payload = try encodeWindowRows(allocator, &windowRows);
defer allocator.free(payload);
const decoded = try decodeWindowRows(allocator, payload);
defer freeWindowRows(allocator, decoded);
try std.testing.expectEqual(@as(usize, 1), decoded.len);
try std.testing.expectEqual(@as(usize, 3), decoded[0].values.len);
try std.testing.expectEqual(@as(i64, 30), decoded[0].values[0].int);
try std.testing.expectEqual(@as(f64, 15.0), decoded[0].values[1].float);
try std.testing.expectEqual(@as(i64, 1), decoded[0].values[2].int);
}
test "empty window row list round trips" {
const allocator = std.testing.allocator;
const payload = try encodeWindowRows(allocator, &[_]WindowRow{});
defer allocator.free(payload);
const decoded = try decodeWindowRows(allocator, payload);
defer freeWindowRows(allocator, decoded);
try std.testing.expectEqual(@as(usize, 0), decoded.len);
}
test "subscribe args round trip a multi-channel list" {
const allocator = std.testing.allocator;
const payload = try encodeSubscribe(allocator, &.{ "orders", "alerts" });
defer allocator.free(payload);
const args = try decodeSubscribe(allocator, payload);
defer args.deinit(allocator);
try std.testing.expectEqual(@as(usize, 2), args.channels.len);
try std.testing.expectEqualStrings("orders", args.channels[0]);
try std.testing.expectEqualStrings("alerts", args.channels[1]);
}
test "subscribe args round trip an empty channel list" {
const allocator = std.testing.allocator;
const payload = try encodeSubscribe(allocator, &.{});
defer allocator.free(payload);
const args = try decodeSubscribe(allocator, payload);
defer args.deinit(allocator);
try std.testing.expectEqual(@as(usize, 0), args.channels.len);
}
test "decodeSubscribe rejects a truncated channel count" {
const allocator = std.testing.allocator;
try std.testing.expectError(error.Malformed, decodeSubscribe(allocator, &.{ 0, 0 }));
}
test "decodeSubscribe rejects a channel count larger than the payload can hold" {
const allocator = std.testing.allocator;
var buf = std.ArrayList(u8).init(allocator);
defer buf.deinit();
const w = encode.ByteWriter.init(&buf);
try w.putU32(5);
try std.testing.expectError(error.Malformed, decodeSubscribe(allocator, buf.items));
}
test "subscribe ack round trips a subscriber id and channel count" {
const allocator = std.testing.allocator;
const payload = try encodeSubscribeAck(allocator, 42, 3);
defer allocator.free(payload);
const ack = try decodeSubscribeAck(payload);
try std.testing.expectEqual(@as(u64, 42), ack.subscriberId);
try std.testing.expectEqual(@as(u32, 3), ack.channelCount);
}