Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<int, _>` and `Map<Guid, _>` regression coverage and matched those paths in the C#/F# isomorphic BSON tests.

## [0.5.0-beta]

Expand Down
16 changes: 16 additions & 0 deletions tests/CSharpDataModels/DataModels.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Dictionary<Guid, string>, Guid, string>
{
public GuidStringDictionarySerializer()
: base(
DictionaryRepresentation.ArrayOfDocuments,
new GuidSerializer(GuidRepresentation.Standard),
new StringSerializer())
{
}
}

public record Pair
{
Expand Down Expand Up @@ -41,6 +53,10 @@ public record RecordDataModel
public Pair? RecordOpt { get; init; }

public required Dictionary<string, int> Map { get; init; }
[BsonDictionaryOptions(DictionaryRepresentation.ArrayOfDocuments)]
public required Dictionary<int, int> IntKeyMap { get; init; }
[BsonSerializer(typeof(GuidStringDictionarySerializer))]
public required Dictionary<Guid, string> GuidKeyMap { get; init; }
}

public record UserId([property: BsonGuidRepresentation(GuidRepresentation.Standard)] Guid Value);
Expand Down
68 changes: 60 additions & 8 deletions tests/FSharp.MongoDB.Bson.Tests/Isomorphic/IsomorphicTests.fs
Original file line number Diff line number Diff line change
@@ -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<Guid, string>(
DictionaryInterfaceImplementerSerializer<System.Collections.Generic.Dictionary<Guid, string>, Guid, string>(
DictionaryRepresentation.ArrayOfDocuments,
GuidSerializer(GuidRepresentation.Standard),
StringSerializer()))

type Pair =
{ First: int
Second: string option }
Expand Down Expand Up @@ -39,7 +51,11 @@ module IsomorphicSerialization =
Record: Pair
RecordOpt: Pair option

Map: Map<string, int> }
Map: Map<string, int>
[<BsonDictionaryOptions(DictionaryRepresentation.ArrayOfDocuments)>]
IntKeyMap: Map<int, int>
[<BsonSerializer(typeof<GuidStringMapSerializer>)>]
GuidKeyMap: Map<Guid, string> }

let ModelSome() =
let csModel =
Expand All @@ -48,7 +64,19 @@ module IsomorphicSerialization =
map.Add("1", 1)
map.Add("2", 2)
map


let intKeyMap =
let map = System.Collections.Generic.Dictionary<int, int>()
map.Add(1, 10)
map.Add(2, 20)
map

let guidKeyMap =
let map = System.Collections.Generic.Dictionary<Guid, string>()
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,
Expand All @@ -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
Expand All @@ -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

Expand All @@ -92,7 +126,19 @@ module IsomorphicSerialization =
map.Add("1", 1)
map.Add("2", 2)
map


let intKeyMap =
let map = System.Collections.Generic.Dictionary<int, int>()
map.Add(1, 10)
map.Add(2, 20)
map

let guidKeyMap =
let map = System.Collections.Generic.Dictionary<Guid, string>()
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,
Expand All @@ -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
Expand All @@ -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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<Guid, string>(
DictionaryInterfaceImplementerSerializer<System.Collections.Generic.Dictionary<Guid, string>, Guid, string>(
DictionaryRepresentation.ArrayOfDocuments,
GuidSerializer(GuidRepresentation.Standard),
StringSerializer()))

type Primitive =
{ Bool : Map<string, bool>
Int : Map<string, int>
String : Map<string, string>
Float : Map<string, float> }

type PrimitiveIntKey =
{ [<BsonDictionaryOptions(DictionaryRepresentation.ArrayOfDocuments)>]
Int : Map<int, int>
[<BsonDictionaryOptions(DictionaryRepresentation.ArrayOfDocuments)>]
String : Map<int, string> }

type PrimitiveGuidKey =
{ [<BsonSerializer(typeof<GuidStringMapSerializer>)>]
String : Map<Guid, string> }

[<Test>]
let ``test serialize an empty map``() =
let value = { Bool = Map.empty<string, bool>
Expand Down Expand Up @@ -138,3 +160,97 @@ module FSharpMapSerialization =
Float = Map.ofList<string, float> [ ("a", 0.0); ("b", 1.0); ("c", 2.0) ] }

result |> should equal expected

[<Test>]
let ``test serialize a map with int keys``() =
let value =
{ Int = Map.ofList<int, int> [ (1, 10); (2, 20); (3, 30) ]
String = Map.ofList<int, string> [ (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

[<Test>]
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<PrimitiveIntKey> doc
let expected =
{ Int = Map.ofList<int, int> [ (1, 10); (2, 20); (3, 30) ]
String = Map.ofList<int, string> [ (1, "one"); (2, "two"); (3, "three") ] }

result |> should equal expected

[<Test>]
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<Guid, string> [ (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

[<Test>]
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<PrimitiveGuidKey> doc
let expected =
{ String = Map.ofList<Guid, string> [ (firstGuid, "one"); (secondGuid, "two") ] }

result |> should equal expected
Loading