diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9c00ca1..7144b76 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,9 +11,9 @@ jobs: build: strategy: matrix: - os: [windows-latest] + os: [ubuntu-latest] fail-fast: false - runs-on: windows-latest + runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v2 @@ -23,8 +23,6 @@ jobs: uses: actions/setup-dotnet@v1 with: dotnet-version: '8.0.x' - - name: Start SQL Local DB - run: sqllocaldb start mssqllocaldb - name: Build and Test run: ./Build.ps1 shell: pwsh diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8e85079..d7aff19 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -20,8 +20,6 @@ jobs: uses: actions/setup-dotnet@v1 with: dotnet-version: '8.0.x' - - name: Start SQL Local DB - run: sqllocaldb start mssqllocaldb - name: Build and Test run: ./Build.ps1 shell: pwsh diff --git a/Build.ps1 b/Build.ps1 index 53056ab..d536afe 100644 --- a/Build.ps1 +++ b/Build.ps1 @@ -30,10 +30,6 @@ exec { & dotnet clean --configuration Release } exec { & dotnet build --configuration Release } -if (-Not (Test-Path 'env:CI')) { - exec { & docker-compose up -d } -} - exec { & dotnet test --configuration Release --results-directory $artifacts --no-build --logger trx --verbosity=normal } exec { & dotnet pack .\Respawn\Respawn.csproj --configuration Release --output $artifacts --no-build } diff --git a/Directory.Build.props b/Directory.Build.props index 2825fa0..2f9a220 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,7 +1,10 @@ - 10.0 + 12.0 $(NoWarn);CS1701;CS1702;CS1591 true + + $([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::get_OSX()))) + $([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::get_Windows()))) diff --git a/Push.ps1 b/Push.ps1 index 9663563..989042d 100644 --- a/Push.ps1 +++ b/Push.ps1 @@ -6,7 +6,7 @@ if ([string]::IsNullOrEmpty($Env:NUGET_API_KEY)) { } else { Get-ChildItem $artifacts -Filter "*.nupkg" | ForEach-Object { Write-Host "$($scriptName): Pushing $($_.Name)" - dotnet nuget push $_ --source $Env:NUGET_URL --api-key $Env:NUGET_API_KEY + dotnet nuget push $_ --source $Env:NUGET_URL --api-key $Env:NUGET_API_KEY --skip-duplicate if ($lastexitcode -ne 0) { throw ("Exec: " + $errorMessage) } diff --git a/Respawn.DatabaseTests/DB2Tests.cs b/Respawn.DatabaseTests/DB2Tests.cs index c1896ac..df5da1e 100644 --- a/Respawn.DatabaseTests/DB2Tests.cs +++ b/Respawn.DatabaseTests/DB2Tests.cs @@ -1,9 +1,12 @@ -using IBM.Data.DB2.Core; -using Shouldly; +using Shouldly; using System; using System.Threading.Tasks; +using DotNet.Testcontainers.Builders; +using DotNet.Testcontainers.Images; +using IBM.Data.Db2; using NPoco; using Respawn.Graph; +using Testcontainers.Db2; using Xunit; using Xunit.Abstractions; @@ -11,29 +14,36 @@ namespace Respawn.DatabaseTests { public class DB2Tests : IAsyncLifetime { + private Db2Container _sqlContainer; private DB2Connection _connection; private readonly ITestOutputHelper _output; - private const string _connectionString = "Server=127.0.0.1:50000;Database=SAMPLEDB;UID=db2inst1;PWD=password;Persist Security Info=True;Authentication=Server;"; - public DB2Tests(ITestOutputHelper output) { _output = output; } - public Task DisposeAsync() + public async Task InitializeAsync() { - _connection?.Close(); - _connection?.Dispose(); - _connection = null; - return Task.FromResult(0); + _sqlContainer = new Db2Builder() + .WithAcceptLicenseAgreement(true) + .Build(); + await _sqlContainer.StartAsync(); + + _connection = new DB2Connection(_sqlContainer.GetConnectionString()); + + await _connection.OpenAsync(); } - public async Task InitializeAsync() + public async Task DisposeAsync() { - _connection = new DB2Connection(_connectionString); + _connection?.Close(); + _connection?.Dispose(); + _connection = null; - await _connection.OpenAsync(); + await _sqlContainer.StopAsync(); + await _sqlContainer.DisposeAsync(); + _sqlContainer = null; } [SkipOnCI] diff --git a/Respawn.DatabaseTests/EmptyDbTests.cs b/Respawn.DatabaseTests/EmptyDbTests.cs index e3b0ae3..009a993 100644 --- a/Respawn.DatabaseTests/EmptyDbTests.cs +++ b/Respawn.DatabaseTests/EmptyDbTests.cs @@ -1,6 +1,7 @@ using System.Threading.Tasks; using Microsoft.Data.SqlClient; using Respawn.Graph; +using Testcontainers.MsSql; using Xunit; using Xunit.Abstractions; @@ -15,6 +16,7 @@ public class EmptyDbTests : IAsyncLifetime { private SqlConnection _connection; private Database _database; + private MsSqlContainer _msSqlContainer; public EmptyDbTests(ITestOutputHelper output) @@ -23,7 +25,10 @@ public EmptyDbTests(ITestOutputHelper output) public async Task InitializeAsync() { - var connString = @"Server=(LocalDb)\mssqllocaldb;Database=tempdb;Integrated Security=True"; + _msSqlContainer = new MsSqlBuilder().Build(); + await _msSqlContainer.StartAsync(); + + var connString = _msSqlContainer.GetConnectionString(); await using (var connection = new SqlConnection(connString)) { @@ -35,10 +40,13 @@ public async Task InitializeAsync() await database.ExecuteAsync("create database [EmptyDbTests]"); } } + + var newConnString = new SqlConnectionStringBuilder(connString) + { + InitialCatalog = "EmptyDbTests" + }.ConnectionString; - connString = @"Server=(LocalDb)\mssqllocaldb;Database=EmptyDbTests;Integrated Security=True"; - - _connection = new SqlConnection(connString); + _connection = new SqlConnection(newConnString); _connection.Open(); _database = new Database(_connection); diff --git a/Respawn.DatabaseTests/InformixTests.cs b/Respawn.DatabaseTests/InformixTests.cs index a37a482..57e4380 100644 --- a/Respawn.DatabaseTests/InformixTests.cs +++ b/Respawn.DatabaseTests/InformixTests.cs @@ -1,11 +1,15 @@ //#if INFORMIX -using IBM.Data.DB2.Core; using Respawn; using Shouldly; using System; using System.Data; +using System.IO; using System.Linq; using System.Threading.Tasks; +using DotNet.Testcontainers.Builders; +using DotNet.Testcontainers.Configurations; +using DotNet.Testcontainers.Containers; +using IBM.Data.Db2; using NPoco; using Respawn.Graph; using Xunit; @@ -18,24 +22,76 @@ public class InformixTests : IAsyncLifetime private DB2Connection _connection; private readonly ITestOutputHelper _output; private string _databaseName; + private IContainer _sqlContainer; public InformixTests(ITestOutputHelper output) { _output = output; } - public Task DisposeAsync() + public async Task DisposeAsync() { _connection?.Close(); _connection?.Dispose(); _connection = null; - return Task.FromResult(0); + + await _sqlContainer.StopAsync(); + await _sqlContainer.DisposeAsync(); + _sqlContainer = null; } public async Task InitializeAsync() { - const string connString = "Server=127.0.0.1:9089;Database=sysadmin;UID=informix;PWD=in4mix;Persist Security Info=True;Authentication=Server;"; - + //const string connString = "Server=127.0.0.1:9089;Database=sysadmin;UID=informix;PWD=in4mix;Persist Security Info=True;Authentication=Server;"; + + _sqlContainer = new ContainerBuilder() + .WithImage("ibmcom/informix-developer-database:14.10.FC5DE") + + // = environment: + .WithEnvironment("LICENSE", "accept") + .WithEnvironment("ONCONFIG_FILE", "onconfig") + .WithEnvironment("RUN_FILE_PRE_INIT", "my_post.sh") + + // = ports: + .WithPortBinding(9088, 9088) + .WithPortBinding(9089, 9089) + .WithPortBinding(27017, 27017) + .WithPortBinding(27018, 27018) + .WithPortBinding(27883, 27883) + + // = volumes: + .WithBindMount( + source: Path.GetFullPath("./informix-server"), + destination: "/opt/ibm/config", + AccessMode.ReadWrite) + + // = privileged: true + .WithPrivileged(true) + + // = user: root + //.WithUser("root") + + // = tty: true + //.WithTty(true) + + // optional: equivalent to "restart: always" but Testcontainers + // does not automatically restart containers (it recreates instead) + // .WithAutoRemove(false) + + .WithWaitStrategy(Wait.ForUnixContainer() + .UntilExternalTcpPortIsAvailable(9088) + .UntilExternalTcpPortIsAvailable(9089) + .UntilInternalTcpPortIsAvailable(9088) + .UntilInternalTcpPortIsAvailable(9089) + // This is the last success message + .UntilMessageIsLogged("starting mqtt listener on port 27883") + ) + .Build(); + + await _sqlContainer.StartAsync(); + + var connString = GetConnectionString(_sqlContainer); + await using (var connection = new DB2Connection(connString)) { await connection.OpenAsync(); @@ -47,13 +103,33 @@ public async Task InitializeAsync() await database.ExecuteAsync($"CREATE DATABASE {_databaseName} WITH BUFFERED LOG;"); } - var testDbConnString = $"Server=127.0.0.1:9089;Database={_databaseName};UID=informix;PWD=in4mix;Persist Security Info=True;Authentication=Server;"; + var testDbConnString = new DB2ConnectionStringBuilder(connString) + { + Database = _databaseName + }.ToString(); _connection = new DB2Connection(testDbConnString); await _connection.OpenAsync(); } + private static string GetConnectionString( + IContainer container, + string database = "sysadmin", + string user = "informix", + string password = "in4mix") + { + var host = container.Hostname; + var port = container.GetMappedPublicPort(9089); // SQL port + + return + $"Server={host}:{port};" + + $"Database={database};" + + $"UID={user};" + + $"Password={password};" + + $"Persist Security Info=True;Authentication=Server;"; + } + [SkipOnCI] public async Task ShouldDeleteData() { diff --git a/Respawn.DatabaseTests/MySqlTests.cs b/Respawn.DatabaseTests/MySqlTests.cs index e0fbc8b..5ad306c 100644 --- a/Respawn.DatabaseTests/MySqlTests.cs +++ b/Respawn.DatabaseTests/MySqlTests.cs @@ -1,6 +1,7 @@ using System.Threading.Tasks; using MySql.Data.MySqlClient; using Respawn.Graph; +using Testcontainers.MySql; using Xunit; using Xunit.Abstractions; @@ -11,12 +12,13 @@ namespace Respawn.DatabaseTests using NPoco; using Shouldly; - public class MySqlTests : IDisposable + public class MySqlTests : IAsyncLifetime { private readonly ITestOutputHelper _output; private MySqlConnection _connection; - private readonly IDatabase _database; - + private IDatabase _database; + private MySqlContainer _sqlContainer; + public class Foo { public int Value { get; set; } @@ -26,24 +28,35 @@ public class Bar public int Value { get; set; } } - public MySqlTests(ITestOutputHelper output) + + public async Task InitializeAsync() { - _output = output; - var isCI = Environment.GetEnvironmentVariable("CI")?.ToUpperInvariant() == "TRUE"; - - var connString = - isCI - ? @"Server=127.0.0.1; port = 3306; User Id = root; Password = Password12!" - : @"Server=127.0.0.1; port = 8082; User Id = root; Password = testytest"; + _sqlContainer = new MySqlBuilder() + .WithImage("mysql:8.0") + .WithDatabase("MySqlTests") + .Build(); + await _sqlContainer.StartAsync(); + + var connString = _sqlContainer.GetConnectionString(); _connection = new MySqlConnection(connString); _connection.Open(); _database = new Database(_connection); + } - _database.Execute(@"DROP DATABASE IF EXISTS MySqlTests"); - _database.Execute("create database MySqlTests"); - _database.Execute("use MySqlTests"); + public async Task DisposeAsync() + { + _connection?.Close(); + _connection?.Dispose(); + _connection = null; + await _sqlContainer.StopAsync(); + await _sqlContainer.DisposeAsync(); + } + + public MySqlTests(ITestOutputHelper output) + { + _output = output; } [SkipOnCI] @@ -364,5 +377,6 @@ public void Dispose() _connection.Close(); _connection.Dispose(); } + } } diff --git a/Respawn.DatabaseTests/OracleTests.cs b/Respawn.DatabaseTests/OracleTests.cs index eabde91..6da7a6e 100644 --- a/Respawn.DatabaseTests/OracleTests.cs +++ b/Respawn.DatabaseTests/OracleTests.cs @@ -1,4 +1,5 @@ -using Xunit.Abstractions; +using Testcontainers.Oracle; +using Xunit.Abstractions; #if ORACLE namespace Respawn.DatabaseTests @@ -17,14 +18,15 @@ public class OracleTests : IAsyncLifetime private OracleConnection _connection; private Database _database; private string _createdUser; + private OracleContainer _sqlContainer; - public class foo + public class Foo { - public int value { get; set; } + public int Value { get; set; } } - public class bar + public class Bar { - public int value { get; set; } + public int Value { get; set; } } public OracleTests(ITestOutputHelper output) @@ -35,14 +37,36 @@ public OracleTests(ITestOutputHelper output) public async Task InitializeAsync() { _createdUser = Guid.NewGuid().ToString().Substring(0, 8); - await CreateUser(_createdUser); - _connection = new OracleConnection("Data Source=(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=localhost)(PORT=10521))(CONNECT_DATA=(SID=xe)));User Id=\"" + _createdUser + "\";Password=123456;"); + + _sqlContainer = new OracleBuilder() + .WithImage("gvenzl/oracle-xe:21.3.0-slim-faststart") + .WithUsername(_createdUser) + .Build(); + await _sqlContainer.StartAsync(); + + var connString = _sqlContainer.GetConnectionString(); + + _connection = new OracleConnection(connString); await _connection.OpenAsync(); _database = new Database(_connection, DatabaseType.OracleManaged); } + + public async Task DisposeAsync() + { + _database.Dispose(); + _database = null; + + _connection.Close(); + _connection.Dispose(); + _connection = null; + + await _sqlContainer.StopAsync(); + await _sqlContainer.DisposeAsync(); + } + [SkipOnCI] public async Task ShouldDeleteData() { @@ -463,9 +487,10 @@ public async Task ShouldIncludeSchemas() await DropUser(userB); } - private static async Task CreateUser(string userName) + private async Task CreateUser(string userName) { - using (var connection = new OracleConnection("Data Source=(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=localhost)(PORT=10521))(CONNECT_DATA=(SID=xe)));User Id=system;Password=oracle;")) + var connString = _sqlContainer.GetConnectionString(); + using (var connection = new OracleConnection(connString)) { await connection.OpenAsync(); @@ -482,9 +507,10 @@ private static async Task CreateUser(string userName) } } - private static async Task DropUser(string userName) + private async Task DropUser(string userName) { - using (var connection = new OracleConnection("Data Source=(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=localhost)(PORT=10521))(CONNECT_DATA=(SID=xe)));User Id=system;Password=oracle;")) + var connString = _sqlContainer.GetConnectionString(); + using (var connection = new OracleConnection(connString)) { await connection.OpenAsync(); @@ -509,20 +535,6 @@ private static async Task DropUser(string userName) } } - public async Task DisposeAsync() - { - _database.Dispose(); - _database = null; - - OracleConnection.ClearPool(_connection); - - _connection.Close(); - _connection.Dispose(); - _connection = null; - - // Clean up our mess before leaving - await DropUser(_createdUser); - } } } #endif \ No newline at end of file diff --git a/Respawn.DatabaseTests/PostgresTests.cs b/Respawn.DatabaseTests/PostgresTests.cs index b2859ae..039f601 100644 --- a/Respawn.DatabaseTests/PostgresTests.cs +++ b/Respawn.DatabaseTests/PostgresTests.cs @@ -1,6 +1,7 @@ using System.Linq; using System.Threading.Tasks; using Respawn.Graph; +using Testcontainers.PostgreSql; using Xunit; using Xunit.Abstractions; @@ -16,41 +17,55 @@ public class PostgresTests : IAsyncLifetime private readonly ITestOutputHelper _output; private NpgsqlConnection _connection; private Database _database; + private PostgreSqlContainer _sqlContainer; public PostgresTests(ITestOutputHelper output) => _output = output; public async Task InitializeAsync() { - var rootConnString = "Server=127.0.0.1;Port=8081;User ID=docker;Password=Password12!;database=postgres"; - var dbConnString = "Server=127.0.0.1;Port=8081;User ID=docker;Password=Password12!;database={0}"; - if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("CI"))) - { - rootConnString = "Server=127.0.0.1;Port=5432;User ID=postgres;Password=root;database=postgres"; - dbConnString = "Server=127.0.0.1;Port=5432;User ID=postgres;Password=root;database={0}"; - } + // var rootConnString = "Server=127.0.0.1;Port=8081;User ID=docker;Password=Password12!;database=postgres"; + // var dbConnString = "Server=127.0.0.1;Port=8081;User ID=docker;Password=Password12!;database={0}"; + // if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("CI"))) + // { + // rootConnString = "Server=127.0.0.1;Port=5432;User ID=postgres;Password=root;database=postgres"; + // dbConnString = "Server=127.0.0.1;Port=5432;User ID=postgres;Password=root;database={0}"; + // } + // var dbName = DateTime.Now.ToString("yyyyMMddHHmmss") + Guid.NewGuid().ToString("N"); + // await using (var connection = new NpgsqlConnection(rootConnString)) + // { + // connection.Open(); + // + // await using (var cmd = connection.CreateCommand()) + // { + // cmd.CommandText = "create database \"" + dbName + "\""; + // await cmd.ExecuteNonQueryAsync(); + // } + // } + var dbName = DateTime.Now.ToString("yyyyMMddHHmmss") + Guid.NewGuid().ToString("N"); - await using (var connection = new NpgsqlConnection(rootConnString)) - { - connection.Open(); - - await using (var cmd = connection.CreateCommand()) - { - cmd.CommandText = "create database \"" + dbName + "\""; - await cmd.ExecuteNonQueryAsync(); - } - } - _connection = new NpgsqlConnection(string.Format(dbConnString, dbName)); + + _sqlContainer = new PostgreSqlBuilder() + .WithImage("postgres:16") + .WithDatabase(dbName) + .Build(); + + await _sqlContainer.StartAsync(); + + var dbConnString = _sqlContainer.GetConnectionString(); + + _connection = new NpgsqlConnection(dbConnString); _connection.Open(); _database = new Database(_connection, DatabaseType.PostgreSQL); } - public Task DisposeAsync() + public async Task DisposeAsync() { _connection?.Close(); _connection?.Dispose(); _connection = null; - return Task.FromResult(0); + await _sqlContainer.StopAsync(); + await _sqlContainer.DisposeAsync(); } [SkipOnCI] diff --git a/Respawn.DatabaseTests/Respawn.DatabaseTests.csproj b/Respawn.DatabaseTests/Respawn.DatabaseTests.csproj index 459dd48..0da14ac 100644 --- a/Respawn.DatabaseTests/Respawn.DatabaseTests.csproj +++ b/Respawn.DatabaseTests/Respawn.DatabaseTests.csproj @@ -5,23 +5,32 @@ $(NoWarn);NU1902;NU1903;NU1904 - + + - - - + + + + + + + + - + - + + PreserveNewest + + \ No newline at end of file diff --git a/Respawn.DatabaseTests/SqlServerTests.cs b/Respawn.DatabaseTests/SqlServerTests.cs index 8e1b2f2..7139438 100644 --- a/Respawn.DatabaseTests/SqlServerTests.cs +++ b/Respawn.DatabaseTests/SqlServerTests.cs @@ -1,6 +1,7 @@ using System.Threading.Tasks; using Microsoft.Data.SqlClient; using Respawn.Graph; +using Testcontainers.MsSql; using Xunit; using Xunit.Abstractions; @@ -16,6 +17,7 @@ public class SqlServerTests : IAsyncLifetime private readonly ITestOutputHelper _output; private SqlConnection _connection; private Database _database; + private MsSqlContainer _sqlContainer; public class Foo { @@ -49,7 +51,12 @@ public class Child public async Task InitializeAsync() { - var connString = @"Server=(LocalDb)\mssqllocaldb;Database=tempdb;Integrated Security=True"; + _sqlContainer = new MsSqlBuilder() + .WithImage("mcr.microsoft.com/mssql/server:2022-CU10-ubuntu-22.04") + .Build(); + await _sqlContainer.StartAsync(); + + var connString = _sqlContainer.GetConnectionString(); await using (var connection = new SqlConnection(connString)) { @@ -61,21 +68,25 @@ public async Task InitializeAsync() await database.ExecuteAsync("create database [SqlServerTests]"); } } + + var newConnString = new SqlConnectionStringBuilder(connString) + { + InitialCatalog = "SqlServerTests" + }.ConnectionString; - connString = @"Server=(LocalDb)\mssqllocaldb;Database=SqlServerTests;Integrated Security=True"; - - _connection = new SqlConnection(connString); + _connection = new SqlConnection(newConnString); _connection.Open(); _database = new Database(_connection); } - public Task DisposeAsync() + public async Task DisposeAsync() { _connection?.Close(); _connection?.Dispose(); _connection = null; - return Task.FromResult(0); + await _sqlContainer.StopAsync(); + await _sqlContainer.DisposeAsync(); } [Fact] @@ -272,7 +283,7 @@ public async Task ShouldHandleCircularRelationships() { await checkpoint.ResetAsync(_connection); } - catch + catch { _output.WriteLine(checkpoint.DeleteSql); throw; @@ -383,7 +394,7 @@ public async Task ShouldIncludeTables() [Fact] public async Task ShouldExcludeSchemas() - { + { await _database.ExecuteAsync("drop schema if exists A"); await _database.ExecuteAsync("drop schema if exists B"); await _database.ExecuteAsync("create schema A"); @@ -481,10 +492,10 @@ public async Task ShouldReseedId_TableWithSchema() { await _database.ExecuteAsync("IF EXISTS (SELECT * FROM sys.schemas WHERE name = 'A') DROP SCHEMA A"); await _database.ExecuteAsync("create schema A"); - await _database.ExecuteAsync("create table A.Foo ([id] [int] IDENTITY(1,1), Value int)"); - - for (int i = 0; i < 100; i++) - { + await _database.ExecuteAsync("create table A.Foo ([id] [int] IDENTITY(1,1), Value int)"); + + for (int i = 0; i < 100; i++) + { await _database.ExecuteAsync("INSERT A.Foo VALUES (" + i + ")"); } @@ -502,8 +513,8 @@ public async Task ShouldReseedId_TableWithSchema() { _output.WriteLine(checkpoint.ReseedSql); throw; - } - + } + await _database.ExecuteAsync("INSERT A.Foo VALUES (0)"); _database.ExecuteScalar("SELECT MAX(id) FROM A.Foo").ShouldBe(1); @@ -511,7 +522,7 @@ public async Task ShouldReseedId_TableWithSchema() [Fact] public async Task ShouldReseedId_TableHasNeverHadAnyData() - { + { await _database.ExecuteAsync("drop schema if exists A"); await _database.ExecuteAsync("create schema A"); await _database.ExecuteAsync("create table A.Foo ([id] [int] IDENTITY(1,1), Value int)"); @@ -527,12 +538,12 @@ public async Task ShouldReseedId_TableHasNeverHadAnyData() { _output.WriteLine(checkpoint.ReseedSql); throw; - } - + } + await _database.ExecuteAsync("INSERT A.Foo VALUES (0)"); _database.ExecuteScalar("SELECT MAX(id) FROM A.Foo").ShouldBe(1); - } - + } + [Fact] public async Task ShouldReseedId_TableWithSchemaHasNeverHadAnyData() { @@ -580,17 +591,17 @@ public async Task ShouldNotReseedId() await _database.InsertAsync(new Foo { Value = 0 }); _database.ExecuteScalar("SELECT MAX(id) FROM Foo").ShouldBe(101); - } - + } + [Fact] public async Task ShouldNotReseedId_TableWithSchema() - { + { await _database.ExecuteAsync("drop schema if exists A"); await _database.ExecuteAsync("create schema A"); - await _database.ExecuteAsync("create table A.Foo ([id] [int] IDENTITY(1,1), Value int)"); - - for (int i = 0; i < 100; i++) - { + await _database.ExecuteAsync("create table A.Foo ([id] [int] IDENTITY(1,1), Value int)"); + + for (int i = 0; i < 100; i++) + { await _database.ExecuteAsync("INSERT A.Foo VALUES (" + i + ")"); } @@ -608,8 +619,8 @@ public async Task ShouldNotReseedId_TableWithSchema() { _output.WriteLine(checkpoint.ReseedSql); throw; - } - + } + await _database.ExecuteAsync("INSERT A.Foo VALUES (0)"); _database.ExecuteScalar("SELECT MAX(id) FROM A.Foo").ShouldBe(101); } @@ -643,14 +654,14 @@ public async Task ShouldReseedIdAccordingToIdentityInitialSeedValue() } [Fact] - public async Task ShouldReseedIdAccordingToIdentityInitialSeedValue_TableWithSchema() - { + public async Task ShouldReseedIdAccordingToIdentityInitialSeedValue_TableWithSchema() + { await _database.ExecuteAsync("drop schema if exists A"); - await _database.ExecuteAsync("create schema A"); - await _database.ExecuteAsync("create table A.Foo ([id] [int] IDENTITY(1001,1), Value int)"); - - for (int i = 0; i < 100; i++) - { + await _database.ExecuteAsync("create schema A"); + await _database.ExecuteAsync("create table A.Foo ([id] [int] IDENTITY(1001,1), Value int)"); + + for (int i = 0; i < 100; i++) + { await _database.ExecuteAsync("INSERT A.Foo VALUES (" + i + ")"); } @@ -669,10 +680,10 @@ public async Task ShouldReseedIdAccordingToIdentityInitialSeedValue_TableWithSch { _output.WriteLine(checkpoint.ReseedSql); throw; - } - + } + await _database.ExecuteAsync("INSERT A.Foo VALUES (0)"); - _database.ExecuteScalar("SELECT MAX(id) FROM A.Foo").ShouldBe(1001); + _database.ExecuteScalar("SELECT MAX(id) FROM A.Foo").ShouldBe(1001); } [Fact] @@ -697,11 +708,11 @@ public async Task ShouldReseedIdAccordingToIdentityInitialSeedValue_TableHasNeve await _database.InsertAsync(new Foo { Value = 0 }); _database.ExecuteScalar("SELECT MAX(id) FROM Foo").ShouldBe(1001); - } - + } + [Fact] public async Task ShouldReseedIdAccordingToIdentityInitialSeedValue_TableWithSchemaHasNeverHadAnyData() - { + { await _database.ExecuteAsync("drop schema if exists A"); await _database.ExecuteAsync("create schema A"); await _database.ExecuteAsync("create table A.Foo ([id] [int] IDENTITY(1001,1), Value int)"); @@ -719,8 +730,8 @@ public async Task ShouldReseedIdAccordingToIdentityInitialSeedValue_TableWithSch { _output.WriteLine(checkpoint.ReseedSql); throw; - } - + } + await _database.ExecuteAsync("INSERT A.Foo VALUES (0)"); _database.ExecuteScalar("SELECT MAX(id) FROM A.Foo").ShouldBe(1001); } @@ -808,13 +819,13 @@ FROM sys.tables t1 WHERE t1.object_id = (SELECT history_table_id FROM sys.tables t2 WHERE t2.name = 'Foo') "; _database.ExecuteScalar(sql).ShouldStartWith("MSSQL_TemporalHistoryFor_"); - } - + } + [Fact] public async Task ShouldDeleteTemporalTablesDataFromNotDefaultSchemas() { - await _database.ExecuteAsync("CREATE SCHEMA [TableSchema] AUTHORIZATION [dbo];"); - await _database.ExecuteAsync("CREATE SCHEMA [HistorySchema] AUTHORIZATION [dbo];"); + await _database.ExecuteAsync("CREATE SCHEMA [TableSchema] AUTHORIZATION [dbo];"); + await _database.ExecuteAsync("CREATE SCHEMA [HistorySchema] AUTHORIZATION [dbo];"); await _database.ExecuteAsync("create table TableSchema.Foo (Value [int] not null primary key clustered, " + "ValidFrom datetime2 generated always as row start, " + diff --git a/Respawn/DB2DbAdapter.cs b/Respawn/DB2DbAdapter.cs index cc031cf..96e357d 100644 --- a/Respawn/DB2DbAdapter.cs +++ b/Respawn/DB2DbAdapter.cs @@ -220,5 +220,10 @@ public Task CheckSupportsTemporalTables(DbConnection connection) { return Task.FromResult(false); } + + public bool RequiresStatementsToBeExecutedIndividually() + { + return false; + } } } diff --git a/Respawn/Respawner.cs b/Respawn/Respawner.cs index 24a25a8..5832d43 100644 --- a/Respawn/Respawner.cs +++ b/Respawn/Respawner.cs @@ -78,6 +78,9 @@ public virtual async Task ResetAsync(DbConnection connection) try { + if (Options.DbAdapter == null) + throw new InvalidOperationException("Database adapter is not set in options."); + if (Options.DbAdapter.RequiresStatementsToBeExecutedIndividually()) { await ExecuteDeleteSqlIndividuallyAsync(connection).ConfigureAwait(false); diff --git a/Respawn/SnowflakeDbAdapter.cs b/Respawn/SnowflakeDbAdapter.cs index 027031e..cd6fa0d 100644 --- a/Respawn/SnowflakeDbAdapter.cs +++ b/Respawn/SnowflakeDbAdapter.cs @@ -103,7 +103,7 @@ public string BuildRelationshipCommandText(RespawnerOptions options) return commandText; } - public string BuildDeleteCommandText(GraphBuilder graph) + public string BuildDeleteCommandText(GraphBuilder graph, RespawnerOptions options) { var builder = new StringBuilder(); diff --git a/Respawn/SqliteDbAdapter.cs b/Respawn/SqliteDbAdapter.cs index c7c52cc..8afcfba 100644 --- a/Respawn/SqliteDbAdapter.cs +++ b/Respawn/SqliteDbAdapter.cs @@ -103,5 +103,10 @@ public Task CheckSupportsTemporalTables(DbConnection connection) { return Task.FromResult(false); } + + public bool RequiresStatementsToBeExecutedIndividually() + { + return false; + } } }