Identity/MusicStore/ErrorPage test re-anabling

Fixes #10672 #7925 #10670

Also, moves MusicStore tests to SQLite for better reliability
This commit is contained in:
Arthur Vickers 2019-06-01 17:09:04 -07:00
parent 400835e0b4
commit 173cf1786e
21 changed files with 403 additions and 375 deletions

View File

@ -1,7 +1,6 @@
// 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.Data.SqlClient;
using Microsoft.AspNetCore.Builder.Internal;
using Microsoft.AspNetCore.Testing.xunit;
using Microsoft.Data.Sqlite;
@ -24,59 +23,73 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test
services
.AddSingleton<IConfiguration>(new ConfigurationBuilder().Build())
.AddDbContext<IdentityDbContext>(o =>
.AddDbContext<VerstappenDbContext>(o =>
o.UseSqlite(fixture.Connection)
.ConfigureWarnings(b => b.Log(CoreEventId.ManyServiceProvidersCreatedWarning)))
.AddIdentity<IdentityUser, IdentityRole>(o => o.Stores.MaxLengthForKeys = 128)
.AddEntityFrameworkStores<IdentityDbContext>();
.AddEntityFrameworkStores<VerstappenDbContext>();
services.AddLogging();
var provider = services.BuildServiceProvider();
_builder = new ApplicationBuilder(provider);
_builder = new ApplicationBuilder(services.BuildServiceProvider());
using (var scoped = provider.GetRequiredService<IServiceScopeFactory>().CreateScope())
using (var db = scoped.ServiceProvider.GetRequiredService<IdentityDbContext>())
using (var scope = _builder.ApplicationServices.GetRequiredService<IServiceScopeFactory>().CreateScope())
{
db.Database.EnsureCreated();
scope.ServiceProvider.GetRequiredService<VerstappenDbContext>().Database.EnsureCreated();
}
}
[ConditionalFact(Skip = "https://github.com/aspnet/AspNetCore/issues/7925")]
// Need a different context type here since EF model is changing by having MaxLengthForKeys
public class VerstappenDbContext : IdentityDbContext<IdentityUser, IdentityRole, string>
{
public VerstappenDbContext(DbContextOptions options)
: base(options)
{
}
}
[ConditionalFact]
[FrameworkSkipCondition(RuntimeFrameworks.Mono)]
[OSSkipCondition(OperatingSystems.Linux)]
[OSSkipCondition(OperatingSystems.MacOSX)]
public void EnsureDefaultSchema()
{
var db = _builder.ApplicationServices.GetRequiredService<IdentityDbContext>();
VerifyDefaultSchema(db);
using (var scope = _builder.ApplicationServices.GetRequiredService<IServiceScopeFactory>().CreateScope())
{
var db = scope.ServiceProvider.GetRequiredService<VerstappenDbContext>();
VerifyDefaultSchema(db);
}
}
private static void VerifyDefaultSchema(IdentityDbContext dbContext)
private static void VerifyDefaultSchema(VerstappenDbContext dbContext)
{
var sqlConn = dbContext.Database.GetDbConnection();
var sqlConn = (SqliteConnection)dbContext.Database.GetDbConnection();
using (var db = new SqliteConnection(sqlConn.ConnectionString))
try
{
db.Open();
Assert.True(DbUtil.VerifyColumns(db, "AspNetUsers", "Id", "UserName", "Email", "PasswordHash", "SecurityStamp",
sqlConn.Open();
Assert.True(DbUtil.VerifyColumns(sqlConn, "AspNetUsers", "Id", "UserName", "Email", "PasswordHash", "SecurityStamp",
"EmailConfirmed", "PhoneNumber", "PhoneNumberConfirmed", "TwoFactorEnabled", "LockoutEnabled",
"LockoutEnd", "AccessFailedCount", "ConcurrencyStamp", "NormalizedUserName", "NormalizedEmail"));
Assert.True(DbUtil.VerifyColumns(db, "AspNetRoles", "Id", "Name", "NormalizedName", "ConcurrencyStamp"));
Assert.True(DbUtil.VerifyColumns(db, "AspNetUserRoles", "UserId", "RoleId"));
Assert.True(DbUtil.VerifyColumns(db, "AspNetUserClaims", "Id", "UserId", "ClaimType", "ClaimValue"));
Assert.True(DbUtil.VerifyColumns(db, "AspNetUserLogins", "UserId", "ProviderKey", "LoginProvider", "ProviderDisplayName"));
Assert.True(DbUtil.VerifyColumns(db, "AspNetUserTokens", "UserId", "LoginProvider", "Name", "Value"));
Assert.True(DbUtil.VerifyColumns(sqlConn, "AspNetRoles", "Id", "Name", "NormalizedName", "ConcurrencyStamp"));
Assert.True(DbUtil.VerifyColumns(sqlConn, "AspNetUserRoles", "UserId", "RoleId"));
Assert.True(DbUtil.VerifyColumns(sqlConn, "AspNetUserClaims", "Id", "UserId", "ClaimType", "ClaimValue"));
Assert.True(DbUtil.VerifyColumns(sqlConn, "AspNetUserLogins", "UserId", "ProviderKey", "LoginProvider", "ProviderDisplayName"));
Assert.True(DbUtil.VerifyColumns(sqlConn, "AspNetUserTokens", "UserId", "LoginProvider", "Name", "Value"));
Assert.True(DbUtil.VerifyMaxLength(dbContext, "AspNetUsers", 256, "UserName", "Email", "NormalizedUserName", "NormalizedEmail"));
Assert.True(DbUtil.VerifyMaxLength(dbContext, "AspNetRoles", 256, "Name", "NormalizedName"));
Assert.True(DbUtil.VerifyMaxLength(dbContext, "AspNetUserLogins", 128, "LoginProvider", "ProviderKey"));
Assert.True(DbUtil.VerifyMaxLength(dbContext, "AspNetUserTokens", 128, "LoginProvider", "Name"));
DbUtil.VerifyIndex(db, "AspNetRoles", "RoleNameIndex", isUnique: true);
DbUtil.VerifyIndex(db, "AspNetUsers", "UserNameIndex", isUnique: true);
DbUtil.VerifyIndex(db, "AspNetUsers", "EmailIndex");
db.Close();
DbUtil.VerifyIndex(sqlConn, "AspNetRoles", "RoleNameIndex", isUnique: true);
DbUtil.VerifyIndex(sqlConn, "AspNetUsers", "UserNameIndex", isUnique: true);
DbUtil.VerifyIndex(sqlConn, "AspNetUsers", "EmailIndex");
}
finally
{
sqlConn.Close();
}
}
}

View File

@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Data.Common;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity.Test;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Xunit;
@ -105,10 +106,10 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test
public InkProtector() { }
public string Unprotect(string keyId, string data)
=> "ink";
=> data?.Substring(4);
public string Protect(string keyId, string data)
=> "ink";
=> "ink:" + data;
}
private class CustomUser : IdentityUser
@ -133,8 +134,8 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test
{
if (reader.Read())
{
Assert.Equal("Default:ink", reader.GetString(0));
return reader.GetString(0) == "Default:ink";
var value = reader.GetString(0);
return value.StartsWith("Default:ink:");
}
}
}
@ -155,8 +156,8 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test
{
if (reader.Read())
{
Assert.Equal("Default:ink", reader.GetString(0));
return reader.GetString(0) == "Default:ink";
var value = reader.GetString(0);
return value.StartsWith("Default:ink:");
}
}
}
@ -167,75 +168,103 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test
/// Test.
/// </summary>
/// <returns>Task</returns>
[Theory(Skip = "https://github.com/aspnet/AspNetCore/issues/7925")]
[InlineData(true)]
[InlineData(false)]
public async Task CustomPersonalDataPropertiesAreProtected(bool protect)
[Fact]
public Task CustomPersonalDataPropertiesCanBeProtected()
=> CustomPersonalDataPropertiesAreProtected<ProtectedIdentityDbContext>(true);
/// <summary>
/// Test.
/// </summary>
/// <returns>Task</returns>
[Fact]
public Task CustomPersonalDataPropertiesCanBeNotProtected()
=> CustomPersonalDataPropertiesAreProtected<UnprotectedIdentityDbContext>(false);
private async Task CustomPersonalDataPropertiesAreProtected<TContext>(bool protect)
where TContext : DbContext
{
using (var scratch = new ScratchDatabaseFixture())
using (var connection = new SqliteConnection($"DataSource=D{Guid.NewGuid()}.db"))
{
var services = new ServiceCollection().AddLogging();
services.AddIdentity<CustomUser, IdentityRole>(options =>
{
options.Stores.ProtectPersonalData = protect;
})
.AddEntityFrameworkStores<IdentityDbContext<CustomUser>>()
.AddEntityFrameworkStores<TContext>()
.AddPersonalDataProtection<InkProtector, DefaultKeyRing>();
var dbOptions = new DbContextOptionsBuilder().UseSqlite(scratch.Connection)
.UseApplicationServiceProvider(services.BuildServiceProvider())
.Options;
var dbContext = new IdentityDbContext<CustomUser>(dbOptions);
services.AddSingleton(dbContext);
dbContext.Database.EnsureCreated();
services.AddDbContext<TContext>(b => b.UseSqlite(connection));
var sp = services.BuildServiceProvider();
var manager = sp.GetService<UserManager<CustomUser>>();
var applicationServiceProvider = services.BuildServiceProvider();
var guid = Guid.NewGuid().ToString();
var user = new CustomUser();
user.Id = guid;
user.UserName = guid;
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
user.Email = "test@test.com";
user.PersonalData1 = "p1";
user.PersonalData2 = "p2";
user.NonPersonalData1 = "np1";
user.NonPersonalData2 = "np2";
user.SafePersonalData = "safe";
user.PhoneNumber = "12345678";
IdentityResultAssert.IsSuccess(await manager.UpdateAsync(user));
using (var scope = applicationServiceProvider.CreateScope())
{
var dbContext = scope.ServiceProvider.GetRequiredService<TContext>();
dbContext.Database.EnsureCreated();
IdentityResultAssert.IsSuccess(await manager.ResetAuthenticatorKeyAsync(user));
IdentityResultAssert.IsSuccess(await manager.SetAuthenticationTokenAsync(user, "loginProvider", "token", "value"));
var manager = scope.ServiceProvider.GetService<UserManager<CustomUser>>();
var conn = dbContext.Database.GetDbConnection();
conn.Open();
if (protect)
{
Assert.True(FindInk(conn, "PhoneNumber", guid));
Assert.True(FindInk(conn, "Email", guid));
Assert.True(FindInk(conn, "UserName", guid));
Assert.True(FindInk(conn, "PersonalData1", guid));
Assert.True(FindInk(conn, "PersonalData2", guid));
Assert.True(FindAuthenticatorKeyInk(conn, guid));
Assert.True(FindTokenInk(conn, guid, "loginProvider", "token"));
var guid = Guid.NewGuid().ToString();
var user = new CustomUser();
user.Id = guid;
user.UserName = guid;
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
user.Email = "test@test.com";
user.PersonalData1 = "p1";
user.PersonalData2 = "p2";
user.NonPersonalData1 = "np1";
user.NonPersonalData2 = "np2";
user.SafePersonalData = "safe";
user.PhoneNumber = "12345678";
IdentityResultAssert.IsSuccess(await manager.UpdateAsync(user));
IdentityResultAssert.IsSuccess(await manager.ResetAuthenticatorKeyAsync(user));
IdentityResultAssert.IsSuccess(await manager.SetAuthenticationTokenAsync(user, "loginProvider", "token", "value"));
connection.Open();
if (protect)
{
Assert.True(FindInk(connection, "PhoneNumber", guid));
Assert.True(FindInk(connection, "Email", guid));
Assert.True(FindInk(connection, "UserName", guid));
Assert.True(FindInk(connection, "PersonalData1", guid));
Assert.True(FindInk(connection, "PersonalData2", guid));
Assert.True(FindAuthenticatorKeyInk(connection, guid));
Assert.True(FindTokenInk(connection, guid, "loginProvider", "token"));
}
else
{
Assert.False(FindInk(connection, "PhoneNumber", guid));
Assert.False(FindInk(connection, "Email", guid));
Assert.False(FindInk(connection, "UserName", guid));
Assert.False(FindInk(connection, "PersonalData1", guid));
Assert.False(FindInk(connection, "PersonalData2", guid));
Assert.False(FindAuthenticatorKeyInk(connection, guid));
Assert.False(FindTokenInk(connection, guid, "loginProvider", "token"));
}
Assert.False(FindInk(connection, "NonPersonalData1", guid));
Assert.False(FindInk(connection, "NonPersonalData2", guid));
Assert.False(FindInk(connection, "SafePersonalData", guid));
connection.Close();
}
else
{
Assert.False(FindInk(conn, "PhoneNumber", guid));
Assert.False(FindInk(conn, "Email", guid));
Assert.False(FindInk(conn, "UserName", guid));
Assert.False(FindInk(conn, "PersonalData1", guid));
Assert.False(FindInk(conn, "PersonalData2", guid));
Assert.False(FindAuthenticatorKeyInk(conn, guid));
Assert.False(FindTokenInk(conn, guid, "loginProvider", "token"));
}
Assert.False(FindInk(conn, "NonPersonalData1", guid));
Assert.False(FindInk(conn, "NonPersonalData2", guid));
Assert.False(FindInk(conn, "SafePersonalData", guid));
}
}
conn.Close();
private class ProtectedIdentityDbContext : IdentityDbContext<CustomUser>
{
public ProtectedIdentityDbContext(DbContextOptions<ProtectedIdentityDbContext> options)
: base(options)
{
}
}
private class UnprotectedIdentityDbContext : IdentityDbContext<CustomUser>
{
public UnprotectedIdentityDbContext(DbContextOptions<UnprotectedIdentityDbContext> options)
: base(options)
{
}
}

View File

@ -258,7 +258,7 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test
}
}
[ConditionalFact(Skip = "TODO: Fix for new EF. Issue #10670")]
[ConditionalFact]
[FrameworkSkipCondition(RuntimeFrameworks.Mono)]
[OSSkipCondition(OperatingSystems.Linux)]
[OSSkipCondition(OperatingSystems.MacOSX)]

