// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; using Xunit; namespace Microsoft.AspNetCore.Mvc.ViewFeatures { public class SessionStateTempDataProviderTest { [Fact] public void Load_ThrowsException_WhenSessionIsNotEnabled() { // Arrange var testProvider = new SessionStateTempDataProvider(); // Act & Assert Assert.Throws(() => { testProvider.LoadTempData(GetHttpContext(sessionEnabled: false)); }); } [Fact] public void Save_ThrowsException_WhenSessionIsNotEnabled() { // Arrange var testProvider = new SessionStateTempDataProvider(); var values = new Dictionary(); values.Add("key1", "value1"); // Act & Assert Assert.Throws(() => { testProvider.SaveTempData(GetHttpContext(sessionEnabled: false), values); }); } [Fact] public void Load_ReturnsEmptyDictionary_WhenNoSessionDataIsAvailable() { // Arrange var testProvider = new SessionStateTempDataProvider(); // Act var tempDataDictionary = testProvider.LoadTempData(GetHttpContext()); // Assert Assert.Empty(tempDataDictionary); } [Fact] public void Load_ReturnsEmptyDictionary_WhenSessionDataIsEmpty() { // Arrange var testProvider = new SessionStateTempDataProvider(); var httpContext = GetHttpContext(); httpContext.Session.Set(SessionStateTempDataProvider.TempDataSessionStateKey, new byte[] { }); // Act var tempDataDictionary = testProvider.LoadTempData(httpContext); // Assert Assert.Empty(tempDataDictionary); } public static TheoryData InvalidTypes { get { return new TheoryData { { new object(), typeof(object) }, { new object[3], typeof(object) }, { new TestItem(), typeof(TestItem) }, { new List(), typeof(TestItem) }, { new Dictionary(), typeof(TestItem) }, }; } } [Theory] [MemberData(nameof(InvalidTypes))] public void EnsureObjectCanBeSerialized_ThrowsException_OnInvalidType(object value, Type type) { // Arrange var testProvider = new SessionStateTempDataProvider(); // Act & Assert var exception = Assert.Throws(() => { testProvider.EnsureObjectCanBeSerialized(value); }); Assert.Equal($"The '{typeof(SessionStateTempDataProvider).FullName}' cannot serialize " + $"an object of type '{type}' to session state.", exception.Message); } public static TheoryData InvalidDictionaryTypes { get { return new TheoryData { { new Dictionary(), typeof(int) }, { new Dictionary(), typeof(Uri) }, { new Dictionary(), typeof(object) }, { new Dictionary(), typeof(TestItem) } }; } } [Theory] [MemberData(nameof(InvalidDictionaryTypes))] public void EnsureObjectCanBeSerialized_ThrowsException_OnInvalidDictionaryType(object value, Type type) { // Arrange var testProvider = new SessionStateTempDataProvider(); // Act & Assert var exception = Assert.Throws(() => { testProvider.EnsureObjectCanBeSerialized(value); }); Assert.Equal($"The '{typeof(SessionStateTempDataProvider).FullName}' cannot serialize a dictionary " + $"with a key of type '{type}' to session state.", exception.Message); } public static TheoryData ValidTypes { get { return new TheoryData { { 10 }, { new int[]{ 10, 20 } }, { "FooValue" }, { new Uri("http://Foo") }, { Guid.NewGuid() }, { new List { "foo", "bar" } }, { new DateTimeOffset() }, { 100.1m }, { new Dictionary() }, { new Uri[] { new Uri("http://Foo"), new Uri("http://Bar") } }, { DayOfWeek.Sunday }, }; } } [Theory] [MemberData(nameof(ValidTypes))] public void EnsureObjectCanBeSerialized_DoesNotThrow_OnValidType(object value) { // Arrange var testProvider = new SessionStateTempDataProvider(); // Act & Assert (Does not throw) testProvider.EnsureObjectCanBeSerialized(value); } [Fact] public void SaveAndLoad_StringCanBeStoredAndLoaded() { // Arrange var testProvider = new SessionStateTempDataProvider(); var input = new Dictionary { { "string", "value" } }; var context = GetHttpContext(); // Act testProvider.SaveTempData(context, input); var TempData = testProvider.LoadTempData(context); // Assert var stringVal = Assert.IsType(TempData["string"]); Assert.Equal("value", stringVal); } [Theory] [InlineData(10)] [InlineData(int.MaxValue)] [InlineData(int.MinValue)] public void SaveAndLoad_IntCanBeStoredAndLoaded(int expected) { // Arrange var testProvider = new SessionStateTempDataProvider(); var input = new Dictionary { { "int", expected } }; var context = GetHttpContext(); // Act testProvider.SaveTempData(context, input); var TempData = testProvider.LoadTempData(context); // Assert var intVal = Assert.IsType(TempData["int"]); Assert.Equal(expected, intVal); } [Theory] [InlineData(false)] [InlineData(true)] public void SaveAndLoad_BoolCanBeStoredAndLoaded(bool value) { // Arrange var testProvider = new SessionStateTempDataProvider(); var input = new Dictionary { { "bool", value } }; var context = GetHttpContext(); // Act testProvider.SaveTempData(context, input); var TempData = testProvider.LoadTempData(context); // Assert var boolVal = Assert.IsType(TempData["bool"]); Assert.Equal(value, boolVal); } [Fact] public void SaveAndLoad_DateTimeCanBeStoredAndLoaded() { // Arrange var testProvider = new SessionStateTempDataProvider(); var inputDatetime = new DateTime(2010, 12, 12, 1, 2, 3, DateTimeKind.Local); var input = new Dictionary { { "DateTime", inputDatetime } }; var context = GetHttpContext(); // Act testProvider.SaveTempData(context, input); var TempData = testProvider.LoadTempData(context); // Assert var datetime = Assert.IsType(TempData["DateTime"]); Assert.Equal(inputDatetime, datetime); } [Fact] public void SaveAndLoad_GuidCanBeStoredAndLoaded() { // Arrange var testProvider = new SessionStateTempDataProvider(); var inputGuid = Guid.NewGuid(); var input = new Dictionary { { "Guid", inputGuid } }; var context = GetHttpContext(); // Act testProvider.SaveTempData(context, input); var TempData = testProvider.LoadTempData(context); // Assert var guidVal = Assert.IsType(TempData["Guid"]); Assert.Equal(inputGuid, guidVal); } [Fact] public void SaveAndLoad_EnumCanBeSavedAndLoaded() { // Arrange var key = "EnumValue"; var testProvider = new SessionStateTempDataProvider(); var expected = DayOfWeek.Friday; var input = new Dictionary { { key, expected } }; var context = GetHttpContext(); // Act testProvider.SaveTempData(context, input); var TempData = testProvider.LoadTempData(context); var result = TempData[key]; // Assert var actual = (DayOfWeek)result; Assert.Equal(expected, actual); } [Theory] [InlineData(3100000000L)] [InlineData(-3100000000L)] public void SaveAndLoad_LongCanBeSavedAndLoaded(long expected) { // Arrange var key = "LongValue"; var testProvider = new SessionStateTempDataProvider(); var input = new Dictionary { { key, expected } }; var context = GetHttpContext(); // Act testProvider.SaveTempData(context, input); var TempData = testProvider.LoadTempData(context); var result = TempData[key]; // Assert var actual = Assert.IsType(result); Assert.Equal(expected, actual); } [Fact] public void SaveAndLoad_ListCanBeStoredAndLoaded() { // Arrange var testProvider = new SessionStateTempDataProvider(); var input = new Dictionary { { "List`string", new List { "one", "two" } } }; var context = GetHttpContext(); // Act testProvider.SaveTempData(context, input); var TempData = testProvider.LoadTempData(context); // Assert var list = (IList)TempData["List`string"]; Assert.Equal(2, list.Count); Assert.Equal("one", list[0]); Assert.Equal("two", list[1]); } [Fact] public void SaveAndLoad_DictionaryCanBeStoredAndLoaded() { // Arrange var testProvider = new SessionStateTempDataProvider(); var inputDictionary = new Dictionary { { "Hello", "World" }, }; var input = new Dictionary { { "Dictionary", inputDictionary } }; var context = GetHttpContext(); // Act testProvider.SaveTempData(context, input); var TempData = testProvider.LoadTempData(context); // Assert var dictionary = Assert.IsType>(TempData["Dictionary"]); Assert.Equal("World", dictionary["Hello"]); } [Fact] public void SaveAndLoad_EmptyDictionary_RoundTripsAsNull() { // Arrange var testProvider = new SessionStateTempDataProvider(); var input = new Dictionary { { "EmptyDictionary", new Dictionary() } }; var context = GetHttpContext(); // Act testProvider.SaveTempData(context, input); var TempData = testProvider.LoadTempData(context); // Assert var emptyDictionary = (IDictionary)TempData["EmptyDictionary"]; Assert.Null(emptyDictionary); } [Fact] public void SaveAndLoad_NullValue_RoundTripsSuccessfully() { // Arrange var testProvider = new SessionStateTempDataProvider(); var input = new Dictionary { { "NullKey", null } }; var context = GetHttpContext(); // Act testProvider.SaveTempData(context, input); var TempData = testProvider.LoadTempData(context); // Assert Assert.True(TempData.ContainsKey("NullKey")); Assert.Null(TempData["NullKey"]); } private class TestItem { public int DummyInt { get; set; } } private HttpContext GetHttpContext(bool sessionEnabled = true) { var httpContext = new DefaultHttpContext(); if (sessionEnabled) { httpContext.Features.Set(new SessionFeature() { Session = new TestSession() }); } return httpContext; } private class SessionFeature : ISessionFeature { public ISession Session { get; set; } } private class TestSession : ISession { private Dictionary _innerDictionary = new Dictionary(); public IEnumerable Keys { get { return _innerDictionary.Keys; } } public string Id => "TestId"; public bool IsAvailable { get; } = true; public Task LoadAsync() { return Task.FromResult(0); } public Task CommitAsync() { return Task.FromResult(0); } public void Clear() { _innerDictionary.Clear(); } public void Remove(string key) { _innerDictionary.Remove(key); } public void Set(string key, byte[] value) { _innerDictionary[key] = value.ToArray(); } public bool TryGetValue(string key, out byte[] value) { return _innerDictionary.TryGetValue(key, out value); } } } }