From 21f45fa424b9046352aed46003303ccf1edcd499 Mon Sep 17 00:00:00 2001 From: Pierre Chalamet Date: Sun, 21 Jun 2026 20:36:12 +0200 Subject: [PATCH] chore: add map key test coverage --- CHANGELOG.md | 1 + tests/CSharpDataModels/DataModels.cs | 16 +++ .../Isomorphic/IsomorphicTests.fs | 68 ++++++++-- .../FSharpMapSerializationTests.fs | 116 ++++++++++++++++++ 4 files changed, 193 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2482d11..9a0b1b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ All notable changes to FSharp.MongoDB are documented in this file. - Updated the test harness projects to target .NET 10 so local and CI test execution stays aligned with the repository SDK baseline. - Fixed F# map support for `BsonDictionaryOptions`, including `ArrayOfDocuments` coverage. +- Added `Map` and `Map` regression coverage and matched those paths in the C#/F# isomorphic BSON tests. ## [0.5.0-beta] diff --git a/tests/CSharpDataModels/DataModels.cs b/tests/CSharpDataModels/DataModels.cs index 5245fda..b1dfeec 100644 --- a/tests/CSharpDataModels/DataModels.cs +++ b/tests/CSharpDataModels/DataModels.cs @@ -2,6 +2,18 @@ using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; using MongoDB.Bson.Serialization.Options; +using MongoDB.Bson.Serialization.Serializers; + +public sealed class GuidStringDictionarySerializer : DictionaryInterfaceImplementerSerializer, Guid, string> +{ + public GuidStringDictionarySerializer() + : base( + DictionaryRepresentation.ArrayOfDocuments, + new GuidSerializer(GuidRepresentation.Standard), + new StringSerializer()) + { + } +} public record Pair { @@ -41,6 +53,10 @@ public record RecordDataModel public Pair? RecordOpt { get; init; } public required Dictionary Map { get; init; } + [BsonDictionaryOptions(DictionaryRepresentation.ArrayOfDocuments)] + public required Dictionary IntKeyMap { get; init; } + [BsonSerializer(typeof(GuidStringDictionarySerializer))] + public required Dictionary GuidKeyMap { get; init; } } public record UserId([property: BsonGuidRepresentation(GuidRepresentation.Standard)] Guid Value); diff --git a/tests/FSharp.MongoDB.Bson.Tests/Isomorphic/IsomorphicTests.fs b/tests/FSharp.MongoDB.Bson.Tests/Isomorphic/IsomorphicTests.fs index 8b0f0a8..6ce9c61 100644 --- a/tests/FSharp.MongoDB.Bson.Tests/Isomorphic/IsomorphicTests.fs +++ b/tests/FSharp.MongoDB.Bson.Tests/Isomorphic/IsomorphicTests.fs @@ -1,12 +1,24 @@ namespace FSharp.MongoDB.Bson.Tests.Serialization +open System open CSharpDataModels open FsUnit -open NUnit.Framework open MongoDB.Bson +open MongoDB.Bson.Serialization.Attributes +open MongoDB.Bson.Serialization.Options +open MongoDB.Bson.Serialization.Serializers +open NUnit.Framework module IsomorphicSerialization = + type GuidStringMapSerializer() = + inherit + FSharpMapSerializer( + DictionaryInterfaceImplementerSerializer, Guid, string>( + DictionaryRepresentation.ArrayOfDocuments, + GuidSerializer(GuidRepresentation.Standard), + StringSerializer())) + type Pair = { First: int Second: string option } @@ -39,7 +51,11 @@ module IsomorphicSerialization = Record: Pair RecordOpt: Pair option - Map: Map } + Map: Map + [] + IntKeyMap: Map + [)>] + GuidKeyMap: Map } let ModelSome() = let csModel = @@ -48,7 +64,19 @@ module IsomorphicSerialization = map.Add("1", 1) map.Add("2", 2) map - + + let intKeyMap = + let map = System.Collections.Generic.Dictionary() + map.Add(1, 10) + map.Add(2, 20) + map + + let guidKeyMap = + let map = System.Collections.Generic.Dictionary() + map.Add(Guid.Parse("1c6d4e6a-8f8c-4a9d-a7ce-28a7f6f1d111"), "two") + map.Add(Guid.Parse("550e8400-e29b-41d4-a716-446655440000"), "one") + map + CSharpDataModels.RecordDataModel( Id = ObjectId.GenerateNewId(), Int = 42, @@ -65,7 +93,9 @@ module IsomorphicSerialization = ValueArrayOpt = [| CSharpDataModels.Value.IntValue(101) |], Record = CSharpDataModels.Pair(First = 1, Second = "Second"), RecordOpt = CSharpDataModels.Pair(First = -1, Second = "SecondOpt"), - Map = map) + Map = map, + IntKeyMap = intKeyMap, + GuidKeyMap = guidKeyMap) let fsModel = { Id = csModel.Id @@ -81,7 +111,11 @@ module IsomorphicSerialization = ValueArrayOpt = Some [| Value.IntValue 101 |] Record = { First = 1; Second = Some "Second" } RecordOpt = Some { First = -1; Second = Some "SecondOpt" } - Map = Map [ "1", 1; "2", 2 ] } + Map = Map [ "1", 1; "2", 2 ] + IntKeyMap = Map [ 1, 10; 2, 20 ] + GuidKeyMap = + Map [ Guid.Parse("550e8400-e29b-41d4-a716-446655440000"), "one" + Guid.Parse("1c6d4e6a-8f8c-4a9d-a7ce-28a7f6f1d111"), "two" ] } csModel, fsModel @@ -92,7 +126,19 @@ module IsomorphicSerialization = map.Add("1", 1) map.Add("2", 2) map - + + let intKeyMap = + let map = System.Collections.Generic.Dictionary() + map.Add(1, 10) + map.Add(2, 20) + map + + let guidKeyMap = + let map = System.Collections.Generic.Dictionary() + map.Add(Guid.Parse("1c6d4e6a-8f8c-4a9d-a7ce-28a7f6f1d111"), "two") + map.Add(Guid.Parse("550e8400-e29b-41d4-a716-446655440000"), "one") + map + CSharpDataModels.RecordDataModel( Id = ObjectId.GenerateNewId(), Int = 42, @@ -103,7 +149,9 @@ module IsomorphicSerialization = CSharpDataModels.Value.StringValue("String") CSharpDataModels.Value.PairValue(CSharpDataModels.Pair(First = 99, Second = "SecondPair")) |], Record = CSharpDataModels.Pair(First = 1, Second = "Second"), - Map = map) + Map = map, + IntKeyMap = intKeyMap, + GuidKeyMap = guidKeyMap) let fsModel = { Id = csModel.Id @@ -119,7 +167,11 @@ module IsomorphicSerialization = ValueArrayOpt = None Record = { First = 1; Second = Some "Second" } RecordOpt = None - Map = Map [ "1", 1; "2", 2 ] } + Map = Map [ "1", 1; "2", 2 ] + IntKeyMap = Map [ 1, 10; 2, 20 ] + GuidKeyMap = + Map [ Guid.Parse("550e8400-e29b-41d4-a716-446655440000"), "one" + Guid.Parse("1c6d4e6a-8f8c-4a9d-a7ce-28a7f6f1d111"), "two" ] } csModel, fsModel diff --git a/tests/FSharp.MongoDB.Bson.Tests/Serialization/FSharpMapSerializationTests.fs b/tests/FSharp.MongoDB.Bson.Tests/Serialization/FSharpMapSerializationTests.fs index 46f1e28..77c0323 100644 --- a/tests/FSharp.MongoDB.Bson.Tests/Serialization/FSharpMapSerializationTests.fs +++ b/tests/FSharp.MongoDB.Bson.Tests/Serialization/FSharpMapSerializationTests.fs @@ -15,18 +15,40 @@ namespace FSharp.MongoDB.Bson.Tests.Serialization +open System open MongoDB.Bson +open MongoDB.Bson.Serialization.Attributes +open MongoDB.Bson.Serialization.Options +open MongoDB.Bson.Serialization.Serializers open FsUnit open NUnit.Framework module FSharpMapSerialization = + type GuidStringMapSerializer() = + inherit + FSharpMapSerializer( + DictionaryInterfaceImplementerSerializer, Guid, string>( + DictionaryRepresentation.ArrayOfDocuments, + GuidSerializer(GuidRepresentation.Standard), + StringSerializer())) + type Primitive = { Bool : Map Int : Map String : Map Float : Map } + type PrimitiveIntKey = + { [] + Int : Map + [] + String : Map } + + type PrimitiveGuidKey = + { [)>] + String : Map } + [] let ``test serialize an empty map``() = let value = { Bool = Map.empty @@ -138,3 +160,97 @@ module FSharpMapSerialization = Float = Map.ofList [ ("a", 0.0); ("b", 1.0); ("c", 2.0) ] } result |> should equal expected + + [] + let ``test serialize a map with int keys``() = + let value = + { Int = Map.ofList [ (1, 10); (2, 20); (3, 30) ] + String = Map.ofList [ (1, "one"); (2, "two"); (3, "three") ] } + + let result = serialize value + let expected = + BsonDocument( + [ BsonElement("Int", + BsonArray( + [ BsonDocument([ BsonElement("k", BsonInt32 1) + BsonElement("v", BsonInt32 10) ]) + BsonDocument([ BsonElement("k", BsonInt32 2) + BsonElement("v", BsonInt32 20) ]) + BsonDocument([ BsonElement("k", BsonInt32 3) + BsonElement("v", BsonInt32 30) ]) ])) + BsonElement("String", + BsonArray( + [ BsonDocument([ BsonElement("k", BsonInt32 1) + BsonElement("v", BsonString "one") ]) + BsonDocument([ BsonElement("k", BsonInt32 2) + BsonElement("v", BsonString "two") ]) + BsonDocument([ BsonElement("k", BsonInt32 3) + BsonElement("v", BsonString "three") ]) ])) ]) + + result |> should equal expected + + [] + let ``test deserialize a map with int keys``() = + let doc = + BsonDocument( + [ BsonElement("Int", + BsonArray( + [ BsonDocument([ BsonElement("k", BsonInt32 1) + BsonElement("v", BsonInt32 10) ]) + BsonDocument([ BsonElement("k", BsonInt32 2) + BsonElement("v", BsonInt32 20) ]) + BsonDocument([ BsonElement("k", BsonInt32 3) + BsonElement("v", BsonInt32 30) ]) ])) + BsonElement("String", + BsonArray( + [ BsonDocument([ BsonElement("k", BsonInt32 1) + BsonElement("v", BsonString "one") ]) + BsonDocument([ BsonElement("k", BsonInt32 2) + BsonElement("v", BsonString "two") ]) + BsonDocument([ BsonElement("k", BsonInt32 3) + BsonElement("v", BsonString "three") ]) ])) ]) + + let result = deserialize doc + let expected = + { Int = Map.ofList [ (1, 10); (2, 20); (3, 30) ] + String = Map.ofList [ (1, "one"); (2, "two"); (3, "three") ] } + + result |> should equal expected + + [] + let ``test serialize a map with guid keys``() = + let firstGuid = Guid.Parse("550e8400-e29b-41d4-a716-446655440000") + let secondGuid = Guid.Parse("1c6d4e6a-8f8c-4a9d-a7ce-28a7f6f1d111") + let value = + { String = Map.ofList [ (firstGuid, "one"); (secondGuid, "two") ] } + + let result = serialize value + let expected = + BsonDocument( + [ BsonElement("String", + BsonArray( + [ BsonDocument([ BsonElement("k", BsonBinaryData(secondGuid, GuidRepresentation.Standard)) + BsonElement("v", BsonString "two") ]) + BsonDocument([ BsonElement("k", BsonBinaryData(firstGuid, GuidRepresentation.Standard)) + BsonElement("v", BsonString "one") ]) ])) ]) + + result |> should equal expected + + [] + let ``test deserialize a map with guid keys``() = + let firstGuid = Guid.Parse("550e8400-e29b-41d4-a716-446655440000") + let secondGuid = Guid.Parse("1c6d4e6a-8f8c-4a9d-a7ce-28a7f6f1d111") + let doc = + BsonDocument( + [ BsonElement("String", + BsonArray( + [ BsonDocument([ BsonElement("k", BsonBinaryData(secondGuid, GuidRepresentation.Standard)) + BsonElement("v", BsonString "two") ]) + BsonDocument([ BsonElement("k", BsonBinaryData(firstGuid, GuidRepresentation.Standard)) + BsonElement("v", BsonString "one") ]) ])) ]) + + let result = deserialize doc + let expected = + { String = Map.ofList [ (firstGuid, "one"); (secondGuid, "two") ] } + + result |> should equal expected