From 0739c3f0013bb6f5e23212b95c96f44bc60d4370 Mon Sep 17 00:00:00 2001 From: Shay Rojansky Date: Sat, 22 Oct 2016 09:19:38 +0000 Subject: [PATCH] Added functional tests for NpgsqlDatabaseCreator --- ...workCore.PostgreSQL.FunctionalTests.csproj | 1 + .../NpgsqlDatabaseCreatorTest.cs | 421 ++++++++++++++++++ .../Utilities/NpgsqlTestStore.cs | 2 + 3 files changed, 424 insertions(+) create mode 100644 test/Npgsql.EntityFrameworkCore.PostgreSQL.FunctionalTests/NpgsqlDatabaseCreatorTest.cs diff --git a/test/Npgsql.EntityFrameworkCore.PostgreSQL.FunctionalTests/Npgsql.EntityFrameworkCore.PostgreSQL.FunctionalTests.csproj b/test/Npgsql.EntityFrameworkCore.PostgreSQL.FunctionalTests/Npgsql.EntityFrameworkCore.PostgreSQL.FunctionalTests.csproj index 2ecb020d0..a135e78a2 100644 --- a/test/Npgsql.EntityFrameworkCore.PostgreSQL.FunctionalTests/Npgsql.EntityFrameworkCore.PostgreSQL.FunctionalTests.csproj +++ b/test/Npgsql.EntityFrameworkCore.PostgreSQL.FunctionalTests/Npgsql.EntityFrameworkCore.PostgreSQL.FunctionalTests.csproj @@ -80,6 +80,7 @@ + diff --git a/test/Npgsql.EntityFrameworkCore.PostgreSQL.FunctionalTests/NpgsqlDatabaseCreatorTest.cs b/test/Npgsql.EntityFrameworkCore.PostgreSQL.FunctionalTests/NpgsqlDatabaseCreatorTest.cs new file mode 100644 index 000000000..f44ca0fbc --- /dev/null +++ b/test/Npgsql.EntityFrameworkCore.PostgreSQL.FunctionalTests/NpgsqlDatabaseCreatorTest.cs @@ -0,0 +1,421 @@ +// 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.Data; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Internal; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.EntityFrameworkCore.Storage.Internal; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Npgsql.EntityFrameworkCore.PostgreSQL.FunctionalTests.Utilities; +using Xunit; + +namespace Npgsql.EntityFrameworkCore.PostgreSQL.FunctionalTests +{ + public class NpgsqlDatabaseCreatorTest + { + [Fact] + public async Task Exists_returns_false_when_database_doesnt_exist() + { + await Exists_returns_false_when_database_doesnt_exist_test(async: false); + } + + [Fact] + public async Task ExistsAsync_returns_false_when_database_doesnt_exist() + { + await Exists_returns_false_when_database_doesnt_exist_test(async: true); + } + + private static async Task Exists_returns_false_when_database_doesnt_exist_test(bool async) + { + using (var testDatabase = NpgsqlTestStore.CreateScratch(createDatabase: false)) + { + var creator = GetDatabaseCreator(testDatabase); + + Assert.False(async ? await creator.ExistsAsync() : creator.Exists()); + } + } + + [Fact] + public async Task Exists_returns_true_when_database_exists() + { + await Exists_returns_true_when_database_exists_test(async: false); + } + + [Fact] + public async Task ExistsAsync_returns_true_when_database_exists() + { + await Exists_returns_true_when_database_exists_test(async: true); + } + + private static async Task Exists_returns_true_when_database_exists_test(bool async) + { + using (var testDatabase = NpgsqlTestStore.CreateScratch(createDatabase: true)) + { + var creator = GetDatabaseCreator(testDatabase); + + Assert.True(async ? await creator.ExistsAsync() : creator.Exists()); + } + } + + [Fact] + public async Task HasTables_throws_when_database_doesnt_exist() + { + await HasTables_throws_when_database_doesnt_exist_test(async: false); + } + + [Fact] + public async Task HasTablesAsync_throws_when_database_doesnt_exist() + { + await HasTables_throws_when_database_doesnt_exist_test(async: true); + } + + private static async Task HasTables_throws_when_database_doesnt_exist_test(bool async) + { + using (var testDatabase = NpgsqlTestStore.CreateScratch(createDatabase: false)) + { + await ((TestDatabaseCreator)GetDatabaseCreator(testDatabase)).ExecutionStrategyFactory.Create() + .ExecuteAsync(async creator => + { + var ex = async + ? await Assert.ThrowsAsync(() => creator.HasTablesAsyncBase()) + : Assert.Throws(() => creator.HasTablesBase()); + Assert.Equal( + "3D000", // Login failed error number + ex.SqlState); + }, (TestDatabaseCreator)GetDatabaseCreator(testDatabase)); + } + } + + [Fact] + public async Task HasTables_returns_false_when_database_exists_but_has_no_tables() + { + await HasTables_returns_false_when_database_exists_but_has_no_tables_test(async: false); + } + + [Fact] + public async Task HasTablesAsync_returns_false_when_database_exists_but_has_no_tables() + { + await HasTables_returns_false_when_database_exists_but_has_no_tables_test(async: true); + } + + private static async Task HasTables_returns_false_when_database_exists_but_has_no_tables_test(bool async) + { + using (var testDatabase = NpgsqlTestStore.CreateScratch(createDatabase: true)) + { + var creator = GetDatabaseCreator(testDatabase); + + Assert.False(async + ? await ((TestDatabaseCreator)creator).HasTablesAsyncBase() + : ((TestDatabaseCreator)creator).HasTablesBase()); + } + } + + [Fact] + public async Task HasTables_returns_true_when_database_exists_and_has_any_tables() + { + await HasTables_returns_true_when_database_exists_and_has_any_tables_test(async: false); + } + + [Fact] + public async Task HasTablesAsync_returns_true_when_database_exists_and_has_any_tables() + { + await HasTables_returns_true_when_database_exists_and_has_any_tables_test(async: true); + } + + private static async Task HasTables_returns_true_when_database_exists_and_has_any_tables_test(bool async) + { + using (var testDatabase = NpgsqlTestStore.CreateScratch(createDatabase: true)) + { + await testDatabase.ExecuteNonQueryAsync("CREATE TABLE some_table (id SERIAL PRIMARY KEY)"); + + var creator = GetDatabaseCreator(testDatabase); + + Assert.True(async ? await ((TestDatabaseCreator)creator).HasTablesAsyncBase() : ((TestDatabaseCreator)creator).HasTablesBase()); + } + } + + [Fact] + public async Task Delete_will_delete_database() + { + await Delete_will_delete_database_test(async: false); + } + + [Fact] + public async Task DeleteAsync_will_delete_database() + { + await Delete_will_delete_database_test(async: true); + } + + private static async Task Delete_will_delete_database_test(bool async) + { + using (var testDatabase = NpgsqlTestStore.CreateScratch(createDatabase: true)) + { + testDatabase.Connection.Close(); + + var creator = GetDatabaseCreator(testDatabase); + + Assert.True(async ? await creator.ExistsAsync() : creator.Exists()); + + if (async) + { + await creator.DeleteAsync(); + } + else + { + creator.Delete(); + } + + Assert.False(async ? await creator.ExistsAsync() : creator.Exists()); + } + } + + [Fact] + public async Task Delete_throws_when_database_doesnt_exist() + { + await Delete_throws_when_database_doesnt_exist_test(async: false); + } + + [Fact] + public async Task DeleteAsync_throws_when_database_doesnt_exist() + { + await Delete_throws_when_database_doesnt_exist_test(async: true); + } + + private static async Task Delete_throws_when_database_doesnt_exist_test(bool async) + { + using (var testDatabase = NpgsqlTestStore.CreateScratch(createDatabase: false)) + { + var creator = GetDatabaseCreator(testDatabase); + + if (async) + { + await Assert.ThrowsAsync(() => creator.DeleteAsync()); + } + else + { + Assert.Throws(() => creator.Delete()); + } + } + } + + [Fact] + public async Task CreateTables_creates_schema_in_existing_database() + { + await CreateTables_creates_schema_in_existing_database_test(async: false); + } + + [Fact] + public async Task CreateTablesAsync_creates_schema_in_existing_database() + { + await CreateTables_creates_schema_in_existing_database_test(async: true); + } + + private static async Task CreateTables_creates_schema_in_existing_database_test(bool async) + { + using (var testDatabase = NpgsqlTestStore.CreateScratch(createDatabase: true)) + { + var serviceProvider = new ServiceCollection() + .AddEntityFrameworkNpgsql() + .BuildServiceProvider(); + + var optionsBuilder = new DbContextOptionsBuilder() + .UseInternalServiceProvider(serviceProvider) + .UseNpgsql(testDatabase.ConnectionString, b => b.ApplyConfiguration()); + + using (var context = new BloggingContext(optionsBuilder.Options)) + { + var creator = (RelationalDatabaseCreator)context.GetService(); + + if (async) + { + await creator.CreateTablesAsync(); + } + else + { + creator.CreateTables(); + } + + if (testDatabase.Connection.State != ConnectionState.Open) + { + await testDatabase.Connection.OpenAsync(); + } + + var tables = await testDatabase.QueryAsync("SELECT table_name FROM information_schema.tables WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema')"); + Assert.Equal(1, tables.Count()); + Assert.Equal("Blogs", tables.Single()); + + var columns = await testDatabase.QueryAsync("SELECT table_name || '.' || column_name FROM information_schema.columns WHERE table_name='Blogs'"); + Assert.Equal(2, columns.Count()); + Assert.True(columns.Any(c => c == "Blogs.Id")); + Assert.True(columns.Any(c => c == "Blogs.Name")); + } + } + } + + [Fact] + public async Task CreateTables_throws_if_database_does_not_exist() + { + await CreateTables_throws_if_database_does_not_exist_test(async: false); + } + + [Fact] + public async Task CreateTablesAsync_throws_if_database_does_not_exist() + { + await CreateTables_throws_if_database_does_not_exist_test(async: true); + } + + private static async Task CreateTables_throws_if_database_does_not_exist_test(bool async) + { + using (var testDatabase = NpgsqlTestStore.CreateScratch(createDatabase: false)) + { + var creator = GetDatabaseCreator(testDatabase); + + var ex = async + ? await Assert.ThrowsAsync(() => creator.CreateTablesAsync()) + : Assert.Throws(() => creator.CreateTables()); + Assert.Equal( + "3D000", // Login failed error number + ex.SqlState); + } + } + + [Fact] + public async Task Create_creates_physical_database_but_not_tables() + { + await Create_creates_physical_database_but_not_tables_test(async: false); + } + + [Fact] + public async Task CreateAsync_creates_physical_database_but_not_tables() + { + await Create_creates_physical_database_but_not_tables_test(async: true); + } + + private static async Task Create_creates_physical_database_but_not_tables_test(bool async) + { + using (var testDatabase = NpgsqlTestStore.CreateScratch(createDatabase: false)) + { + var creator = GetDatabaseCreator(testDatabase); + + Assert.False(creator.Exists()); + + if (async) + { + await creator.CreateAsync(); + } + else + { + creator.Create(); + } + + Assert.True(creator.Exists()); + + if (testDatabase.Connection.State != ConnectionState.Open) + { + await testDatabase.Connection.OpenAsync(); + } + + Assert.Equal(0, (await testDatabase.QueryAsync("SELECT table_name FROM information_schema.tables WHERE table_type = 'BASE TABLE' AND table_schema NOT IN ('pg_catalog', 'information_schema')")).Count()); + } + } + + [Fact] + public async Task Create_throws_if_database_already_exists() + { + await Create_throws_if_database_already_exists_test(async: false); + } + + [Fact] + public async Task CreateAsync_throws_if_database_already_exists() + { + await Create_throws_if_database_already_exists_test(async: true); + } + + private static async Task Create_throws_if_database_already_exists_test(bool async) + { + using (var testDatabase = NpgsqlTestStore.CreateScratch(createDatabase: true)) + { + var creator = GetDatabaseCreator(testDatabase); + + var ex = async + ? await Assert.ThrowsAsync(() => creator.CreateAsync()) + : Assert.Throws(() => creator.Create()); + Assert.Equal( + "42P04", // Database with given name already exists + ex.SqlState); + } + } + + private static IServiceProvider CreateContextServices(NpgsqlTestStore testStore) + => ((IInfrastructure)new BloggingContext( + new DbContextOptionsBuilder() + .UseNpgsql(testStore.ConnectionString, b => b.ApplyConfiguration()) + .UseInternalServiceProvider(new ServiceCollection() + .AddEntityFrameworkNpgsql() + //.AddScoped() + .AddScoped().BuildServiceProvider()).Options)) + .Instance; + + private static IRelationalDatabaseCreator GetDatabaseCreator(NpgsqlTestStore testStore) + => CreateContextServices(testStore).GetRequiredService(); + + /* + // ReSharper disable once ClassNeverInstantiated.Local + private class TestNpgsqlExecutionStrategyFactory : NpgsqlExecutionStrategyFactory + { + public TestNpgsqlExecutionStrategyFactory(IDbContextOptions options, ICurrentDbContext currentDbContext, ILogger logger) + : base(options, currentDbContext, logger) + { + } + + protected override IExecutionStrategy CreateDefaultStrategy(ExecutionStrategyContext context) => NoopExecutionStrategy.Instance; + }*/ + + private class BloggingContext : DbContext + { + public BloggingContext(DbContextOptions options) + : base(options) + { + } + + public DbSet Blogs { get; set; } + + public class Blog + { + public int Id { get; set; } + public string Name { get; set; } + } + } + + public class TestDatabaseCreator : NpgsqlDatabaseCreator + { + public TestDatabaseCreator( + NpgsqlRelationalConnection connection, + IMigrationsModelDiffer modelDiffer, + IMigrationsSqlGenerator sqlGenerator, + IMigrationCommandExecutor migrationCommandExecutor, + IModel model, + IRawSqlCommandBuilder rawSqlCommandBuilder, + IExecutionStrategyFactory executionStrategyFactory) + : base(connection, modelDiffer, sqlGenerator, migrationCommandExecutor, model, rawSqlCommandBuilder, executionStrategyFactory) + { + } + + public bool HasTablesBase() => HasTables(); + + public Task HasTablesAsyncBase(CancellationToken cancellationToken = default(CancellationToken)) + => HasTablesAsync(cancellationToken); + + public new IExecutionStrategyFactory ExecutionStrategyFactory => base.ExecutionStrategyFactory; + } + } +} diff --git a/test/Npgsql.EntityFrameworkCore.PostgreSQL.FunctionalTests/Utilities/NpgsqlTestStore.cs b/test/Npgsql.EntityFrameworkCore.PostgreSQL.FunctionalTests/Utilities/NpgsqlTestStore.cs index 548e20f2f..06ee6f18c 100644 --- a/test/Npgsql.EntityFrameworkCore.PostgreSQL.FunctionalTests/Utilities/NpgsqlTestStore.cs +++ b/test/Npgsql.EntityFrameworkCore.PostgreSQL.FunctionalTests/Utilities/NpgsqlTestStore.cs @@ -178,6 +178,8 @@ static bool DatabaseExists(string name) void DeleteDatabase(string name) { + if (!DatabaseExists(name)) + return; using (var master = new NpgsqlConnection(CreateAdminConnectionString())) { ExecuteNonQuery(master, GetDisconnectDatabaseSql(name));