feat: grow protocol
This commit is contained in:
1 parent
6ff5e832eb
commit
c9960ef22a
1 file changed
+159
@@ -2670,3 +2670,162 @@ test "table_aggregate args round trip a groupField, several specs, and an expr"
|
||||
try std.testing.expectEqualStrings("city", args.specs[3].field);
|
||||
}
|
||||
|
||||
test "table_aggregate args round trip an empty groupField (global, no grouping) and empty expr" {
|
||||
const allocator = std.testing.allocator;
|
||||
const specs = [_]aggregatemod.AggSpec{.{ .field = "", .kind = .count }};
|
||||
const payload = try encodeTableAggregate(allocator, "orders", "", &specs, 0, 0, &.{}, "", "");
|
||||
defer allocator.free(payload);
|
||||
const args = try decodeTableAggregate(allocator, payload);
|
||||
defer allocator.free(args.specs);
|
||||
defer allocator.free(args.orderBy);
|
||||
|
||||
try std.testing.expectEqualStrings("", args.groupField);
|
||||
try std.testing.expectEqualStrings("", args.exprText);
|
||||
try std.testing.expectEqual(@as(usize, 1), args.specs.len);
|
||||
}
|
||||
|
||||
test "table_aggregate args round trip a nonzero offset and limit" {
|
||||
const allocator = std.testing.allocator;
|
||||
const specs = [_]aggregatemod.AggSpec{.{ .field = "", .kind = .count }};
|
||||
const payload = try encodeTableAggregate(allocator, "orders", "city", &specs, 2, 5, &.{}, "", "");
|
||||
defer allocator.free(payload);
|
||||
const args = try decodeTableAggregate(allocator, payload);
|
||||
defer allocator.free(args.specs);
|
||||
defer allocator.free(args.orderBy);
|
||||
|
||||
try std.testing.expectEqual(@as(u32, 2), args.offset);
|
||||
try std.testing.expectEqual(@as(u32, 5), args.limit);
|
||||
}
|
||||
|
||||
test "table_aggregate args round trip a non-empty havingText alongside a non-empty exprText" {
|
||||
const allocator = std.testing.allocator;
|
||||
const specs = [_]aggregatemod.AggSpec{
|
||||
.{ .field = "", .kind = .count },
|
||||
.{ .field = "total", .kind = .sum },
|
||||
};
|
||||
const payload = try encodeTableAggregate(allocator, "orders", "city", &specs, 0, 0, &.{}, "sum_total gt i:1000", "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("sum_total gt i:1000", args.havingText);
|
||||
try std.testing.expectEqualStrings("total gt i:10", args.exprText);
|
||||
}
|
||||
|
||||
test "table_aggregate args round trip a multi-column orderBy alongside havingText and exprText" {
|
||||
const allocator = std.testing.allocator;
|
||||
const specs = [_]aggregatemod.AggSpec{
|
||||
.{ .field = "", .kind = .count },
|
||||
.{ .field = "total", .kind = .sum },
|
||||
};
|
||||
const orderBy = [_]querymod.OrderBy{
|
||||
.{ .field = "sum_total", .descending = true },
|
||||
.{ .field = "city" },
|
||||
};
|
||||
const payload = try encodeTableAggregate(allocator, "orders", "city", &specs, 0, 0, &orderBy, "sum_total gt i:100", "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.expectEqual(@as(usize, 2), args.orderBy.len);
|
||||
try std.testing.expectEqualStrings("sum_total", args.orderBy[0].field);
|
||||
try std.testing.expect(args.orderBy[0].descending);
|
||||
try std.testing.expectEqualStrings("city", args.orderBy[1].field);
|
||||
try std.testing.expect(!args.orderBy[1].descending);
|
||||
try std.testing.expectEqualStrings("sum_total gt i:100", args.havingText);
|
||||
try std.testing.expectEqualStrings("total gt i:10", args.exprText);
|
||||
}
|
||||
|
||||
test "table_aggregate args round trip an empty orderBy the same as before this feature existed" {
|
||||
const allocator = std.testing.allocator;
|
||||
const specs = [_]aggregatemod.AggSpec{.{ .field = "", .kind = .count }};
|
||||
const payload = try encodeTableAggregate(allocator, "orders", "city", &specs, 0, 0, &.{}, "", "");
|
||||
defer allocator.free(payload);
|
||||
const args = try decodeTableAggregate(allocator, payload);
|
||||
defer allocator.free(args.specs);
|
||||
defer allocator.free(args.orderBy);
|
||||
|
||||
try std.testing.expectEqual(@as(usize, 0), args.orderBy.len);
|
||||
}
|
||||
|
||||
test "decodeTableAggregate rejects an unknown agg 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 putLenPrefixed(w, "city");
|
||||
try w.putU32(1);
|
||||
try w.byte(200); // not a valid AggKind tag
|
||||
try putLenPrefixed(w, "total");
|
||||
|
||||
try std.testing.expectError(error.Malformed, decodeTableAggregate(allocator, buf.items));
|
||||
}
|
||||
|
||||
test "agg global result round trips count/sum/max values, including a none from an empty avg" {
|
||||
const allocator = std.testing.allocator;
|
||||
const values = [_]rowmod.Value{
|
||||
.{ .int = 4 },
|
||||
.{ .float = 10.5 },
|
||||
.{ .none = {} },
|
||||
.{ .text = "warsaw" },
|
||||
};
|
||||
const payload = try encodeAggGlobalResult(allocator, &values);
|
||||
defer allocator.free(payload);
|
||||
|
||||
const decoded = try decodeAggGlobalResult(allocator, payload);
|
||||
defer freeAggValues(allocator, decoded);
|
||||
|
||||
try std.testing.expectEqual(@as(usize, 4), decoded.len);
|
||||
try std.testing.expectEqual(@as(i64, 4), decoded[0].int);
|
||||
try std.testing.expectApproxEqAbs(@as(f64, 10.5), decoded[1].float, 1e-9);
|
||||
try std.testing.expectEqual(rowmod.ValueTag.none, @as(rowmod.ValueTag, decoded[2]));
|
||||
try std.testing.expectEqualStrings("warsaw", decoded[3].text);
|
||||
}
|
||||
|
||||
test "empty agg global result round trips" {
|
||||
const allocator = std.testing.allocator;
|
||||
const payload = try encodeAggGlobalResult(allocator, &[_]rowmod.Value{});
|
||||
defer allocator.free(payload);
|
||||
const decoded = try decodeAggGlobalResult(allocator, payload);
|
||||
defer freeAggValues(allocator, decoded);
|
||||
try std.testing.expectEqual(@as(usize, 0), decoded.len);
|
||||
}
|
||||
|
||||
test "agg group rows round trip several groups with several values each" {
|
||||
const allocator = std.testing.allocator;
|
||||
const groups = [_]aggregatemod.GroupRow{
|
||||
.{ .group = .{ .text = "warsaw" }, .values = @constCast(&[_]rowmod.Value{ .{ .int = 2 }, .{ .int = 350 } }) },
|
||||
.{ .group = .{ .text = "krakow" }, .values = @constCast(&[_]rowmod.Value{ .{ .int = 1 }, .{ .int = 40 } }) },
|
||||
};
|
||||
const payload = try encodeAggGroupRows(allocator, &groups);
|
||||
defer allocator.free(payload);
|
||||
|
||||
const decoded = try decodeAggGroupRows(allocator, payload);
|
||||
defer freeAggGroupRows(allocator, decoded);
|
||||
|
||||
try std.testing.expectEqual(@as(usize, 2), decoded.len);
|
||||
try std.testing.expectEqualStrings("warsaw", decoded[0].group.text);
|
||||
try std.testing.expectEqual(@as(i64, 2), decoded[0].values[0].int);
|
||||
try std.testing.expectEqual(@as(i64, 350), decoded[0].values[1].int);
|
||||
try std.testing.expectEqualStrings("krakow", decoded[1].group.text);
|
||||
try std.testing.expectEqual(@as(i64, 40), decoded[1].values[1].int);
|
||||
}
|
||||
|
||||
test "empty agg group rows list round trips" {
|
||||
const allocator = std.testing.allocator;
|
||||
const payload = try encodeAggGroupRows(allocator, &[_]aggregatemod.GroupRow{});
|
||||
defer allocator.free(payload);
|
||||
const decoded = try decodeAggGroupRows(allocator, payload);
|
||||
defer freeAggGroupRows(allocator, decoded);
|
||||
try std.testing.expectEqual(@as(usize, 0), decoded.len);
|
||||
}
|
||||
|
||||
test "decodeAggGroupRows rejects a corrupted second group without leaking the first" {
|
||||
const allocator = std.testing.allocator;
|
||||
var buf = std.ArrayList(u8).init(allocator);
|
||||
defer buf.deinit();
|
||||
const w = encode.ByteWriter.init(&buf);
|
||||
|
||||
Reference in new issue
Block a user