Move Database Error Page tests to SQLite

In an attempt to make them more reliable.

See also https://github.com/aspnet/EntityFrameworkCore/issues/15997
This commit is contained in:
Arthur Vickers 2019-06-07 14:58:31 -07:00
parent d3d6e0e709
commit b5adc6b156
10 changed files with 56 additions and 96 deletions

View File

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Reflection;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
@ -131,6 +132,12 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
{ {
var databaseExists = await relationalDatabaseCreator.ExistsAsync(); var databaseExists = await relationalDatabaseCreator.ExistsAsync();
if (databaseExists)
{
// Also check if the database is completely empty - see https://github.com/aspnet/EntityFrameworkCore/issues/15997
databaseExists = (bool)typeof(RelationalDatabaseCreator).GetMethod("HasTables", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(relationalDatabaseCreator, null);
}
var migrationsAssembly = context.GetService<IMigrationsAssembly>(); var migrationsAssembly = context.GetService<IMigrationsAssembly>();
var modelDiffer = context.GetService<IMigrationsModelDiffer>(); var modelDiffer = context.GetService<IMigrationsModelDiffer>();

View File

@ -80,13 +80,13 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests
[OSSkipCondition(OperatingSystems.MacOSX)] [OSSkipCondition(OperatingSystems.MacOSX)]
public async Task Existing_database_not_using_migrations_exception_passes_thru() public async Task Existing_database_not_using_migrations_exception_passes_thru()
{ {
using (var database = SqlServerTestStore.CreateScratch()) using (var database = SqlTestStore.CreateScratch())
{ {
TestServer server = SetupTestServer<BloggingContext, DatabaseErrorButNoMigrationsMiddleware>(database); TestServer server = SetupTestServer<BloggingContext, DatabaseErrorButNoMigrationsMiddleware>(database);
var ex = await Assert.ThrowsAsync<DbUpdateException>(async () => var ex = await Assert.ThrowsAsync<DbUpdateException>(async () =>
await server.CreateClient().GetAsync("http://localhost/")); await server.CreateClient().GetAsync("http://localhost/"));
Assert.Equal("Invalid column name 'Name'.", ex.InnerException.Message); Assert.Equal("SQLite Error 1: 'no such table: Blogs'.", ex.InnerException.Message);
} }
} }
@ -99,7 +99,7 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests
{ {
var db = context.RequestServices.GetService<BloggingContext>(); var db = context.RequestServices.GetService<BloggingContext>();
db.Database.EnsureCreated(); db.Database.EnsureCreated();
db.Database.ExecuteSqlRaw("ALTER TABLE dbo.Blogs DROP COLUMN Name"); db.Database.ExecuteSqlRaw("ALTER TABLE Blogs RENAME TO Bloogs");
db.Blogs.Add(new Blog()); db.Blogs.Add(new Blog());
db.SaveChanges(); db.SaveChanges();
@ -112,7 +112,7 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests
[OSSkipCondition(OperatingSystems.MacOSX)] [OSSkipCondition(OperatingSystems.MacOSX)]
public async Task Error_page_displayed_no_migrations() public async Task Error_page_displayed_no_migrations()
{ {
using (var database = SqlServerTestStore.CreateScratch()) using (var database = SqlTestStore.CreateScratch())
{ {
TestServer server = SetupTestServer<BloggingContext, NoMigrationsMiddleware>(database); TestServer server = SetupTestServer<BloggingContext, NoMigrationsMiddleware>(database);
HttpResponseMessage response = await server.CreateClient().GetAsync("http://localhost/"); HttpResponseMessage response = await server.CreateClient().GetAsync("http://localhost/");
@ -144,7 +144,7 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests
[OSSkipCondition(OperatingSystems.MacOSX)] [OSSkipCondition(OperatingSystems.MacOSX)]
public void No_exception_on_diagnostic_event_received_when_null_state() public void No_exception_on_diagnostic_event_received_when_null_state()
{ {
using (var database = SqlServerTestStore.CreateScratch()) using (var database = SqlTestStore.CreateScratch())
{ {
using (var server = SetupTestServer<BloggingContext, NoMigrationsMiddleware>(database)) using (var server = SetupTestServer<BloggingContext, NoMigrationsMiddleware>(database))
{ {
@ -158,7 +158,7 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests
} }
catch (Exception e) catch (Exception e)
{ {
Assert.Equal("SqlException", e.GetType().Name); Assert.Equal("DbUpdateException", e.GetType().Name);
} }
} }
} }
@ -170,7 +170,7 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests
[OSSkipCondition(OperatingSystems.MacOSX)] [OSSkipCondition(OperatingSystems.MacOSX)]
public async Task Error_page_displayed_pending_migrations() public async Task Error_page_displayed_pending_migrations()
{ {
using (var database = SqlServerTestStore.CreateScratch()) using (var database = SqlTestStore.CreateScratch())
{ {
TestServer server = SetupTestServer<BloggingContextWithMigrations, PendingMigrationsMiddleware>(database); TestServer server = SetupTestServer<BloggingContextWithMigrations, PendingMigrationsMiddleware>(database);
HttpResponseMessage response = await server.CreateClient().GetAsync("http://localhost/"); HttpResponseMessage response = await server.CreateClient().GetAsync("http://localhost/");
@ -206,7 +206,7 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests
[OSSkipCondition(OperatingSystems.MacOSX)] [OSSkipCondition(OperatingSystems.MacOSX)]
public async Task Error_page_displayed_pending_model_changes() public async Task Error_page_displayed_pending_model_changes()
{ {
using (var database = SqlServerTestStore.CreateScratch()) using (var database = SqlTestStore.CreateScratch())
{ {
TestServer server = SetupTestServer<BloggingContextWithPendingModelChanges, PendingModelChangesMiddleware>(database); TestServer server = SetupTestServer<BloggingContextWithPendingModelChanges, PendingModelChangesMiddleware>(database);
HttpResponseMessage response = await server.CreateClient().GetAsync("http://localhost/"); HttpResponseMessage response = await server.CreateClient().GetAsync("http://localhost/");
@ -243,7 +243,7 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests
[OSSkipCondition(OperatingSystems.MacOSX)] [OSSkipCondition(OperatingSystems.MacOSX)]
public async Task Error_page_then_apply_migrations() public async Task Error_page_then_apply_migrations()
{ {
using (var database = SqlServerTestStore.CreateScratch()) using (var database = SqlTestStore.CreateScratch())
{ {
TestServer server = SetupTestServer<BloggingContextWithMigrations, ApplyMigrationsMiddleware>(database); TestServer server = SetupTestServer<BloggingContextWithMigrations, ApplyMigrationsMiddleware>(database);
var client = server.CreateClient(); var client = server.CreateClient();
@ -300,7 +300,7 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests
{ {
var migrationsEndpoint = "/MyCustomEndPoints/ApplyMyMigrationsHere"; var migrationsEndpoint = "/MyCustomEndPoints/ApplyMyMigrationsHere";
using (var database = SqlServerTestStore.CreateScratch()) using (var database = SqlTestStore.CreateScratch())
{ {
var builder = new WebHostBuilder() var builder = new WebHostBuilder()
.Configure(app => .Configure(app =>
@ -314,10 +314,8 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests
}) })
.ConfigureServices(services => .ConfigureServices(services =>
{ {
services.AddDbContext<BloggingContextWithMigrations>(optionsBuilder => services.AddDbContext<BloggingContextWithMigrations>(
{ optionsBuilder => optionsBuilder.UseSqlite(database.ConnectionString));
optionsBuilder.UseSqlServer(database.ConnectionString);
});
}); });
var server = new TestServer(builder); var server = new TestServer(builder);
@ -355,8 +353,8 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests
catch (Exception exception) catch (Exception exception)
{ {
Assert.True( Assert.True(
exception.GetType().Name == "SqlException" exception.GetType().Name == "SqliteException"
|| exception.InnerException?.GetType().Name == "SqlException"); || exception.InnerException?.GetType().Name == "SqliteException");
} }
Assert.Contains(logProvider.Logger.Messages.ToList(), m => Assert.Contains(logProvider.Logger.Messages.ToList(), m =>
@ -370,18 +368,11 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests
public virtual Task Invoke(HttpContext context) public virtual Task Invoke(HttpContext context)
{ {
using (var database = SqlServerTestStore.CreateScratch()) using (var database = SqlTestStore.CreateScratch())
{ {
var optionsBuilder = new DbContextOptionsBuilder() var optionsBuilder = new DbContextOptionsBuilder()
.UseLoggerFactory(context.RequestServices.GetService<ILoggerFactory>()); .UseLoggerFactory(context.RequestServices.GetService<ILoggerFactory>())
if (!PlatformHelper.IsMono) .UseSqlite(database.ConnectionString);
{
optionsBuilder.UseSqlServer(database.ConnectionString, b => b.CommandTimeout(600).EnableRetryOnFailure());
}
else
{
optionsBuilder.UseInMemoryDatabase("Scratch");
}
var db = new BloggingContext(optionsBuilder.Options); var db = new BloggingContext(optionsBuilder.Options);
db.Blogs.Add(new Blog()); db.Blogs.Add(new Blog());
@ -396,7 +387,7 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests
[OSSkipCondition(OperatingSystems.MacOSX)] [OSSkipCondition(OperatingSystems.MacOSX)]
public async Task Pass_thru_when_exception_in_logic() public async Task Pass_thru_when_exception_in_logic()
{ {
using (var database = SqlServerTestStore.CreateScratch()) using (var database = SqlTestStore.CreateScratch())
{ {
var logProvider = new TestLoggerProvider(); var logProvider = new TestLoggerProvider();
@ -409,8 +400,8 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests
catch (Exception exception) catch (Exception exception)
{ {
Assert.True( Assert.True(
exception.GetType().Name == "SqlException" exception.GetType().Name == "SqliteException"
|| exception.InnerException?.GetType().Name == "SqlException"); || exception.InnerException?.GetType().Name == "SqliteException");
} }
Assert.Contains(logProvider.Logger.Messages.ToList(), m => Assert.Contains(logProvider.Logger.Messages.ToList(), m =>
@ -437,7 +428,7 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests
[OSSkipCondition(OperatingSystems.MacOSX)] [OSSkipCondition(OperatingSystems.MacOSX)]
public async Task Error_page_displayed_when_exception_wrapped() public async Task Error_page_displayed_when_exception_wrapped()
{ {
using (var database = SqlServerTestStore.CreateScratch()) using (var database = SqlTestStore.CreateScratch())
{ {
TestServer server = SetupTestServer<BloggingContext, WrappedExceptionMiddleware>(database); TestServer server = SetupTestServer<BloggingContext, WrappedExceptionMiddleware>(database);
HttpResponseMessage response = await server.CreateClient().GetAsync("http://localhost/"); HttpResponseMessage response = await server.CreateClient().GetAsync("http://localhost/");
@ -470,7 +461,7 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests
} }
} }
private static TestServer SetupTestServer<TContext, TMiddleware>(SqlServerTestStore database, ILoggerProvider logProvider = null) private static TestServer SetupTestServer<TContext, TMiddleware>(SqlTestStore database, ILoggerProvider logProvider = null)
where TContext : DbContext where TContext : DbContext
{ {
var builder = new WebHostBuilder() var builder = new WebHostBuilder()
@ -489,20 +480,9 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests
}) })
.ConfigureServices(services => .ConfigureServices(services =>
{ {
services.AddDbContext<TContext>(optionsBuilder => services.AddDbContext<TContext>(optionsBuilder => optionsBuilder.UseSqlite(database.ConnectionString));
{
if (!PlatformHelper.IsMono)
{
optionsBuilder.UseSqlServer(
database.ConnectionString,
b => b.CommandTimeout(600).EnableRetryOnFailure());
}
else
{
optionsBuilder.UseInMemoryDatabase("Scratch");
}
});
}); });
return new TestServer(builder); return new TestServer(builder);
} }

View File

@ -4,14 +4,12 @@
<TargetFramework>netcoreapp3.0</TargetFramework> <TargetFramework>netcoreapp3.0</TargetFramework>
<!-- Mitigation for long path issues --> <!-- Mitigation for long path issues -->
<AssemblyName>Diagnostics.EFCore.FunctionalTests</AssemblyName> <AssemblyName>Diagnostics.EFCore.FunctionalTests</AssemblyName>
<TestDependsOnMssql>true</TestDependsOnMssql>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" /> <Reference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" />
<Reference Include="Microsoft.AspNetCore.TestHost" /> <Reference Include="Microsoft.AspNetCore.TestHost" />
<Reference Include="Microsoft.EntityFrameworkCore.InMemory" /> <Reference Include="Microsoft.EntityFrameworkCore.Sqlite" />
<Reference Include="Microsoft.EntityFrameworkCore.SqlServer" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -68,10 +68,10 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests
private async Task Migration_request(bool useCustomPath) private async Task Migration_request(bool useCustomPath)
{ {
using (var database = SqlServerTestStore.CreateScratch()) using (var database = SqlTestStore.CreateScratch())
{ {
var optionsBuilder = new DbContextOptionsBuilder(); var optionsBuilder = new DbContextOptionsBuilder();
optionsBuilder.UseSqlServer(database.ConnectionString); optionsBuilder.UseSqlite(database.ConnectionString);
var path = useCustomPath ? new PathString("/EndPoints/ApplyMyMigrations") : MigrationsEndPointOptions.DefaultPath; var path = useCustomPath ? new PathString("/EndPoints/ApplyMyMigrations") : MigrationsEndPointOptions.DefaultPath;
@ -94,7 +94,7 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests
{ {
services.AddDbContext<BloggingContextWithMigrations>(options => services.AddDbContext<BloggingContextWithMigrations>(options =>
{ {
options.UseSqlServer(database.ConnectionString); options.UseSqlite(database.ConnectionString);
}); });
}); });
var server = new TestServer(builder); var server = new TestServer(builder);
@ -174,7 +174,7 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests
{ {
var builder = new WebHostBuilder() var builder = new WebHostBuilder()
.Configure(app => app.UseMigrationsEndPoint()) .Configure(app => app.UseMigrationsEndPoint())
.ConfigureServices(services => services.AddEntityFrameworkSqlServer()); .ConfigureServices(services => services.AddEntityFrameworkSqlite());
var server = new TestServer(builder); var server = new TestServer(builder);
var formData = new FormUrlEncodedContent(new List<KeyValuePair<string, string>> var formData = new FormUrlEncodedContent(new List<KeyValuePair<string, string>>
@ -195,7 +195,7 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests
[OSSkipCondition(OperatingSystems.MacOSX)] [OSSkipCondition(OperatingSystems.MacOSX)]
public async Task Exception_while_applying_migrations() public async Task Exception_while_applying_migrations()
{ {
using (var database = SqlServerTestStore.CreateScratch()) using (var database = SqlTestStore.CreateScratch())
{ {
var builder = new WebHostBuilder() var builder = new WebHostBuilder()
.Configure(app => app.UseMigrationsEndPoint()) .Configure(app => app.UseMigrationsEndPoint())
@ -203,7 +203,7 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests
{ {
services.AddDbContext<BloggingContextWithSnapshotThatThrows>(optionsBuilder => services.AddDbContext<BloggingContextWithSnapshotThatThrows>(optionsBuilder =>
{ {
optionsBuilder.UseSqlServer(database.ConnectionString); optionsBuilder.UseSqlite(database.ConnectionString);
}); });
}); });
var server = new TestServer(builder); var server = new TestServer(builder);

View File

@ -2,52 +2,29 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using System.Threading;
using Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.FunctionalTests.Helpers;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests
{ {
public class SqlServerTestStore : IDisposable public class SqlTestStore : IDisposable
{ {
private static int _scratchCount; public static SqlTestStore CreateScratch() => new SqlTestStore($"D{Guid.NewGuid()}");
public static SqlServerTestStore CreateScratch() private SqlTestStore(string name)
{ {
var name = "Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.FunctionalTests.Scratch_" + Interlocked.Increment(ref _scratchCount); ConnectionString = $"Data Source = {name}.db";
var db = new SqlServerTestStore(name);
return db;
} }
private readonly string _connectionString; public string ConnectionString { get; }
private SqlServerTestStore(string name)
{
_connectionString = $@"Server=(localdb)\mssqllocaldb;Database={name};Timeout=600";
}
public string ConnectionString
{
get { return _connectionString; }
}
private void EnsureDeleted() private void EnsureDeleted()
{ {
if (!PlatformHelper.IsMono) using (var db = new DbContext(new DbContextOptionsBuilder().UseSqlite(ConnectionString).Options))
{ {
var optionsBuilder = new DbContextOptionsBuilder(); db.Database.EnsureDeleted();
optionsBuilder.UseSqlServer(_connectionString, b => b.CommandTimeout(600).EnableRetryOnFailure());
using (var db = new DbContext(optionsBuilder.Options))
{
db.Database.EnsureDeleted();
}
} }
} }
public virtual void Dispose() public virtual void Dispose() => EnsureDeleted();
{
EnsureDeleted();
}
} }
} }

View File

@ -46,7 +46,7 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests
migrationBuilder.CreateTable("Blogs", migrationBuilder.CreateTable("Blogs",
c => new c => new
{ {
BlogId = c.Column<int>().Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn), BlogId = c.Column<int>().Annotation("Sqlite:Autoincrement", true),
Name = c.Column<string>(nullable: true), Name = c.Column<string>(nullable: true),
}) })
.PrimaryKey("PK_Blog", t => t.BlogId); .PrimaryKey("PK_Blog", t => t.BlogId);

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk.Web"> <Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework> <TargetFramework>netcoreapp3.0</TargetFramework>
@ -12,7 +12,7 @@
<ItemGroup> <ItemGroup>
<Reference Include="Microsoft.AspNetCore.Server.IISIntegration" /> <Reference Include="Microsoft.AspNetCore.Server.IISIntegration" />
<Reference Include="Microsoft.AspNetCore.Server.Kestrel" /> <Reference Include="Microsoft.AspNetCore.Server.Kestrel" />
<Reference Include="Microsoft.EntityFrameworkCore.SqlServer" /> <Reference Include="Microsoft.EntityFrameworkCore.Sqlite" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -12,7 +12,7 @@ namespace DatabaseErrorPageSample
public void ConfigureServices(IServiceCollection services) public void ConfigureServices(IServiceCollection services)
{ {
services.AddDbContext<MyContext>( services.AddDbContext<MyContext>(
options => options.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=DatabaseErrorPageSample;Trusted_Connection=True;")); options => options.UseSqlite($"Data Source = DatabaseErrorPageSample.db"));
} }
public void Configure(IApplicationBuilder app) public void Configure(IApplicationBuilder app)

View File

@ -32,10 +32,8 @@ namespace HealthChecksSample
// //
// The health check added by AddDbContextCheck will create instances of MyContext from the service provider, // The health check added by AddDbContextCheck will create instances of MyContext from the service provider,
// and so will reuse the configuration provided here. // and so will reuse the configuration provided here.
services.AddDbContext<MyContext>(options => services.AddDbContext<MyContext>(
{ options => options.UseSqlite(Configuration["ConnectionStrings:DefaultConnection"]));
options.UseSqlServer(Configuration["ConnectionStrings:DefaultConnection"]);
});
} }
public void Configure(IApplicationBuilder app) public void Configure(IApplicationBuilder app)

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk.Web"> <Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework> <TargetFramework>netcoreapp3.0</TargetFramework>
@ -8,7 +8,7 @@
<Reference Include="Microsoft.AspNetCore.Diagnostics.HealthChecks" /> <Reference Include="Microsoft.AspNetCore.Diagnostics.HealthChecks" />
<Reference Include="Microsoft.AspNetCore.Server.Kestrel" /> <Reference Include="Microsoft.AspNetCore.Server.Kestrel" />
<Reference Include="Microsoft.AspNetCore.StaticFiles" /> <Reference Include="Microsoft.AspNetCore.StaticFiles" />
<Reference Include="Microsoft.EntityFrameworkCore.SqlServer" /> <Reference Include="Microsoft.EntityFrameworkCore.Sqlite" />
<Reference Include="Microsoft.Extensions.Configuration.CommandLine" /> <Reference Include="Microsoft.Extensions.Configuration.CommandLine" />
<Reference Include="Microsoft.Extensions.Configuration.Json" /> <Reference Include="Microsoft.Extensions.Configuration.Json" />
<Reference Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" /> <Reference Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" />