NUC-42: Write partial unit tests for TelemetryPublisherFirestore
This commit is contained in:
parent
5d16e93bda
commit
7efc49596e
9 changed files with 292 additions and 79 deletions
|
@ -186,10 +186,12 @@ namespace NucuCar.Common
|
|||
/// <returns></returns>
|
||||
private sNetHttp.HttpRequestMessage _makeRequest(sNetHttp.HttpMethod method, string path)
|
||||
{
|
||||
var uri = _httpClient.BaseAddress == null ? new Uri(path) : new Uri(_httpClient.BaseAddress, path);
|
||||
|
||||
var requestMessage = new sNetHttp.HttpRequestMessage
|
||||
{
|
||||
Method = method,
|
||||
RequestUri = new Uri(_httpClient.BaseAddress, path)
|
||||
RequestUri = uri
|
||||
};
|
||||
requestMessage.Headers.Authorization = _httpClient.DefaultRequestHeaders.Authorization;
|
||||
|
||||
|
|
|
@ -96,7 +96,7 @@ namespace NucuCar.Domain.Telemetry
|
|||
/// It also adds metadata information such as: source and timestamp.
|
||||
/// </summary>
|
||||
/// <returns>A dictionary containing all telemetry data.</returns>
|
||||
protected Dictionary<string, object> GetTelemetry()
|
||||
protected virtual Dictionary<string, object> GetTelemetry()
|
||||
{
|
||||
var data = new List<Dictionary<string, object>>();
|
||||
foreach (var telemeter in RegisteredTelemeters)
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
|
@ -26,7 +27,7 @@ namespace NucuCar.Telemetry
|
|||
/// </summary>
|
||||
public class TelemetryPublisherFirestore : TelemetryPublisher
|
||||
{
|
||||
private readonly HttpClient _httpClient;
|
||||
protected HttpClient HttpClient;
|
||||
|
||||
private string _idToken;
|
||||
|
||||
|
@ -44,6 +45,7 @@ namespace NucuCar.Telemetry
|
|||
Logger?.LogCritical(
|
||||
$"Can't start {nameof(TelemetryPublisherFirestore)}! Malformed connection string! " +
|
||||
$"Missing ProjectId!");
|
||||
throw new ArgumentException("Malformed connection string!");
|
||||
}
|
||||
|
||||
if (!options.TryGetValue("CollectionName", out var firestoreCollection))
|
||||
|
@ -51,6 +53,7 @@ namespace NucuCar.Telemetry
|
|||
Logger?.LogCritical(
|
||||
$"Can't start {nameof(TelemetryPublisherFirestore)}! Malformed connection string! " +
|
||||
$"Missing CollectionName!");
|
||||
throw new ArgumentException("Malformed connection string!");
|
||||
}
|
||||
|
||||
var timeout = int.Parse(options.GetValueOrDefault("Timeout", "10000") ?? "10000");
|
||||
|
@ -61,7 +64,7 @@ namespace NucuCar.Telemetry
|
|||
// Setup HttpClient
|
||||
var requestUrl = $"https://firestore.googleapis.com/v1/projects/{firestoreProjectId}/" +
|
||||
$"databases/(default)/documents/{firestoreCollection}/";
|
||||
_httpClient = new HttpClient(requestUrl) {Timeout = timeout, Logger = Logger};
|
||||
HttpClient = new HttpClient(requestUrl) {Timeout = timeout, Logger = Logger};
|
||||
Logger?.LogInformation($"Initialized {nameof(TelemetryPublisherFirestore)}");
|
||||
Logger?.LogInformation($"ProjectId: {firestoreProjectId}; CollectionName: {firestoreCollection}.");
|
||||
}
|
||||
|
@ -77,13 +80,13 @@ namespace NucuCar.Telemetry
|
|||
["returnSecureToken"] = true
|
||||
};
|
||||
|
||||
var response = await _httpClient.PostAsync(requestUrl, data);
|
||||
var response = await HttpClient.PostAsync(requestUrl, data);
|
||||
|
||||
if (response?.StatusCode == HttpStatusCode.OK)
|
||||
{
|
||||
var jsonContent = await response.GetJson();
|
||||
_idToken = jsonContent.GetProperty("idToken").ToString();
|
||||
_httpClient.Authorization(_idToken);
|
||||
HttpClient.Authorization(_idToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -100,7 +103,7 @@ namespace NucuCar.Telemetry
|
|||
}
|
||||
|
||||
var data = FirebaseRestTranslator.Translator.Translate(null, GetTelemetry());
|
||||
var responseMessage = await _httpClient.PostAsync("", data);
|
||||
var responseMessage = await HttpClient.PostAsync("", data);
|
||||
|
||||
switch (responseMessage?.StatusCode)
|
||||
{
|
||||
|
@ -111,7 +114,7 @@ namespace NucuCar.Telemetry
|
|||
{
|
||||
Logger?.LogError($"Failed to publish telemetry data! {responseMessage.StatusCode}. Retrying...");
|
||||
await SetupAuthorization();
|
||||
responseMessage = await _httpClient.PostAsync("", data);
|
||||
responseMessage = await HttpClient.PostAsync("", data);
|
||||
if (responseMessage != null && responseMessage.IsSuccessStatusCode)
|
||||
{
|
||||
Logger?.LogInformation("Published data to Firestore on retry!");
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
namespace NucuCar.Domain.Telemetry
|
||||
using NucuCar.Domain.Telemetry;
|
||||
|
||||
namespace NucuCar.Telemetry
|
||||
{
|
||||
/// <summary>
|
||||
/// TelemetryPublisherType holds constants for instantiating <see cref="TelemetryPublisher"/>,
|
|
@ -3,7 +3,7 @@ using System.Collections.Generic;
|
|||
using NucuCar.Common.Utilities;
|
||||
using Xunit;
|
||||
|
||||
namespace NucuCar.UnitTests.NucuCar.Domain.Tests.Utilities
|
||||
namespace NucuCar.UnitTests.NucuCar.Common.Tests
|
||||
{
|
||||
public class ConnectionStringParserTest
|
||||
{
|
|
@ -1,7 +0,0 @@
|
|||
namespace NucuCar.UnitTests.NucuCar.Domain.Tests.Telemetry
|
||||
{
|
||||
public class TelemetryPublisherFirestoreTest
|
||||
{
|
||||
// TODO after refactoring
|
||||
}
|
||||
}
|
|
@ -1,9 +1,8 @@
|
|||
using System;
|
||||
using NucuCar.Domain.Telemetry;
|
||||
using NucuCar.Telemetry;
|
||||
using Xunit;
|
||||
|
||||
namespace NucuCar.UnitTests.NucuCar.Domain.Tests.Telemetry
|
||||
namespace NucuCar.UnitTests.NucuCar.Telemetry.Tests
|
||||
{
|
||||
public class TelemetryPublisherFactoryTest
|
||||
{
|
||||
|
@ -27,6 +26,16 @@ namespace NucuCar.UnitTests.NucuCar.Domain.Tests.Telemetry
|
|||
Assert.IsType<TelemetryPublisherDisk>(telemetryPublisher);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
private void Test_Build_TelemetryPublisherFiresstore()
|
||||
{
|
||||
const string connectionString =
|
||||
"ProjectId=test;CollectionName=test";
|
||||
var telemetryPublisher =
|
||||
TelemetryPublisherFactory.CreateFromConnectionString(TelemetryPublisherType.Firestore, connectionString);
|
||||
Assert.IsType<TelemetryPublisherFirestore>(telemetryPublisher);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
private void Test_Build_ThrowsOnInvalidType()
|
||||
{
|
|
@ -0,0 +1,164 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Moq;
|
||||
using NucuCar.Domain.Telemetry;
|
||||
using NucuCar.Telemetry;
|
||||
using Xunit;
|
||||
using HttpClient = NucuCar.Common.HttpClient;
|
||||
|
||||
namespace NucuCar.UnitTests.NucuCar.Telemetry.Tests
|
||||
{
|
||||
/// <summary>
|
||||
/// Class used to test the TelemetryPublisherFirestore by mocking the GetTelemetry method and HttpClient field.
|
||||
/// </summary>
|
||||
internal class MockTelemetryPublisherFirestore : TelemetryPublisherFirestore
|
||||
{
|
||||
private Dictionary<string, object> _mockData;
|
||||
|
||||
public MockTelemetryPublisherFirestore(TelemetryPublisherBuilderOptions opts) : base(opts)
|
||||
{
|
||||
_mockData = new Dictionary<string, object>();
|
||||
}
|
||||
|
||||
public void SetHttpClient(HttpClient client)
|
||||
{
|
||||
HttpClient = client;
|
||||
}
|
||||
|
||||
public void SetMockData(Dictionary<string, object> data)
|
||||
{
|
||||
_mockData = data;
|
||||
}
|
||||
|
||||
protected override Dictionary<string, object> GetTelemetry()
|
||||
{
|
||||
return _mockData;
|
||||
}
|
||||
}
|
||||
|
||||
public class TelemetryPublisherFirestoreTest
|
||||
{
|
||||
[Fact]
|
||||
private void Test_Construct_BadProjectId()
|
||||
{
|
||||
// Setup
|
||||
var opts = new TelemetryPublisherBuilderOptions()
|
||||
{
|
||||
ConnectionString = "ProjectIdBAD=test;CollectionName=test"
|
||||
};
|
||||
|
||||
// Run & Assert
|
||||
Assert.Throws<ArgumentException>(() => { new MockTelemetryPublisherFirestore(opts); });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
private void Test_Construct_BadCollectiontName()
|
||||
{
|
||||
// Setup
|
||||
var opts = new TelemetryPublisherBuilderOptions()
|
||||
{
|
||||
ConnectionString = "ProjectId=test;CollectionNameBAD=test"
|
||||
};
|
||||
|
||||
// Run & Assert
|
||||
Assert.Throws<ArgumentException>(() => { new MockTelemetryPublisherFirestore(opts); });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
private async Task Test_PublishAsync_OK()
|
||||
{
|
||||
// Setup
|
||||
var opts = new TelemetryPublisherBuilderOptions()
|
||||
{
|
||||
ConnectionString = "ProjectId=test;CollectionName=test"
|
||||
};
|
||||
var publisher = new MockTelemetryPublisherFirestore(opts);
|
||||
var mockHttpClient = new Mock<HttpClient>("http://testing.com");
|
||||
mockHttpClient.Setup(c => c.SendAsync(It.IsAny<HttpRequestMessage>()))
|
||||
.Returns(Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK)));
|
||||
|
||||
publisher.SetHttpClient(mockHttpClient.Object);
|
||||
publisher.SetMockData(new Dictionary<string, object> {["testData"] = 1});
|
||||
|
||||
// Run
|
||||
await publisher.PublishAsync(CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
var expectedContent = "{\"fields\":{\"testData\":{\"integerValue\":1}}}";
|
||||
mockHttpClient.Verify(
|
||||
m => m.SendAsync(
|
||||
It.Is<HttpRequestMessage>(
|
||||
request => request.Method.Equals(HttpMethod.Post))));
|
||||
mockHttpClient.Verify(
|
||||
m => m.SendAsync(
|
||||
It.Is<HttpRequestMessage>(
|
||||
request => request.RequestUri.Equals(new Uri("http://testing.com")))));
|
||||
mockHttpClient.Verify(
|
||||
m => m.SendAsync(
|
||||
It.Is<HttpRequestMessage>(
|
||||
request => request.Content.ReadAsStringAsync().GetAwaiter().GetResult()
|
||||
.Equals(expectedContent))));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
private async Task Test_PublishAsync_Cancel()
|
||||
{
|
||||
// Setup
|
||||
var opts = new TelemetryPublisherBuilderOptions()
|
||||
{
|
||||
ConnectionString = "ProjectId=test;CollectionName=test"
|
||||
};
|
||||
var publisher = new MockTelemetryPublisherFirestore(opts);
|
||||
var mockHttpClient = new Mock<HttpClient>("http://testing.com");
|
||||
mockHttpClient.Setup(c => c.SendAsync(It.IsAny<HttpRequestMessage>()))
|
||||
.Returns(Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK)));
|
||||
|
||||
publisher.SetHttpClient(mockHttpClient.Object);
|
||||
publisher.SetMockData(new Dictionary<string, object> {["testData"] = 1});
|
||||
var cts = new CancellationTokenSource();
|
||||
cts.Cancel();
|
||||
|
||||
// Run
|
||||
await publisher.PublishAsync(cts.Token);
|
||||
|
||||
// Assert
|
||||
mockHttpClient.Verify(m => m.SendAsync(It.IsAny<HttpRequestMessage>()), Times.Never());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
private async Task Test_PublishAsync_Authorization_OK()
|
||||
{
|
||||
// Setup
|
||||
var sendAsyncInvocations = new List<HttpRequestMessage>();
|
||||
|
||||
var opts = new TelemetryPublisherBuilderOptions()
|
||||
{
|
||||
ConnectionString = "ProjectId=test;CollectionName=test"
|
||||
};
|
||||
var publisher = new MockTelemetryPublisherFirestore(opts);
|
||||
var mockHttpClient = new Mock<HttpClient>("http://testing.com");
|
||||
mockHttpClient.SetupSequence(c => c.SendAsync(It.IsAny<HttpRequestMessage>()))
|
||||
.Returns(Task.FromResult(new HttpResponseMessage(HttpStatusCode.Forbidden)))
|
||||
.Returns(Task.FromResult(
|
||||
new HttpResponseMessage(HttpStatusCode.OK)
|
||||
{Content = new StringContent("{\"idToken\":\"testauthtoken\"}")}
|
||||
))
|
||||
.Returns(Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK)));
|
||||
|
||||
|
||||
publisher.SetHttpClient(mockHttpClient.Object);
|
||||
publisher.SetMockData(new Dictionary<string, object> {["testData"] = 1});
|
||||
|
||||
// Run
|
||||
await publisher.PublishAsync(CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
// Can't verify because moq doesn't support that, damn C#.
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,4 +2,44 @@
|
|||
<s:String x:Key="/Default/Environment/AssemblyExplorer/XmlDocument/@EntryValue"><AssemblyExplorer>
|
||||
<Assembly Path="/home/denis/.nuget/packages/iot.device.bindings/1.0.0/lib/netcoreapp2.1/Iot.Device.Bindings.dll" />
|
||||
<Assembly Path="/home/denis/.nuget/packages/firebaseresttranslator/0.1.1/lib/netcoreapp3.0/FirebaseRestTranslator.dll" />
|
||||
</AssemblyExplorer></s:String></wpf:ResourceDictionary>
|
||||
</AssemblyExplorer></s:String>
|
||||
<s:String x:Key="/Default/Environment/UnitTesting/UnitTestSessionStore/Sessions/=2d4aa020_002D92b4_002D4819_002Da752_002Df0a1e75a5e68/@EntryIndexedValue"><SessionState ContinuousTestingIsOn="False" ContinuousTestingMode="0" FrameworkVersion="{x:Null}" IsLocked="False" Name="Test_PublishAsync_Authorization_OK" PlatformMonoPreference="{x:Null}" PlatformType="{x:Null}" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<TestAncestor>
|
||||
<TestId>xUnit::C6F07921-1052-4945-911E-F328A622F229::.NETCoreApp,Version=v3.0::NucuCar.UnitTests.NucuCar.Telemetry.Tests.TelemetryPublisherFirestoreTest.Test_PublishAsync_Authorization_OK</TestId>
|
||||
</TestAncestor>
|
||||
</SessionState></s:String>
|
||||
<s:String x:Key="/Default/Environment/UnitTesting/UnitTestSessionStore/Sessions/=8687c1ea_002Dea4c_002D426e_002Da605_002D3d34b0c68f64/@EntryIndexedValue"><SessionState ContinuousTestingIsOn="False" ContinuousTestingMode="0" FrameworkVersion="{x:Null}" IsLocked="False" Name="Session" PlatformMonoPreference="{x:Null}" PlatformType="{x:Null}" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<Or>
|
||||
<Or>
|
||||
<Or>
|
||||
<Or>
|
||||
<Or>
|
||||
<Or>
|
||||
<TestAncestor>
|
||||
<TestId>xUnit::C6F07921-1052-4945-911E-F328A622F229::.NETCoreApp,Version=v3.0::NucuCar.UnitTests.NucuCar.Telemetry.Tests.TelemetryPublisherFirestoreTest.Test_PublishAsync_OK</TestId>
|
||||
</TestAncestor>
|
||||
<TestAncestor>
|
||||
<TestId>xUnit::C6F07921-1052-4945-911E-F328A622F229::.NETCoreApp,Version=v3.0::NucuCar.UnitTests.NucuCar.Telemetry.Tests.TelemetryPublisherFirestoreTest.Test_PublishAsync_BadProjectId</TestId>
|
||||
</TestAncestor>
|
||||
</Or>
|
||||
<TestAncestor>
|
||||
<TestId>xUnit::C6F07921-1052-4945-911E-F328A622F229::.NETCoreApp,Version=v3.0::NucuCar.UnitTests.NucuCar.Telemetry.Tests.TelemetryPublisherFirestoreTest.Test_PublishAsync_CollectiontName</TestId>
|
||||
</TestAncestor>
|
||||
</Or>
|
||||
<TestAncestor>
|
||||
<TestId>xUnit::C6F07921-1052-4945-911E-F328A622F229::.NETCoreApp,Version=v3.0::NucuCar.UnitTests.NucuCar.Telemetry.Tests.TelemetryPublisherFirestoreTest.Test_PublishAsync_Error_Timeout</TestId>
|
||||
</TestAncestor>
|
||||
</Or>
|
||||
<TestAncestor>
|
||||
<TestId>xUnit::C6F07921-1052-4945-911E-F328A622F229::.NETCoreApp,Version=v3.0::NucuCar.UnitTests.NucuCar.Telemetry.Tests.TelemetryPublisherFirestoreTest.Test_PublishAsync_Cancel</TestId>
|
||||
</TestAncestor>
|
||||
</Or>
|
||||
<TestAncestor>
|
||||
<TestId>xUnit::C6F07921-1052-4945-911E-F328A622F229::.NETCoreApp,Version=v3.0::NucuCar.UnitTests.NucuCar.Telemetry.Tests.TelemetryPublisherFirestoreTest.Test_PublishAsync_UnknownError</TestId>
|
||||
</TestAncestor>
|
||||
</Or>
|
||||
<TestAncestor>
|
||||
<TestId>xUnit::C6F07921-1052-4945-911E-F328A622F229::.NETCoreApp,Version=v3.0::NucuCar.UnitTests.NucuCar.Telemetry.Tests.TelemetryPublisherFirestoreTest.Test_PublishAsync_AuthorizationFail</TestId>
|
||||
</TestAncestor>
|
||||
</Or>
|
||||
</SessionState></s:String></wpf:ResourceDictionary>
|
Loading…
Reference in a new issue