Skip to content

Commit e1c5a5a

Browse files
authored
Avoid nullrefex when a strongly-typed property is null of downloaded content. (#117)
* Reproduction test. * Create and use fake server in unit tests * Avoid nullrefex when a strongly-typed property is null of downloaded content.
1 parent 60b1f04 commit e1c5a5a

8 files changed

Lines changed: 182 additions & 82 deletions

File tree

src/SenseNet.Client.Tests/UnitTests/ContentActionTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ private async Task ActionTest(string expectedUrlPart, string expectedPostData, F
257257
{
258258
services.AddSingleton(restCaller);
259259
});
260-
var repository = await repositories.GetRepositoryAsync("local", CancellationToken.None)
260+
var repository = await repositories.GetRepositoryAsync(FakeServer, CancellationToken.None)
261261
.ConfigureAwait(false);
262262

263263
var content = repository.CreateExistingContent(42);
@@ -283,7 +283,7 @@ private async Task ActionErrorTest(Type expectedExceptionType, string expectedMe
283283
{
284284
var cancel = new CancellationTokenSource(TimeSpan.FromSeconds(10)).Token;
285285
var repository = await GetRepositoryCollection()
286-
.GetRepositoryAsync("local", CancellationToken.None).ConfigureAwait(false);
286+
.GetRepositoryAsync(FakeServer, CancellationToken.None).ConfigureAwait(false);
287287

288288
var content = repository.CreateExistingContent(42);
289289
content.Path = "/Root/Content/MyContent";

src/SenseNet.Client.Tests/UnitTests/ContentLoadingTests.cs

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ public async Task LoadContent_T_Properties_General_ByRealRequest()
269269
services.RegisterGlobalContentType<TestFolder>();
270270
services.RegisterGlobalContentType<TestWorkspace>("Workspace");
271271
});
272-
var repository = await repositories.GetRepositoryAsync("local", CancellationToken.None)
272+
var repository = await repositories.GetRepositoryAsync(FakeServer, CancellationToken.None)
273273
.ConfigureAwait(false);
274274

275275
// ACT
@@ -378,7 +378,7 @@ public async Task LoadContent_T_Properties_General_ByRealRequest_Projected()
378378
services.RegisterGlobalContentType<TestFolder>();
379379
services.RegisterGlobalContentType<TestWorkspace>("Workspace");
380380
});
381-
var repository = await repositories.GetRepositoryAsync("local", CancellationToken.None)
381+
var repository = await repositories.GetRepositoryAsync(FakeServer, CancellationToken.None)
382382
.ConfigureAwait(false);
383383

384384
// ACT
@@ -498,7 +498,7 @@ public async Task LoadContent_T_OnlyPublicInstanceReadWritePropertiesAreBoundBut
498498
services.AddSingleton(restCaller);
499499
services.RegisterGlobalContentType<TestContent_RelevantProperties>();
500500
});
501-
var repository = await repositories.GetRepositoryAsync("local", CancellationToken.None)
501+
var repository = await repositories.GetRepositoryAsync(FakeServer, CancellationToken.None)
502502
.ConfigureAwait(false);
503503

504504
// ACT
@@ -545,7 +545,7 @@ public async Task LoadContent_T_Properties_MultiChoice_StringToString()
545545
services.AddSingleton(restCaller);
546546
services.RegisterGlobalContentType<TestContent_MultiChoice_StringToString>();
547547
});
548-
var repository = await repositories.GetRepositoryAsync("local", CancellationToken.None)
548+
var repository = await repositories.GetRepositoryAsync(FakeServer, CancellationToken.None)
549549
.ConfigureAwait(false);
550550

551551
// ACT
@@ -605,7 +605,7 @@ public async Task LoadContent_T_Properties_MultiChoice_StringToInt()
605605
services.AddSingleton(restCaller);
606606
services.RegisterGlobalContentType<TestContent_MultiChoice_StringToInt>();
607607
});
608-
var repository = await repositories.GetRepositoryAsync("local", CancellationToken.None)
608+
var repository = await repositories.GetRepositoryAsync(FakeServer, CancellationToken.None)
609609
.ConfigureAwait(false);
610610