View File

@ -18,7 +18,7 @@ namespace Microsoft.AspNetCore.Diagnostics.FunctionalTests
public HttpClient Client { get; }
[ConditionalFact(Skip = "TODO: EF query interception for opening connection. Issue #10672")]
[ConditionalFact]
[OSSkipCondition(OperatingSystems.Linux)]
[OSSkipCondition(OperatingSystems.MacOSX)]
public async Task DatabaseErrorPage_ShowsError()

View File

@ -36,7 +36,6 @@ namespace MusicStore.Controllers
{
// Retrieve Genre genre and its Associated associated Albums albums from database
var genreModel = await DbContext.Genres
.Include(g => g.Albums)
.Where(g => g.Name == genre)
.FirstOrDefaultAsync();
@ -45,6 +44,8 @@ namespace MusicStore.Controllers
return NotFound();
}
await DbContext.Entry(genreModel).Collection(g => g.Albums).LoadAsync();
return View(genreModel);
}
@ -83,4 +84,4 @@ namespace MusicStore.Controllers
return View(album);
}
}
}
}

View File

@ -3,16 +3,13 @@ using System.Globalization;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.AspNetCore.Localization;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using MusicStore.Components;
using MusicStore.Mocks.Common;
@ -47,16 +44,9 @@ namespace MusicStore
services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));
// Add EF services to the services container
if (_platform.UseInMemoryStore)
{
services.AddDbContext<MusicStoreContext>(options =>
options.UseInMemoryDatabase("Scratch"));
}
else
{
services.AddDbContext<MusicStoreContext>(options =>
options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]));
}
// Add EF services to the services container
services.AddDbContext<MusicStoreContext>(options =>
options.UseSqlite("Data Source=MusicStore.db"));
// Add Identity services to the services container
services.AddIdentity<ApplicationUser, IdentityRole>()

