feat: flesh out protocol
This commit is contained in:
1 parent
c9960ef22a
commit
d3d9c4e7e5
1 file changed
+154
@@ -2829,3 +2829,157 @@ test "decodeAggGroupRows rejects a corrupted second group without leaking the fi
|
||||
defer buf.deinit();
|
||||
const w = encode.ByteWriter.init(&buf);
|
||||
|
||||
var goodGroup = rowmod.Row.init(allocator);
|
||||
defer goodGroup.deinit();
|
||||
// Use rowmod.encodeValue directly for a real, well-formed first group so
|
||||
// the corruption below is unambiguously in the SECOND group only.
|
||||
const goodGroupValue = try rowmod.encodeValue(allocator, .{ .text = "warsaw" });
|
||||
defer allocator.free(goodGroupValue);
|
||||
const goodMemberValue = try rowmod.encodeValue(allocator, .{ .int = 1 });
|
||||
defer allocator.free(goodMemberValue);
|
||||
|
||||
try w.putU32(2); // claims two groups, only one is well-formed
|
||||
try putLenPrefixed(w, goodGroupValue);
|
||||
try w.putU32(1);
|
||||
try putLenPrefixed(w, goodMemberValue);
|
||||
// Second group: a length-prefixed blob whose declared value tag is
|
||||
// invalid - `takeDecodedValue` must surface `error.Malformed` here
|
||||
// rather than fabricating a bogus `Value`.
|
||||
try putLenPrefixed(w, &[_]u8{99});
|
||||
|
||||
try std.testing.expectError(error.Malformed, decodeAggGroupRows(allocator, buf.items));
|
||||
}
|
||||
|
||||
test "table_explain args round trip a table and an expr" {
|
||||
const allocator = std.testing.allocator;
|
||||
const payload = try encodeTableExplain(allocator, "users", "city eq s:warsaw");
|
||||
defer allocator.free(payload);
|
||||
const args = try decodeTableExplain(payload);
|
||||
try std.testing.expectEqualStrings("users", args.table);
|
||||
try std.testing.expectEqualStrings("city eq s:warsaw", args.exprText);
|
||||
}
|
||||
|
||||
test "table_explain args round trip an empty expr" {
|
||||
const allocator = std.testing.allocator;
|
||||
const payload = try encodeTableExplain(allocator, "users", "");
|
||||
defer allocator.free(payload);
|
||||
const args = try decodeTableExplain(payload);
|
||||
try std.testing.expectEqualStrings("users", args.table);
|
||||
try std.testing.expectEqualStrings("", args.exprText);
|
||||
}
|
||||
|
||||
test "table_update args round trip a table, set fields, and an expr" {
|
||||
const allocator = std.testing.allocator;
|
||||
const sets = [_]rowmod.Field{
|
||||
.{ .name = "active", .value = .{ .int = 1 } },
|
||||
.{ .name = "city", .value = .{ .text = "krakow" } },
|
||||
};
|
||||
const payload = try encodeTableUpdate(allocator, "users", &sets, "city eq s:warsaw");
|
||||
defer allocator.free(payload);
|
||||
const args = try decodeTableUpdate(payload);
|
||||
try std.testing.expectEqualStrings("users", args.table);
|
||||
try std.testing.expectEqualStrings("city eq s:warsaw", args.exprText);
|
||||
|
||||
var decoded = try rowmod.Row.decode(allocator, args.setsBytes);
|
||||
defer decoded.deinit();
|
||||
try std.testing.expectEqual(rowmod.Value{ .int = 1 }, decoded.get("active").?);
|
||||
try std.testing.expectEqualStrings("krakow", decoded.get("city").?.text);
|
||||
}
|
||||
|
||||
test "table_update args round trip an empty expr, updating every row" {
|
||||
const allocator = std.testing.allocator;
|
||||
const sets = [_]rowmod.Field{.{ .name = "seen", .value = .{ .int = 1 } }};
|
||||
const payload = try encodeTableUpdate(allocator, "users", &sets, "");
|
||||
defer allocator.free(payload);
|
||||
const args = try decodeTableUpdate(payload);
|
||||
try std.testing.expectEqualStrings("users", args.table);
|
||||
try std.testing.expectEqualStrings("", args.exprText);
|
||||
|
||||
var decoded = try rowmod.Row.decode(allocator, args.setsBytes);
|
||||
defer decoded.deinit();
|
||||
try std.testing.expectEqual(@as(usize, 1), decoded.count());
|
||||
}
|
||||
|
||||
test "table_delete args round trip a table and an expr" {
|
||||
const allocator = std.testing.allocator;
|
||||
const payload = try encodeTableDelete(allocator, "users", "city eq s:warsaw");
|
||||
defer allocator.free(payload);
|
||||
const args = try decodeTableDelete(payload);
|
||||
try std.testing.expectEqualStrings("users", args.table);
|
||||
try std.testing.expectEqualStrings("city eq s:warsaw", args.exprText);
|
||||
}
|
||||
|
||||
test "table_delete args round trip an empty expr, deleting every row" {
|
||||
const allocator = std.testing.allocator;
|
||||
const payload = try encodeTableDelete(allocator, "users", "");
|
||||
defer allocator.free(payload);
|
||||
const args = try decodeTableDelete(payload);
|
||||
try std.testing.expectEqualStrings("users", args.table);
|
||||
try std.testing.expectEqualStrings("", args.exprText);
|
||||
}
|
||||
|
||||
test "table_export args round trip a table and each format" {
|
||||
const allocator = std.testing.allocator;
|
||||
|
||||
const csvPayload = try encodeTableExport(allocator, "users", .csv);
|
||||
defer allocator.free(csvPayload);
|
||||
const csvArgs = try decodeTableExport(csvPayload);
|
||||
try std.testing.expectEqualStrings("users", csvArgs.table);
|
||||
try std.testing.expectEqual(DumpFormat.csv, csvArgs.format);
|
||||
|
||||
const jsonPayload = try encodeTableExport(allocator, "users", .json);
|
||||
defer allocator.free(jsonPayload);
|
||||
const jsonArgs = try decodeTableExport(jsonPayload);
|
||||
try std.testing.expectEqualStrings("users", jsonArgs.table);
|
||||
try std.testing.expectEqual(DumpFormat.json, jsonArgs.format);
|
||||
}
|
||||
|
||||
test "table_export rejects a format byte outside the DumpFormat enum" {
|
||||
const allocator = std.testing.allocator;
|
||||
var buf = std.ArrayList(u8).init(allocator);
|
||||
defer buf.deinit();
|
||||
const w = encode.ByteWriter.init(&buf);
|
||||
try putLenPrefixed(w, "users");
|
||||
try w.byte(99);
|
||||
try std.testing.expectError(error.Malformed, decodeTableExport(buf.items));
|
||||
}
|
||||
|
||||
test "export result round trips a row count and content bytes" {
|
||||
const allocator = std.testing.allocator;
|
||||
const payload = try encodeExportResult(allocator, 3, "__pk\ni:1\ni:2\ni:3\n");
|
||||
defer allocator.free(payload);
|
||||
const result = try decodeExportResult(payload);
|
||||
try std.testing.expectEqual(@as(u64, 3), result.rowCount);
|
||||
try std.testing.expectEqualStrings("__pk\ni:1\ni:2\ni:3\n", result.content);
|
||||
}
|
||||
|
||||
test "export result round trips a zero row count with empty content" {
|
||||
const allocator = std.testing.allocator;
|
||||
const payload = try encodeExportResult(allocator, 0, "");
|
||||
defer allocator.free(payload);
|
||||
const result = try decodeExportResult(payload);
|
||||
try std.testing.expectEqual(@as(u64, 0), result.rowCount);
|
||||
try std.testing.expectEqualStrings("", result.content);
|
||||
}
|
||||
|
||||
test "table_import args round trip a table, format, and content" {
|
||||
const allocator = std.testing.allocator;
|
||||
const payload = try encodeTableImport(allocator, "users", .json, "[{\"__pk\": \"i:1\"}]");
|
||||
defer allocator.free(payload);
|
||||
const args = try decodeTableImport(payload);
|
||||
try std.testing.expectEqualStrings("users", args.table);
|
||||
try std.testing.expectEqual(DumpFormat.json, args.format);
|
||||
try std.testing.expectEqualStrings("[{\"__pk\": \"i:1\"}]", args.content);
|
||||
}
|
||||
|
||||
test "table_import rejects a format byte outside the DumpFormat enum" {
|
||||
const allocator = std.testing.allocator;
|
||||
var buf = std.ArrayList(u8).init(allocator);
|
||||
defer buf.deinit();
|
||||
const w = encode.ByteWriter.init(&buf);
|
||||
try putLenPrefixed(w, "users");
|
||||
try w.byte(7);
|
||||
try w.slice("ignored");
|
||||
try std.testing.expectError(error.Malformed, decodeTableImport(buf.items));
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user