File: TempData\SessionStorageTempDataProviderTest.cs
Web Access
Project: src\src\Components\Endpoints\test\Microsoft.AspNetCore.Components.Endpoints.Tests.csproj (Microsoft.AspNetCore.Components.Endpoints.Tests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.Logging.Abstractions;
 
namespace Microsoft.AspNetCore.Components.Endpoints;
 
public class SessionStorageTempDataProviderTest
{
    private readonly SessionStorageTempDataProvider _sessionStateTempDataProvider;
 
    internal TempData CreateTempData()
    {
        return new TempData(() => new Dictionary<string, object>());
    }
 
    public SessionStorageTempDataProviderTest()
    {
        _sessionStateTempDataProvider = new SessionStorageTempDataProvider(
            new JsonTempDataSerializer(),
            NullLogger<SessionStorageTempDataProvider>.Instance);
    }
 
    [Fact]
    public void Load_ReturnsEmptyTempData_WhenNoSessionDataExists()
    {
        var httpContext = CreateHttpContext();
        var tempData = _sessionStateTempDataProvider.LoadTempData(httpContext);
 
        Assert.NotNull(tempData);
        Assert.Empty(tempData);
    }
 
    [Fact]
    public void Save_RemovesSessionKey_WhenNoDataToSave()
    {
        var httpContext = CreateHttpContext();
        var session = (TestSession)httpContext.Session;
        session.Set(SessionStorageTempDataProvider.TempDataSessionStateKey, new byte[] { 1, 2, 3 });
 
        var tempData = CreateTempData();
        _sessionStateTempDataProvider.SaveTempData(httpContext, tempData.Save());
 
        Assert.DoesNotContain(SessionStorageTempDataProvider.TempDataSessionStateKey, session.Keys);
    }
 
    [Fact]
    public void Save_SetsSessionData_WhenDataExists()
    {
        var httpContext = CreateHttpContext();
        var tempData = CreateTempData();
        tempData["Key1"] = "Value1";
 
        _sessionStateTempDataProvider.SaveTempData(httpContext, tempData.Save());
 
        var session = (TestSession)httpContext.Session;
        Assert.Contains(SessionStorageTempDataProvider.TempDataSessionStateKey, session.Keys);
    }
 
    [Fact]
    public void Save_ThrowsForUnsupportedType()
    {
        var httpContext = CreateHttpContext();
        var tempData = CreateTempData();
        tempData["Key"] = new object();
 
        Assert.Throws<InvalidOperationException>(() => _sessionStateTempDataProvider.SaveTempData(httpContext, tempData.Save()));
    }
 
    [Fact]
    public void Load_ReturnsEmptyTempData_WhenSessionThrows()
    {
        var httpContext = CreateHttpContext(throwOnSessionAccess: true);
        var tempData = _sessionStateTempDataProvider.LoadTempData(httpContext);
 
        Assert.NotNull(tempData);
        Assert.Empty(tempData);
    }
 
    [Fact]
    public void Load_ReturnsEmptyTempData_WhenSessionContainsInvalidJson()
    {
        var httpContext = CreateHttpContext();
        var session = (TestSession)httpContext.Session;
        session.Set(SessionStorageTempDataProvider.TempDataSessionStateKey, "not valid json"u8.ToArray());
 
        var tempData = _sessionStateTempDataProvider.LoadTempData(httpContext);
 
        Assert.NotNull(tempData);
        Assert.Empty(tempData);
    }
 
    [Fact]
    public void RoundTrip_SaveAndLoad_WorksCorrectly()
    {
        var httpContext = CreateHttpContext();
        var tempData = CreateTempData();
        tempData["StringKey"] = "StringValue";
        tempData["IntKey"] = 42;
 
        _sessionStateTempDataProvider.SaveTempData(httpContext, tempData.Save());
        var loadedTempData = _sessionStateTempDataProvider.LoadTempData(httpContext);
 
        Assert.Equal("StringValue", loadedTempData["StringKey"]);
        Assert.Equal(42, loadedTempData["IntKey"]);
    }
 
    private static DefaultHttpContext CreateHttpContext(bool throwOnSessionAccess = false)
    {
        var httpContext = new DefaultHttpContext();
        httpContext.Request.Scheme = "https";
        httpContext.Request.Host = new HostString("localhost");
 
        var sessionFeature = new TestSessionFeature(throwOnSessionAccess);
        httpContext.Features.Set<ISessionFeature>(sessionFeature);
 
        return httpContext;
    }
 
    private class TestSessionFeature : ISessionFeature
    {
        private readonly bool _throwOnAccess;
 
        public TestSessionFeature(bool throwOnAccess = false)
        {
            _throwOnAccess = throwOnAccess;
            Session = new TestSession();
        }
 
        public ISession Session
        {
            get => _throwOnAccess ? throw new InvalidOperationException("Session not configured") : field;
            set;
        }
    }
 
    private class TestSession : ISession
    {
        private readonly Dictionary<string, byte[]> _store = new();
 
        public string Id => "test-session-id";
        public bool IsAvailable => true;
        public IEnumerable<string> Keys => _store.Keys;
 
        public void Clear() => _store.Clear();
 
        public Task CommitAsync(CancellationToken cancellationToken = default) => Task.CompletedTask;
 
        public Task LoadAsync(CancellationToken cancellationToken = default) => Task.CompletedTask;
 
        public void Remove(string key) => _store.Remove(key);
 
        public void Set(string key, byte[] value) => _store[key] = value;
 
        public bool TryGetValue(string key, out byte[] value) => _store.TryGetValue(key, out value!);
    }
}