View File

@ -1,20 +1,15 @@
using System;
using System.Globalization;
using Microsoft.AspNetCore.Authentication.OAuth;
using Microsoft.AspNetCore.Authentication.Twitter;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.AspNetCore.Localization;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using MusicStore.Components;
using MusicStore.Mocks.Common;
using MusicStore.Mocks.Facebook;
@ -50,16 +45,8 @@ namespace MusicStore
services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));
// Add EF services to the services container
if (_platform.UseInMemoryStore)
{
services.AddDbContext<MusicStoreContext>(options =>
options.UseInMemoryDatabase("Scratch"));
}
else
{
services.AddDbContext<MusicStoreContext>(options =>
options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]));
}
services.AddDbContext<MusicStoreContext>(options =>
options.UseSqlite("Data Source=MusicStore.db"));
// Add Identity services to the services container
services.AddIdentity<ApplicationUser, IdentityRole>()

View File

@ -115,17 +115,18 @@ namespace MusicStore.Models
.SumAsync();
}
public Task<decimal> GetTotal()
public async Task<decimal> GetTotal()
{
// Multiply album price by count of that album to get
// the current price for each of those albums in the cart
// sum all album price totals to get the cart total
return _dbContext
return (await _dbContext
.CartItems
.Where(c => c.CartId == _shoppingCartId)
.Select(c => c.Album.Price * c.Count)
.SumAsync();
.ToListAsync())
.Sum();
}
public async Task CreateOrder(Order order)

View File

@ -31,8 +31,7 @@
<Reference Include="Microsoft.AspNetCore.Server.IISIntegration" />
<Reference Include="Microsoft.AspNetCore.Session" />
<Reference Include="Microsoft.AspNetCore.StaticFiles" />
<Reference Include="Microsoft.EntityFrameworkCore.InMemory" />
<Reference Include="Microsoft.EntityFrameworkCore.SqlServer" />
<Reference Include="Microsoft.EntityFrameworkCore.Sqlite" />
<Reference Include="Microsoft.Extensions.Configuration.CommandLine" />
</ItemGroup>

View File

@ -3,12 +3,10 @@ using System.Runtime.InteropServices;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.AspNetCore.Localization;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using MusicStore.Components;
using MusicStore.Models;
@ -40,16 +38,8 @@ namespace MusicStore
services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));
// Add EF services to the services container
if (_platform.UseInMemoryStore)
{
services.AddDbContext<MusicStoreContext>(options =>
options.UseInMemoryDatabase("Scratch"));
}
else
{
services.AddDbContext<MusicStoreContext>(options =>
options.UseSqlServer(Configuration[StoreConfig.ConnectionStringKey.Replace("__", ":")]));
}
services.AddDbContext<MusicStoreContext>(options =>
options.UseSqlite("Data Source=MusicStore.db"));
// Add Identity services to the services container
services.AddIdentity<ApplicationUser, IdentityRole>()

