Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Source/Csla.AspNetCore/Blazor/State/SessionManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,8 @@ public void PurgeSessions(TimeSpan expiration)
}

// wasm client-side methods
Task<Session> ISessionManager.RetrieveSession() => throw new NotImplementedException();
Task<Session> ISessionManager.RetrieveSession(TimeSpan timeout) => throw new NotImplementedException();
Session ISessionManager.GetCachedSession() => throw new NotImplementedException();
Task ISessionManager.SendSession() => throw new NotImplementedException();
Task ISessionManager.SendSession(TimeSpan timeout) => throw new NotImplementedException();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net8.0</TargetFrameworks>
<IsPackable>false</IsPackable>
<ApplicationIcon />
<OutputType>Library</OutputType>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
<PackageReference Include="Moq" Version="4.20.70" />
<PackageReference Include="MSTest.TestAdapter" Version="3.3.1" />
<PackageReference Include="MSTest.TestFramework" Version="3.3.1" />
<PackageReference Include="coverlet.collector" Version="6.0.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)'=='netcoreapp3.1'">
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="6.0.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Csla.AspNetCore\Csla.AspNetCore.csproj" />
<ProjectReference Include="..\Csla.Blazor.WebAssembly\Csla.Blazor.WebAssembly.csproj" />
<ProjectReference Include="..\Csla.Blazor\Csla.Blazor.csproj" />
<ProjectReference Include="..\Csla.TestHelpers\Csla.TestHelpers.csproj" />
<ProjectReference Include="..\Csla\Csla.csproj" />
</ItemGroup>

</Project>
204 changes: 204 additions & 0 deletions Source/Csla.Blazor.WebAssembly.Tests/SessionManagerTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Threading;
using System.Threading.Tasks;
using Csla.Blazor.WebAssembly.State;
using Csla.State;
using Moq;
using System.Net.Http;
using Csla.Blazor.WebAssembly.Configuration;
using Csla.Core;
using Csla.Runtime;
using Moq.Protected;
using System.Net;
using Csla.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using Csla.Serialization.Mobile;
using Microsoft.AspNetCore.Components.Authorization;

