// 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.Data.SqlClient; using System.Linq; using System.Net; using System.Net.Http; using System.Threading.Tasks; using Microsoft.AspNet.Builder; using Microsoft.AspNet.Diagnostics.Entity.FunctionalTests.Helpers; using Microsoft.AspNet.Http; using Microsoft.AspNet.TestHost; using Microsoft.Data.Entity; using Microsoft.Data.Entity.Infrastructure; using Microsoft.Data.Entity.Internal; using Microsoft.Data.Entity.Relational; using Microsoft.Data.Entity.Relational.Migrations; using Microsoft.Data.Entity.Relational.Migrations.Infrastructure; using Microsoft.Data.Entity.Utilities; using Microsoft.Framework.DependencyInjection; using Microsoft.Framework.Logging; using Xunit; using Microsoft.AspNet.Diagnostics.Entity.Tests.Helpers; namespace Microsoft.AspNet.Diagnostics.Entity.Tests { public class DatabaseErrorPageMiddlewareTest { [Fact] public async Task Successful_requests_pass_thru() { TestServer server = TestServer.Create(app => app .UseDatabaseErrorPage() .UseMiddleware()); HttpResponseMessage response = await server.CreateClient().GetAsync("http://localhost/"); Assert.Equal("Request Handled", await response.Content.ReadAsStringAsync()); Assert.Equal(HttpStatusCode.OK, response.StatusCode); } class SuccessMiddleware { public SuccessMiddleware(RequestDelegate next) { } public virtual async Task Invoke(HttpContext context) { await context.Response.WriteAsync("Request Handled"); context.Response.StatusCode = (int)HttpStatusCode.OK; } } [Fact] public async Task Non_database_exceptions_pass_thru() { TestServer server = TestServer.Create(app => app .UseDatabaseErrorPage() .UseMiddleware()); var ex = await Assert.ThrowsAsync(async () => await server.CreateClient().GetAsync("http://localhost/")); Assert.Equal("Exception requested from TestMiddleware", ex.Message); } class ExceptionMiddleware { public ExceptionMiddleware(RequestDelegate next) { } public virtual Task Invoke(HttpContext context) { throw new InvalidOperationException("Exception requested from TestMiddleware"); } } [Fact] public async Task Error_page_displayed_no_migrations() { TestServer server = SetupTestServer(); HttpResponseMessage response = await server.CreateClient().GetAsync("http://localhost/"); Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode); var content = await response.Content.ReadAsStringAsync(); Assert.Contains(StringsHelpers.GetResourceString("FormatDatabaseErrorPage_NoDbOrMigrationsTitle", typeof(BloggingContext).Name), content); Assert.Contains(StringsHelpers.GetResourceString("FormatDatabaseErrorPage_AddMigrationCommand").Replace(">", ">"), content); Assert.Contains(StringsHelpers.GetResourceString("FormatDatabaseErrorPage_ApplyMigrationsCommand").Replace(">", ">"), content); } class NoMigrationsMiddleware { public NoMigrationsMiddleware(RequestDelegate next) { } public virtual Task Invoke(HttpContext context) { using (var db = context.ApplicationServices.GetService()) { db.Blogs.Add(new Blog()); db.SaveChanges(); throw new Exception("SaveChanges should have thrown"); } } } [Fact] public async Task Error_page_displayed_pending_migrations() { TestServer server = SetupTestServer(); HttpResponseMessage response = await server.CreateClient().GetAsync("http://localhost/"); Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode); var content = await response.Content.ReadAsStringAsync(); Assert.Contains(StringsHelpers.GetResourceString("FormatDatabaseErrorPage_PendingMigrationsTitle", typeof(BloggingContextWithMigrations).Name), content); Assert.Contains(StringsHelpers.GetResourceString("FormatDatabaseErrorPage_ApplyMigrationsCommand").Replace(">", ">"), content); Assert.Contains("
  • 111111111111111_MigrationOne
  • ", content); Assert.Contains("
  • 222222222222222_MigrationTwo
  • ", content); Assert.DoesNotContain(StringsHelpers.GetResourceString("FormatDatabaseErrorPage_AddMigrationCommand").Replace(">", ">"), content); } class PendingMigrationsMiddleware { public PendingMigrationsMiddleware(RequestDelegate next) { } public virtual Task Invoke(HttpContext context) { using (var db = context.ApplicationServices.GetService()) { db.Blogs.Add(new Blog()); db.SaveChanges(); throw new Exception("SaveChanges should have thrown"); } } } [Fact] public async Task Error_page_displayed_pending_model_changes() { TestServer server = SetupTestServer(); HttpResponseMessage response = await server.CreateClient().GetAsync("http://localhost/"); Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode); var content = await response.Content.ReadAsStringAsync(); Assert.Contains(StringsHelpers.GetResourceString("FormatDatabaseErrorPage_PendingChangesTitle", typeof(BloggingContextWithPendingModelChanges).Name), content); Assert.Contains(StringsHelpers.GetResourceString("FormatDatabaseErrorPage_AddMigrationCommand").Replace(">", ">"), content); Assert.Contains(StringsHelpers.GetResourceString("FormatDatabaseErrorPage_ApplyMigrationsCommand").Replace(">", ">"), content); } class PendingModelChangesMiddleware { public PendingModelChangesMiddleware(RequestDelegate next) { } public virtual Task Invoke(HttpContext context) { using (var db = context.ApplicationServices.GetService()) { db.Database.ApplyMigrations(); db.Blogs.Add(new Blog()); db.SaveChanges(); throw new Exception("SaveChanges should have thrown"); } } } [Fact] public async Task Pass_thru_when_context_not_in_services() { using (var database = SqlServerTestStore.CreateScratch()) { var logProvider = new TestLoggerProvider(); var server = TestServer.Create(app => { app.UseDatabaseErrorPage(); app.UseMiddleware(); app.ApplicationServices.GetService().AddProvider(logProvider); }, services => { services.AddEntityFramework().AddSqlServer(); var optionsBuilder = new DbContextOptionsBuilder(); if (!PlatformHelper.IsMono) { optionsBuilder.UseSqlServer(database.ConnectionString); } else { optionsBuilder.UseInMemoryStore(); } services.AddInstance(optionsBuilder.Options); }); var ex = await Assert.ThrowsAsync(async () => await server.CreateClient().GetAsync("http://localhost/")); Assert.True(logProvider.Logger.Messages.Any(m => m.StartsWith(StringsHelpers.GetResourceString("FormatDatabaseErrorPageMiddleware_ContextNotRegistered", typeof(BloggingContext))))); } } class ContextNotRegisteredInServicesMiddleware { public ContextNotRegisteredInServicesMiddleware(RequestDelegate next) { } public virtual Task Invoke(HttpContext context) { var options = context.ApplicationServices.GetService(); using (var db = new BloggingContext(context.ApplicationServices, options)) { db.Blogs.Add(new Blog()); db.SaveChanges(); throw new Exception("SaveChanges should have thrown"); } } } [Fact] public async Task Pass_thru_when_exception_in_logic() { using (var database = SqlServerTestStore.CreateScratch()) { var logProvider = new TestLoggerProvider(); var server = SetupTestServer(logProvider); var ex = await Assert.ThrowsAsync(async () => await server.CreateClient().GetAsync("http://localhost/")); Assert.True(logProvider.Logger.Messages.Any(m => m.StartsWith(StringsHelpers.GetResourceString("FormatDatabaseErrorPageMiddleware_Exception")))); } } class ExceptionInLogicMiddleware { public ExceptionInLogicMiddleware(RequestDelegate next) { } public virtual Task Invoke(HttpContext context) { using (var db = context.ApplicationServices.GetService()) { db.Blogs.Add(new Blog()); db.SaveChanges(); throw new Exception("SaveChanges should have thrown"); } } } [Fact] public async Task Error_page_displayed_when_exception_wrapped() { TestServer server = SetupTestServer(); HttpResponseMessage response = await server.CreateClient().GetAsync("http://localhost/"); Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode); var content = await response.Content.ReadAsStringAsync(); Assert.Contains("I wrapped your exception", content); Assert.Contains(StringsHelpers.GetResourceString("FormatDatabaseErrorPage_NoDbOrMigrationsTitle", typeof(BloggingContext).Name), content); } class WrappedExceptionMiddleware { public WrappedExceptionMiddleware(RequestDelegate next) { } public virtual Task Invoke(HttpContext context) { using (var db = context.ApplicationServices.GetService()) { db.Blogs.Add(new Blog()); try { db.SaveChanges(); throw new Exception("SaveChanges should have thrown"); } catch (Exception ex) { throw new Exception("I wrapped your exception", ex); } } } } private static TestServer SetupTestServer(ILoggerProvider logProvider = null) where TContext : DbContext { using (var database = SqlServerTestStore.CreateScratch()) { return TestServer.Create(app => { app.UseDatabaseErrorPage(); app.UseMiddleware(); if (logProvider != null) { app.ApplicationServices.GetService().AddProvider(logProvider); } }, services => { services.AddEntityFramework() .AddSqlServer(); services.AddScoped(); var optionsBuilder = new DbContextOptionsBuilder(); if (!PlatformHelper.IsMono) { optionsBuilder.UseSqlServer(database.ConnectionString); } else { optionsBuilder.UseInMemoryStore(); } services.AddInstance(optionsBuilder.Options); }); } } } }