Skip to content

Commit a3f3bc7

Browse files
Add tests for links
Add tests for links
1 parent f4eb017 commit a3f3bc7

File tree

8 files changed

+322
-18
lines changed

8 files changed

+322
-18
lines changed

.gitignore

+4-1
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,7 @@
1717
*.cache
1818
*.v2
1919
*.vsidx
20-
*.vs
20+
*.vs
21+
/Optimizely.Graph.Source.Sdk/Optimizely.Graph.Source.Sdk.Tests/bin
22+
/Optimizely.Graph.Source.Sdk/Optimizely.Graph.Source.Sdk.Tests/obj
23+
/Optimizely.Graph.Source.Sdk/Optimizely.Graph.Source.Sdk.Tests/obj
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
7+
namespace Optimizely.Graph.Source.Sdk.Tests.ExampleObjects
8+
{
9+
public class Event
10+
{
11+
public string Name { get; set; }
12+
13+
public string LocationName { get; set; }
14+
15+
public DateTime Time { get; set; }
16+
17+
public ExtraInfo AdditionalInfo { get; set; }
18+
}
19+
20+
public class ExtraInfo
21+
{
22+
public string Example1 { get; set; }
23+
24+
public int Example2 { get; set; }
25+
}
26+
}

Optimizely.Graph.Source.Sdk/Optimizely.Graph.Source.Sdk.Tests/ExampleClassObject.cs renamed to Optimizely.Graph.Source.Sdk/Optimizely.Graph.Source.Sdk.Tests/ExampleObjects/ExampleClassObject.cs

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-