namespace Csla.Test.State
{
[TestClass]
public class SessionManagerTests
{
private SessionManager _sessionManager;
private SessionMessage SessionValue;

[TestInitialize]
public void Initialize()
{
var mockServiceProvider = new Mock<IServiceProvider>();

// Mock AuthenticationStateProvider
var mockAuthStateProvider = new Mock<AuthenticationStateProvider>();

// Mock IServiceProvider
mockServiceProvider.Setup(x => x.GetService(typeof(AuthenticationStateProvider))).Returns(mockAuthStateProvider.Object);

SessionValue = new SessionMessage
{
// Set properties here
// For example:
Principal = new Security.CslaClaimsPrincipal() { },
Session = []
};

// Mock ISerializationFormatter
var mockFormatter = new Mock<ISerializationFormatter>();
mockFormatter.Setup(x => x.Serialize(It.IsAny<Stream>(), It.IsAny<object>()));
mockFormatter.Setup(x => x.Deserialize(It.IsAny<Stream>())).Returns(SessionValue);

// Mock IServiceProvider
mockServiceProvider.Setup(x => x.GetService(typeof(Csla.Serialization.Mobile.MobileFormatter))).Returns(mockFormatter.Object);

var mockActivator = new Mock<Csla.Server.IDataPortalActivator>();
mockActivator.Setup(x => x.CreateInstance(It.Is<Type>(t => t == typeof(Csla.Serialization.Mobile.MobileFormatter)))).Returns(mockFormatter.Object);
mockActivator.Setup(x => x.InitializeInstance(It.IsAny<object>()));

// Mock IServiceProvider
mockServiceProvider.Setup(x => x.GetService(typeof(Csla.Server.IDataPortalActivator))).Returns(mockActivator.Object);

// Mock IServiceProvider

// Mock IContextManager
var mockContextManager = new Mock<IContextManager>();
mockContextManager.Setup(x => x.IsValid).Returns(true);

// Mock IContextManagerLocal
var mockLocalContextManager = new Mock<IContextManagerLocal>();

// Mock IServiceProvider
mockServiceProvider.Setup(x => x.GetService(typeof(IRuntimeInfo))).Returns(new RuntimeInfo());

// Mock IEnumerable<IContextManager>
var mockContextManagerList = new List<IContextManager> { mockContextManager.Object };

// Mock ApplicationContextAccessor
var mockApplicationContextAccessor = new Mock<ApplicationContextAccessor>(mockContextManagerList, mockLocalContextManager.Object, mockServiceProvider.Object);

var _applicationContext = new ApplicationContext(mockApplicationContextAccessor.Object);


_sessionManager = new SessionManager(_applicationContext, GetHttpClient(SessionValue, _applicationContext), new BlazorWebAssemblyConfigurationOptions { SyncContextWithServer = true });
_sessionManager.RetrieveSession(TimeSpan.FromHours(1));
}

private static HttpClient GetHttpClient(SessionMessage session, ApplicationContext _applicationContext)
{
var handlerMock = new Mock<HttpMessageHandler>(MockBehavior.Strict);
handlerMock
.Protected()
// Setup the PROTECTED method to mock
.Setup<Task<HttpResponseMessage>>(
"SendAsync",
ItExpr.IsAny<HttpRequestMessage>(),
ItExpr.IsAny<CancellationToken>()
)
// prepare the expected response of the mocked http call
.ReturnsAsync((HttpRequestMessage request, CancellationToken cancellationToken) =>
{


if (cancellationToken.IsCancellationRequested)
{
throw new OperationCanceledException(cancellationToken);
}
else
{
return new HttpResponseMessage()
{
StatusCode = HttpStatusCode.OK,
Content = new StringContent("{\"ResultStatus\":0, \"SessionData\":\"" + Convert.ToBase64String(GetSession(session, _applicationContext)) + "\"}"),
};
}
})
.Verifiable();

// use real http client with mocked handler here
var httpClient = new HttpClient(handlerMock.Object)
{
BaseAddress = new Uri("http://test.com/"),
};
return httpClient;
}

private static byte[] GetSession(SessionMessage session, ApplicationContext _applicationContext)
{
var info = new MobileFormatter(_applicationContext).SerializeToDTO(session);
var ms = new MemoryStream();
new CslaBinaryWriter(_applicationContext).Write(ms, info);
ms.Position = 0;
var array = ms.ToArray();
return array;
}

[TestMethod]
public async Task RetrieveSession_WithTimeoutValue_ShouldNotThrowException()
{
var timeout = TimeSpan.FromHours(1);
var session = await _sessionManager.RetrieveSession(timeout);
Assert.AreEqual(session, SessionValue.Session);
}

[TestMethod]
public async Task RetrieveSession_WithZeroTimeout_ShouldThrowTimeoutException()
{
var timeout = TimeSpan.Zero;
await Assert.ThrowsExceptionAsync<TimeoutException>(() => _sessionManager.RetrieveSession(timeout));
}

[TestMethod]
public async Task RetrieveSession_WithValidCancellationToken_ShouldNotThrowException()
{
var cts = new CancellationTokenSource();
var session = await _sessionManager.RetrieveSession(cts.Token);
Assert.AreEqual(session, SessionValue.Session);
}

[TestMethod]
public async Task RetrieveSession_WithCancelledCancellationToken_ShouldThrowTaskCanceledException()
{
var cts = new CancellationTokenSource();
cts.Cancel();
await Assert.ThrowsExceptionAsync<TaskCanceledException>(() => _sessionManager.RetrieveSession(cts.Token));
}

[TestMethod]
public async Task SendSession_WithTimeoutValue_ShouldNotThrowException()
{
var timeout = TimeSpan.FromHours(1);
await _sessionManager.SendSession(timeout);
Assert.IsTrue(true);
}

[TestMethod]
public async Task SendSession_WithZeroTimeout_ShouldThrowTimeoutException()
{
var timeout = TimeSpan.Zero;
await Assert.ThrowsExceptionAsync<TimeoutException>(() => _sessionManager.SendSession(timeout));
}

[TestMethod]
public async Task SendSession_WithValidCancellationToken_ShouldNotThrowException()
{
var cts = new CancellationTokenSource();
await _sessionManager.SendSession(cts.Token);
Assert.IsTrue(true);
}

[TestMethod]
public async Task SendSession_WithCancelledCancellationToken_ShouldThrowTaskCanceledException()
{
var cts = new CancellationTokenSource();
cts.Cancel();
await Assert.ThrowsExceptionAsync<TaskCanceledException>(() => _sessionManager.SendSession(cts.Token));
}


[TestMethod]
public void RetrieveCAchedSessionSession()
{
_sessionManager.GetCachedSession();
}
}
}
51 changes: 46 additions & 5 deletions Source/Csla.Blazor.WebAssembly/State/SessionManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
using Csla.State;
using Microsoft.AspNetCore.Components.Authorization;
using Csla.Blazor.State.Messages;
using System.Reflection;
using System.Threading;