611611
// ACT
@@ -671,7 +671,7 @@ public async Task LoadContent_T_Properties_Number()
671671
services.AddSingleton(restCaller);
672672
services.RegisterGlobalContentType<TestContent_Number>();
673673
});
674-
var repository = await repositories.GetRepositoryAsync("local", CancellationToken.None)
674+
var repository = await repositories.GetRepositoryAsync(FakeServer, CancellationToken.None)
675675
.ConfigureAwait(false);
676676

677677
// ACT
@@ -735,7 +735,7 @@ public async Task LoadContent_T_Properties_Binary()
735735
services.AddSingleton(restCaller);
736736
services.RegisterGlobalContentType<TestContent_Binaries>();
737737
});
738-
var repository = await repositories.GetRepositoryAsync("local", CancellationToken.None)
738+
var repository = await repositories.GetRepositoryAsync(FakeServer, CancellationToken.None)
739739
.ConfigureAwait(false);
740740

741741
// ACT
@@ -808,7 +808,7 @@ public async Task LoadContent_T_References_Deferred()
808808
services.RegisterGlobalContentType<MyContent3>();
809809
services.RegisterGlobalContentType<TestContentForReferences>("Workspace");
810810
});
811-
var repository = await repositories.GetRepositoryAsync("local", CancellationToken.None)
811+
var repository = await repositories.GetRepositoryAsync(FakeServer, CancellationToken.None)
812812
.ConfigureAwait(false);
813813

814814
// ACT
@@ -849,7 +849,7 @@ public async Task LoadContent_T_References_Expanded_Simple()
849849
services.RegisterGlobalContentType<MyContent3>();
850850
services.RegisterGlobalContentType<TestContentForReferences>("Workspace");
851851
});
852-
var repository = await repositories.GetRepositoryAsync("local", CancellationToken.None)
852+
var repository = await repositories.GetRepositoryAsync(FakeServer, CancellationToken.None)
853853
.ConfigureAwait(false);
854854

855855
// ACT
@@ -891,7 +891,7 @@ public async Task LoadContent_T_References_Expanded_Multi_IEnumerable()
891891
services.RegisterGlobalContentType<MyContent3>();
892892
services.RegisterGlobalContentType<TestContentForReferences>("Workspace");
893893
});
894-
var repository = await repositories.GetRepositoryAsync("local", CancellationToken.None)
894+
var repository = await repositories.GetRepositoryAsync(FakeServer, CancellationToken.None)
895895
.ConfigureAwait(false);
896896

897897
// ACT
@@ -940,7 +940,7 @@ public async Task LoadContent_T_References_Expanded_Multi_Array()
940940
services.RegisterGlobalContentType<MyContent3>();
941941
services.RegisterGlobalContentType<TestContentForReferences>("Workspace");
942942
});
943-
var repository = await repositories.GetRepositoryAsync("local", CancellationToken.None)
943+
var repository = await repositories.GetRepositoryAsync(FakeServer, CancellationToken.None)
944944
.ConfigureAwait(false);
945945

946946
// ACT
@@ -988,7 +988,7 @@ public async Task LoadContent_T_References_Expanded_Multi_List()
988988
services.RegisterGlobalContentType<MyContent3>();
989989
services.RegisterGlobalContentType<TestContentForReferences>("Workspace");
990990
});
991-
var repository = await repositories.GetRepositoryAsync("local", CancellationToken.None)
991+
var repository = await repositories.GetRepositoryAsync(FakeServer, CancellationToken.None)
992992
.ConfigureAwait(false);
993993

994994
// ACT
@@ -1083,7 +1083,7 @@ public async Task LoadContent_T_Properties_Custom()
10831083
services.AddSingleton(restCaller);
10841084
services.RegisterGlobalContentType<TestContent_CustomProperties>();
10851085
});
1086-
var repository = await repositories.GetRepositoryAsync("local", CancellationToken.None)
1086+
var repository = await repositories.GetRepositoryAsync(FakeServer, CancellationToken.None)
10871087
.ConfigureAwait(false);
10881088

