Skip to content

Commit f469281

Browse files
committed
Serialize dates with min 3 fractional digits when using SourceSerializer in integration tests (#4175)
This commit adds a new JsonConverter to serialize DateTime/DateTimeOffset with a minimum of 3 fractional digits when running integration tests using the test SourceSerializer. Related to a bug in Elasticsearch <7.1.0 in the XML comment of the JsonConverter. Always emit the random values used when running a test/integrate build, not just if they were explicitly set. (cherry picked from commit 57a6266)
1 parent 0564747 commit f469281

File tree

4 files changed

+100
-22
lines changed

4 files changed

+100
-22
lines changed

src/Tests/Tests.Core/Client/Serializers/TestSourceSerializerBase.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ protected override JsonSerializerSettings CreateJsonSerializerSettings() =>
2323
protected override IEnumerable<JsonConverter> CreateJsonConverters()
2424
{
2525
yield return new SourceOnlyUsingBuiltInConverter();
26+
yield return new Domain.JsonConverters.DateTimeConverter();
2627
}
2728

2829
protected override void ModifyContractResolver(ConnectionSettingsAwareContractResolver resolver) =>

src/Tests/Tests.Core/Xunit/NestXunitRunOptions.cs

Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Collections.Generic;
44
using System.Diagnostics;
55
using System.Linq;
6+
using System.Runtime.InteropServices;
67
using System.Text;
78
using Elastic.Xunit;
89
using Tests.Configuration;
@@ -91,18 +92,18 @@ private static string ReproduceCommandLine(ConcurrentBag<Tuple<string, string>>
9192
bool runningIntegrations
9293
)
9394
{
94-
var sb = new StringBuilder("build.bat ")
95-
.Append($"seed:{config.Seed} ");
95+
var sb = new StringBuilder(".\\build.bat ")
96+
.Append("seed:").Append(config.Seed).Append(" ");
9697

97-
AppendExplictConfig(nameof(RandomConfiguration.SourceSerializer), sb);
98-
AppendExplictConfig(nameof(RandomConfiguration.TypedKeys), sb);
99-
AppendExplictConfig(nameof(RandomConfiguration.HttpCompression), sb);
98+
AppendConfig(nameof(RandomConfiguration.SourceSerializer), config.Random.SourceSerializer, sb);
99+
AppendConfig(nameof(RandomConfiguration.TypedKeys), config.Random.TypedKeys, sb);
100+
AppendConfig(nameof(RandomConfiguration.HttpCompression), config.Random.HttpCompression, sb);
100101

101102
if (runningIntegrations)
102103
sb.Append("integrate ")
103104
.Append(TestConfiguration.Instance.ElasticsearchVersion);
104-
105-
else sb.Append("test");
105+
else
106+
sb.Append("test");
106107

107108
if (runningIntegrations && failedCollections.Count > 0)
108109
{
@@ -135,20 +136,9 @@ bool runningIntegrations
135136
}
136137

137138
/// <summary>
138-
/// Append random overwrite to reproduce line only if one was provided explicitly
139+
/// Append random values used
139140
/// </summary>
140-
private static void AppendExplictConfig(string key, StringBuilder sb)
141-
{
142-
if (!TryGetExplicitRandomConfig(key, out var b)) return;
143-
144-
sb.Append($"random:{key}{(b ? "" : ":false")} ");
145-
}
146-
147-
private static bool TryGetExplicitRandomConfig(string key, out bool value)
148-
{
149-
value = false;
150-
var v = Environment.GetEnvironmentVariable($"NEST_RANDOM_{key.ToUpper()}");
151-
return !string.IsNullOrWhiteSpace(v) && bool.TryParse(v, out value);
152-
}
141+
private static void AppendConfig(string key, bool value, StringBuilder sb) =>
142+
sb.Append($"random:{key.ToLowerInvariant()}{(value ? "" : ":false")} ");
153143
}
154144
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
using System;
2+
using System.Globalization;
3+
using System.Text;
4+
using Newtonsoft.Json;
5+
6+
namespace Tests.Domain.JsonConverters
7+
{
8+
/// <summary>
9+
/// DateTime/DateTimeOffset converter that always serializes values with a minimum of three sub second fractions.
10+
/// This is to fix a bug in Elastisearch < 7.1.0: https://github.com/elastic/elasticsearch/pull/41871
11+
/// </summary>
12+
public class DateTimeConverter : Newtonsoft.Json.Converters.IsoDateTimeConverter
13+
{
14+
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
15+
{
16+
if (value == null)
17+
{
18+
writer.WriteNull();
19+
return;
20+
}
21+
22+
const string format = "yyyy'-'MM'-'dd'T'HH':'mm':'ss.FFFFFFF";
23+
var builder = new StringBuilder(33);
24+
25+
if (value is DateTime dateTime)
26+
{
27+
if ((DateTimeStyles & DateTimeStyles.AdjustToUniversal) == DateTimeStyles.AdjustToUniversal
28+
|| (DateTimeStyles & DateTimeStyles.AssumeUniversal) == DateTimeStyles.AssumeUniversal)
29+
{
30+
dateTime = dateTime.ToUniversalTime();
31+
}
32+
33+
builder.Append(dateTime.ToString(format, CultureInfo.InvariantCulture));
34+
}
35+
else if (value is DateTimeOffset dateTimeOffset)
36+
{
37+
if ((DateTimeStyles & DateTimeStyles.AdjustToUniversal) == DateTimeStyles.AdjustToUniversal
38+
|| (DateTimeStyles & DateTimeStyles.AssumeUniversal) == DateTimeStyles.AssumeUniversal)
39+
{
40+
dateTimeOffset = dateTimeOffset.ToUniversalTime();
41+
}
42+
43+
builder.Append(dateTimeOffset.ToString(format, CultureInfo.InvariantCulture));
44+
dateTime = dateTimeOffset.DateTime;
45+
}
46+
else
47+
throw new JsonSerializationException(
48+
$"Unexpected value when converting date. Expected DateTime or DateTimeOffset, got {value.GetType()}.");
49+
50+
if (builder.Length > 20 && builder.Length < 23)
51+
{
52+
var diff = 23 - builder.Length;
53+
for (var i = 0; i < diff; i++)
54+
builder.Append('0');
55+
}
56+
57+
switch (dateTime.Kind)
58+
{
59+
case DateTimeKind.Local:
60+
var offset = TimeZoneInfo.Local.GetUtcOffset(dateTime);
61+
if (offset >= TimeSpan.Zero)
62+
builder.Append('+');
63+
else
64+
{
65+
builder.Append('-');
66+
offset = offset.Negate();
67+
}
68+
69+
AppendTwoDigitNumber(builder, offset.Hours);
70+
builder.Append(':');
71+
AppendTwoDigitNumber(builder, offset.Minutes);
72+
break;
73+
case DateTimeKind.Utc:
74+
builder.Append('Z');
75+
break;
76+
}
77+
78+
writer.WriteValue(builder.ToString());
79+
}
80+
81+
private static void AppendTwoDigitNumber(StringBuilder result, int val)
82+
{
83+
result.Append((char)('0' + (val / 10)));
84+
result.Append((char)('0' + (val % 10)));
85+
}
86+
}
87+
}

src/Tests/Tests/Cluster/TaskManagement/GetTask/GetTaskApiTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ protected override void IntegrationSetup(IElasticClient client, CallUniqueValues
138138
var targetIndex = "tasks-lists-completed-get";
139139
var bulkResponse = client.IndexMany(Project.Generator.Generate(500), sourceIndex);
140140
if (!bulkResponse.IsValid)
141-
throw new Exception("failure in setting up integration");
141+
throw new Exception($"failure in setting up integration for {nameof(GetTaskApiCompletedTaskTests)}. {bulkResponse.DebugInformation}");
142142

143143
var createIndex = client.Indices.Create(targetIndex, i => i
144144
.Settings(settings => settings.Analysis(DefaultSeeder.ProjectAnalysisSettings))

0 commit comments

Comments
 (0)