diff --git a/MusicStore.sln b/MusicStore.sln
index c17a8f8213..0deb72a72c 100644
--- a/MusicStore.sln
+++ b/MusicStore.sln
@@ -24,7 +24,9 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "MusicStore", "src\MusicStor
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "MusicStore.Spa", "src\MusicStore.Spa\MusicStore.Spa.kproj", "{93891170-A8D5-46FD-A291-40F90CF258C2}"
EndProject
-Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "MusicStore.Test", "test\MusicStore.Test\MusicStore.Test.kproj", "{BECA766F-093E-4D08-9530-2BFA63C5BF93}"
+Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "MusicStore.Test", "test\MusicStore.Test\MusicStore.Test.kproj", "{CA663205-77DE-4E55-B300-85594181B5A9}"
+EndProject
+Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "MusicStore.Spa.Test", "test\MusicStore.Spa.Test\MusicStore.Spa.Test.kproj", "{9D3326C4-1F12-4526-9F25-712A1463B3FA}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -86,18 +88,30 @@ Global
{93891170-A8D5-46FD-A291-40F90CF258C2}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{93891170-A8D5-46FD-A291-40F90CF258C2}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{93891170-A8D5-46FD-A291-40F90CF258C2}.Release|x86.ActiveCfg = Release|Any CPU
- {BECA766F-093E-4D08-9530-2BFA63C5BF93}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {BECA766F-093E-4D08-9530-2BFA63C5BF93}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {BECA766F-093E-4D08-9530-2BFA63C5BF93}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
- {BECA766F-093E-4D08-9530-2BFA63C5BF93}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
- {BECA766F-093E-4D08-9530-2BFA63C5BF93}.Debug|x86.ActiveCfg = Debug|Any CPU
- {BECA766F-093E-4D08-9530-2BFA63C5BF93}.Debug|x86.Build.0 = Debug|Any CPU
- {BECA766F-093E-4D08-9530-2BFA63C5BF93}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {BECA766F-093E-4D08-9530-2BFA63C5BF93}.Release|Any CPU.Build.0 = Release|Any CPU
- {BECA766F-093E-4D08-9530-2BFA63C5BF93}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
- {BECA766F-093E-4D08-9530-2BFA63C5BF93}.Release|Mixed Platforms.Build.0 = Release|Any CPU
- {BECA766F-093E-4D08-9530-2BFA63C5BF93}.Release|x86.ActiveCfg = Release|Any CPU
- {BECA766F-093E-4D08-9530-2BFA63C5BF93}.Release|x86.Build.0 = Release|Any CPU
+ {CA663205-77DE-4E55-B300-85594181B5A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CA663205-77DE-4E55-B300-85594181B5A9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CA663205-77DE-4E55-B300-85594181B5A9}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {CA663205-77DE-4E55-B300-85594181B5A9}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {CA663205-77DE-4E55-B300-85594181B5A9}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {CA663205-77DE-4E55-B300-85594181B5A9}.Debug|x86.Build.0 = Debug|Any CPU
+ {CA663205-77DE-4E55-B300-85594181B5A9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CA663205-77DE-4E55-B300-85594181B5A9}.Release|Any CPU.Build.0 = Release|Any CPU
+ {CA663205-77DE-4E55-B300-85594181B5A9}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {CA663205-77DE-4E55-B300-85594181B5A9}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {CA663205-77DE-4E55-B300-85594181B5A9}.Release|x86.ActiveCfg = Release|Any CPU
+ {CA663205-77DE-4E55-B300-85594181B5A9}.Release|x86.Build.0 = Release|Any CPU
+ {9D3326C4-1F12-4526-9F25-712A1463B3FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {9D3326C4-1F12-4526-9F25-712A1463B3FA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {9D3326C4-1F12-4526-9F25-712A1463B3FA}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {9D3326C4-1F12-4526-9F25-712A1463B3FA}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {9D3326C4-1F12-4526-9F25-712A1463B3FA}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {9D3326C4-1F12-4526-9F25-712A1463B3FA}.Debug|x86.Build.0 = Debug|Any CPU
+ {9D3326C4-1F12-4526-9F25-712A1463B3FA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {9D3326C4-1F12-4526-9F25-712A1463B3FA}.Release|Any CPU.Build.0 = Release|Any CPU
+ {9D3326C4-1F12-4526-9F25-712A1463B3FA}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {9D3326C4-1F12-4526-9F25-712A1463B3FA}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {9D3326C4-1F12-4526-9F25-712A1463B3FA}.Release|x86.ActiveCfg = Release|Any CPU
+ {9D3326C4-1F12-4526-9F25-712A1463B3FA}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -108,6 +122,7 @@ Global
{A319ACCE-060B-4385-9534-9F2202F6180E} = {363D2681-31A6-48C9-90BB-9ACFF4A41F06}
{3CFBED5D-2ED8-49DB-96FB-BDAA748DC5A0} = {B7B176B6-8D4D-4EF1-BBD2-DDA650C78FFF}
{93891170-A8D5-46FD-A291-40F90CF258C2} = {B7B176B6-8D4D-4EF1-BBD2-DDA650C78FFF}
- {BECA766F-093E-4D08-9530-2BFA63C5BF93} = {363D2681-31A6-48C9-90BB-9ACFF4A41F06}
+ {CA663205-77DE-4E55-B300-85594181B5A9} = {363D2681-31A6-48C9-90BB-9ACFF4A41F06}
+ {9D3326C4-1F12-4526-9F25-712A1463B3FA} = {363D2681-31A6-48C9-90BB-9ACFF4A41F06}
EndGlobalSection
EndGlobal
diff --git a/test/MusicStore.Spa.Test/MusicStore.Spa.Test.kproj b/test/MusicStore.Spa.Test/MusicStore.Spa.Test.kproj
new file mode 100644
index 0000000000..bdc0cfd13e
--- /dev/null
+++ b/test/MusicStore.Spa.Test/MusicStore.Spa.Test.kproj
@@ -0,0 +1,20 @@
+
+
+
+ 14.0
+ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
+
+
+
+
+ 9d3326c4-1f12-4526-9f25-712a1463b3fa
+ MusicStore.Spa.Test
+ ..\..\artifacts\obj\$(MSBuildProjectName)
+ ..\..\artifacts\bin\$(MSBuildProjectName)\
+
+
+
+ 2.0
+
+
+
\ No newline at end of file
diff --git a/test/MusicStore.Spa.Test/ShoppingCartTest.cs b/test/MusicStore.Spa.Test/ShoppingCartTest.cs
new file mode 100644
index 0000000000..0c1d64fbda
--- /dev/null
+++ b/test/MusicStore.Spa.Test/ShoppingCartTest.cs
@@ -0,0 +1,45 @@
+using Microsoft.AspNet.Http;
+using Microsoft.AspNet.Http.Core;
+using Microsoft.AspNet.Http.Core.Collections;
+using Xunit;
+
+namespace MusicStore.Models
+{
+ public class ShoppingCartTest
+ {
+ [Fact]
+ public void GetCartId_ReturnsCartIdFromCookies()
+ {
+ // Arrange
+ var cartId = "cartId_A";
+
+ var httpContext = new DefaultHttpContext();
+ httpContext.SetFeature(new CookiesFeature("Session=" + cartId));
+
+ var cart = new ShoppingCart(new MusicStoreContext());
+
+ // Act
+ var result = cart.GetCartId(httpContext);
+
+ // Assert
+ Assert.NotNull(result);
+ Assert.Equal(cartId, result);
+ }
+
+ private class CookiesFeature : IRequestCookiesFeature
+ {
+ private readonly RequestCookiesCollection _cookies;
+
+ public CookiesFeature(string cookiesHeader)
+ {
+ _cookies = new RequestCookiesCollection();
+ _cookies.Reparse(cookiesHeader);
+ }
+
+ public IReadableStringCollection Cookies
+ {
+ get { return _cookies; }
+ }
+ }
+ }
+}
diff --git a/test/MusicStore.Spa.Test/project.json b/test/MusicStore.Spa.Test/project.json
new file mode 100644
index 0000000000..048212b9bc
--- /dev/null
+++ b/test/MusicStore.Spa.Test/project.json
@@ -0,0 +1,17 @@
+{
+ "compilationOptions": {
+ "warningsAsErrors": "true"
+ },
+ "dependencies": {
+ "MusicStore.Spa": "",
+ "Microsoft.AspNet.Mvc" : "6.0.0-*",
+ "Microsoft.AspNet.Testing.Logging" : "1.0.0-*",
+ "xunit.runner.kre": "1.0.0-*"
+ },
+ "commands": {
+ "test": "xunit.runner.kre"
+ },
+ "frameworks": {
+ "aspnet50": { },
+ }
+}
\ No newline at end of file
diff --git a/test/MusicStore.Test/ShoppingCartControllerTest.cs b/test/MusicStore.Test/ShoppingCartControllerTest.cs
new file mode 100644
index 0000000000..61cd85a4e6
--- /dev/null
+++ b/test/MusicStore.Test/ShoppingCartControllerTest.cs
@@ -0,0 +1,172 @@
+using System;
+using System.Linq;
+using System.Threading.Tasks;
+using Microsoft.AspNet.Http;
+using Microsoft.AspNet.Http.Core;
+using Microsoft.AspNet.Http.Interfaces;
+using Microsoft.AspNet.Mvc;
+using Microsoft.AspNet.Session;
+using Microsoft.AspNet.Testing.Logging;
+using Microsoft.Framework.Cache.Distributed;
+using Microsoft.Framework.Cache.Memory;
+using Microsoft.Framework.DependencyInjection;
+using MusicStore.Models;
+using MusicStore.ViewModels;
+using Xunit;
+
+namespace MusicStore.Controllers
+{
+ public class ShoppingCartControllerTest
+ {
+ private readonly IServiceProvider _serviceProvider;
+
+ public ShoppingCartControllerTest()
+ {
+ var services = new ServiceCollection();
+
+ services.AddEntityFramework()
+ .AddInMemoryStore()
+ .AddDbContext();
+
+ _serviceProvider = services.BuildServiceProvider();
+ }
+
+ [Fact]
+ public async Task Index_ReturnsNoCartItems_WhenSessionEmpty()
+ {
+ // Arrange
+ var sessionFeature = new SessionFeature()
+ {
+ Session = CreateTestSession(),
+ };
+
+ var httpContext = new DefaultHttpContext();
+ httpContext.SetFeature(sessionFeature);
+
+ var controller = new ShoppingCartController()
+ {
+ DbContext = _serviceProvider.GetRequiredService(),
+ };
+ controller.ActionContext.HttpContext = httpContext;
+
+ // Act
+ var result = await controller.Index();
+
+ // Assert
+ var viewResult = Assert.IsType(result);
+ Assert.NotNull(viewResult.ViewData);
+ Assert.Null(viewResult.ViewName);
+
+ var model = Assert.IsType(viewResult.ViewData.Model);
+ Assert.Equal(0, model.CartItems.Count);
+ Assert.Equal(0, model.CartTotal);
+ }
+
+ [Fact]
+ public async Task Index_ReturnsNoCartItems_WhenNoItemsInCart()
+ {
+ // Arrange
+ var sessionFeature = new SessionFeature()
+ {
+ Session = CreateTestSession(),
+ };
+
+ var httpContext = new DefaultHttpContext();
+ httpContext.SetFeature(sessionFeature);
+ httpContext.Session.SetString("Session", "CartId_A");
+
+ var controller = new ShoppingCartController()
+ {
+ DbContext = _serviceProvider.GetRequiredService(),
+ };
+ controller.ActionContext.HttpContext = httpContext;
+
+ // Act
+ var result = await controller.Index();
+
+ // Assert
+ var viewResult = Assert.IsType(result);
+ Assert.NotNull(viewResult.ViewData);
+ Assert.Null(viewResult.ViewName);
+
+ var model = Assert.IsType(viewResult.ViewData.Model);
+ Assert.Equal(0, model.CartItems.Count);
+ Assert.Equal(0, model.CartTotal);
+ }
+
+ [Fact]
+ public async Task Index_ReturnsCartItems_WhenItemsInCart()
+ {
+ // Arrange
+ var cartId = "CartId_A";
+ var sessionFeature = new SessionFeature()
+ {
+ Session = CreateTestSession(),
+ };
+
+ var httpContext = new DefaultHttpContext();
+ httpContext.SetFeature(sessionFeature);
+ httpContext.Session.SetString("Session", cartId);
+
+ var dbContext = _serviceProvider.GetRequiredService();
+ var cartItems = CreateTestCartItems(
+ cartId,
+ itemPrice: 10,
+ numberOfItem: 5);
+ dbContext.AddRange(cartItems.Select(n => n.Album).Distinct());
+ dbContext.AddRange(cartItems);
+ dbContext.SaveChanges();
+
+ var controller = new ShoppingCartController()
+ {
+ DbContext = dbContext,
+ };
+ controller.ActionContext.HttpContext = httpContext;
+
+ // Act
+ var result = await controller.Index();
+
+ // Assert
+ var viewResult = Assert.IsType(result);
+ Assert.NotNull(viewResult.ViewData);
+ Assert.Null(viewResult.ViewName);
+
+ var model = Assert.IsType(viewResult.ViewData.Model);
+ Assert.Equal(5, model.CartItems.Count);
+ Assert.Equal(5 * 10, model.CartTotal);
+ }
+
+ private static ISession CreateTestSession()
+ {
+
+ return new DistributedSession(
+ new LocalCache(new MemoryCache(new MemoryCacheOptions())),
+ "sessionId_A",
+ idleTimeout: TimeSpan.MaxValue,
+ tryEstablishSession: () => true,
+ loggerFactory: new NullLoggerFactory(),
+ isNewSessionKey: true);
+ }
+
+ private static CartItem[] CreateTestCartItems(string cartId, decimal itemPrice, int numberOfItem)
+ {
+ var albums = Enumerable.Range(1, 10).Select(n =>
+ new Album()
+ {
+ AlbumId = n,
+ Price = itemPrice,
+ }).ToArray();
+
+ var cartItems = Enumerable.Range(1, numberOfItem).Select(n =>
+ new CartItem()
+ {
+ Count = 1,
+ CartId = cartId,
+ AlbumId = n,
+ Album = albums[n - 1],
+ }).ToArray();
+
+ return cartItems;
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/MusicStore.Test/project.json b/test/MusicStore.Test/project.json
index 28300d1ed5..35111a9dbb 100644
--- a/test/MusicStore.Test/project.json
+++ b/test/MusicStore.Test/project.json
@@ -5,6 +5,7 @@
"dependencies": {
"MusicStore": "",
"Microsoft.AspNet.Mvc" : "6.0.0-*",
+ "Microsoft.AspNet.Testing.Logging" : "1.0.0-*",
"xunit.runner.kre": "1.0.0-*"
},
"commands": {