Skip to content

Commit b39720b

Browse files
Vector bugfix (#872)
* Add support for UnsupportedType * Enable serialization and get TestKit tests working * Slight tidyup to internal vector namespaces * Update Neo4j.Driver/Neo4j.Driver/Neo4j.Driver.csproj Co-authored-by: Copilot <[email protected]> * Fix broken test * Add string representation and tests * Remove redundant ItemGroup in Neo4j.Driver.csproj * Review notes addressed * Add object-to-dictionary conversion utilities and tests for parameter value transformation, to fix a bug with serializing parameters. * Simplify quick exit code * Moving unit tests around --------- Co-authored-by: Copilot <[email protected]>
1 parent 9878d27 commit b39720b

File tree

10 files changed

+895
-639
lines changed

10 files changed

+895
-639
lines changed
Lines changed: 366 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,366 @@
1+
// Copyright (c) "Neo4j"
2+
// Neo4j Sweden AB [https://neo4j.com]
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License").
5+
// You may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
16+
using System;
17+
using System.Collections;
18+
using System.Collections.Generic;
19+
using FluentAssertions;
20+
using Neo4j.Driver.Internal;
21+
using Neo4j.Driver.Internal.Util;
22+
using Xunit;
23+
24+
namespace Neo4j.Driver.Tests.Internal.Util;
25+
26+
public class ObjectToDictionaryConverterTests
27+
{
28+
private readonly ObjectToDictionaryConverter _converter = new();
29+
30+
[Fact]
31+
public void ShouldReturnNullGivenNull()
32+
{
33+
var dict = _converter.Convert(null);
34+
dict.Should().BeNull();
35+
}
36+
37+
[Theory]
38+
[InlineData((sbyte)0)]
39+
[InlineData((byte)0)]
40+
[InlineData((short)0)]
41+
[InlineData((ushort)0)]
42+
[InlineData(0)]
43+
[InlineData((uint)0)]
44+
[InlineData((long)0)]
45+
[InlineData((ulong)0)]
46+
[InlineData((char)0)]
47+
[InlineData((float)0)]
48+
[InlineData((double)0)]
49+
[InlineData(true)]
50+
public void ShouldHandleSimpleTypes(object value)
51+
{
52+
var dict = _converter.Convert(new { key = value });
53+
dict.Should().NotBeNull();
54+
dict.Should().HaveCount(1);
55+
dict.Should().ContainKey("key");
56+
dict.Should().ContainValue(value);
57+
}
58+
59+
[Fact]
60+
public void ShouldHandleString()
61+
{
62+
var dict = _converter.Convert(new { key = "value" });
63+
dict.Should().NotBeNull();
64+
dict.Should().HaveCount(1);
65+
dict.Should().ContainKey("key");
66+
dict.Should().ContainValue("value");
67+
}
68+
69+
[Fact]
70+
public void ShouldHandleArray()
71+
{
72+
var array = new byte[2];
73+
var dict = _converter.Convert(new { key = array });
74+
dict.Should().NotBeNull();
75+
dict.Should().HaveCount(1);
76+
dict.Should().ContainKey("key");
77+
dict.Should().ContainValue(array);
78+
}
79+
80+
[Fact]
81+
public void ShouldHandleAnonymousObjects()
82+
{
83+
var dict = _converter.Convert(new { key1 = "value1", key2 = "value2" });
84+
dict.Should().NotBeNull();
85+
dict.Should().HaveCount(2);
86+
dict.Should().Contain(
87+
new KeyValuePair<string, object>("key1", "value1"),
88+
new KeyValuePair<string, object>("key2", "value2"));
89+
}
90+
91+
[Fact]
92+
public void ShouldHandleVectors()
93+
{
94+
var vector = Vector.Create([1.0, 2.0, 3.0]);
95+
var dict = _converter.Convert(new { vector });
96+
dict.Should().NotBeNull();
97+
dict.Should().HaveCount(1);
98+
dict.Should().ContainKey("vector");
99+
dict["vector"].Should().BeOfType<Vector<double>>();
100+
((Vector<double>)dict["vector"]).Values.Should().BeEquivalentTo([1.0, 2.0, 3.0]);
101+
}
102+
103+
[Fact]
104+
public void ShouldHandlePoco()
105+
{
106+
var dict = _converter.Convert(new MyPOCO { Key1 = "value1", Key2 = "value2" });
107+
dict.Should().NotBeNull();
108+
dict.Should().HaveCount(2);
109+
dict.Should().Contain(
110+
new KeyValuePair<string, object>("Key1", "value1"),
111+
new KeyValuePair<string, object>("Key2", "value2"));
112+
}
113+
114+
[Fact]
115+
public void ShouldHandleDeeperObjects()
116+
{
117+
var dict = _converter.Convert(new { InnerObject = new { Key1 = 1, Key2 = "a", Key3 = 0L } });
118+
dict.Should().NotBeNull();
119+
dict.Should().HaveCount(1);
120+
dict.Should().ContainKey("InnerObject");
121+
var innerObjectObject = dict["InnerObject"];
122+
innerObjectObject.Should().NotBeNull();
123+
innerObjectObject.Should().BeAssignableTo<IDictionary<string, object>>();
124+
var innerObject = (IDictionary<string, object>)innerObjectObject;
125+
innerObject.Should().Contain(
126+
new KeyValuePair<string, object>("Key1", 1),
127+
new KeyValuePair<string, object>("Key2", "a"),
128+
new KeyValuePair<string, object>("Key3", 0L));
129+
}
130+
131+
[Fact]
132+
public void ShouldHandleDictionary()
133+
{
134+
var dict = _converter.Convert(new
135+
{
136+
InnerDictionary = new Dictionary<string, object>
137+
{
138+
{ "Key1", 1 },
139+
{ "Key2", "a" },
140+
{ "Key3", 0L }
141+
}
142+
});
143+
dict.Should().NotBeNull();
144+
dict.Should().HaveCount(1);
145+
dict.Should().ContainKey("InnerDictionary");
146+
var innerDictionaryObject = dict["InnerDictionary"];
147+
innerDictionaryObject.Should().NotBeNull();
148+
innerDictionaryObject.Should().BeAssignableTo<IDictionary<string, object>>();
149+
var innerDictionary = (IDictionary<string, object>)innerDictionaryObject;
150+
innerDictionary.Should().Contain(
151+
new KeyValuePair<string, object>("Key1", 1),
152+
new KeyValuePair<string, object>("Key2", "a"),
153+
new KeyValuePair<string, object>("Key3", 0L));
154+
}
155+
156+
[Fact]
157+
public void ShouldHandleCollections()
158+
{
159+
var dict = _converter.Convert(new { InnerCollection = new List<int> { 1, 2, 3 } });
160+
dict.Should().NotBeNull();
161+
dict.Should().HaveCount(1);
162+
dict.Should().ContainKey("InnerCollection");
163+
var innerCollectionObject = dict["InnerCollection"];
164+
innerCollectionObject.Should().NotBeNull();
165+
innerCollectionObject.Should().BeAssignableTo<IList<int>>();
166+
var innerCollection = (IList<int>)innerCollectionObject;
167+
innerCollection.Should().Contain(new[] { 1, 2, 3 });
168+
}
169+
170+
[Fact]
171+
public void ShouldHandleCollectionsOfArbitraryObjects()
172+
{
173+
var dict = _converter.Convert(new
174+
{
175+
InnerCollection = new List<object>
176+
{
177+
new { a = "a" },
178+
3,
179+
new MyPOCO { Key1 = "value1" }
180+
}
181+
});
182+
dict.Should().NotBeNull();
183+
dict.Should().HaveCount(1);
184+
dict.Should().ContainKey("InnerCollection");
185+
var innerCollectionObject = dict["InnerCollection"];
186+
innerCollectionObject.Should().NotBeNull();
187+
innerCollectionObject.Should().BeAssignableTo<IList<object>>();
188+
var innerCollection = (IList<object>)innerCollectionObject;
189+
innerCollection.Should().HaveCount(3);
190+
innerCollection.Should().Contain(
191+
o => o is IDictionary<string, object> &&
192+
((IDictionary<string, object>)o).Contains(new KeyValuePair<string, object>("a", "a")));
193+
innerCollection.Should().Contain(3);
194+
innerCollection.Should().Contain(
195+
o => o is IDictionary<string, object> &&
196+
((IDictionary<string, object>)o).Contains(new KeyValuePair<string, object>("Key1", "value1")));
197+
}
198+
199+
[Fact]
200+
public void ShouldHandleDictionaryOfArbitraryObjects()
201+
{
202+
var dict = _converter.Convert(new
203+
{
204+
InnerDictionary = new Dictionary<string, object>
205+
{
206+
{ "a", new { a = "a" } },
207+
{ "b", "b" },
208+
{ "c", 3 }
209+
}
210+
});
211+
dict.Should().NotBeNull();
212+
dict.Should().HaveCount(1);
213+
dict.Should().ContainKey("InnerDictionary");
214+
var innerDictionaryObject = dict["InnerDictionary"];
215+
innerDictionaryObject.Should().NotBeNull();
216+
innerDictionaryObject.Should().BeAssignableTo<IDictionary<string, object>>();
217+
var innerDictionary = (IDictionary<string, object>)innerDictionaryObject;
218+
innerDictionary.Should().HaveCount(3);
219+
innerDictionary.Should().ContainKey("a");
220+
innerDictionary["a"].Should().BeAssignableTo<IDictionary<string, object>>();
221+
innerDictionary["a"].As<IDictionary<string, object>>().Should().Contain(new KeyValuePair<string, object>("a", "a"));
222+
innerDictionary.Should().Contain(new KeyValuePair<string, object>("b", "b"));
223+
innerDictionary.Should().Contain(new KeyValuePair<string, object>("c", 3));
224+
}
225+
226+
[Fact]
227+
public void ShouldRaiseExceptionWhenDictionaryKeysAreNotStrings()
228+
{
229+
var ex = Record.Exception(
230+
() => _converter.Convert(new
231+
{
232+
InnerDictionary = new Dictionary<int, object>
233+
{
234+
{ 1, new { a = "a" } },
235+
{ 2, "b" },
236+
{ 3, 3 }
237+
}
238+
}));
239+
ex.Should().NotBeNull();
240+
ex.Should().BeOfType<InvalidOperationException>();
241+
ex.Message.Should().Contain("string keys");
242+
}
243+
244+
[Fact]
245+
public void ShouldHandleListOfArbitraryObjects()
246+
{
247+
var dict = _converter.Convert(new
248+
{
249+
InnerList = new List<object>
250+
{
251+
new { a = "a" },
252+
"b",
253+
3
254+
}
255+
});
256+
dict.Should().NotBeNull();
257+
dict.Should().HaveCount(1);
258+
dict.Should().ContainKey("InnerList");
259+
var innerListObject = dict["InnerList"];
260+
innerListObject.Should().NotBeNull();
261+
innerListObject.Should().BeAssignableTo<IList<object>>();
262+
var innerList = (IList<object>)innerListObject;
263+
innerList.Should().HaveCount(3);
264+
innerList[0].Should().BeAssignableTo<IDictionary<string, object>>();
265+
innerList[0].As<IDictionary<string, object>>().Should().Contain(new KeyValuePair<string, object>("a", "a"));
266+
innerList[1].Should().Be("b");
267+
innerList[2].As<int>().Should().Be(3);
268+
}
269+
270+
public class Person
271+
{
272+
public string Name { get; set; }
273+
public int Age { get; set; }
274+
}
275+
276+
[Fact]
277+
public void ToDictionary_ShouldHandleEmptyDictionary()
278+
{
279+
var emptyDictionary = new Dictionary<string, Person>();
280+
var result = _converter.Convert(emptyDictionary);
281+
result.Should().BeEmpty();
282+
}
283+
284+
[Fact]
285+
public void ToDictionary_ShouldConvertDictionaryWithSimpleObjectsCorrectly()
286+
{
287+
var sourceDictionary = new Dictionary<string, Person>
288+
{
289+
{ "Key1", new Person { Name = "John", Age = 30 } },
290+
{ "Key2", new Person { Name = "Jane", Age = 25 } }
291+
};
292+
var result = _converter.Convert(sourceDictionary);
293+
result.Should().HaveCount(2);
294+
result["Key1"].Should().BeEquivalentTo(sourceDictionary["Key1"]);
295+
result["Key2"].Should().BeEquivalentTo(sourceDictionary["Key2"]);
296+
}
297+
298+
[Fact]
299+
public void ToDictionary_ShouldReturnNullForNullDictionary()
300+
{
301+
Dictionary<string, Person> nullDictionary = null;
302+
var actual = _converter.Convert(nullDictionary);
303+
actual.Should().BeNull();
304+
}
305+
306+
[Fact]
307+
public void ToDictionary_ShouldHandleNestedDictionaryCorrectly()
308+
{
309+
var nestedDictionary = new Dictionary<string, Dictionary<string, Person>>
310+
{
311+
{
312+
"Nested", new Dictionary<string, Person>
313+
{
314+
{ "InnerKey", new Person { Name = "Doe", Age = 40 } }
315+
}
316+
}
317+
};
318+
var result = _converter.Convert(nestedDictionary);
319+
result.Should().ContainKey("Nested");
320+
var innerDict = result["Nested"].As<Dictionary<string, Person>>();
321+
innerDict.Should().ContainKey("InnerKey");
322+
innerDict["InnerKey"].Should().BeEquivalentTo(new Person { Name = "Doe", Age = 40 });
323+
}
324+
325+
[Fact]
326+
public void ShouldHandleEnumerable()
327+
{
328+
var array = new[] { 1, 2, 3 };
329+
var value = new MyCollection<int>(array);
330+
var dict = _converter.Convert(new { key = value });
331+
dict.Should().NotBeNull();
332+
dict.Should().HaveCount(1);
333+
dict.Should().ContainKey("key");
334+
var s = dict["key"].ToContentString();
335+
s.Should().Be("[1, 2, 3]");
336+
}
337+
338+
[Fact]
339+
public void ShouldHandleEnumerableofEnumerable()
340+
{
341+
var array = new[] { 1, 2, 3 };
342+
IEnumerable element = new MyCollection<int>(array);
343+
var value = new MyCollection<object>(new[] { element, "a" });
344+
var dict = _converter.Convert(new { key = value });
345+
dict.Should().NotBeNull();
346+
dict.Should().HaveCount(1);
347+
dict.Should().ContainKey("key");
348+
var s = dict["key"].ToContentString();
349+
s.Should().Be("[[1, 2, 3], a]");
350+
}
351+
352+
private class MyPOCO
353+
{
354+
public string Key1 { get; set; }
355+
public string Key2 { get; set; }
356+
}
357+
358+
public class MyCollection<T> : IEnumerable<T>
359+
{
360+
private readonly IEnumerable<T> _values;
361+
public MyCollection(IEnumerable<T> values) { _values = values; }
362+
public string Name => "My Collection implements IEnumerable<T>";
363+
public IEnumerator<T> GetEnumerator() => _values.GetEnumerator();
364+
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
365+
}
366+
}

0 commit comments

Comments
 (0)