10891089
// ACT
@@ -1147,7 +1147,7 @@ public async Task LoadContent_T_Properties_Error_TypeMismatch()
11471147
services.AddSingleton(restCaller);
11481148
services.RegisterGlobalContentType<TestContent_CustomProperties_WrongConversion>();
11491149
});
1150-
var repository = await repositories.GetRepositoryAsync("local", CancellationToken.None)
1150+
var repository = await repositories.GetRepositoryAsync(FakeServer, CancellationToken.None)
11511151
.ConfigureAwait(false);
11521152

11531153
var request = new LoadContentRequest { Path = "/Root/Content" };
@@ -1186,7 +1186,7 @@ public async Task LoadContent_T_Error_UnknownType()
11861186
services.AddSingleton(restCaller);
11871187
services.RegisterGlobalContentType<MyContent>();
11881188
});
1189-
var repository = await repositories.GetRepositoryAsync("local", CancellationToken.None)
1189+
var repository = await repositories.GetRepositoryAsync(FakeServer, CancellationToken.None)
11901190
.ConfigureAwait(false);
11911191

11921192
// ACT

src/SenseNet.Client.Tests/UnitTests/ContentSavingTests.cs

Lines changed: 96 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public async Task Content_T_SaveFirst_Dynamic_AddProperty()
3737
{
3838
services.AddSingleton(restCaller);
3939
});
40-
var repository = await repositories.GetRepositoryAsync("local", CancellationToken.None)
40+
var repository = await repositories.GetRepositoryAsync(FakeServer, CancellationToken.None)
4141
.ConfigureAwait(false);
4242

4343
// ACT
@@ -84,7 +84,7 @@ public async Task Content_T_SaveFirst_Custom_SetProperty()
8484
services.RegisterGlobalContentType<TestContent_A>();
8585
services.AddSingleton(restCaller);
8686
});
87-
var repository = await repositories.GetRepositoryAsync("local", CancellationToken.None)
87+
var repository = await repositories.GetRepositoryAsync(FakeServer, CancellationToken.None)
8888
.ConfigureAwait(false);
8989

9090
// ACT
@@ -131,7 +131,7 @@ public async Task Content_T_SaveFirst_Custom_AddAndSetProperty()
131131
services.RegisterGlobalContentType<TestContent_A>();
132132
services.AddSingleton(restCaller);
133133
});
134-
var repository = await repositories.GetRepositoryAsync("local", CancellationToken.None)
134+
var repository = await repositories.GetRepositoryAsync(FakeServer, CancellationToken.None)
135135
.ConfigureAwait(false);
136136

137137
// ACT
@@ -233,7 +233,7 @@ private async Task<IDictionary<string, object>> UpdateBaseTypeTest(Action<dynami
233233
{
234234
services.AddSingleton(restCaller);
235235
});
236-
var repository = await repositories.GetRepositoryAsync("local", CancellationToken.None)
236+
var repository = await repositories.GetRepositoryAsync(FakeServer, CancellationToken.None)
237237
.ConfigureAwait(false);
238238

239239
// ACT-1: Load
@@ -341,7 +341,7 @@ private async Task<IDictionary<string, object>> UpdateStronglyTypedTest<T>(Actio
341341
services.RegisterGlobalContentType<T>();
342342
services.AddSingleton(restCaller);
343343
});
344-
var repository = await repositories.GetRepositoryAsync("local", CancellationToken.None)
344+
var repository = await repositories.GetRepositoryAsync(FakeServer, CancellationToken.None)
345345
.ConfigureAwait(false);
346346

347347
// ACT-1: Load
@@ -467,7 +467,7 @@ public async Task Content_T_StronglyTyped_UpdateCustomProperties()
467467
services.RegisterGlobalContentType<TestContent_CustomProperties>();
468468
services.AddSingleton(restCaller);
469469
});
470-
var repository = await repositories.GetRepositoryAsync("local", CancellationToken.None)
470+
var repository = await repositories.GetRepositoryAsync(FakeServer, CancellationToken.None)
471471
.ConfigureAwait(false);
472472

