Skip to content

Commit 18e3cf0

Browse files
perf(client): add json deserialization caching
fix(client): ensure deep immutability for deep array/dict structures
1 parent 46021b5 commit 18e3cf0

File tree

384 files changed

+21134
-20392
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

384 files changed

+21134
-20392
lines changed

src/Orb.Tests/Core/FreezableDictionaryTest.cs

Lines changed: 0 additions & 43 deletions
This file was deleted.
Lines changed: 334 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,334 @@
1+
using System;
2+
using System.Collections.Frozen;
3+
using System.Collections.Generic;
4+
using System.Text.Json;
5+
using Orb.Core;
6+
using Orb.Exceptions;
7+
8+
namespace Orb.Tests.Core;
9+
10+
public class JsonDictionaryTest : TestBase
11+
{
12+
[Fact]
13+
public void DefaultConstructor_CreatesEmptyDictionary()
14+
{
15+
var dict = new JsonDictionary();
16+
17+
Assert.Empty(dict.Freeze());
18+
}
19+
20+
[Fact]
21+
public void DictionaryConstructor_CopiesData()
22+
{
23+
var source = new Dictionary<string, JsonElement>
24+
{
25+
["foo"] = JsonSerializer.SerializeToElement("bar"),
26+
["bar"] = JsonSerializer.SerializeToElement(42),
27+
};
28+
29+
var dict = new JsonDictionary(source);
30+
31+
var frozen = dict.Freeze();
32+
Assert.Equal(2, frozen.Count);
33+
Assert.Equal("bar", frozen["foo"].GetString());
34+
Assert.Equal(42, frozen["bar"].GetInt32());
35+
}
36+
37+
[Fact]
38+
public void FrozenDictionaryConstructor_UsesProvidedDictionary()
39+
{
40+
var source = FrozenDictionary.ToFrozenDictionary(
41+
new Dictionary<string, JsonElement>
42+
{
43+
["foo"] = JsonSerializer.SerializeToElement("bar"),
44+
}
45+
);
46+
47+
var dict = new JsonDictionary(source);
48+
49+
var frozen = dict.Freeze();
50+
Assert.Same(source, frozen);
51+
}
52+
53+
[Fact]
54+
public void Set_AddsValueWhenUnfrozen()
55+
{
56+
var dict = new JsonDictionary();
57+
58+
dict.Set("name", "Alice");
59+
dict.Set("age", 30);
60+
61+
var frozen = dict.Freeze();
62+
Assert.Equal("Alice", frozen["name"].GetString());
63+
Assert.Equal(30, frozen["age"].GetInt32());
64+
}
65+
66+
[Fact]
67+
public void Set_OverwritesExistingValue()
68+
{
69+
var dict = new JsonDictionary();
70+
71+
dict.Set("foo", "bar");
72+
dict.Set("foo", "baz");
73+
74+
var frozen = dict.Freeze();
75+
Assert.Equal("baz", frozen["foo"].GetString());
76+
}
77+
78+
[Fact]
79+
public void Set_ThrowsAfterFreezing()
80+
{
81+
var dict = new JsonDictionary();
82+
dict.Freeze();
83+
84+
Assert.Throws<InvalidOperationException>(() => dict.Set("foo", "bar"));
85+
}
86+
87+
[Fact]
88+
public void Freeze_ReturnsFrozenDictionary()
89+
{
90+
var dict = new JsonDictionary();
91+
dict.Set("foo", "bar");
92+
93+
var frozen1 = dict.Freeze();
94+
var frozen2 = dict.Freeze();
95+
96+
Assert.Same(frozen1, frozen2);
97+
}
98+
99+
[Fact]
100+
public void GetNotNullClass_ReturnsValue()
101+
{
102+
var dict = new JsonDictionary();
103+
dict.Set("name", "Alice");
104+
105+
Assert.Equal("Alice", dict.GetNotNullClass<string>("name"));
106+
}
107+
108+
[Fact]
109+
public void GetNotNullClass_CachesDeserializedValue()
110+
{
111+
var dict = new JsonDictionary();
112+
dict.Set("name", "Alice");
113+
114+
var first = dict.GetNotNullClass<string>("name");
115+
var second = dict.GetNotNullClass<string>("name");
116+
117+
Assert.Same(first, second);
118+
}
119+
120+
[Fact]
121+
public void GetNotNullClass_ThrowsWhenKeyAbsent()
122+
{
123+
var dict = new JsonDictionary();
124+
125+
var exception = Assert.Throws<OrbInvalidDataException>(() =>
126+
dict.GetNotNullClass<string>("missing")
127+
);
128+
Assert.Contains("'missing' cannot be absent", exception.Message);
129+
}
130+
131+
[Fact]
132+
public void GetNotNullClass_ThrowsWhenValueIsNull()
133+
{
134+
var dict = new JsonDictionary(
135+
new Dictionary<string, JsonElement>
136+
{
137+
["nullable"] = JsonSerializer.SerializeToElement<string?>(null),
138+
}
139+
);
140+
141+
var exception = Assert.Throws<OrbInvalidDataException>(() =>
142+
dict.GetNotNullClass<string>("nullable")
143+
);
144+
Assert.Contains("'nullable' cannot be null", exception.Message);
145+
}
146+
147+
[Fact]
148+
public void GetNotNullClass_ThrowsWhenTypeInvalid()
149+
{
150+
var dict = new JsonDictionary();
151+
dict.Set("number", 42);
152+
153+
var exception = Assert.Throws<OrbInvalidDataException>(() =>
154+
dict.GetNotNullClass<string>("number")
155+
);
156+
Assert.Contains("'number' must be of type", exception.Message);
157+
}
158+
159+
[Fact]
160+
public void GetNotNullStruct_ReturnsValue()
161+
{
162+
var dict = new JsonDictionary();
163+
dict.Set("age", 30);
164+
165+
var age = dict.GetNotNullStruct<int>("age");
166+
167+
Assert.Equal(30, age);
168+
}
169+
170+
[Fact]
171+
public void GetNotNullStruct_ThrowsWhenKeyAbsent()
172+
{
173+
var dict = new JsonDictionary();
174+
175+
var exception = Assert.Throws<OrbInvalidDataException>(() =>
176+
dict.GetNotNullStruct<int>("missing")
177+
);
178+
Assert.Contains("'missing' cannot be absent", exception.Message);
179+
}
180+
181+
[Fact]
182+
public void GetNotNullStruct_ThrowsWhenValueIsNull()
183+
{
184+
var dict = new JsonDictionary(
185+
new Dictionary<string, JsonElement>
186+
{
187+
["nullable"] = JsonSerializer.SerializeToElement<int?>(null),
188+
}
189+
);
190+
191+
var exception = Assert.Throws<OrbInvalidDataException>(() =>
192+
dict.GetNotNullStruct<int>("nullable")
193+
);
194+
Assert.Contains("'nullable' cannot be null", exception.Message);
195+
}
196+
197+
[Fact]
198+
public void GetNotNullStruct_ThrowsWhenTypeInvalid()
199+
{
200+
var dict = new JsonDictionary();
201+
dict.Set("text", "not a number");
202+
203+
var exception = Assert.Throws<OrbInvalidDataException>(() =>
204+
dict.GetNotNullStruct<int>("text")
205+
);
206+
Assert.Contains("'text' must be of type", exception.Message);
207+
}
208+
209+
[Fact]
210+
public void GetNullableClass_ReturnsValueWhenPresent()
211+
{
212+
var dict = new JsonDictionary();
213+
dict.Set("name", "Alice");
214+
215+
var name = dict.GetNullableClass<string>("name");
216+
217+
Assert.Equal("Alice", name);
218+
}
219+
220+
[Fact]
221+
public void GetNullableClass_ReturnsNullWhenKeyAbsent()
222+
{
223+
var dict = new JsonDictionary();
224+
225+
var missing = dict.GetNullableClass<string>("missing");
226+
227+
Assert.Null(missing);
228+
}
229+
230+
[Fact]
231+
public void GetNullableClass_ReturnsNullWhenValueIsNull()
232+
{
233+
var dict = new JsonDictionary(
234+
new Dictionary<string, JsonElement>
235+
{
236+
["nullable"] = JsonSerializer.SerializeToElement<string?>(null),
237+
}
238+
);
239+
240+
var nullable = dict.GetNullableClass<string>("nullable");
241+
242+
Assert.Null(nullable);
243+
}
244+
245+
[Fact]
246+
public void GetNullableClass_CachesDeserializedValue()
247+
{
248+
var dict = new JsonDictionary();
249+
dict.Set("name", "Alice");
250+
251+
var first = dict.GetNullableClass<string>("name");
252+
var second = dict.GetNullableClass<string>("name");
253+
254+
Assert.Same(first, second);
255+
}
256+
257+
[Fact]
258+
public void GetNullableClass_ThrowsWhenTypeInvalid()
259+
{
260+
var dict = new JsonDictionary();
261+
dict.Set("number", 42);
262+
263+
var exception = Assert.Throws<OrbInvalidDataException>(() =>
264+
dict.GetNullableClass<string>("number")
265+
);
266+
Assert.Contains("'number' must be of type", exception.Message);
267+
}
268+
269+
[Fact]
270+
public void GetNullableStruct_ReturnsValueWhenPresent()
271+
{
272+
var dict = new JsonDictionary();
273+
dict.Set("age", 30);
274+
275+
var age = dict.GetNullableStruct<int>("age");
276+
277+
Assert.Equal(30, age);
278+
}
279+
280+
[Fact]
281+
public void GetNullableStruct_ReturnsNullWhenKeyAbsent()
282+
{
283+
var dict = new JsonDictionary();
284+
285+
var missing = dict.GetNullableStruct<int>("missing");
286+
287+
Assert.Null(missing);
288+
}
289+
290+
[Fact]
291+
public void GetNullableStruct_ReturnsNullWhenValueIsNull()
292+
{
293+
var dict = new JsonDictionary(
294+
new Dictionary<string, JsonElement>
295+
{
296+
["nullable"] = JsonSerializer.SerializeToElement<int?>(null),
297+
}
298+
);
299+
300+
var nullable = dict.GetNullableStruct<int>("nullable");
301+
302+
Assert.Null(nullable);
303+
}
304+
305+
[Fact]
306+
public void GetNullableStruct_ThrowsWhenTypeInvalid()
307+
{
308+
var dict = new JsonDictionary();
309+
dict.Set("text", "not a number");
310+
311+
var exception = Assert.Throws<OrbInvalidDataException>(() =>
312+
dict.GetNullableStruct<int>("text")
313+
);
314+
Assert.Contains("'text' must be of type", exception.Message);
315+
}
316+
317+
[Fact]
318+
public void ComplexWorkflow_SetFreezeAndGet()
319+
{
320+
var dict = new JsonDictionary();
321+
dict.Set("name", "Alice");
322+
dict.Set("age", 30);
323+
dict.Set("active", true);
324+
325+
var frozen = dict.Freeze();
326+
327+
Assert.Equal("Alice", dict.GetNotNullClass<string>("name"));
328+
Assert.Equal(30, dict.GetNotNullStruct<int>("age"));
329+
Assert.True(dict.GetNotNullStruct<bool>("active"));
330+
Assert.Null(dict.GetNullableClass<string>("missing"));
331+
332+
Assert.Throws<InvalidOperationException>(() => dict.Set("new", "value"));
333+
}
334+
}

0 commit comments

Comments
 (0)