2-
namespace Optimizely.Graph.Source.Sdk.Tests
1+
namespace Optimizely.Graph.Source.Sdk.Tests.ExampleObjects
32
{
43
public class ExampleClassObject
54
{
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
7+
namespace Optimizely.Graph.Source.Sdk.Tests.ExampleObjects
8+
{
9+
public class Location
10+
{
11+
public double Longitude { get; set; }
12+
13+
public double Latitude { get; set; }
14+
15+
public string Name { get; set; }
16+
}
17+
}

Optimizely.Graph.Source.Sdk/Optimizely.Graph.Source.Sdk.Tests/RepositoryTests/GraphSourceRepositoryTests.cs

+246-7
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using System.Text;
88
using Optimizely.Graph.Source.Sdk.RestClientHelpers;
99
using Optimizely.Graph.Source.Sdk.SourceConfiguration;
10+
using Optimizely.Graph.Source.Sdk.Tests.ExampleObjects;
1011

1112
namespace Optimizely.Graph.Source.Sdk.Tests.RepositoryTests
1213
{
@@ -24,6 +25,12 @@ public GraphSourceRepositoryTests()
2425
repository = new GraphSourceRepository(mockRestClient.Object, source);
2526
}
2627

28+
[TestInitialize]
29+
public void TestInit()
30+
{
31+
SourceConfigurationModel.Reset();
32+
}
33+
2734
[TestMethod]
2835
public void Constructor_WithEmptySource_ThrowsArgumentNullException()
2936
{
@@ -114,6 +121,123 @@ public async Task SaveTypesAsync_BuildsExpectedJsonString_AndCallsGraphClient()
114121
mockRestClient.VerifyAll();
115122
}
116123

124+
[TestMethod]
125+
public async Task SaveTypesAsync_WithLink_ShouldGenerateTypesAndLink()
126+
{
127+
// Arrange
128+
repository.ConfigureContentType<Location>()
129+
.Field(x => x.Longitude, IndexingType.Queryable)
130+
.Field(x => x.Latitude, IndexingType.Queryable)
131+
.Field(x => x.Name, IndexingType.Searchable);
132+
133+
repository.ConfigureContentType<Event>()
134+
.Field(x => x.LocationName, IndexingType.Queryable)
135+
.Field(x => x.Time, IndexingType.Queryable)
136+
.Field(x => x.Name, IndexingType.Searchable)
137+
.Field(x => x.AdditionalInfo, IndexingType.PropertyType);
138+
139+
repository.ConfigureContentType<ExtraInfo>()
140+
.Field(x => x.Example1, IndexingType.OnlyStored)
141+
.Field(x => x.Example2, IndexingType.Queryable);
142+
143+
repository.ConfigureLink<Location, Event>(
144+
"NameToLocationName",
145+
x => x.Name,
146+
x => x.LocationName
147+
);
148+
149+
var expectedJsonString = @"{
150+
""useTypedFieldNames"": true,
151+
""languages"": [],
152+
""links"": {
153+
""NameToLocationName"": {
154+
""from"": ""Name$$String___searchable"",
155+
""to"": ""LocationName$$String""
156+
}
157+
},
158+
""contentTypes"": {
159+
""Location"": {
160+
""contentType"": [],
161+
""properties"": {
162+
""Longitude"": {
163+
""type"": ""Float"",
164+
""searchable"": false,
165+
""skip"": false
166+
},
167+
""Latitude"": {
168+
""type"": ""Float"",
169+
""searchable"": false,
170+
""skip"": false
171+
},
172+
""Name"": {
173+
""type"": ""String"",
174+
""searchable"": true,
175+
""skip"": false
176+
}
177+
}
178+
},
179+
""Event"": {
180+
""contentType"": [],
181+
""properties"": {
182+
""LocationName"": {
183+
""type"": ""String"",
184+
""searchable"": false,
185+
""skip"": false
186+
},
187+
""Time"": {
188+
""type"": ""DateTime"",
189+
""searchable"": false,
190+
""skip"": false
191+
},
192+
""Name"": {
193+
""type"": ""String"",
194+
""searchable"": true,
195+
""skip"": false
196+
},
197+
""AdditionalInfo"": {
198+
""type"": ""ExtraInfo""
199+
}
200+
}
201+
},
202+
""ExtraInfo"": {
203+
""contentType"": [],
204+
""properties"": {
205+
""Example1"": {
206+
""type"": ""String"",
207+
""searchable"": false,
208+
""skip"": true
209+
},
210+
""Example2"": {
211+
""type"": ""Int"",
212+
""searchable"": false,
213+
""skip"": false
214+
}
215+
}
216+
}
217+
},
218+
""propertyTypes"": {}
219+
}";
220+
221+
var jsonString = BuildExpectedTypeJsonString();
222+
var content = new StringContent(jsonString, Encoding.UTF8, "application/json");
223+
224+
var response = new HttpResponseMessage(HttpStatusCode.OK);
225+
var request = new HttpRequestMessage(HttpMethod.Put, $"/api/content/v3/types?id={source}") { Content = content };
226+
227+
mockRestClient.Setup(c => c.SendAsync(It.IsAny<HttpRequestMessage>())).ReturnsAsync(response);
228+
mockRestClient.Setup(c => c.HandleResponse(response));
229+
230+
// Act
231+
await repository.SaveTypesAsync();
232+
233+
// Assert
234+
Assert.AreEqual(expectedJsonString, jsonString);
235+
236+
mockRestClient.Verify(c => c.SendAsync(It.Is<HttpRequestMessage>(x => Compare(request, x))), Times.Once);
237+
mockRestClient.Verify(c => c.HandleResponse(response), Times.Once);
238+
mockRestClient.VerifyAll();
239+
}
240+
117241
[TestMethod]
118242
public async Task SaveContentAsync_SerializesData_AndCallsGraphClient()
119243
{
@@ -140,7 +264,7 @@ public async Task SaveContentAsync_SerializesData_AndCallsGraphClient()
140264
}
141265
};
142266

143-
var expectedJsonString = BuildExpextedContentJsonString(exampleData);
267+
var expectedJsonString = BuildExpextedContentJsonString(x => x.ToString(), exampleData);
144268

145269
var content = new StringContent(expectedJsonString, Encoding.UTF8, "application/json");
146270

@@ -159,6 +283,118 @@ public async Task SaveContentAsync_SerializesData_AndCallsGraphClient()
159283
mockRestClient.VerifyAll();
160284
}
161285

