diff --git a/src/bson.jl b/src/bson.jl index 8cddbe3..a599cd6 100644 --- a/src/bson.jl +++ b/src/bson.jl @@ -186,6 +186,7 @@ end """ A `BSON` represents a document in *Binary JSON* format, defined at https://bsonspec.org/. +Dictionary inputs, including nested dictionaries, are encoded in iteration order. Indexing reads decoded values. Assignment appends a field, even when its key already exists. Embedded documents and arrays are decoded into independent @@ -723,7 +724,7 @@ function Base.setindex!(document::BSON, value::BSON, key::AbstractString) nothing end -Base.setindex!(document::BSON, value::Dict, key::AbstractString) = setindex!(document, BSON(value), key) +Base.setindex!(document::BSON, value::AbstractDict, key::AbstractString) = setindex!(document, BSON(value), key) function Base.setindex!(document::BSON, value::Vector{T}, key::AbstractString) where T sub_document = BSON(value) diff --git a/test/bson_tests.jl b/test/bson_tests.jl index 4751827..f2bee2b 100644 --- a/test/bson_tests.jl +++ b/test/bson_tests.jl @@ -268,6 +268,55 @@ using Distributed @test dict == convert(Dict{String, Any}, doc) end + @testset "Nested AbstractDict values" begin + entries = ("first" => Int32(7), "second" => "two", "third" => false) + immutable = Base.ImmutableDict{String, Any}() + for entry in entries + immutable = Base.ImmutableDict(immutable, entry) + end + wrappers = ( + value -> begin + document = Mongoc.BSON() + document[SubString("_child", 2)] = value + document + end, + value -> Mongoc.BSON("child" => value), + value -> Mongoc.BSON(Dict("child" => value)), + value -> Mongoc.BSON("children" => [value]), + value -> Mongoc.BSON("outer" => Dict("child" => value)), + ) + for value in (Dict{String, Any}(entries), immutable) + encoded = Mongoc.BSON(value) + @test collect(keys(encoded)) == collect(keys(value)) + @test Mongoc.as_dict(encoded) == Dict(value) + for wrap in wrappers + @test Mongoc.as_json(wrap(value), canonical=true) == + Mongoc.as_json(wrap(encoded), canonical=true) + end + end + + for value in (Dict{String, Any}(), Base.ImmutableDict{String, Any}()) + @test isempty(Mongoc.BSON("child" => value)["child"]) + @test Mongoc.as_json(Mongoc.BSON("child" => value)) == + Mongoc.as_json(Mongoc.BSON("child" => Mongoc.BSON())) + end + nested = Base.ImmutableDict("child" => Base.ImmutableDict("value" => Int32(1))) + @test Mongoc.BSON("outer" => nested)["outer"] == Dict("child" => Dict("value" => 1)) + + value = Dict("value" => 1) + document = Mongoc.BSON("child" => value) + value["value"] = 2 + @test document["child"]["value"] == 1 + + # BSON subdocuments preserve native value types and an encoded snapshot. + child = Mongoc.BSON(raw"""{"stamp":{"$timestamp":{"t":1,"i":2}}}""") + expected = Mongoc.BSON(raw"""{"child":{"stamp":{"$timestamp":{"t":1,"i":2}}}}""") + document = Mongoc.BSON("child" => child) + @test Mongoc.as_json(document, canonical=true) == Mongoc.as_json(expected, canonical=true) + child["extra"] = true + @test Mongoc.as_json(document, canonical=true) == Mongoc.as_json(expected, canonical=true) + end + @testset "BSON Dict API" begin doc = Mongoc.BSON("a" => 1, "b" => false, "c" => "string", "d" => nothing) diff --git a/test/mongodb_tests.jl b/test/mongodb_tests.jl index 7b0847e..0098971 100644 --- a/test/mongodb_tests.jl +++ b/test/mongodb_tests.jl @@ -867,9 +867,15 @@ const DB_NAME = "mongoc" @testset "Session Options" begin opt = Mongoc.SessionOptions() - @test Mongoc.get_casual_consistency(opt) - Mongoc.set_casual_consistency!(opt, false) - @test !Mongoc.get_casual_consistency(opt) + try + @test Mongoc.get_casual_consistency(opt) + Mongoc.set_casual_consistency!(opt, false) + @test !Mongoc.get_casual_consistency(opt) + finally + Mongoc.destroy!(opt) + end + @test opt.handle == C_NULL + @test Mongoc.destroy!(opt) === nothing end server_version = Mongoc.get_server_mongodb_version(client) @@ -879,10 +885,18 @@ const DB_NAME = "mongoc" else @testset "Session" begin session = Mongoc.Session(client) - db = session[DB_NAME] - collection = db["session_collection"] - push!(collection, Mongoc.BSON("""{ "try-insert" : 1 }""")) - Mongoc.drop(collection) + try + db = session[DB_NAME] + collection = db["session_collection"] + push!(collection, Mongoc.BSON("""{ "try-insert" : 1 }""")) + Mongoc.drop(collection) + finally + Mongoc.destroy!(session) + Mongoc.destroy!(session.options) + end + @test session.handle == C_NULL + @test session.options.handle == C_NULL + @test Mongoc.destroy!(session) === nothing end end