Skip to content

Commit c90ecbe

Browse files
mrdevrobotCopilot
andauthored
Support byte[] BSON binary and add tests (#29)
* Support byte[] BSON binary and add tests Add full byte[] (BSON Binary) support to the source generator and tests. Map byte[] to WriteBinary/ReadBinary in CodeGenerator, handle ReadBinary's ReadOnlySpan<byte> by calling .ToArray(), and add null handling for nullable byte[] properties. Exclude byte[] from being treated as a sequence in SyntaxHelper. Add BinaryEntity model and register the collection in TestDbContext, and introduce comprehensive BinaryPropertyTests covering round-trips, empty arrays, nullable fields, updates, persistence across reopen, large payloads, BLiteEngine (dynamic/BSON) interop, and JSON base64 behavior. * Fix test cleanup: delete WAL sidecar files and dispose JsonDocument Agent-Logs-Url: https://github.com/EntglDb/BLite/sessions/29b1ef90-8d5e-487d-b058-6b6ca476f085 Co-authored-by: mrdevrobot <12503462+mrdevrobot@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
1 parent e70010d commit c90ecbe

5 files changed

Lines changed: 336 additions & 1 deletion

File tree

src/BLite.SourceGenerators/CodeGenerator.cs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,7 @@ private static void GenerateWriteProperty(StringBuilder sb, PropertyInfo prop, s
333333
var writeMethod = GetPrimitiveWriteMethod(prop, allowKey: false);
334334
if (writeMethod != null)
335335
{
336-
if (prop.IsNullable || prop.TypeName == "string" || prop.TypeName == "String")
336+
if (prop.IsNullable || prop.TypeName == "string" || prop.TypeName == "String" || prop.TypeName == "byte[]")
337337
{
338338
sb.AppendLine($" if (entity.{prop.Name} != null)");
339339
sb.AppendLine($" {{");
@@ -718,6 +718,27 @@ private static void GenerateReadPropertyToLocal(StringBuilder sb, PropertyInfo p
718718
var readMethod = GetPrimitiveReadMethod(prop);
719719
if (readMethod != null)
720720
{
721+
if (readMethod == "ReadBinary")
722+
{
723+
// ReadBinary returns ReadOnlySpan<byte> — must call .ToArray() to materialise
724+
if (prop.IsNullable)
725+
{
726+
sb.AppendLine($" if ({bsonTypeVar} == global::BLite.Bson.BsonType.Null)");
727+
sb.AppendLine($" {{");
728+
sb.AppendLine($" {localVar} = null;");
729+
sb.AppendLine($" }}");
730+
sb.AppendLine($" else");
731+
sb.AppendLine($" {{");
732+
sb.AppendLine($" {localVar} = reader.ReadBinary(out _).ToArray();");
733+
sb.AppendLine($" }}");
734+
}
735+
else
736+
{
737+
sb.AppendLine($" {localVar} = reader.ReadBinary(out _).ToArray();");
738+
}
739+
}
740+
else
741+
{
721742
var cast = (prop.TypeName == "float" || prop.TypeName == "Single") ? "(float)" : "";
722743

723744
// Handle nullable types - check for null in BSON stream
@@ -738,6 +759,7 @@ private static void GenerateReadPropertyToLocal(StringBuilder sb, PropertyInfo p
738759
var readArgs = IsCoercedReadMethod(readMethod) ? $"({bsonTypeVar})" : "()";
739760
sb.AppendLine($" {localVar} = {cast}reader.{readMethod}{readArgs};");
740761
}
762+
}
741763
}
742764
else if (prop.ConverterTypeName != null)
743765
{
@@ -931,6 +953,7 @@ private static string GetBaseMapperClass(PropertyInfo? keyProp, EntityInfo entit
931953
if (cleanType.EndsWith("TimeOnly")) return "WriteTimeOnly";
932954
if (cleanType.EndsWith("Guid")) return "WriteGuid";
933955
if (cleanType.EndsWith("ObjectId")) return "WriteObjectId";
956+
if (cleanType == "byte[]") return "WriteBinary";
934957

935958
return null;
936959
}
@@ -964,6 +987,7 @@ private static string GetBaseMapperClass(PropertyInfo? keyProp, EntityInfo entit
964987
if (cleanType.EndsWith("TimeOnly")) return "ReadTimeOnly";
965988
if (cleanType.EndsWith("Guid")) return "ReadGuid";
966989
if (cleanType.EndsWith("ObjectId")) return "ReadObjectId";
990+
if (cleanType == "byte[]") return "ReadBinary";
967991

968992
return null;
969993
}
@@ -1025,6 +1049,7 @@ private static string QualifyType(string typeName)
10251049
case "object":
10261050
case "dynamic":
10271051
case "void":
1052+
case "byte[]":
10281053
return baseType + (isNullable ? "?" : "");
10291054
case "Guid": return "global::System.Guid" + (isNullable ? "?" : "");
10301055
case "DateTime": return "global::System.DateTime" + (isNullable ? "?" : "");

src/BLite.SourceGenerators/Helpers/SyntaxHelper.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,9 @@ public static bool IsCollectionType(ITypeSymbol type, out ITypeSymbol? itemType)
143143
// Handle arrays
144144
if (type is IArrayTypeSymbol arrayType)
145145
{
146+
// Exclude byte[] — it maps to BSON Binary, not a sequence of integers
147+
if (arrayType.ElementType.SpecialType == SpecialType.System_Byte)
148+
return false;
146149
itemType = arrayType.ElementType;
147150
return true;
148151
}

tests/BLite.Shared/MockEntities.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,16 @@ public class DddLineItem
199199
public int Quantity { get; set; }
200200
}
201201

202+
// --- Binary Property Tests ---
203+
204+
public class BinaryEntity
205+
{
206+
public ObjectId Id { get; set; }
207+
public string Label { get; set; } = "";
208+
public byte[] Data { get; set; } = Array.Empty<byte>();
209+
public byte[]? OptionalData { get; set; }
210+
}
211+
202212
public record OrderId(string Value)
203213
{
204214
public OrderId() : this(string.Empty) { }

tests/BLite.Shared/TestDbContext.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,9 @@ public partial class TestDbContext : DocumentDbContext
8080
// Device – HasConversion on a non-ID property (ulong → long)
8181
public DocumentCollection<string, Device> Devices { get; set; } = null!;
8282

83+
// Binary Property Tests
84+
public DocumentCollection<ObjectId, BinaryEntity> BinaryEntities { get; set; } = null!;
85+
8386
public TestDbContext(string databasePath) : base(databasePath)
8487
{
8588
InitializeCollections();
@@ -183,6 +186,9 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
183186

184187
// Benchmark entities
185188
modelBuilder.Entity<CustomerOrder>().ToCollection("customer_orders").HasKey(e => e.Id);
189+
190+
// Binary Property Tests
191+
modelBuilder.Entity<BinaryEntity>().ToCollection("binary_entities");
186192
}
187193

188194
public void ForceCheckpoint()

0 commit comments

Comments
 (0)