feat: flesh out protocol

This commit is contained in:
dualfroz committed 2026-09-08 14:17:49 +02:00
1 parent c9960ef22a
commit d3d9c4e7e5
1 file changed
+154
+154
View File
@@ -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));
}