286+
[TestMethod]
287+
public async Task SaveContentAsync_WithMultipleTypes_ShouldGenerateJsonForContentOfDifferentTypes()
288+
{
289+
// Arrange
290+
repository.ConfigureContentType<Location>()
291+
.Field(x => x.Longitude, IndexingType.Queryable)
292+
.Field(x => x.Latitude, IndexingType.Queryable)
293+
.Field(x => x.Name, IndexingType.Searchable);
294+
295+
repository.ConfigureContentType<Event>()
296+
.Field(x => x.LocationName, IndexingType.Queryable)
297+
.Field(x => x.Time, IndexingType.Queryable)
298+
.Field(x => x.Name, IndexingType.Searchable)
299+
.Field(x => x.AdditionalInfo, IndexingType.PropertyType);
300+
301+
repository.ConfigureContentType<ExtraInfo>()
302+
.Field(x => x.Example1, IndexingType.OnlyStored)
303+
.Field(x => x.Example2, IndexingType.Queryable);
304+
305+
var locationStockholm = new Location
306+
{
307+
Name = "Stockholm",
308+
Latitude = 59.334591,
309+
Longitude = 18.063241,
310+
};
311+
var locationLondon = new Location
312+
{
313+
Name = "London",
314+
Latitude = 51.5072,
315+
Longitude = 0.1275,
316+
};
317+
var event1 = new Event
318+
{
319+
Name = "Future of Project Management",
320+
Time = new DateTime(2024, 10, 22),
321+
LocationName = "Stockholm",
322+
AdditionalInfo = new ExtraInfo
323+
{
324+
Example1 = "test1",
325+
Example2 = 1
326+
}
327+
};
328+
var event2 = new Event
329+
{
330+
Name = "Week of Hope: Football Camp for Homeless Children in Hanoi!",
331+
Time = new DateTime(2024, 10, 27),
332+
LocationName = "Hanoi",
333+
AdditionalInfo = new ExtraInfo
334+
{
335+
Example1 = "test2",
336+
Example2 = 2
337+
}
338+
};
339+
var event3 = new Event
340+
{
341+
Name = "Optimizing Project Management: Strategies for Success",
342+
Time = new DateTime(2024, 11, 03),
343+
LocationName = "London",
344+
AdditionalInfo = new ExtraInfo
345+
{
346+
Example1 = "test3",
347+
Example2 = 3
348+
}
349+
};
350+
351+
var expectedJsonString = @"{ ""index"": { ""_id"": ""Location-Stockholm"", ""language_routing"": ""en"" } }
352+
{ ""Status$$String"": ""Published"", ""__typename"": ""Location"", ""_rbac"": ""r:Everyone:Read"", ""ContentType$$String"": [ ""Location"" ], ""Language"": { ""Name$$String"": ""en"" }, ""Longitude$$Float"": 18.063241, ""Latitude$$Float"": 59.334591, ""Name$$String___searchable"": ""Stockholm""}
353+
{ ""index"": { ""_id"": ""Location-London"", ""language_routing"": ""en"" } }
354+
{ ""Status$$String"": ""Published"", ""__typename"": ""Location"", ""_rbac"": ""r:Everyone:Read"", ""ContentType$$String"": [ ""Location"" ], ""Language"": { ""Name$$String"": ""en"" }, ""Longitude$$Float"": 0.1275, ""Latitude$$Float"": 51.5072, ""Name$$String___searchable"": ""London""}
355+
{ ""index"": { ""_id"": ""Event-Future of Project Management"", ""language_routing"": ""en"" } }
356+
{ ""Status$$String"": ""Published"", ""__typename"": ""Event"", ""_rbac"": ""r:Everyone:Read"", ""ContentType$$String"": [ ""Event"" ], ""Language"": { ""Name$$String"": ""en"" }, ""LocationName$$String"": ""Stockholm"", ""Time$$DateTime"": ""2024-10-21T22:00:00Z"", ""Name$$String___searchable"": ""Future of Project Management"", ""AdditionalInfo"": { ""Example1$$String___skip"": ""test1"", ""Example2$$Int"": 1 }}
357+
{ ""index"": { ""_id"": ""Event-Week of Hope: Football Camp for Homeless Children in Hanoi!"", ""language_routing"": ""en"" } }
358+
{ ""Status$$String"": ""Published"", ""__typename"": ""Event"", ""_rbac"": ""r:Everyone:Read"", ""ContentType$$String"": [ ""Event"" ], ""Language"": { ""Name$$String"": ""en"" }, ""LocationName$$String"": ""Hanoi"", ""Time$$DateTime"": ""2024-10-26T22:00:00Z"", ""Name$$String___searchable"": ""Week of Hope: Football Camp for Homeless Children in Hanoi!"", ""AdditionalInfo"": { ""Example1$$String___skip"": ""test2"", ""Example2$$Int"": 2 }}
359+
{ ""index"": { ""_id"": ""Event-Optimizing Project Management: Strategies for Success"", ""language_routing"": ""en"" } }
360+
{ ""Status$$String"": ""Published"", ""__typename"": ""Event"", ""_rbac"": ""r:Everyone:Read"", ""ContentType$$String"": [ ""Event"" ], ""Language"": { ""Name$$String"": ""en"" }, ""LocationName$$String"": ""London"", ""Time$$DateTime"": ""2024-11-02T23:00:00Z"", ""Name$$String___searchable"": ""Optimizing Project Management: Strategies for Success"", ""AdditionalInfo"": { ""Example1$$String___skip"": ""test3"", ""Example2$$Int"": 3 }}
361+
";
362+
363+
Func<object, string> generateId = (x) =>
364+
{
365+
if (x is Location location)
366+
{
367+
return $"Location-{location.Name}";
368+
}
369+
if (x is Event ev)
370+
{
371+
return $"Event-{ev.Name}";
372+
}
373+
throw new NotImplementedException();
374+
375+
};
376+
377+
var jsonString = BuildExpextedContentJsonString<object>(generateId, locationStockholm, locationLondon, event1, event2, event3);
378+
379+
var content = new StringContent(expectedJsonString, Encoding.UTF8, "application/json");
380+
381+
var response = new HttpResponseMessage(HttpStatusCode.OK);
382+
var request = new HttpRequestMessage(HttpMethod.Post, $"/api/content/v2/data?id={source}") { Content = content };
383+
384+
mockRestClient.Setup(c => c.SendAsync(It.IsAny<HttpRequestMessage>())).ReturnsAsync(response);
385+
mockRestClient.Setup(c => c.HandleResponse(response));
386+
387+
// Act
388+
await repository.SaveContentAsync<object>(generateId, locationStockholm, locationLondon, event1, event2, event3);
389+
390+
// Assert
391+
Assert.AreEqual(expectedJsonString, jsonString);
392+
393+
mockRestClient.Verify(c => c.SendAsync(It.Is<HttpRequestMessage>(x => Compare(request, x))), Times.Once);
394+
mockRestClient.Verify(c => c.HandleResponse(response), Times.Once);
395+
mockRestClient.VerifyAll();
396+
}
397+
162398
[TestMethod]
163399
public async Task DeleteContentAsync_ThrowsNotImplementedException()
164400
{
@@ -177,6 +413,7 @@ public async Task DeleteContentAsync_ThrowsNotImplementedException()
177413
mockRestClient.VerifyAll();
178414
}
179415