473473
// ACT-1 Load
@@ -532,7 +532,7 @@ public async Task Content_T_StronglyTyped_UpdateCustomProperties_OnlyChanged()
532532
services.RegisterGlobalContentType<TestContent_CustomProperties>();
533533
services.AddSingleton(restCaller);
534534
});
535-
var repository = await repositories.GetRepositoryAsync("local", CancellationToken.None)
535+
var repository = await repositories.GetRepositoryAsync(FakeServer, CancellationToken.None)
536536
.ConfigureAwait(false);
537537

538538
// ACT-1 Load
@@ -613,7 +613,7 @@ public async Task Content_T_StronglyTyped_References_SaveFirst()
613613
services.RegisterGlobalContentType<TestContent_References>();
614614
services.AddSingleton(restCaller);
615615
});
616-
var repository = await repositories.GetRepositoryAsync("local", CancellationToken.None)
616+
var repository = await repositories.GetRepositoryAsync(FakeServer, CancellationToken.None)
617617
.ConfigureAwait(false);
618618

619619
var contents = new Content[7];
@@ -712,7 +712,7 @@ public async Task Content_T_StronglyTyped_References_Update()
712712
services.RegisterGlobalContentType<TestContent_References>();
713713
services.AddSingleton(restCaller);
714714
});
715-
var repository = await repositories.GetRepositoryAsync("local", CancellationToken.None)
715+
var repository = await repositories.GetRepositoryAsync(FakeServer, CancellationToken.None)
716716
.ConfigureAwait(false);
717717

718718
var contents = new Content[7];
@@ -799,7 +799,7 @@ public async Task Content_T_StronglyTyped_References_Update_Null()
799799
services.RegisterGlobalContentType<TestContent_References>();
800800
services.AddSingleton(restCaller);
801801
});
802-
var repository = await repositories.GetRepositoryAsync("local", CancellationToken.None)
802+
var repository = await repositories.GetRepositoryAsync(FakeServer, CancellationToken.None)
803803
.ConfigureAwait(false);
804804

805805
var contents = new Content[7];
@@ -862,6 +862,91 @@ public async Task Content_T_StronglyTyped_References_Update_Null()
862862
"\"References_WellKnownArray\":null," +
863863
"\"References_WellKnownList\":[400006,\"/Root/Refs2/ReferredContent-7\"]}", JsonHelper.Serialize(data));
864864
}
865+
[TestMethod]
866+
public async Task Content_T_StronglyTyped_References_Update_NullToNull()
867+
{
868+
var restCaller = CreateRestCallerFor(@"{
869+
""d"": {
870+
""Id"": 899612,
871+
""Reference_Content"": null,
872+
""References_ContentArray"": null,
873+
""References_ContentEnumerable"": null,
874+
""References_ContentList"": null,
875+
""Reference_WellKnown"": null,
876+
""References_WellKnownArray"": null,
877+
""References_WellKnownEnumerable"": null,
878+
""References_WellKnownList"": null,
879+
}
880+
}
881+
");
882+
883+
var repositories = GetRepositoryCollection(services =>
884+
{
885+
services.RegisterGlobalContentType<ReferredContent>();
886+
services.RegisterGlobalContentType<TestContent_References>();
887+
services.AddSingleton(restCaller);
888+
});
889+
var repository = await repositories.GetRepositoryAsync(FakeServer, CancellationToken.None)
890+
.ConfigureAwait(false);
891+
892+
var contents = new Content[7];
893+
for (int i = 0; i < contents.Length; i++)
894+
{
895+
contents[i] = new Content(null, null)
896+
{
897+
Id = i % 2 == 0 ? 300001 + i : 0,
898+
Name = $"Content-{i + 1}",
899+
Path = $"/Root/Refs2/Content-{i + 1}",
900+
ParentId = 100000,
901+
ParentPath = "/Root/Refs2",
902+
Repository = repository,
903+
Server = repository.Server
904+
};
905+
}
906+
var referredContents = new ReferredContent[7];
907+
for (int i = 0; i < contents.Length; i++)
908+
{
909+
referredContents[i] = new ReferredContent(null, null)
910+
{
911+
Id = i % 2 == 1 ? 400001 + i : 0,
912+
Name = $"ReferredContent-{i + 1}",
913+
Path = $"/Root/Refs2/ReferredContent-{i + 1}",
914+
ParentId = 100000,
915+
ParentPath = "/Root/Refs2",
916+
Repository = repository,
917+
Server = repository.Server
918+
};
919+
}
920+
var request = new LoadContentRequest { ContentId = 999543, Select = new[] { "Id", "Name", "Path", "Type", "Index" } };
921+
dynamic content = await repository.LoadContentAsync<TestContent_References>(request, CancellationToken.None);
922+
923+
// ACT
924+
content.Reference_Content = null;
925+
content.References_ContentEnumerable = new[] { contents[3], null };
926+
content.References_WellKnownArray = new ReferredContent?[] { null, null };
927+
content.References_WellKnownList = new List<ReferredContent> { referredContents[5], referredContents[6] };
928+
await content.SaveAsync().ConfigureAwait(false);
929+
930+
// ASSERT
931+
var calls = restCaller.ReceivedCalls().ToArray();
932+
Assert.IsNotNull(calls);
933+
Assert.AreEqual(3, calls.Length);
934+
Assert.AreEqual("GetResponseStringAsync", calls[2].GetMethodInfo().Name);
935+
var arguments = calls[2].GetArguments();
936+
Assert.IsTrue(arguments[0].ToString().Contains("content(899612)"));
937+
Assert.AreEqual(HttpMethod.Patch, arguments[1]);
938+
var json = (string)arguments[2]!;
939+
json = json.Substring("models=[".Length).TrimEnd(']');
940+
JObject data = JsonHelper.Deserialize(json);
941+
var fields = data.ToObject<Dictionary<string, object>>();
942+
943+
var names = string.Join(", ", fields.Keys.OrderBy(x => x));
944+
Assert.AreEqual("Name, References_ContentEnumerable, References_WellKnownList", names);
945+
Assert.IsNotNull(data);
946+
Assert.AreEqual("{\"Name\":null," +
947+
"\"References_ContentEnumerable\":[\"/Root/Refs2/Content-4\"]," +
948+
"\"References_WellKnownList\":[400006,\"/Root/Refs2/ReferredContent-7\"]}", JsonHelper.Serialize(data));
949+
}
865950

