feat: build out protocol
This commit is contained in:
1 parent
96ab73e9e3
commit
e0d6e2b8f9
1 file changed
+162
@@ -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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user