View File

@ -57,7 +57,7 @@ namespace MusicStore
// Add EF services to the services container
services.AddDbContext<MusicStoreContext>(options =>
options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]));
options.UseSqlite("Data Source=MusicStore.db"));
// Add Identity services to the services container
services.AddIdentity<ApplicationUser, IdentityRole>()

View File

@ -2,12 +2,10 @@ using System.Globalization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.AspNetCore.Localization;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using MusicStore.Components;
using MusicStore.Models;
@ -57,16 +55,8 @@ namespace MusicStore
services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));
// Add EF services to the services container
if (_platform.UseInMemoryStore)
{
services.AddDbContext<MusicStoreContext>(options =>
options.UseInMemoryDatabase("Scratch"));
}
else
{
services.AddDbContext<MusicStoreContext>(options =>
options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]));
}
services.AddDbContext<MusicStoreContext>(options =>
options.UseSqlite("Data Source=MusicStore.db"));
// Add Identity services to the services container
services.AddIdentity<ApplicationUser, IdentityRole>()

View File

@ -1,33 +1,22 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewComponents;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using MusicStore.Controllers;
using MusicStore.Models;
using Xunit;
namespace MusicStore.Components
{
public class CartSummaryComponentTest
public class CartSummaryComponentTest : IClassFixture<SqliteInMemoryFixture>
{
private readonly IServiceProvider _serviceProvider;
private readonly SqliteInMemoryFixture _fixture;
public CartSummaryComponentTest()
public CartSummaryComponentTest(SqliteInMemoryFixture fixture)
{
var efServiceProvider = new ServiceCollection().AddEntityFrameworkInMemoryDatabase().BuildServiceProvider();
var services = new ServiceCollection();
services
.AddMemoryCache()
.AddLogging()
.AddDbContext<MusicStoreContext>(b => b.UseInMemoryDatabase("Scratch").UseInternalServiceProvider(efServiceProvider));
_serviceProvider = services.BuildServiceProvider();
_fixture = fixture;
_fixture.CreateDatabase();
}
[Fact]
@ -45,7 +34,7 @@ namespace MusicStore.Components
viewContext.HttpContext.Session.SetString("Session", cartId);
// DbContext initialization
var dbContext = _serviceProvider.GetRequiredService<MusicStoreContext>();
var dbContext = _fixture.Context;
PopulateData(dbContext, cartId, albumTitle: "AlbumA", itemCount: 10);
// CartSummaryComponent initialization
@ -68,10 +57,20 @@ namespace MusicStore.Components
private static void PopulateData(MusicStoreContext context, string cartId, string albumTitle, int itemCount)
{
var album = new Album()
var album = new Album
{
AlbumId = 1,
Title = albumTitle,
Artist = new Artist
{
ArtistId = 1,
Name = "Kung Fu Kenny"
},
Genre = new Genre
{
GenreId = 1,
Name = "Rap"
}
};
var cartItems = Enumerable.Range(1, itemCount).Select(n =>

View File

@ -6,7 +6,6 @@ using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Primitives;
@ -15,29 +14,21 @@ using Xunit;
namespace MusicStore.Controllers
{
public class CheckoutControllerTest
public class CheckoutControllerTest : IClassFixture<SqliteInMemoryFixture>
{
private readonly IServiceProvider _serviceProvider;
private readonly SqliteInMemoryFixture _fixture;
public CheckoutControllerTest()
public CheckoutControllerTest(SqliteInMemoryFixture fixture)
{
var efServiceProvider = new ServiceCollection().AddEntityFrameworkInMemoryDatabase().BuildServiceProvider();
var services = new ServiceCollection();
services
.AddMemoryCache()
.AddLogging()
.AddDbContext<MusicStoreContext>(b => b.UseInMemoryDatabase("Scratch").UseInternalServiceProvider(efServiceProvider));
_serviceProvider = services.BuildServiceProvider();
_fixture = fixture;
_fixture.CreateDatabase();
}
[Fact]
public void AddressAndPayment_ReturnsDefaultView()
{
// Arrange
var controller = new CheckoutController(_serviceProvider.GetService<ILogger<CheckoutController>>());
var controller = new CheckoutController(_fixture.ServiceProvider.GetService<ILogger<CheckoutController>>());
// Act
var result = controller.AddressAndPayment();
@ -54,10 +45,7 @@ namespace MusicStore.Controllers
var httpContext = new DefaultHttpContext();
var orderId = 10;
var order = new Order()
{
OrderId = orderId,
};
var order = CreateOrder(10);
// Session initialization
var cartId = "CartId_A";
@ -76,7 +64,7 @@ namespace MusicStore.Controllers
httpContext.User = new ClaimsPrincipal(new ClaimsIdentity(claims));
// DbContext initialization
var dbContext = _serviceProvider.GetRequiredService<MusicStoreContext>();
var dbContext = _fixture.Context;
var cartItems = CreateTestCartItems(
cartId,
itemPrice: 10,
@ -85,7 +73,7 @@ namespace MusicStore.Controllers
dbContext.AddRange(cartItems);
dbContext.SaveChanges();
var controller = new CheckoutController(_serviceProvider.GetService<ILogger<CheckoutController>>());
var controller = new CheckoutController(_fixture.ServiceProvider.GetService<ILogger<CheckoutController>>());
controller.ControllerContext.HttpContext = httpContext;
// Act
@ -105,17 +93,17 @@ namespace MusicStore.Controllers
{
// Arrange
var context = new DefaultHttpContext();
var dbContext = _serviceProvider.GetRequiredService<MusicStoreContext>();
var dbContext = _fixture.Context;
// AddressAndPayment action reads the Promo code from FormCollection.
context.Request.Form =
new FormCollection(new Dictionary<string, StringValues>());
var controller = new CheckoutController(_serviceProvider.GetService<ILogger<CheckoutController>>());
var controller = new CheckoutController(_fixture.ServiceProvider.GetService<ILogger<CheckoutController>>());
controller.ControllerContext.HttpContext = context;
// Do not need actual data for Order; the Order object will be checked for the reference equality.
var order = new Order();
var order = CreateOrder();
// Act
var result = await controller.AddressAndPayment(dbContext, order, CancellationToken.None);
@ -135,12 +123,12 @@ namespace MusicStore.Controllers
var context = new DefaultHttpContext();
context.Request.Form =
new FormCollection(new Dictionary<string, StringValues>());
var dbContext = _serviceProvider.GetRequiredService<MusicStoreContext>();
var dbContext = _fixture.Context;
var controller = new CheckoutController(_serviceProvider.GetService<ILogger<CheckoutController>>());
var controller = new CheckoutController(_fixture.ServiceProvider.GetService<ILogger<CheckoutController>>());
controller.ControllerContext.HttpContext = context;
var order = new Order();
var order = CreateOrder();
// Act
var result = await controller.AddressAndPayment(dbContext, order, new CancellationToken(true));
@ -157,11 +145,11 @@ namespace MusicStore.Controllers
public async Task AddressAndPayment_ReturnsOrderIfInvalidOrderModel()
{
// Arrange
var controller = new CheckoutController(_serviceProvider.GetService<ILogger<CheckoutController>>());
var controller = new CheckoutController(_fixture.ServiceProvider.GetService<ILogger<CheckoutController>>());
controller.ModelState.AddModelError("a", "ModelErrorA");
var dbContext = _serviceProvider.GetRequiredService<MusicStoreContext>();
var dbContext = _fixture.Context;
var order = new Order();
var order = CreateOrder();
// Act
var result = await controller.AddressAndPayment(dbContext, order, CancellationToken.None);
@ -174,6 +162,22 @@ namespace MusicStore.Controllers
Assert.Same(order, viewResult.ViewData.Model);
}
private Order CreateOrder(int orderId = 100, string userName = "TestUserA")
=> new Order
{
OrderId = orderId,
Username = userName,
FirstName = "Macavity",
LastName = "Clark",
Address = "11 Meadow Drive",
City = "Healing",
State = "IA",
PostalCode = "DN37 7RU",
Country = "USK",
Phone = "555 887876",
Email = "mc@sample.com"
};
[Fact]
public async Task Complete_ReturnsOrderIdIfValid()
{
@ -187,16 +191,11 @@ namespace MusicStore.Controllers
User = new ClaimsPrincipal(new ClaimsIdentity(claims)),
};
var dbContext =
_serviceProvider.GetRequiredService<MusicStoreContext>();
dbContext.Add(new Order()
{
OrderId = orderId,
Username = userName
});
var dbContext = _fixture.Context;
dbContext.Add(CreateOrder(orderId, userName));
dbContext.SaveChanges();
var controller = new CheckoutController(_serviceProvider.GetService<ILogger<CheckoutController>>());
var controller = new CheckoutController(_fixture.ServiceProvider.GetService<ILogger<CheckoutController>>());
controller.ControllerContext.HttpContext = httpContext;
// Act
@ -215,10 +214,9 @@ namespace MusicStore.Controllers
{
// Arrange
var invalidOrderId = 100;
var dbContext =
_serviceProvider.GetRequiredService<MusicStoreContext>();
var dbContext = _fixture.Context;
var controller = new CheckoutController(_serviceProvider.GetService<ILogger<CheckoutController>>());
var controller = new CheckoutController(_fixture.ServiceProvider.GetService<ILogger<CheckoutController>>());
controller.ControllerContext.HttpContext = new DefaultHttpContext();
// Act
@ -233,10 +231,21 @@ namespace MusicStore.Controllers
private static CartItem[] CreateTestCartItems(string cartId, decimal itemPrice, int numberOfItem)
{
var albums = Enumerable.Range(1, 10).Select(n =>
new Album()
new Album
{
AlbumId = n,
Price = itemPrice,
Title = "Greatest Hits",
Artist = new Artist
{
ArtistId = 1,
Name = "Kung Fu Kenny"
},
Genre = new Genre
{
GenreId = 1,
Name = "Rap"
}
}).ToArray();
var cartItems = Enumerable.Range(1, numberOfItem).Select(n =>

View File

@ -1,38 +1,27 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.ViewComponents;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using MusicStore.Models;
using Xunit;
namespace MusicStore.Components
{
public class GenreMenuComponentTest
public class GenreMenuComponentTest : IClassFixture<SqliteInMemoryFixture>
{
private readonly IServiceProvider _serviceProvider;
private readonly SqliteInMemoryFixture _fixture;
public GenreMenuComponentTest()
public GenreMenuComponentTest(SqliteInMemoryFixture fixture)
{
var efServiceProvider = new ServiceCollection().AddEntityFrameworkInMemoryDatabase().BuildServiceProvider();
var services = new ServiceCollection();
services
.AddMemoryCache()
.AddLogging()
.AddDbContext<MusicStoreContext>(b => b.UseInMemoryDatabase("Scratch").UseInternalServiceProvider(efServiceProvider));
_serviceProvider = services.BuildServiceProvider();
_fixture = fixture;
_fixture.CreateDatabase();
}
[Fact]
public async Task GenreMenuComponent_Returns_NineGenres()
{
// Arrange
var dbContext = _serviceProvider.GetRequiredService<MusicStoreContext>();
var dbContext = _fixture.Context;
var genreMenuComponent = new GenreMenuComponent(dbContext);
PopulateData(dbContext);
@ -50,7 +39,7 @@ namespace MusicStore.Components
private static void PopulateData(MusicStoreContext context)
{
var genres = Enumerable.Range(1, 10).Select(n => new Genre { GenreId = n });
var genres = Enumerable.Range(1, 10).Select(n => new Genre { GenreId = n, Name = $"G{n}" });
context.AddRange(genres);
context.SaveChanges();

View File

@ -12,22 +12,14 @@ using Xunit;
namespace MusicStore.Controllers
{
public class HomeControllerTest
public class HomeControllerTest : IClassFixture<SqliteInMemoryFixture>
{
private readonly IServiceProvider _serviceProvider;
private readonly SqliteInMemoryFixture _fixture;
public HomeControllerTest()
public HomeControllerTest(SqliteInMemoryFixture fixture)
{
var efServiceProvider = new ServiceCollection().AddEntityFrameworkInMemoryDatabase().BuildServiceProvider();
var services = new ServiceCollection();
services
.AddMemoryCache()
.AddLogging()
.AddDbContext<MusicStoreContext>(b => b.UseInMemoryDatabase("Scratch").UseInternalServiceProvider(efServiceProvider));
_serviceProvider = services.BuildServiceProvider();
_fixture = fixture;
_fixture.CreateDatabase();
}
[Fact]
@ -50,8 +42,8 @@ namespace MusicStore.Controllers
public async Task Index_GetsSixTopAlbums()
{
// Arrange
var dbContext = _serviceProvider.GetRequiredService<MusicStoreContext>();
var cache = _serviceProvider.GetRequiredService<IMemoryCache>();
var dbContext = _fixture.Context;
var cache = _fixture.ServiceProvider.GetRequiredService<IMemoryCache>();
var controller = new HomeController(new TestAppSettings());
PopulateData(dbContext);
@ -116,12 +108,13 @@ namespace MusicStore.Controllers
}).ToArray();
var albums = Enumerable.Range(1, 10).Select(n =>
new Album()
new Album
{
Artist = artists[n - 1],
ArtistId = n,
Genre = generes[n - 1],
GenreId = n,
Title = "Greatest Hits",
}).ToArray();
return albums;

View File

@ -5,9 +5,7 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using MusicStore.Models;
@ -15,37 +13,14 @@ using Xunit;
namespace MusicStore.Controllers
{
public class ManageControllerTest
public class ManageControllerTest : IClassFixture<ManageControllerTest.Fixture>
{
private readonly IServiceProvider _serviceProvider;
private readonly Fixture _fixture;
public ManageControllerTest()
public ManageControllerTest(Fixture fixture)
{
var efServiceProvider = new ServiceCollection().AddEntityFrameworkInMemoryDatabase().BuildServiceProvider();
var services = new ServiceCollection();
services.AddSingleton<IConfiguration>(new ConfigurationBuilder().Build());
services.AddOptions();
services
.AddMemoryCache()
.AddDbContext<MusicStoreContext>(b => b.UseInMemoryDatabase("Scratch").UseInternalServiceProvider(efServiceProvider));
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<MusicStoreContext>();
services.AddMvc();
services.AddSingleton<IAuthenticationService, NoOpAuth>();
services.AddLogging();
// IHttpContextAccessor is required for SignInManager, and UserManager
var context = new DefaultHttpContext();
services.AddSingleton<IHttpContextAccessor>(
new HttpContextAccessor()
{
HttpContext = context,
});
_serviceProvider = services.BuildServiceProvider();
_fixture = fixture;
_fixture.CreateDatabase();
}
[Fact]
@ -56,19 +31,19 @@ namespace MusicStore.Controllers
var phone = "abcdefg";
var claims = new List<Claim> { new Claim(ClaimTypes.NameIdentifier, userId) };
var userManager = _serviceProvider.GetRequiredService<UserManager<ApplicationUser>>();
var userManager = _fixture.ServiceProvider.GetRequiredService<UserManager<ApplicationUser>>();
var userManagerResult = await userManager.CreateAsync(
new ApplicationUser { Id = userId, UserName = "Test", TwoFactorEnabled = true, PhoneNumber = phone },
"Pass@word1");
Assert.True(userManagerResult.Succeeded);
var signInManager = _serviceProvider.GetRequiredService<SignInManager<ApplicationUser>>();
var signInManager = _fixture.ServiceProvider.GetRequiredService<SignInManager<ApplicationUser>>();
var httpContext = _serviceProvider.GetRequiredService<IHttpContextAccessor>().HttpContext;
var httpContext = _fixture.ServiceProvider.GetRequiredService<IHttpContextAccessor>().HttpContext;
httpContext.User = new ClaimsPrincipal(new ClaimsIdentity(claims));
httpContext.RequestServices = _serviceProvider;
httpContext.RequestServices = _fixture.ServiceProvider;
var schemeProvider = _serviceProvider.GetRequiredService<IAuthenticationSchemeProvider>();
var schemeProvider = _fixture.ServiceProvider.GetRequiredService<IAuthenticationSchemeProvider>();
var controller = new ManageController(userManager, signInManager, schemeProvider);
controller.ControllerContext.HttpContext = httpContext;
@ -117,5 +92,34 @@ namespace MusicStore.Controllers
}
}
public class Fixture : SqliteInMemoryFixture
{
public override IServiceCollection ConfigureServices(IServiceCollection services)
{
services = base.ConfigureServices(services);
services.AddSingleton<IConfiguration>(new ConfigurationBuilder().Build());
services.AddOptions();
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<MusicStoreContext>();
services.AddMvc();
services.AddSingleton<IAuthenticationService, NoOpAuth>();
services.AddLogging();
// IHttpContextAccessor is required for SignInManager, and UserManager
var context = new DefaultHttpContext();
services.AddSingleton<IHttpContextAccessor>(
new HttpContextAccessor()
{
HttpContext = context,
});
return services;
}
}
}
}

View File

@ -3,62 +3,49 @@
using System;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using MusicStore.Models;
using Xunit;
namespace MusicStore.Test
{
public class ShoppingCartTest : IClassFixture<ShoppingCartFixture>
public class ShoppingCartTest : IClassFixture<SqliteInMemoryFixture>
{
private readonly ShoppingCartFixture _fixture;
private readonly SqliteInMemoryFixture _fixture;
public ShoppingCartTest(ShoppingCartFixture fixture)
public ShoppingCartTest(SqliteInMemoryFixture fixture)
{
_fixture = fixture;
_fixture.CreateDatabase();
}
[Fact]
public async Task ComputesTotal()
{
var cartId = Guid.NewGuid().ToString();
using (var db = _fixture.CreateContext())
{
var a = db.Albums.Add(
new Album
var db = _fixture.Context;
var a = db.Albums.Add(
new Album
{
AlbumId = 1,
Title = "Greatest Hits",
Price = 15.99m,
Artist = new Artist
{
Price = 15.99m
}).Entity;
ArtistId = 1,
Name = "Kung Fu Kenny"
},
Genre = new Genre
{
GenreId = 1,
Name = "Rap"
}
}).Entity;
db.CartItems.Add(new CartItem { Album = a, Count = 2, CartId = cartId });
db.CartItems.Add(new CartItem { Album = a, Count = 2, CartId = cartId });
db.SaveChanges();
db.SaveChanges();
Assert.Equal(31.98m, await ShoppingCart.GetCart(db, cartId).GetTotal());
}
Assert.Equal(31.98m, await ShoppingCart.GetCart(db, cartId).GetTotal());
}
}
public class ShoppingCartFixture
{
private readonly IServiceProvider _serviceProvider;
public ShoppingCartFixture()
{
var efServiceProvider = new ServiceCollection().AddEntityFrameworkInMemoryDatabase().BuildServiceProvider();
var services = new ServiceCollection();
services
.AddMemoryCache()
.AddLogging()
.AddDbContext<MusicStoreContext>(b => b.UseInMemoryDatabase("Scratch").UseInternalServiceProvider(efServiceProvider));
_serviceProvider = services.BuildServiceProvider();
}
public virtual MusicStoreContext CreateContext()
=> _serviceProvider.GetRequiredService<MusicStoreContext>();
}
}

View File

@ -7,10 +7,8 @@ using Microsoft.AspNetCore.Antiforgery;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.ObjectPool;
using Microsoft.Extensions.Primitives;
using MusicStore.Models;
using MusicStore.ViewModels;
@ -18,23 +16,14 @@ using Xunit;
namespace MusicStore.Controllers
{
public class ShoppingCartControllerTest
public class ShoppingCartControllerTest : IClassFixture<ShoppingCartControllerTest.Fixture>
{
private readonly IServiceProvider _serviceProvider;
private readonly Fixture _fixture;
public ShoppingCartControllerTest()
public ShoppingCartControllerTest(Fixture fixture)
{
var efServiceProvider = new ServiceCollection().AddEntityFrameworkInMemoryDatabase().BuildServiceProvider();
var services = new ServiceCollection();
services
.AddMemoryCache()
.AddLogging()
.AddDbContext<MusicStoreContext>(b => b.UseInMemoryDatabase("Scratch").UseInternalServiceProvider(efServiceProvider));
services.AddMvc();
_serviceProvider = services.BuildServiceProvider();
_fixture = fixture;
_fixture.CreateDatabase();
}
[Fact]
@ -45,8 +34,8 @@ namespace MusicStore.Controllers
httpContext.Session = new TestSession();
var controller = new ShoppingCartController(
_serviceProvider.GetRequiredService<MusicStoreContext>(),
_serviceProvider.GetService<ILogger<ShoppingCartController>>());
_fixture.Context,
_fixture.ServiceProvider.GetService<ILogger<ShoppingCartController>>());
controller.ControllerContext.HttpContext = httpContext;
// Act
@ -71,8 +60,8 @@ namespace MusicStore.Controllers
httpContext.Session.SetString("Session", "CartId_A");
var controller = new ShoppingCartController(
_serviceProvider.GetRequiredService<MusicStoreContext>(),
_serviceProvider.GetService<ILogger<ShoppingCartController>>());
_fixture.Context,
_fixture.ServiceProvider.GetService<ILogger<ShoppingCartController>>());
controller.ControllerContext.HttpContext = httpContext;
// Act
@ -97,7 +86,7 @@ namespace MusicStore.Controllers
httpContext.Session = new TestSession();
httpContext.Session.SetString("Session", cartId);
var dbContext = _serviceProvider.GetRequiredService<MusicStoreContext>();
var dbContext = _fixture.Context;
var cartItems = CreateTestCartItems(
cartId,
itemPrice: 10,
@ -108,7 +97,7 @@ namespace MusicStore.Controllers
var controller = new ShoppingCartController(
dbContext,
_serviceProvider.GetService<ILogger<ShoppingCartController>>());
_fixture.ServiceProvider.GetService<ILogger<ShoppingCartController>>());
controller.ControllerContext.HttpContext = httpContext;
// Act
@ -134,14 +123,14 @@ namespace MusicStore.Controllers
httpContext.Session.SetString("Session", "CartId_A");
// Creates the albums of AlbumId = 1 ~ 10.
var dbContext = _serviceProvider.GetRequiredService<MusicStoreContext>();
var dbContext = _fixture.Context;
var albums = CreateTestAlbums(itemPrice: 10);
dbContext.AddRange(albums);
dbContext.SaveChanges();
var controller = new ShoppingCartController(
dbContext,
_serviceProvider.GetService<ILogger<ShoppingCartController>>());
_fixture.ServiceProvider.GetService<ILogger<ShoppingCartController>>());
controller.ControllerContext.HttpContext = httpContext;
// Act
@ -172,7 +161,7 @@ namespace MusicStore.Controllers
httpContext.Session.SetString("Session", cartId);
// DbContext initialization
var dbContext = _serviceProvider.GetRequiredService<MusicStoreContext>();
var dbContext = _fixture.Context;
var cartItems = CreateTestCartItems(cartId, unitPrice, numberOfItem);
dbContext.AddRange(cartItems.Select(n => n.Album).Distinct());
dbContext.AddRange(cartItems);
@ -183,7 +172,7 @@ namespace MusicStore.Controllers
httpContext.Features.Set<IServiceProvidersFeature>(serviceProviderFeature);
// AntiForgery initialization
serviceProviderFeature.RequestServices = _serviceProvider;
serviceProviderFeature.RequestServices = _fixture.ServiceProvider;
var antiForgery = serviceProviderFeature.RequestServices.GetRequiredService<IAntiforgery>();
var tokens = antiForgery.GetTokens(httpContext);
@ -196,7 +185,7 @@ namespace MusicStore.Controllers
// Cotroller initialization
var controller = new ShoppingCartController(
dbContext,
_serviceProvider.GetService<ILogger<ShoppingCartController>>());
_fixture.ServiceProvider.GetService<ILogger<ShoppingCartController>>());
controller.ControllerContext.HttpContext = httpContext;
// Act
@ -207,7 +196,7 @@ namespace MusicStore.Controllers
var viewModel = Assert.IsType<ShoppingCartRemoveViewModel>(jsonResult.Value);
Assert.Equal(numberOfItem - 1, viewModel.CartCount);
Assert.Equal((numberOfItem - 1) * 10, viewModel.CartTotal);
Assert.Equal(" has been removed from your shopping cart.", viewModel.Message);
Assert.Equal("Greatest Hits has been removed from your shopping cart.", viewModel.Message);
var cart = ShoppingCart.GetCart(dbContext, httpContext);
Assert.DoesNotContain((await cart.GetCartItems()), c => c.CartItemId == cartItemId);
@ -232,11 +221,34 @@ namespace MusicStore.Controllers
private static Album[] CreateTestAlbums(decimal itemPrice)
{
return Enumerable.Range(1, 10).Select(n =>
new Album()
new Album
{
Title = "Greatest Hits",
AlbumId = n,
Price = itemPrice,
Artist = new Artist
{
ArtistId = 1,
Name = "Kung Fu Kenny"
},
Genre = new Genre
{
GenreId = 1,
Name = "Rap"
}
}).ToArray();
}
public class Fixture : SqliteInMemoryFixture
{
public override IServiceCollection ConfigureServices(IServiceCollection services)
{
services = base.ConfigureServices(services);
services.AddMvc();
return services;
}
}
}
}

View File

@ -0,0 +1,52 @@
using System;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using MusicStore.Models;
namespace MusicStore
{
public class SqliteInMemoryFixture : IDisposable
{
private IServiceScope _serviceScope;
private SqliteConnection _connection;
public virtual void Dispose()
{
_connection?.Close();
_connection?.Dispose();
_serviceScope?.Dispose();
_serviceScope= null;
}
public virtual IServiceCollection ConfigureServices(IServiceCollection services)
=> services
.AddMemoryCache()
.AddLogging()
.AddDbContext<MusicStoreContext>(b => b.UseSqlite(_connection));
public virtual IServiceProvider ServiceProvider
{
get
{
if (_serviceScope == null)
{
_serviceScope = ConfigureServices(new ServiceCollection()).BuildServiceProvider().CreateScope();
}
return _serviceScope.ServiceProvider;
}
}
public virtual MusicStoreContext Context
=> ServiceProvider.GetRequiredService<MusicStoreContext>();
public virtual void CreateDatabase()
{
Dispose();
_connection = new SqliteConnection("Data Source=:memory:");
_connection.Open();
Context.Database.EnsureCreated();
}
}
}

View File

@ -12,29 +12,21 @@ using Xunit;
namespace MusicStore.Controllers
{
public class StoreControllerTest
public class StoreControllerTest : IClassFixture<SqliteInMemoryFixture>
{
private readonly IServiceProvider _serviceProvider;
private readonly SqliteInMemoryFixture _fixture;
public StoreControllerTest()
public StoreControllerTest(SqliteInMemoryFixture fixture)
{
var efServiceProvider = new ServiceCollection().AddEntityFrameworkInMemoryDatabase().BuildServiceProvider();
var services = new ServiceCollection();
services
.AddMemoryCache()
.AddLogging()
.AddDbContext<MusicStoreContext>(b => b.UseInMemoryDatabase("Scratch").UseInternalServiceProvider(efServiceProvider));
_serviceProvider = services.BuildServiceProvider();
_fixture = fixture;
_fixture.CreateDatabase();
}
[Fact]
public async Task Index_CreatesViewWithGenres()
{
// Arrange
var dbContext = _serviceProvider.GetRequiredService<MusicStoreContext>();
var dbContext = _fixture.Context;
CreateTestGenres(numberOfGenres: 10, numberOfAlbums: 1, dbContext: dbContext);
var controller = new StoreController(dbContext, new TestAppSettings());
@ -56,7 +48,7 @@ namespace MusicStore.Controllers
{
// Arrange
var controller = new StoreController(
_serviceProvider.GetRequiredService<MusicStoreContext>(),
_fixture.Context,
new TestAppSettings());
// Act
@ -72,7 +64,7 @@ namespace MusicStore.Controllers
// Arrange
var genreName = "Genre 1";
var dbContext = _serviceProvider.GetRequiredService<MusicStoreContext>();
var dbContext = _fixture.Context;
CreateTestGenres(numberOfGenres: 3, numberOfAlbums: 3, dbContext: dbContext);
var controller = new StoreController(dbContext, new TestAppSettings());
@ -97,11 +89,11 @@ namespace MusicStore.Controllers
// Arrange
var albumId = int.MinValue;
var controller = new StoreController(
_serviceProvider.GetRequiredService<MusicStoreContext>(),
_fixture.Context,
new TestAppSettings());
// Act
var result = await controller.Details(_serviceProvider.GetRequiredService<IMemoryCache>(), albumId);
var result = await controller.Details(_fixture.ServiceProvider.GetRequiredService<IMemoryCache>(), albumId);
// Assert
Assert.IsType<NotFoundResult>(result);
@ -113,10 +105,10 @@ namespace MusicStore.Controllers
// Arrange
var albumId = 1;
var dbContext = _serviceProvider.GetRequiredService<MusicStoreContext>();
var dbContext = _fixture.Context;
var genres = CreateTestGenres(numberOfGenres: 3, numberOfAlbums: 3, dbContext: dbContext);
var cache = _serviceProvider.GetRequiredService<IMemoryCache>();
var cache = _fixture.ServiceProvider.GetRequiredService<IMemoryCache>();
var controller = new StoreController(dbContext, new TestAppSettings());
@ -148,31 +140,22 @@ namespace MusicStore.Controllers
artist.Name = "Artist1";
var albums = Enumerable.Range(1, numberOfAlbums * numberOfGenres).Select(n =>
new Album()
new Album
{
AlbumId = n,
Artist = artist,
ArtistId = artist.ArtistId
ArtistId = artist.ArtistId,
Title = "Greatest Hits",
}).ToList();
var generes = Enumerable.Range(1, numberOfGenres).Select(n =>
new Genre()
new Genre
{
Albums = albums.Where(i => i.AlbumId % numberOfGenres == n - 1).ToList(),
GenreId = n,
Name = "Genre " + n
});
var artis = Enumerable.Range(1, numberOfGenres).Select(n =>
new Genre()
{
Albums = albums.Where(i => i.AlbumId % numberOfGenres == n - 1).ToList(),
GenreId = n,
Name = "Genre " + n,
});
dbContext.Add(artist);
dbContext.AddRange(albums);
dbContext.AddRange(generes);
dbContext.SaveChanges();