866951
[TestMethod]
867952
public async Task Content_T_StronglyTyped_References_Error_Content()
@@ -959,7 +1044,7 @@ private async Task TestStronglyTypedReferencesError(string fieldName, Action<Tes
9591044
services.RegisterGlobalContentType<TestContent_References>();
9601045
services.AddSingleton(restCaller);
9611046
});
962-
var repository = await repositories.GetRepositoryAsync("local", CancellationToken.None)
1047+
var repository = await repositories.GetRepositoryAsync(FakeServer, CancellationToken.None)
9631048
.ConfigureAwait(false);
9641049

9651050
var request = new LoadContentRequest { ContentId = 999999 };

src/SenseNet.Client.Tests/UnitTests/DownloadTests.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ private async Task DownloadTest(DownloadRequest request, string expectedUrl)
7777
{
7878
services.AddSingleton(restCaller);
7979
});
80-
var repository = await repositories.GetRepositoryAsync("local", _cancel)
80+
var repository = await repositories.GetRepositoryAsync(FakeServer, _cancel)
8181
.ConfigureAwait(false);
8282

8383
// ACT
@@ -220,7 +220,7 @@ public async Task Download_WithIndexer()
220220
{
221221
services.AddSingleton(restCaller);
222222
});
223-
var repository = await repositories.GetRepositoryAsync("local", _cancel)
223+
var repository = await repositories.GetRepositoryAsync(FakeServer, _cancel)
224224
.ConfigureAwait(false);
225225

226226
string? text = null;
@@ -348,7 +348,7 @@ public async Task Download_T_ById()
348348
services.RegisterGlobalContentType<File>();
349349
services.RegisterGlobalContentType<Contract>();
350350
});
351-
var repository = await repositories.GetRepositoryAsync("local", _cancel)
351+
var repository = await repositories.GetRepositoryAsync(FakeServer, _cancel)
352352
.ConfigureAwait(false);
353353

354354
string? text = null;

0 commit comments

Comments
 (0)