diff --git a/tests/ByteSync.Functions.UnitTests/Http/AnnouncementFunctionTests.cs b/tests/ByteSync.Functions.UnitTests/Http/AnnouncementFunctionTests.cs new file mode 100644 index 00000000..d0e11167 --- /dev/null +++ b/tests/ByteSync.Functions.UnitTests/Http/AnnouncementFunctionTests.cs @@ -0,0 +1,66 @@ +using System.Net; +using ByteSync.Common.Business.Announcements; +using ByteSync.Functions.Http; +using ByteSync.Functions.UnitTests.TestHelpers; +using ByteSync.ServerCommon.Commands.Announcements; +using FluentAssertions; +using MediatR; +using Microsoft.Azure.Functions.Worker; +using Moq; + +namespace ByteSync.Functions.UnitTests.Http; + +[TestFixture] +public class AnnouncementFunctionTests +{ + [Test] + public async Task GetAnnouncements_ReturnsOk_AndSendsRequest() + { + var mediatorMock = new Mock(); + var expectedAnnouncements = new List + { + new() + { + Id = "announcement-1", + StartDate = DateTime.UtcNow.AddDays(-1), + EndDate = DateTime.UtcNow.AddDays(7), + Message = new Dictionary { ["en"] = "Test announcement" } + } + }; + + mediatorMock + .Setup(m => m.Send(It.IsAny(), It.IsAny())) + .ReturnsAsync(expectedAnnouncements); + + var function = new AnnouncementFunction(mediatorMock.Object); + var mockContext = new Mock(); + var context = mockContext.Object; + var request = new FakeHttpRequestData(context); + + var response = await function.GetAnnouncements(request, context); + + response.StatusCode.Should().Be(HttpStatusCode.OK); + mediatorMock.Verify(m => m.Send(It.IsAny(), It.IsAny()), Times.Once); + } + + [Test] + public async Task GetAnnouncements_ReturnsEmptyList_WhenNoAnnouncements() + { + var mediatorMock = new Mock(); + var emptyAnnouncements = new List(); + + mediatorMock + .Setup(m => m.Send(It.IsAny(), It.IsAny())) + .ReturnsAsync(emptyAnnouncements); + + var function = new AnnouncementFunction(mediatorMock.Object); + var mockContext = new Mock(); + var context = mockContext.Object; + var request = new FakeHttpRequestData(context); + + var response = await function.GetAnnouncements(request, context); + + response.StatusCode.Should().Be(HttpStatusCode.OK); + mediatorMock.Verify(m => m.Send(It.IsAny(), It.IsAny()), Times.Once); + } +} diff --git a/tests/ByteSync.Functions.UnitTests/Http/AuthFunctionTests.cs b/tests/ByteSync.Functions.UnitTests/Http/AuthFunctionTests.cs new file mode 100644 index 00000000..29af88a4 --- /dev/null +++ b/tests/ByteSync.Functions.UnitTests/Http/AuthFunctionTests.cs @@ -0,0 +1,193 @@ +using System.Net; +using System.Text; +using ByteSync.Common.Business.Auth; +using ByteSync.Common.Business.Misc; +using ByteSync.Common.Controls.Json; +using ByteSync.Functions.Http; +using ByteSync.Functions.UnitTests.TestHelpers; +using ByteSync.ServerCommon.Commands.Authentication; +using FluentAssertions; +using MediatR; +using Microsoft.Azure.Functions.Worker; +using Moq; + +namespace ByteSync.Functions.UnitTests.Http; + +[TestFixture] +public class AuthFunctionTests +{ + [Test] + public async Task Login_WithValidCredentials_ReturnsOk() + { + var mediatorMock = new Mock(); + var loginData = new LoginData + { + ClientId = "test-client", + ClientInstanceId = "test-instance", + Version = "1.0.0", + OsPlatform = OSPlatforms.Windows + }; + + var authResponse = new InitialAuthenticationResponse(InitialConnectionStatus.Success) + { + AuthenticationTokens = new AuthenticationTokens + { + JwtToken = "test-jwt-token", + RefreshToken = "test-refresh-token", + JwtTokenDurationInSeconds = 3600, + RefreshTokenExpiration = DateTimeOffset.UtcNow.AddDays(30) + } + }; + + AuthenticateRequest? captured = null; + mediatorMock + .Setup(m => m.Send(It.IsAny(), It.IsAny())) + .Callback((r, _) => captured = (AuthenticateRequest)r) + .ReturnsAsync(authResponse); + + var function = new AuthFunction(mediatorMock.Object); + var mockContext = new Mock(); + var context = mockContext.Object; + var request = new FakeHttpRequestData(context); + request.Headers.Add("x-forwarded-for", "192.168.1.1"); + + var json = JsonHelper.Serialize(loginData); + await using (var writer = new StreamWriter(request.Body, Encoding.UTF8, 1024, leaveOpen: true)) + { + await writer.WriteAsync(json); + } + request.Body.Position = 0; + + var response = await function.Login(request, context); + + response.StatusCode.Should().Be(HttpStatusCode.OK); + captured.Should().NotBeNull(); + captured!.LoginData.Should().NotBeNull(); + captured.LoginData.ClientId.Should().Be("test-client"); + captured.LoginData.ClientInstanceId.Should().Be("test-instance"); + captured.IpAddress.Should().Be("192.168.1.1"); + } + + [Test] + public async Task Login_WithInvalidCredentials_ReturnsUnauthorized() + { + var mediatorMock = new Mock(); + var loginData = new LoginData + { + ClientId = "invalid-client", + ClientInstanceId = "test-instance", + Version = "1.0.0", + OsPlatform = OSPlatforms.Windows + }; + + var authResponse = new InitialAuthenticationResponse(InitialConnectionStatus.UndefinedClientId); + + mediatorMock + .Setup(m => m.Send(It.IsAny(), It.IsAny())) + .ReturnsAsync(authResponse); + + var function = new AuthFunction(mediatorMock.Object); + var mockContext = new Mock(); + var context = mockContext.Object; + var request = new FakeHttpRequestData(context); + request.Headers.Add("x-forwarded-for", "192.168.1.1"); + + var json = JsonHelper.Serialize(loginData); + await using (var writer = new StreamWriter(request.Body, Encoding.UTF8, 1024, leaveOpen: true)) + { + await writer.WriteAsync(json); + } + request.Body.Position = 0; + + var response = await function.Login(request, context); + + response.StatusCode.Should().Be(HttpStatusCode.Unauthorized); + mediatorMock.Verify(m => m.Send(It.IsAny(), It.IsAny()), Times.Once); + } + + [Test] + public async Task RefreshTokens_WithValidToken_ReturnsOk() + { + var mediatorMock = new Mock(); + var refreshTokensData = new RefreshTokensData + { + Token = "valid-refresh-token", + ClientInstanceId = "test-instance", + Version = "1.0.0", + OsPlatform = OSPlatforms.Windows + }; + + var refreshResponse = new RefreshTokensResponse(RefreshTokensStatus.RefreshTokenOk, new AuthenticationTokens + { + JwtToken = "new-jwt-token", + RefreshToken = "new-refresh-token", + JwtTokenDurationInSeconds = 3600, + RefreshTokenExpiration = DateTimeOffset.UtcNow.AddDays(30) + }); + + RefreshTokensRequest? captured = null; + mediatorMock + .Setup(m => m.Send(It.IsAny(), It.IsAny())) + .Callback((r, _) => captured = (RefreshTokensRequest)r) + .ReturnsAsync(refreshResponse); + + var function = new AuthFunction(mediatorMock.Object); + var mockContext = new Mock(); + var context = mockContext.Object; + var request = new FakeHttpRequestData(context); + request.Headers.Add("x-forwarded-for", "10.0.0.1"); + + var json = JsonHelper.Serialize(refreshTokensData); + await using (var writer = new StreamWriter(request.Body, Encoding.UTF8, 1024, leaveOpen: true)) + { + await writer.WriteAsync(json); + } + request.Body.Position = 0; + + var response = await function.RefreshTokens(request, context); + + response.StatusCode.Should().Be(HttpStatusCode.OK); + captured.Should().NotBeNull(); + captured!.RefreshTokensData.Should().NotBeNull(); + captured.RefreshTokensData.Token.Should().Be("valid-refresh-token"); + captured.RefreshTokensData.ClientInstanceId.Should().Be("test-instance"); + captured.IpAddress.Should().Be("10.0.0.1"); + } + + [Test] + public async Task RefreshTokens_WithInvalidToken_ReturnsUnauthorized() + { + var mediatorMock = new Mock(); + var refreshTokensData = new RefreshTokensData + { + Token = "invalid-refresh-token", + ClientInstanceId = "test-instance", + Version = "1.0.0", + OsPlatform = OSPlatforms.Windows + }; + + var refreshResponse = new RefreshTokensResponse(RefreshTokensStatus.RefreshTokenNotFound); + + mediatorMock + .Setup(m => m.Send(It.IsAny(), It.IsAny())) + .ReturnsAsync(refreshResponse); + + var function = new AuthFunction(mediatorMock.Object); + var mockContext = new Mock(); + var context = mockContext.Object; + var request = new FakeHttpRequestData(context); + request.Headers.Add("x-forwarded-for", "10.0.0.1"); + + var json = JsonHelper.Serialize(refreshTokensData); + await using (var writer = new StreamWriter(request.Body, Encoding.UTF8, 1024, leaveOpen: true)) + { + await writer.WriteAsync(json); + } + request.Body.Position = 0; + + var response = await function.RefreshTokens(request, context); + + response.StatusCode.Should().Be(HttpStatusCode.Unauthorized); + mediatorMock.Verify(m => m.Send(It.IsAny(), It.IsAny()), Times.Once); + } +} diff --git a/tests/ByteSync.Functions.UnitTests/Http/CloudSessionFunctionTests.cs b/tests/ByteSync.Functions.UnitTests/Http/CloudSessionFunctionTests.cs new file mode 100644 index 00000000..2cbfd5d7 --- /dev/null +++ b/tests/ByteSync.Functions.UnitTests/Http/CloudSessionFunctionTests.cs @@ -0,0 +1,365 @@ +using System.Net; +using System.Text; +using ByteSync.Common.Business.EndPoints; +using ByteSync.Common.Business.Misc; +using ByteSync.Common.Business.Sessions; +using ByteSync.Common.Business.Sessions.Cloud; +using ByteSync.Common.Business.Sessions.Cloud.Connections; +using ByteSync.Common.Controls.Json; +using ByteSync.Functions.Http; +using ByteSync.Functions.UnitTests.TestHelpers; +using ByteSync.ServerCommon.Business.Auth; +using ByteSync.ServerCommon.Commands.CloudSessions; +using FluentAssertions; +using MediatR; +using Microsoft.Azure.Functions.Worker; +using Moq; + +namespace ByteSync.Functions.UnitTests.Http; + +[TestFixture] +public class CloudSessionFunctionTests +{ + private static FunctionContext BuildFunctionContextWithClient() + { + var mockContext = new Mock(); + var items = new Dictionary(); + mockContext.SetupGet(c => c.Items).Returns(items); + + var client = new Client("cli", "cliInst", "1.0.0", OSPlatforms.Windows, "127.0.0.1"); + items[AuthConstants.FUNCTION_CONTEXT_CLIENT] = client; + + return mockContext.Object; + } + + private static async Task WriteBodyAsync(FakeHttpRequestData request, T body) + { + var json = JsonHelper.Serialize(body); + var bytes = Encoding.UTF8.GetBytes(json); + request.Body.SetLength(0); + await request.Body.WriteAsync(bytes, 0, bytes.Length); + request.Body.Position = 0; + } + + [Test] + public async Task Create_ReturnsOk_AndSendsRequest() + { + var mediatorMock = new Mock(); + CreateSessionRequest? captured = null; + mediatorMock + .Setup(m => m.Send(It.IsAny(), It.IsAny())) + .Callback((r, _) => captured = (CreateSessionRequest)r) + .ReturnsAsync(new CloudSessionResult + { + CloudSession = new CloudSession("S1", "cliInst"), + SessionSettings = new EncryptedSessionSettings { Id = "S1", Data = [21], IV = [22] }, + SessionMemberInfo = new SessionMemberInfoDTO + { + Endpoint = new ByteSyncEndpoint + { + ClientId = "cli", + ClientInstanceId = "cliInst", + Version = "1.0.0", + OSPlatform = OSPlatforms.Windows, + IpAddress = "127.0.0.1" + }, + EncryptedPrivateData = new EncryptedSessionMemberPrivateData { Id = "priv1", Data = [23], IV = [24] }, + SessionId = "S1", + JoinedSessionOn = DateTimeOffset.UtcNow, + PositionInList = 0 + }, + MembersIds = ["cliInst"] + }); + + var function = new CloudSessionFunction(mediatorMock.Object); + var context = BuildFunctionContextWithClient(); + var request = new FakeHttpRequestData(context); + + var parameters = new CreateCloudSessionParameters + { + LobbyId = "L1", + CreatorProfileClientId = "creator", + SessionSettings = new EncryptedSessionSettings { Id = "S1", Data = [1, 2], IV = [3, 4] }, + CreatorPublicKeyInfo = new PublicKeyInfo { ClientId = "cli", PublicKey = [5, 6], ProtocolVersion = 1 }, + CreatorPrivateData = new EncryptedSessionMemberPrivateData { Id = "priv", Data = [7], IV = [8] } + }; + await WriteBodyAsync(request, parameters); + + var response = await function.Create(request, context); + + response.StatusCode.Should().Be(HttpStatusCode.OK); + captured.Should().NotBeNull(); + captured!.CreateCloudSessionParameters.LobbyId.Should().Be("L1"); + captured.Client.ClientId.Should().Be("cli"); + response.Body.Length.Should().BeGreaterThan(0); + } + + [Test] + public async Task AskPasswordExchangeKey_ReturnsOk_AndSendsRequest() + { + var mediatorMock = new Mock(); + AskPasswordExchangeKeyRequest? captured = null; + mediatorMock + .Setup(m => m.Send(It.IsAny(), It.IsAny())) + .Callback((r, _) => captured = (AskPasswordExchangeKeyRequest)r) + .ReturnsAsync(JoinSessionResult.BuildProcessingNormally()); + + var function = new CloudSessionFunction(mediatorMock.Object); + var context = BuildFunctionContextWithClient(); + var request = new FakeHttpRequestData(context); + + var parameters = new AskCloudSessionPasswordExchangeKeyParameters + { + SessionId = "S1", + PublicKeyInfo = new PublicKeyInfo { ClientId = "cli", PublicKey = [9], ProtocolVersion = 2 }, + LobbyId = "L2", + ProfileClientId = "prof" + }; + await WriteBodyAsync(request, parameters); + + var response = await function.AskPasswordExchangeKey(request, context, "S1"); + + response.StatusCode.Should().Be(HttpStatusCode.OK); + captured.Should().NotBeNull(); + captured!.Parameters.SessionId.Should().Be("S1"); + captured.Client.ClientInstanceId.Should().Be("cliInst"); + response.Body.Length.Should().BeGreaterThan(0); + } + + [Test] + public async Task ValidateJoinCloudSession_ReturnsOk_AndSendsRequest() + { + var mediatorMock = new Mock(); + ValidateJoinCloudSessionRequest? captured = null; + mediatorMock + .Setup(m => m.Send(It.IsAny(), It.IsAny())) + .Callback((r, _) => captured = (ValidateJoinCloudSessionRequest)r) + .ReturnsAsync(Unit.Value); + + var function = new CloudSessionFunction(mediatorMock.Object); + var context = new Mock().Object; + var request = new FakeHttpRequestData(context); + + var parameters = new ValidateJoinCloudSessionParameters + { + SessionId = "S2", + JoinerClientInstanceId = "joiner", + ValidatorInstanceId = "validator", + EncryptedAesKey = [10, 11], + FinalizationPassword = "pwd" + }; + await WriteBodyAsync(request, parameters); + + var response = await function.ValidateJoinCloudSession(request, context, "S2"); + + response.StatusCode.Should().Be(HttpStatusCode.OK); + captured.Should().NotBeNull(); + captured!.Parameters.SessionId.Should().Be("S2"); + } + + [Test] + public async Task FinalizeJoinCloudSession_ReturnsOk_AndSendsRequest() + { + var mediatorMock = new Mock(); + FinalizeJoinCloudSessionRequest? captured = null; + mediatorMock + .Setup(m => m.Send(It.IsAny(), It.IsAny())) + .Callback((r, _) => captured = (FinalizeJoinCloudSessionRequest)r) + .ReturnsAsync(FinalizeJoinSessionResult.BuildFrom(FinalizeJoinSessionStatuses.FinalizeJoinSessionSucess)); + + var function = new CloudSessionFunction(mediatorMock.Object); + var context = BuildFunctionContextWithClient(); + var request = new FakeHttpRequestData(context); + + var parameters = new FinalizeJoinCloudSessionParameters + { + SessionId = "S3", + JoinerInstanceId = "joiner2", + ValidatorInstanceId = "validator2", + FinalizationPassword = "finalPwd", + EncryptedSessionMemberPrivateData = new EncryptedSessionMemberPrivateData { Id = "mid", Data = [12], IV = [13] } + }; + await WriteBodyAsync(request, parameters); + + var response = await function.FinalizeJoinCloudSession(request, context, "S3"); + + response.StatusCode.Should().Be(HttpStatusCode.OK); + captured.Should().NotBeNull(); + captured!.FinalizeJoinCloudSessionParameters.SessionId.Should().Be("S3"); + captured.Client.ClientInstanceId.Should().Be("cliInst"); + response.Body.Length.Should().BeGreaterThan(0); + } + + [Test] + public async Task AskJoinCloudSession_ReturnsOk_AndSendsRequest() + { + var mediatorMock = new Mock(); + AskJoinCloudSessionRequest? captured = null; + mediatorMock + .Setup(m => m.Send(It.IsAny(), It.IsAny())) + .Callback((r, _) => captured = (AskJoinCloudSessionRequest)r) + .ReturnsAsync(JoinSessionResult.BuildProcessingNormally()); + + var function = new CloudSessionFunction(mediatorMock.Object); + var context = BuildFunctionContextWithClient(); + var request = new FakeHttpRequestData(context); + + var parameters = new AskJoinCloudSessionParameters + { + SessionId = "S4", + JoinerClientInstanceId = "joiner3", + ValidatorInstanceId = "validator3", + EncryptedPassword = [14, 15] + }; + await WriteBodyAsync(request, parameters); + + var response = await function.AskJoinCloudSession(request, context, "S4"); + + response.StatusCode.Should().Be(HttpStatusCode.OK); + captured.Should().NotBeNull(); + captured!.Parameters.SessionId.Should().Be("S4"); + captured.Client.ClientInstanceId.Should().Be("cliInst"); + response.Body.Length.Should().BeGreaterThan(0); + } + + [Test] + public async Task GiveCloudSessionPasswordExchangeKey_ReturnsOk_AndSendsRequest() + { + var mediatorMock = new Mock(); + GiveCloudSessionPasswordExchangeKeyRequest? captured = null; + mediatorMock + .Setup(m => m.Send(It.IsAny(), It.IsAny())) + .Callback((r, _) => captured = (GiveCloudSessionPasswordExchangeKeyRequest)r) + .ReturnsAsync(Unit.Value); + + var function = new CloudSessionFunction(mediatorMock.Object); + var context = BuildFunctionContextWithClient(); + var request = new FakeHttpRequestData(context); + + var parameters = new GiveCloudSessionPasswordExchangeKeyParameters + { + SessionId = "S5", + JoinerInstanceId = "joiner4", + ValidatorInstanceId = "validator4", + PublicKeyInfo = new PublicKeyInfo { ClientId = "cli", PublicKey = [16], ProtocolVersion = 3 } + }; + await WriteBodyAsync(request, parameters); + + var response = await function.GiveCloudSessionPasswordExchangeKey(request, context, "S5"); + + response.StatusCode.Should().Be(HttpStatusCode.OK); + captured.Should().NotBeNull(); + captured!.Parameters.SessionId.Should().Be("S5"); + captured.Client.ClientInstanceId.Should().Be("cliInst"); + } + + [Test] + public async Task InformPasswordIsWrong_ReturnsOk_AndSendsRequest() + { + var mediatorMock = new Mock(); + InformPasswordIsWrongRequest? captured = null; + mediatorMock + .Setup(m => m.Send(It.IsAny(), It.IsAny())) + .Callback((r, _) => captured = (InformPasswordIsWrongRequest)r) + .ReturnsAsync(Unit.Value); + + var function = new CloudSessionFunction(mediatorMock.Object); + var context = BuildFunctionContextWithClient(); + var request = new FakeHttpRequestData(context); + + await WriteBodyAsync(request, "bad-client-inst"); + + var response = await function.InformPasswordIsWrong(request, context, "S6"); + + response.StatusCode.Should().Be(HttpStatusCode.OK); + captured.Should().NotBeNull(); + captured!.SessionId.Should().Be("S6"); + captured.ClientInstanceId.Should().Be("bad-client-inst"); + } + + [Test] + public async Task UpdateSettings_ReturnsOk_WhenUpdated() + { + var mediatorMock = new Mock(); + UpdateSessionSettingsRequest? captured = null; + mediatorMock + .Setup(m => m.Send(It.IsAny(), It.IsAny())) + .Callback((r, _) => captured = (UpdateSessionSettingsRequest)r) + .ReturnsAsync(true); + + var function = new CloudSessionFunction(mediatorMock.Object); + var context = BuildFunctionContextWithClient(); + var request = new FakeHttpRequestData(context); + + var settings = new EncryptedSessionSettings { Id = "set1", Data = [17], IV = [18] }; + await WriteBodyAsync(request, settings); + + var response = await function.UpdateSettings(request, context, "S7"); + + response.StatusCode.Should().Be(HttpStatusCode.OK); + captured.Should().NotBeNull(); + captured!.SessionId.Should().Be("S7"); + } + + [Test] + public async Task UpdateSettings_ReturnsConflict_WhenNotUpdated() + { + var mediatorMock = new Mock(); + mediatorMock + .Setup(m => m.Send(It.IsAny(), It.IsAny())) + .ReturnsAsync(false); + + var function = new CloudSessionFunction(mediatorMock.Object); + var context = BuildFunctionContextWithClient(); + var request = new FakeHttpRequestData(context); + + var settings = new EncryptedSessionSettings { Id = "set2", Data = [19], IV = [20] }; + await WriteBodyAsync(request, settings); + + var response = await function.UpdateSettings(request, context, "S8"); + + response.StatusCode.Should().Be(HttpStatusCode.Conflict); + } + + [Test] + public async Task Quit_ReturnsOk_AndSendsRequest() + { + var mediatorMock = new Mock(); + QuitSessionRequest? captured = null; + mediatorMock + .Setup(m => m.Send(It.IsAny(), It.IsAny())) + .Callback((r, _) => captured = (QuitSessionRequest)r) + .Returns(Task.FromResult(Unit.Value)); + + var function = new CloudSessionFunction(mediatorMock.Object); + var context = BuildFunctionContextWithClient(); + var request = new FakeHttpRequestData(context); + + var response = await function.Quit(request, context, "S9"); + + response.StatusCode.Should().Be(HttpStatusCode.OK); + captured.Should().NotBeNull(); + captured!.SessionId.Should().Be("S9"); + } + + [Test] + public async Task Reset_ReturnsOk_AndSendsRequest() + { + var mediatorMock = new Mock(); + ResetSessionRequest? captured = null; + mediatorMock + .Setup(m => m.Send(It.IsAny(), It.IsAny())) + .Callback((r, _) => captured = (ResetSessionRequest)r) + .Returns(Task.FromResult(Unit.Value)); + + var function = new CloudSessionFunction(mediatorMock.Object); + var context = BuildFunctionContextWithClient(); + var request = new FakeHttpRequestData(context); + + var response = await function.Reset(request, context, "S10"); + + response.StatusCode.Should().Be(HttpStatusCode.OK); + captured.Should().NotBeNull(); + captured!.SessionId.Should().Be("S10"); + } +} \ No newline at end of file diff --git a/tests/ByteSync.Functions.UnitTests/Http/SynchronizationFunctionTests.cs b/tests/ByteSync.Functions.UnitTests/Http/SynchronizationFunctionTests.cs index fe36f487..fcbc4199 100644 --- a/tests/ByteSync.Functions.UnitTests/Http/SynchronizationFunctionTests.cs +++ b/tests/ByteSync.Functions.UnitTests/Http/SynchronizationFunctionTests.cs @@ -44,7 +44,7 @@ public async Task LocalCopyIsDone_ForwardsMetrics_AndReturnsOk() var request = new FakeHttpRequestData(context); var body = new SynchronizationActionRequest { - ActionsGroupIds = new List { "A1" }, + ActionsGroupIds = ["A1"], NodeId = "N1", ActionMetricsByActionId = new Dictionary { @@ -89,7 +89,7 @@ public async Task DateIsCopied_ReturnsOk_AndSendsRequest() var request = new FakeHttpRequestData(context); var body = new SynchronizationActionRequest { - ActionsGroupIds = new List { "B1" }, + ActionsGroupIds = ["B1"], NodeId = "N2" }; var json = ByteSync.Common.Controls.Json.JsonHelper.Serialize(body); @@ -128,14 +128,14 @@ public async Task StartSynchronization_ReturnsOk_AndSendsRequest() var body = new SynchronizationStartRequest { SessionId = "S3", - ActionsGroupDefinitions = new List() - { + ActionsGroupDefinitions = + [ new() { ActionsGroupId = "G1", FileSystemType = ByteSync.Common.Business.Inventories.FileSystemTypes.File } - } + ] }; var json = ByteSync.Common.Controls.Json.JsonHelper.Serialize(body); await using (var writer = new StreamWriter(request.Body, Encoding.UTF8, 1024, leaveOpen: true)) @@ -172,7 +172,7 @@ public async Task FileOrDirectoryIsDeleted_ReturnsOk_AndSendsRequest() var request = new FakeHttpRequestData(context); var body = new SynchronizationActionRequest { - ActionsGroupIds = new List { "C1" }, + ActionsGroupIds = ["C1"], NodeId = "N3" }; var json = ByteSync.Common.Controls.Json.JsonHelper.Serialize(body); @@ -210,7 +210,7 @@ public async Task DirectoryIsCreated_ReturnsOk_AndSendsRequest() var request = new FakeHttpRequestData(context); var body = new SynchronizationActionRequest { - ActionsGroupIds = new List { "D1" }, + ActionsGroupIds = ["D1"], NodeId = "N4" }; var json = ByteSync.Common.Controls.Json.JsonHelper.Serialize(body); @@ -296,7 +296,7 @@ public async Task SynchronizationErrors_ReturnsOk_AndSendsRequest() var request = new FakeHttpRequestData(context); var body = new SynchronizationActionRequest { - ActionsGroupIds = new List { "E1" }, + ActionsGroupIds = ["E1"], NodeId = "N5" }; var json = ByteSync.Common.Controls.Json.JsonHelper.Serialize(body); diff --git a/tests/ByteSync.Functions.UnitTests/TestHelpers/FakeHttpRequestData.cs b/tests/ByteSync.Functions.UnitTests/TestHelpers/FakeHttpRequestData.cs index e626b404..343371fa 100644 --- a/tests/ByteSync.Functions.UnitTests/TestHelpers/FakeHttpRequestData.cs +++ b/tests/ByteSync.Functions.UnitTests/TestHelpers/FakeHttpRequestData.cs @@ -1,17 +1,37 @@ -using System.Security.Claims; +using System.Net; +using System.Security.Claims; +using System.Text.Json; +using Azure.Core.Serialization; using Microsoft.Azure.Functions.Worker; using Microsoft.Azure.Functions.Worker.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; using Moq; namespace ByteSync.Functions.UnitTests.TestHelpers; public class FakeHttpRequestData : HttpRequestData { + private static readonly IServiceProvider SharedServiceProvider = BuildServiceProvider(); + + private static IServiceProvider BuildServiceProvider() + { + var jsonSerializer = new JsonObjectSerializer(new JsonSerializerOptions(JsonSerializerDefaults.Web)); + var workerOptions = Options.Create(new WorkerOptions { Serializer = jsonSerializer }); + + return new ServiceCollection() + .AddSingleton(jsonSerializer) + .AddSingleton>(workerOptions) + .BuildServiceProvider(); + } + public FakeHttpRequestData(FunctionContext functionContext) : base(functionContext) { Headers = new HttpHeadersCollection(); Body = new MemoryStream(); Url = new Uri("http://localhost"); + Cookies = new List(); + Method = "GET"; } public override HttpHeadersCollection Headers { get; } @@ -28,12 +48,43 @@ public FakeHttpRequestData(FunctionContext functionContext) : base(functionConte public override HttpResponseData CreateResponse() { - var responseMock = new Mock(FunctionContext); - responseMock.SetupProperty(r => r.StatusCode); - responseMock.SetupGet(r => r.Headers).Returns(new HttpHeadersCollection()); - responseMock.SetupProperty(r => r.Body, new MemoryStream()); - responseMock.SetupGet(r => r.Cookies).Returns(new Mock().Object); + return new FakeHttpResponseData(new FakeFunctionContext(SharedServiceProvider)); + } + + private class FakeFunctionContext : FunctionContext + { + public FakeFunctionContext(IServiceProvider serviceProvider) + { + InstanceServices = serviceProvider; + } + + public override string InvocationId => Guid.NewGuid().ToString(); + public override string FunctionId => "test-function"; + public override TraceContext TraceContext => null!; + public override BindingContext BindingContext => null!; + public override RetryContext RetryContext => null!; + public override IServiceProvider InstanceServices { get; set; } + public override FunctionDefinition FunctionDefinition => null!; + public override IDictionary Items { get; set; } = new Dictionary(); + public override IInvocationFeatures Features => null!; + } + + private class FakeHttpResponseData : HttpResponseData + { + public FakeHttpResponseData(FunctionContext functionContext) : base(functionContext) + { + StatusCode = HttpStatusCode.OK; + Headers = new HttpHeadersCollection(); + Body = new MemoryStream(); + Cookies = new Mock().Object; + } - return responseMock.Object; + public override HttpStatusCode StatusCode { get; set; } + + public override HttpHeadersCollection Headers { get; set; } + + public override Stream Body { get; set; } + + public override HttpCookies Cookies { get; } } }