416+
180417
#region Private
181418
private string BuildExpectedTypeJsonString()
182419
{
@@ -192,7 +429,7 @@ private string BuildExpectedTypeJsonString()
192429
return JsonSerializer.Serialize(SourceConfigurationModel.GetTypeFieldConfiguration(), serializeOptions);
193430
}
194431

195-
private string BuildExpextedContentJsonString(ExampleClassObject data)
432+
private string BuildExpextedContentJsonString<T>(Func<T, string> generateId, params T[] items)
196433
{
197434
var serializeOptions = new JsonSerializerOptions
198435
{
@@ -205,11 +442,13 @@ private string BuildExpextedContentJsonString(ExampleClassObject data)
205442

206443
var itemJson = string.Empty;
207444

208-
itemJson += $"{{ \"index\": {{ \"_id\": \"Optimizely.Graph.Source.Sdk.Tests.ExampleClassObject\", \"language_routing\": \"en\" }} }}";
209-
itemJson += Environment.NewLine;
210-
itemJson += JsonSerializer.Serialize(data, serializeOptions).Replace("\r\n", "");
211-
itemJson += Environment.NewLine;
212-
445+
foreach (var data in items)
446+
{
447+
itemJson += $"{{ \"index\": {{ \"_id\": \"{generateId(data)}\", \"language_routing\": \"en\" }} }}";
448+
itemJson += Environment.NewLine;
449+
itemJson += JsonSerializer.Serialize(data, serializeOptions).Replace("\r\n", "");
450+
itemJson += Environment.NewLine;
451+
}
213452
return itemJson;
214453
}
215454

Optimizely.Graph.Source.Sdk/Optimizely.Graph.Source.Sdk/JsonConverters/SourceSdkContentConverter.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ private static void WriteField(Utf8JsonWriter writer, object fieldValue, FieldIn
9595
else
9696
{
9797
writer.WriteStartObject(fieldInfoItem.Name);
98-
var fields = SourceConfigurationModel.GetPropertyFields(type);
98+
var fields = SourceConfigurationModel.HasContentType(type) ? SourceConfigurationModel.GetContentFields(type) : SourceConfigurationModel.GetPropertyFields(type);
9999
foreach (var field in fields)
100100
{
101101
var propertyFieldValue = type.GetProperty(field.Name)?.GetValue(fieldValue);

0 commit comments

Comments
 (0)