namespace Csla.Blazor.WebAssembly.State
{
Expand Down Expand Up @@ -44,16 +46,32 @@ public Session GetCachedSession()
/// the web server to the wasm client
/// if SyncContextWithServer is true.
/// </summary>
public async Task<Session> RetrieveSession()
public async Task<Session> RetrieveSession(TimeSpan timeout)
{
try
{
return await RetrieveSession(GetCancellationToken(timeout));
}
catch (TaskCanceledException tcex)
{
throw new TimeoutException($"{this.GetType().FullName}.{nameof(RetrieveSession)}.", tcex);
}
}

/// <summary>
/// Retrieves the current user's session from
/// the web server to the wasm client
/// if SyncContextWithServer is true.
/// </summary>
public async Task<Session> RetrieveSession(CancellationToken ct)
{
if (_options.SyncContextWithServer)
{
long lastTouched = 0;
if (_session != null)
lastTouched = _session.LastTouched;
var url = $"{_options.StateControllerName}?lastTouched={lastTouched}";

var stateResult = await client.GetFromJsonAsync<StateResult>(url);
var stateResult = await client.GetFromJsonAsync<StateResult>(url, ct);
if (stateResult.ResultStatus == ResultStatuses.Success)
{
var formatter = SerializationFormatterFactory.GetFormatter(ApplicationContext);
Expand Down Expand Up @@ -86,7 +104,30 @@ public async Task<Session> RetrieveSession()
/// the wasm client to the web server
/// if SyncContextWithServer is true.
/// </summary>
public async Task SendSession()
public async Task SendSession(TimeSpan timeout)
{
try
{
await SendSession(GetCancellationToken(timeout));
}
catch (TaskCanceledException tcex)
{
throw new TimeoutException($"{this.GetType().FullName}.{nameof(SendSession)}.", tcex);
}
}

private static CancellationToken GetCancellationToken(TimeSpan timeout)
{
var cts = new CancellationTokenSource(timeout);
return cts.Token;
}

/// <summary>
/// Sends the current user's session from
/// the wasm client to the web server
/// if SyncContextWithServer is true.
/// </summary>
public async Task SendSession(CancellationToken ct)
{
_session.Touch();
if (_options.SyncContextWithServer)
Expand All @@ -95,7 +136,7 @@ public async Task SendSession()
var buffer = new MemoryStream();
formatter.Serialize(buffer, _session);
buffer.Position = 0;
await client.PutAsJsonAsync<byte[]>(_options.StateControllerName, buffer.ToArray());
await client.PutAsJsonAsync<byte[]>(_options.StateControllerName, buffer.ToArray(), ct);
}
}

Expand Down
7 changes: 3 additions & 4 deletions Source/Csla.Blazor/State/StateManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,9 @@ public Task InitializeAsync(TimeSpan timeout)
/// <param name="timeout">Time to wait before timing out</param>
private async Task GetState(TimeSpan timeout)
{
Session session;
var isBrowser = OperatingSystem.IsBrowser();
if (isBrowser)
session = await _sessionManager.RetrieveSession();
_ = await _sessionManager.RetrieveSession(timeout);
}

/// <summary>
Expand All @@ -57,12 +56,12 @@ private async Task GetState(TimeSpan timeout)
/// at which you know the user is navigating to another
/// page.
/// </remarks>
public async Task SaveState()
public async Task SaveState(TimeSpan timeout)
{
var isBrowser = OperatingSystem.IsBrowser();
if (isBrowser)
{
await _sessionManager.SendSession();
await _sessionManager.SendSession(timeout);
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions Source/Csla/State/ISessionManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public interface ISessionManager
/// Retrieves the current user's session from
/// the web server to the wasm client.
/// </summary>
Task<Session> RetrieveSession();
Task<Session> RetrieveSession(TimeSpan timeout);
/// <summary>
/// Gets the current user's session from the cache.
/// </summary>
Expand All @@ -27,7 +27,7 @@ public interface ISessionManager
/// Sends the current user's session from
/// the wasm client to the web server.
/// </summary>
Task SendSession();
Task SendSession(TimeSpan timeout);
#endregion

#region Server
Expand Down
Loading