diff --git a/Curiosity.Migrations.sln b/Curiosity.Migrations.sln index eb0d6e0..2790148 100644 --- a/Curiosity.Migrations.sln +++ b/Curiosity.Migrations.sln @@ -27,7 +27,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Curiosity.Migrations.UnitTe EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Curiosity.Migrations.PostgreSql.IntegrationTests", "tests\IntegrationTests\Curiosity.Migrations.PostgreSql.IntegrationTests\Curiosity.Migrations.PostgreSql.IntegrationTests.csproj", "{3BDC5BA6-1A8B-443D-B3F7-D0983921E3C6}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Curiosity.Migrations.TransactionTests", "tests\IntegrationTests\Curiosity.Migrations.TransactionTests\Curiosity.Migrations.TransactionTests.csproj", "{A37CE3F0-5A47-49F9-A777-D3661607E4D5}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Curiosity.Migrations.IntegrationTests", "tests\IntegrationTests\Curiosity.Migrations.IntegrationTests\Curiosity.Migrations.IntegrationTests.csproj", "{A37CE3F0-5A47-49F9-A777-D3661607E4D5}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "images", "images", "{D80331ED-3602-4967-8813-2F38B9D1CCE3}" ProjectSection(SolutionItems) = preProject diff --git a/src/Curiosity.Migrations.PostgreSQL/Properties/AssemblyInfo.cs b/src/Curiosity.Migrations.PostgreSQL/Properties/AssemblyInfo.cs index 0165086..0ea3138 100644 --- a/src/Curiosity.Migrations.PostgreSQL/Properties/AssemblyInfo.cs +++ b/src/Curiosity.Migrations.PostgreSQL/Properties/AssemblyInfo.cs @@ -2,4 +2,4 @@ [assembly: InternalsVisibleTo("Curiosity.Migrations.PostgreSQL.UnitTests")] [assembly: InternalsVisibleTo("Curiosity.Migrations.PostgreSQL.IntegrationTests")] -[assembly: InternalsVisibleTo("Curiosity.Migrations.TransactionTests")] +[assembly: InternalsVisibleTo("Curiosity.Migrations.IntegrationTests")] diff --git a/src/Curiosity.Migrations/CHANGELOG.md b/src/Curiosity.Migrations/CHANGELOG.md index b0de756..bc81c20 100644 --- a/src/Curiosity.Migrations/CHANGELOG.md +++ b/src/Curiosity.Migrations/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [4.3.0] - 2023-11-14 + +### Added + +- Migration dependencies: ability to specify how one migration depends from another. +Both code and script migrations supports this. + ## [4.2.0] - 2023-04-21 ### Changed diff --git a/src/Curiosity.Migrations/Curiosity.Migrations.csproj b/src/Curiosity.Migrations/Curiosity.Migrations.csproj index fefc843..10e3c43 100644 --- a/src/Curiosity.Migrations/Curiosity.Migrations.csproj +++ b/src/Curiosity.Migrations/Curiosity.Migrations.csproj @@ -7,7 +7,7 @@ migrations; migration; migrator; versioning; migration-tool; .net-core; .netstandard; database; siisltd; curiosity English - 4.2.0 + 4.3.0 1.0.0 1.0.0 diff --git a/src/Curiosity.Migrations/MigrationEngine.cs b/src/Curiosity.Migrations/MigrationEngine.cs index 0e14312..aefc78f 100644 --- a/src/Curiosity.Migrations/MigrationEngine.cs +++ b/src/Curiosity.Migrations/MigrationEngine.cs @@ -159,7 +159,7 @@ private async Task MigrateAsync( await _migrationConnection.CreateMigrationHistoryTableIfNotExistsAsync(cancellationToken); _logger?.LogInformation($"Creating \"{_migrationConnection.MigrationHistoryTableName}\" table completed"); } - + // get migrations to apply var migrationsToApply = await GetMigrationsToApplyAsync(isUpgrade, false, cancellationToken); if (migrationsToApply.Count == 0) @@ -196,7 +196,7 @@ private async Task MigrateAsync( var policy = isUpgrade ? _upgradePolicy : _downgradePolicy; - var migrationResult = await MigrateAsync(migrationsToApply, isUpgrade, policy, cancellationToken); + var migrationResult = await ApplyMigrations(migrationsToApply, isUpgrade, policy, cancellationToken); await _migrationConnection.CloseConnectionAsync(); _logger?.LogInformation($"Migrating database \"{_migrationConnection.DatabaseName}\" completed. Successfully applied {migrationsToApply.Count} migrations"); @@ -380,7 +380,7 @@ private async Task ExecutePreMigrationScriptsAsync(CancellationToken token return true; } - private async Task<(IReadOnlyList Applied, IReadOnlyList Skipped)> MigrateAsync( + private async Task<(IReadOnlyList Applied, IReadOnlyList Skipped)> ApplyMigrations( IReadOnlyList orderedMigrations, bool isUpgrade, MigrationPolicy policy, @@ -399,8 +399,9 @@ private async Task ExecutePreMigrationScriptsAsync(CancellationToken token ? "Upgrade" : "Downgrade"; - var appliedMigrations = new List(orderedMigrations.Count); - var skippedMigrations = new List(); + var appliedMigrationVersions = new HashSet(await _migrationConnection.GetAppliedMigrationVersionsAsync(cancellationToken)); + var currentAppliedMigrations = new List(orderedMigrations.Count); + var currentSkippedMigrations = new List(); for (var i = 0; i < orderedMigrations.Count; i++) { @@ -410,17 +411,27 @@ private async Task ExecutePreMigrationScriptsAsync(CancellationToken token // check policies if (!policy.HasFlag(MigrationPolicy.LongRunningAllowed) && migration.IsLongRunning) { - skippedMigrations.Add(currentMigration); + currentSkippedMigrations.Add(currentMigration); _logger?.LogWarning($"Skip long-running migration \"{migration.Version}\" due to policy restriction"); continue; } if (!policy.HasFlag(MigrationPolicy.ShortRunningAllowed) && !migration.IsLongRunning) { - skippedMigrations.Add(currentMigration); + currentSkippedMigrations.Add(currentMigration); _logger?.LogWarning($"Skip short-running migration \"{migration.Version}\" due to policy restriction"); continue; } + if (migration.Dependencies.Count > 0) + { + foreach (var dependency in migration.Dependencies) + { + if(!appliedMigrationVersions.Contains(dependency)) + throw new MigrationException(MigrationErrorCode.MigratingError, + $"Migration with version \"{migration.Version}\" depends on unapplied migration \"{dependency}\""); + } + } + DbTransaction? transaction = null; try @@ -461,7 +472,8 @@ private async Task ExecutePreMigrationScriptsAsync(CancellationToken token // when disposed if either commands fails transaction?.Commit(); - appliedMigrations.Add(currentMigration); + currentAppliedMigrations.Add(currentMigration); + appliedMigrationVersions.Add(currentMigration.Version); _logger?.LogInformation($"{operationName} to \"{migration.Version}\" (database \"{_migrationConnection.DatabaseName}\") completed."); } catch (MigrationException e) @@ -486,7 +498,7 @@ private async Task ExecutePreMigrationScriptsAsync(CancellationToken token } } - return (appliedMigrations, skippedMigrations); + return (currentAppliedMigrations, currentSkippedMigrations); } /// diff --git a/src/Curiosity.Migrations/MigrationProviders/ScriptMigrationsProvider.cs b/src/Curiosity.Migrations/MigrationProviders/ScriptMigrationsProvider.cs index a729ede..beed266 100644 --- a/src/Curiosity.Migrations/MigrationProviders/ScriptMigrationsProvider.cs +++ b/src/Curiosity.Migrations/MigrationProviders/ScriptMigrationsProvider.cs @@ -316,6 +316,21 @@ private MigrationOptions ExtractMigrationOptions(string sourceScript) throw new InvalidOperationException($"Value \"{optionsMatch.Groups[2].Value}\" is not assignable to the option \"{optionsMatch.Groups[1].Value}\""); } + break; + case "DEPENDENCIES": + var migrationDependencies = optionsMatch.Groups[2].Value.ToUpper().Trim().TrimEnd(';'); + + if(String.IsNullOrWhiteSpace(migrationDependencies)) + throw new InvalidOperationException($"Value \"{optionsMatch.Groups[2].Value}\" is not assignable to the option \"{optionsMatch.Groups[1].Value}\""); + + var rawVersions = migrationDependencies.Split(','); + var versions = rawVersions + .Select(x => + MigrationVersion.TryParse(x.Trim(), out var version) + ? version + : throw new InvalidOperationException($"Can't parse migration dependency {version}")); + options.Dependencies.AddRange(versions); + break; default: throw new InvalidOperationException($"Option \"{optionsMatch.Groups[1].Value}\" is unknown"); @@ -324,7 +339,7 @@ private MigrationOptions ExtractMigrationOptions(string sourceScript) return options; } - + /// /// Creates script migration. Replace variables placeholders with real values /// @@ -370,7 +385,8 @@ private IMigration CreateScriptMigration( downScript, migrationScriptInfo.Comment, migrationScriptInfo.Options.IsTransactionRequired, - migrationScriptInfo.Options.IsLongRunning) + migrationScriptInfo.Options.IsLongRunning, + migrationScriptInfo.Options.Dependencies) : new ScriptMigration( migrationLogger, migrationConnection, @@ -378,7 +394,8 @@ private IMigration CreateScriptMigration( upScript, migrationScriptInfo.Comment, migrationScriptInfo.Options.IsTransactionRequired, - migrationScriptInfo.Options.IsLongRunning); + migrationScriptInfo.Options.IsLongRunning, + migrationScriptInfo.Options.Dependencies); } private struct ScriptParsingOptions @@ -418,5 +435,6 @@ private class MigrationOptions public bool IsTransactionRequired { get; set; } = true; public bool IsLongRunning { get; set; } + public List Dependencies { get; set; } = new(); } } diff --git a/src/Curiosity.Migrations/Migrations/CodeMigration.cs b/src/Curiosity.Migrations/Migrations/CodeMigration.cs index 4cff9c5..18ad0ce 100644 --- a/src/Curiosity.Migrations/Migrations/CodeMigration.cs +++ b/src/Curiosity.Migrations/Migrations/CodeMigration.cs @@ -29,6 +29,10 @@ public abstract class CodeMigration : IMigration /// public bool IsLongRunning { get; protected set; } = false; + /// + [SuppressMessage("ReSharper", "AutoPropertyCanBeMadeGetOnly.Global")] + public IReadOnlyList Dependencies { get; protected set; } = Array.Empty(); + /// /// Provides access to underlying database. /// @@ -64,6 +68,8 @@ internal void Init( MigrationConnection = migrationConnection ?? throw new ArgumentNullException(nameof(migrationConnection)); Variables = variables ?? throw new ArgumentNullException(nameof(variables)); Logger = migrationLogger; + + Dependencies ??= Array.Empty(); } /// diff --git a/src/Curiosity.Migrations/Migrations/DowngradeScriptMigration.cs b/src/Curiosity.Migrations/Migrations/DowngradeScriptMigration.cs index ab7b58e..fe74a6e 100644 --- a/src/Curiosity.Migrations/Migrations/DowngradeScriptMigration.cs +++ b/src/Curiosity.Migrations/Migrations/DowngradeScriptMigration.cs @@ -27,7 +27,8 @@ public DowngradeScriptMigration( IReadOnlyList? downScripts, string? comment, bool isTransactionRequired = true, - bool isLongRunning = false) + bool isLongRunning = false, + List? dependencies = null) : base( migrationLogger, migrationConnection, @@ -35,7 +36,8 @@ public DowngradeScriptMigration( upScripts, comment, isTransactionRequired, - isLongRunning) + isLongRunning, + dependencies) { DownScripts = downScripts?.ToArray() ?? Array.Empty(); } diff --git a/src/Curiosity.Migrations/Migrations/IMigration.cs b/src/Curiosity.Migrations/Migrations/IMigration.cs index b65e21d..d1a21ba 100644 --- a/src/Curiosity.Migrations/Migrations/IMigration.cs +++ b/src/Curiosity.Migrations/Migrations/IMigration.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using System.Data.Common; using System.Threading; using System.Threading.Tasks; @@ -35,6 +36,14 @@ public interface IMigration /// /// bool IsLongRunning { get; } + + /// + /// Migrations, that should be applied before + /// + /// + /// + /// + IReadOnlyList Dependencies { get; } /// /// Upgrades database to the version specified in . diff --git a/src/Curiosity.Migrations/Migrations/ScriptMigration.cs b/src/Curiosity.Migrations/Migrations/ScriptMigration.cs index 65c9890..9284a3a 100644 --- a/src/Curiosity.Migrations/Migrations/ScriptMigration.cs +++ b/src/Curiosity.Migrations/Migrations/ScriptMigration.cs @@ -33,6 +33,10 @@ public class ScriptMigration : IMigration [SuppressMessage("ReSharper", "AutoPropertyCanBeMadeGetOnly.Global")] public bool IsLongRunning { get; protected set; } + /// + [SuppressMessage("ReSharper", "AutoPropertyCanBeMadeGetOnly.Global")] + public IReadOnlyList Dependencies { get; protected set; } + /// /// SQL script to apply migration split into batches. /// @@ -46,7 +50,9 @@ public ScriptMigration( IReadOnlyList upScripts, string? comment, bool isTransactionRequired = true, - bool isLongRunning = false) + bool isLongRunning = false, + IReadOnlyList? dependencies = null + ) { Guard.AssertNotNull(migrationConnection, nameof(migrationConnection)); Guard.AssertNotEmpty(upScripts, nameof(upScripts)); @@ -59,6 +65,7 @@ public ScriptMigration( Comment = comment; IsTransactionRequired = isTransactionRequired; IsLongRunning = isLongRunning; + Dependencies = dependencies ?? Array.Empty(); } /// diff --git a/tests/IntegrationTests/Curiosity.Migrations.TransactionTests/Config.cs b/tests/IntegrationTests/Curiosity.Migrations.IntegrationTests/Config.cs similarity index 81% rename from tests/IntegrationTests/Curiosity.Migrations.TransactionTests/Config.cs rename to tests/IntegrationTests/Curiosity.Migrations.IntegrationTests/Config.cs index f9bd716..f72bd8a 100644 --- a/tests/IntegrationTests/Curiosity.Migrations.TransactionTests/Config.cs +++ b/tests/IntegrationTests/Curiosity.Migrations.IntegrationTests/Config.cs @@ -1,4 +1,4 @@ -namespace Curiosity.Migrations.TransactionTests; +namespace Curiosity.Migrations.IntegrationTests; public class Config { diff --git a/tests/IntegrationTests/Curiosity.Migrations.TransactionTests/ConfigProvider.cs b/tests/IntegrationTests/Curiosity.Migrations.IntegrationTests/ConfigProvider.cs similarity index 82% rename from tests/IntegrationTests/Curiosity.Migrations.TransactionTests/ConfigProvider.cs rename to tests/IntegrationTests/Curiosity.Migrations.IntegrationTests/ConfigProvider.cs index 0377113..c41d097 100644 --- a/tests/IntegrationTests/Curiosity.Migrations.TransactionTests/ConfigProvider.cs +++ b/tests/IntegrationTests/Curiosity.Migrations.IntegrationTests/ConfigProvider.cs @@ -1,6 +1,6 @@ using Microsoft.Extensions.Configuration; -namespace Curiosity.Migrations.TransactionTests; +namespace Curiosity.Migrations.IntegrationTests; public static class ConfigProvider { diff --git a/tests/IntegrationTests/Curiosity.Migrations.TransactionTests/Curiosity.Migrations.TransactionTests.csproj b/tests/IntegrationTests/Curiosity.Migrations.IntegrationTests/Curiosity.Migrations.IntegrationTests.csproj similarity index 65% rename from tests/IntegrationTests/Curiosity.Migrations.TransactionTests/Curiosity.Migrations.TransactionTests.csproj rename to tests/IntegrationTests/Curiosity.Migrations.IntegrationTests/Curiosity.Migrations.IntegrationTests.csproj index 296e77d..bab9b8d 100644 --- a/tests/IntegrationTests/Curiosity.Migrations.TransactionTests/Curiosity.Migrations.TransactionTests.csproj +++ b/tests/IntegrationTests/Curiosity.Migrations.IntegrationTests/Curiosity.Migrations.IntegrationTests.csproj @@ -5,7 +5,7 @@ false - Curiosity.Migrations.TransactionTests + Curiosity.Migrations.IntegrationTests 11 @@ -45,6 +45,24 @@ Always + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + diff --git a/tests/IntegrationTests/Curiosity.Migrations.TransactionTests/CodeMigrations/CodeMigration_2_0.cs b/tests/IntegrationTests/Curiosity.Migrations.IntegrationTests/DependenciesTests/DependencyCodeMigrations/CodeMigration_2_0.cs similarity index 76% rename from tests/IntegrationTests/Curiosity.Migrations.TransactionTests/CodeMigrations/CodeMigration_2_0.cs rename to tests/IntegrationTests/Curiosity.Migrations.IntegrationTests/DependenciesTests/DependencyCodeMigrations/CodeMigration_2_0.cs index bd0fcd6..28a09fd 100644 --- a/tests/IntegrationTests/Curiosity.Migrations.TransactionTests/CodeMigrations/CodeMigration_2_0.cs +++ b/tests/IntegrationTests/Curiosity.Migrations.IntegrationTests/DependenciesTests/DependencyCodeMigrations/CodeMigration_2_0.cs @@ -2,9 +2,9 @@ using System.Threading; using System.Threading.Tasks; -namespace Curiosity.Migrations.TransactionTests.CodeMigrations; +namespace Curiosity.Migrations.IntegrationTests.DependenciesTests.DependencyCodeMigrations; -public class CodeMigration_2_0 : CodeMigration +public class CodeMigration_2_0 : CodeMigration, IDependencyMigration { /// public override MigrationVersion Version { get; } = new(2); diff --git a/tests/IntegrationTests/Curiosity.Migrations.IntegrationTests/DependenciesTests/DependencyCodeMigrations/CodeMigration_3_0_OkDependencies.cs b/tests/IntegrationTests/Curiosity.Migrations.IntegrationTests/DependenciesTests/DependencyCodeMigrations/CodeMigration_3_0_OkDependencies.cs new file mode 100644 index 0000000..80caaa9 --- /dev/null +++ b/tests/IntegrationTests/Curiosity.Migrations.IntegrationTests/DependenciesTests/DependencyCodeMigrations/CodeMigration_3_0_OkDependencies.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; +using System.Data.Common; +using System.Threading; +using System.Threading.Tasks; + +namespace Curiosity.Migrations.IntegrationTests.DependenciesTests.DependencyCodeMigrations; + +public class CodeMigration_3_0_OkDependencies : CodeMigration, IDependencyMigration +{ + /// + public override MigrationVersion Version { get; } = new(3); + + /// + public override string Comment { get; } = "Migration using multiple EF context with one connection"; + + public CodeMigration_3_0_OkDependencies() + { + Dependencies = new List() { new(1,0), new(2,0) }; + } + + /// + public override async Task UpgradeAsync(DbTransaction? transaction = null, CancellationToken cancellationToken = default) + { + await MigrationConnection.ExecuteNonQuerySqlAsync("select 1;", null, cancellationToken); + } +} diff --git a/tests/IntegrationTests/Curiosity.Migrations.IntegrationTests/DependenciesTests/DependencyCodeMigrations/CodeMigration_5_0_NotOkDependencies.cs b/tests/IntegrationTests/Curiosity.Migrations.IntegrationTests/DependenciesTests/DependencyCodeMigrations/CodeMigration_5_0_NotOkDependencies.cs new file mode 100644 index 0000000..da75c44 --- /dev/null +++ b/tests/IntegrationTests/Curiosity.Migrations.IntegrationTests/DependenciesTests/DependencyCodeMigrations/CodeMigration_5_0_NotOkDependencies.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; +using System.Data.Common; +using System.Threading; +using System.Threading.Tasks; + +namespace Curiosity.Migrations.IntegrationTests.DependenciesTests.DependencyCodeMigrations; + +public class CodeMigration_5_0_NotOkDependencies : CodeMigration, IDependencyMigration +{ + /// + public override MigrationVersion Version { get; } = new(5); + + /// + public override string Comment => "Migrations with switched off transactions"; + + public CodeMigration_5_0_NotOkDependencies() + { + Dependencies = new List() { new(1,0), new(6,0) }; + IsLongRunning = true; + } + + public override async Task UpgradeAsync(DbTransaction? transaction = null, CancellationToken cancellationToken = default) + { + await MigrationConnection.ExecuteNonQuerySqlAsync("select 1;", null, cancellationToken); + } +} diff --git a/tests/IntegrationTests/Curiosity.Migrations.IntegrationTests/DependenciesTests/DependencyCodeMigrations/IDependencyMigration.cs b/tests/IntegrationTests/Curiosity.Migrations.IntegrationTests/DependenciesTests/DependencyCodeMigrations/IDependencyMigration.cs new file mode 100644 index 0000000..16455ba --- /dev/null +++ b/tests/IntegrationTests/Curiosity.Migrations.IntegrationTests/DependenciesTests/DependencyCodeMigrations/IDependencyMigration.cs @@ -0,0 +1,6 @@ +namespace Curiosity.Migrations.IntegrationTests.DependenciesTests.DependencyCodeMigrations; + +public interface IDependencyMigration +{ + +} \ No newline at end of file diff --git a/tests/IntegrationTests/Curiosity.Migrations.IntegrationTests/DependenciesTests/DependencyScriptMigrations/1.0-correct_script.sql b/tests/IntegrationTests/Curiosity.Migrations.IntegrationTests/DependenciesTests/DependencyScriptMigrations/1.0-correct_script.sql new file mode 100644 index 0000000..2d8cb83 --- /dev/null +++ b/tests/IntegrationTests/Curiosity.Migrations.IntegrationTests/DependenciesTests/DependencyScriptMigrations/1.0-correct_script.sql @@ -0,0 +1,8 @@ +CREATE TABLE public.background_processor_requests +( + id bigserial NOT NULL, + CONSTRAINT background_processor_requests_pkey PRIMARY KEY (id) +); + +ALTER TABLE public.background_processor_requests + OWNER to %USER%; \ No newline at end of file diff --git a/tests/IntegrationTests/Curiosity.Migrations.IntegrationTests/DependenciesTests/DependencyScriptMigrations/4.0-script_with_ok_dependencies.sql b/tests/IntegrationTests/Curiosity.Migrations.IntegrationTests/DependenciesTests/DependencyScriptMigrations/4.0-script_with_ok_dependencies.sql new file mode 100644 index 0000000..308ef94 --- /dev/null +++ b/tests/IntegrationTests/Curiosity.Migrations.IntegrationTests/DependenciesTests/DependencyScriptMigrations/4.0-script_with_ok_dependencies.sql @@ -0,0 +1,10 @@ +--CURIOSITY:Dependencies=1.0, 2.0 + +CREATE TABLE public.background_processor_requests1 +( + id bigserial NOT NULL, + CONSTRAINT background_processor_requests_pkey1 PRIMARY KEY (id) +); + +ALTER TABLE public.background_processor_requests1 + OWNER to %USER%; \ No newline at end of file diff --git a/tests/IntegrationTests/Curiosity.Migrations.IntegrationTests/DependenciesTests/DependencyScriptMigrations/7.0-script_with_not_ok_dependencies.sql b/tests/IntegrationTests/Curiosity.Migrations.IntegrationTests/DependenciesTests/DependencyScriptMigrations/7.0-script_with_not_ok_dependencies.sql new file mode 100644 index 0000000..d6b35c1 --- /dev/null +++ b/tests/IntegrationTests/Curiosity.Migrations.IntegrationTests/DependenciesTests/DependencyScriptMigrations/7.0-script_with_not_ok_dependencies.sql @@ -0,0 +1,3 @@ +--CURIOSITY:Dependencies=5.0, 8.0 + +DROP TABLE public.background_processor_requests; \ No newline at end of file diff --git a/tests/IntegrationTests/Curiosity.Migrations.IntegrationTests/DependenciesTests/DependencyTests.cs b/tests/IntegrationTests/Curiosity.Migrations.IntegrationTests/DependenciesTests/DependencyTests.cs new file mode 100644 index 0000000..78da56f --- /dev/null +++ b/tests/IntegrationTests/Curiosity.Migrations.IntegrationTests/DependenciesTests/DependencyTests.cs @@ -0,0 +1,121 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Threading.Tasks; +using Curiosity.Migrations.IntegrationTests.DependenciesTests.DependencyCodeMigrations; +using Curiosity.Migrations.PostgreSQL; +using Xunit; + +namespace Curiosity.Migrations.IntegrationTests.DependenciesTests; + +public class DependencyTests +{ + [Fact] + public async Task Migrate_Script_OkDependencies() + { + var config = ConfigProvider.GetConfig(); + var connectionString = String.Format(config.ConnectionStringMask, "test_script_code_ok"); + + + var builder = new MigrationEngineBuilder(); + builder.UseCodeMigrations().FromAssembly(Assembly.GetExecutingAssembly()); + builder.UseScriptMigrations().FromDirectory(Path.Combine(Directory.GetCurrentDirectory(), "DependenciesTests/DependencyScriptMigrations")); + builder.ConfigureForPostgreSql(connectionString); + + builder.UseUpgradeMigrationPolicy(MigrationPolicy.AllAllowed); + builder.UseDowngradeMigrationPolicy(MigrationPolicy.AllAllowed); + builder.SetUpTargetVersion(new MigrationVersion(4)); + + var migrator = builder.Build(); + + var result = await migrator.UpgradeDatabaseAsync(); + + var migrationProvider = new PostgresMigrationConnection(new PostgresMigrationConnectionOptions(connectionString)); + await migrationProvider.OpenConnectionAsync(); + var actualAppliedMigrations = await migrationProvider.GetAppliedMigrationVersionsAsync(); + await migrationProvider.CloseConnectionAsync(); + + var expectedAppliedMigrations = new HashSet + { + new(1), + new(2), + new(3), + new(4) + }; + Assert.True(result.IsSuccessfully); + Assert.Equal(expectedAppliedMigrations, actualAppliedMigrations); + } + + [Fact] + public async Task Migrate_Script_CodeNotOkDependencies() + { + var config = ConfigProvider.GetConfig(); + var connectionString = String.Format(config.ConnectionStringMask, "test_code_not_ok"); + + + var builder = new MigrationEngineBuilder(); + builder.UseCodeMigrations().FromAssembly(Assembly.GetExecutingAssembly()); + builder.UseScriptMigrations().FromDirectory(Path.Combine(Directory.GetCurrentDirectory(), "DependenciesTests/DependencyScriptMigrations")); + builder.ConfigureForPostgreSql(connectionString); + + builder.UseUpgradeMigrationPolicy(MigrationPolicy.AllAllowed); + builder.UseDowngradeMigrationPolicy(MigrationPolicy.AllAllowed); + builder.SetUpTargetVersion(new MigrationVersion(5)); + + var migrator = builder.Build(); + + var result = await migrator.UpgradeDatabaseAsync(); + + var migrationProvider = new PostgresMigrationConnection(new PostgresMigrationConnectionOptions(connectionString)); + await migrationProvider.OpenConnectionAsync(); + var actualAppliedMigrations = await migrationProvider.GetAppliedMigrationVersionsAsync(); + await migrationProvider.CloseConnectionAsync(); + + var expectedAppliedMigrations = new HashSet + { + new(1), + new(2), + new(3), + new(4) + }; + Assert.True(result.ErrorCode == MigrationErrorCode.MigratingError); + Assert.Equal(expectedAppliedMigrations, actualAppliedMigrations); + } + + [Fact] + public async Task Migrate_Script_ScriptNotOkDependencies() + { + var config = ConfigProvider.GetConfig(); + var connectionString = String.Format(config.ConnectionStringMask, "test_script_not_ok"); + + + var builder = new MigrationEngineBuilder(); + builder.UseCodeMigrations().FromAssembly(Assembly.GetExecutingAssembly()); + builder.UseScriptMigrations().FromDirectory(Path.Combine(Directory.GetCurrentDirectory(), "DependenciesTests/DependencyScriptMigrations")); + builder.ConfigureForPostgreSql(connectionString); + + builder.UseUpgradeMigrationPolicy(MigrationPolicy.ShortRunningAllowed); + builder.UseDowngradeMigrationPolicy(MigrationPolicy.AllAllowed); + builder.SetUpTargetVersion(new MigrationVersion(7)); + + var migrator = builder.Build(); + + var result = await migrator.UpgradeDatabaseAsync(); + + var migrationProvider = new PostgresMigrationConnection(new PostgresMigrationConnectionOptions(connectionString)); + await migrationProvider.OpenConnectionAsync(); + var actualAppliedMigrations = await migrationProvider.GetAppliedMigrationVersionsAsync(); + await migrationProvider.CloseConnectionAsync(); + + var expectedAppliedMigrations = new HashSet + { + new(1), + new(2), + new(3), + new(4) + }; + Assert.True(result.ErrorCode == MigrationErrorCode.MigratingError); + Assert.Equal(expectedAppliedMigrations, actualAppliedMigrations); + } +} \ No newline at end of file diff --git a/tests/IntegrationTests/Curiosity.Migrations.IntegrationTests/TransactionsTests/TransactionCodeMigrations/CodeMigration_2_0.cs b/tests/IntegrationTests/Curiosity.Migrations.IntegrationTests/TransactionsTests/TransactionCodeMigrations/CodeMigration_2_0.cs new file mode 100644 index 0000000..00d4d21 --- /dev/null +++ b/tests/IntegrationTests/Curiosity.Migrations.IntegrationTests/TransactionsTests/TransactionCodeMigrations/CodeMigration_2_0.cs @@ -0,0 +1,20 @@ +using System.Data.Common; +using System.Threading; +using System.Threading.Tasks; + +namespace Curiosity.Migrations.IntegrationTests.TransactionsTests.TransactionCodeMigrations; + +public class CodeMigration_2_0 : CodeMigration, ITransactionMigration +{ + /// + public override MigrationVersion Version { get; } = new(2); + + /// + public override string Comment => "Correct script via provider"; + + /// + public override async Task UpgradeAsync(DbTransaction? transaction = null, CancellationToken cancellationToken = default) + { + await MigrationConnection.ExecuteNonQuerySqlAsync("select 1;", null, cancellationToken); + } +} diff --git a/tests/IntegrationTests/Curiosity.Migrations.TransactionTests/CodeMigrations/CodeMigration_3_0.cs b/tests/IntegrationTests/Curiosity.Migrations.IntegrationTests/TransactionsTests/TransactionCodeMigrations/CodeMigration_3_0.cs similarity index 96% rename from tests/IntegrationTests/Curiosity.Migrations.TransactionTests/CodeMigrations/CodeMigration_3_0.cs rename to tests/IntegrationTests/Curiosity.Migrations.IntegrationTests/TransactionsTests/TransactionCodeMigrations/CodeMigration_3_0.cs index 26b3e35..e8ec144 100644 --- a/tests/IntegrationTests/Curiosity.Migrations.TransactionTests/CodeMigrations/CodeMigration_3_0.cs +++ b/tests/IntegrationTests/Curiosity.Migrations.IntegrationTests/TransactionsTests/TransactionCodeMigrations/CodeMigration_3_0.cs @@ -5,9 +5,9 @@ using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; -namespace Curiosity.Migrations.TransactionTests.CodeMigrations; +namespace Curiosity.Migrations.IntegrationTests.TransactionsTests.TransactionCodeMigrations; -public class CodeMigration_3_0 : CodeMigration +public class CodeMigration_3_0 : CodeMigration, ITransactionMigration { /// public override MigrationVersion Version { get; } = new(3); diff --git a/tests/IntegrationTests/Curiosity.Migrations.TransactionTests/CodeMigrations/CodeMigration_5_0 _TransactionOff.cs b/tests/IntegrationTests/Curiosity.Migrations.IntegrationTests/TransactionsTests/TransactionCodeMigrations/CodeMigration_5_0 _TransactionOff.cs similarity index 96% rename from tests/IntegrationTests/Curiosity.Migrations.TransactionTests/CodeMigrations/CodeMigration_5_0 _TransactionOff.cs rename to tests/IntegrationTests/Curiosity.Migrations.IntegrationTests/TransactionsTests/TransactionCodeMigrations/CodeMigration_5_0 _TransactionOff.cs index 6ec7737..c35df8c 100644 --- a/tests/IntegrationTests/Curiosity.Migrations.TransactionTests/CodeMigrations/CodeMigration_5_0 _TransactionOff.cs +++ b/tests/IntegrationTests/Curiosity.Migrations.IntegrationTests/TransactionsTests/TransactionCodeMigrations/CodeMigration_5_0 _TransactionOff.cs @@ -5,9 +5,9 @@ using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; -namespace Curiosity.Migrations.TransactionTests.CodeMigrations; +namespace Curiosity.Migrations.IntegrationTests.TransactionsTests.TransactionCodeMigrations; -public class CodeMigration_5_0 : CodeMigration +public class CodeMigration_5_0 : CodeMigration, ITransactionMigration { /// public override MigrationVersion Version { get; } = new(5); diff --git a/tests/IntegrationTests/Curiosity.Migrations.IntegrationTests/TransactionsTests/TransactionCodeMigrations/ITransactionMigration.cs b/tests/IntegrationTests/Curiosity.Migrations.IntegrationTests/TransactionsTests/TransactionCodeMigrations/ITransactionMigration.cs new file mode 100644 index 0000000..74d0836 --- /dev/null +++ b/tests/IntegrationTests/Curiosity.Migrations.IntegrationTests/TransactionsTests/TransactionCodeMigrations/ITransactionMigration.cs @@ -0,0 +1,6 @@ +namespace Curiosity.Migrations.IntegrationTests.TransactionsTests.TransactionCodeMigrations; + +public interface ITransactionMigration +{ + +} \ No newline at end of file diff --git a/tests/IntegrationTests/Curiosity.Migrations.TransactionTests/ScriptMigrations/1.0-correct_script.sql b/tests/IntegrationTests/Curiosity.Migrations.IntegrationTests/TransactionsTests/TransactionScriptMigrations/1.0-correct_script.sql similarity index 100% rename from tests/IntegrationTests/Curiosity.Migrations.TransactionTests/ScriptMigrations/1.0-correct_script.sql rename to tests/IntegrationTests/Curiosity.Migrations.IntegrationTests/TransactionsTests/TransactionScriptMigrations/1.0-correct_script.sql diff --git a/tests/IntegrationTests/Curiosity.Migrations.TransactionTests/ScriptMigrations/4.0-script_without_transction.sql b/tests/IntegrationTests/Curiosity.Migrations.IntegrationTests/TransactionsTests/TransactionScriptMigrations/4.0-script_without_transction.sql similarity index 100% rename from tests/IntegrationTests/Curiosity.Migrations.TransactionTests/ScriptMigrations/4.0-script_without_transction.sql rename to tests/IntegrationTests/Curiosity.Migrations.IntegrationTests/TransactionsTests/TransactionScriptMigrations/4.0-script_without_transction.sql diff --git a/tests/IntegrationTests/Curiosity.Migrations.TransactionTests/ScriptMigrations/6.0-incorrect_script.sql b/tests/IntegrationTests/Curiosity.Migrations.IntegrationTests/TransactionsTests/TransactionScriptMigrations/6.0-incorrect_script.sql similarity index 100% rename from tests/IntegrationTests/Curiosity.Migrations.TransactionTests/ScriptMigrations/6.0-incorrect_script.sql rename to tests/IntegrationTests/Curiosity.Migrations.IntegrationTests/TransactionsTests/TransactionScriptMigrations/6.0-incorrect_script.sql diff --git a/tests/IntegrationTests/Curiosity.Migrations.TransactionTests/TransactionTests.cs b/tests/IntegrationTests/Curiosity.Migrations.IntegrationTests/TransactionsTests/TransactionTests.cs similarity index 85% rename from tests/IntegrationTests/Curiosity.Migrations.TransactionTests/TransactionTests.cs rename to tests/IntegrationTests/Curiosity.Migrations.IntegrationTests/TransactionsTests/TransactionTests.cs index 78e7413..236165b 100644 --- a/tests/IntegrationTests/Curiosity.Migrations.TransactionTests/TransactionTests.cs +++ b/tests/IntegrationTests/Curiosity.Migrations.IntegrationTests/TransactionsTests/TransactionTests.cs @@ -3,10 +3,11 @@ using System.IO; using System.Reflection; using System.Threading.Tasks; +using Curiosity.Migrations.IntegrationTests.TransactionsTests.TransactionCodeMigrations; using Curiosity.Migrations.PostgreSQL; using Xunit; -namespace Curiosity.Migrations.TransactionTests; +namespace Curiosity.Migrations.IntegrationTests.TransactionsTests; public class TransactionTests { @@ -18,8 +19,8 @@ public async Task Migrate_AllScriptOk_NoRollback() var builder = new MigrationEngineBuilder(); - builder.UseCodeMigrations().FromAssembly(Assembly.GetExecutingAssembly()); - builder.UseScriptMigrations().FromDirectory(Path.Combine(Directory.GetCurrentDirectory(), "ScriptMigrations")); + builder.UseCodeMigrations().FromAssembly(Assembly.GetExecutingAssembly()); + builder.UseScriptMigrations().FromDirectory(Path.Combine(Directory.GetCurrentDirectory(), "TransactionsTests/TransactionScriptMigrations")); builder.ConfigureForPostgreSql(connectionString); builder.UseUpgradeMigrationPolicy(MigrationPolicy.AllAllowed); @@ -53,8 +54,8 @@ public async Task Migrate_AllScriptOk_SwitchedOffTransaction() var builder = new MigrationEngineBuilder(); - builder.UseCodeMigrations().FromAssembly(Assembly.GetExecutingAssembly()); - builder.UseScriptMigrations().FromDirectory(Path.Combine(Directory.GetCurrentDirectory(), "ScriptMigrations")); + builder.UseCodeMigrations().FromAssembly(Assembly.GetExecutingAssembly()); + builder.UseScriptMigrations().FromDirectory(Path.Combine(Directory.GetCurrentDirectory(), "TransactionsTests/TransactionScriptMigrations")); builder.ConfigureForPostgreSql(connectionString); builder.UseUpgradeMigrationPolicy(MigrationPolicy.AllAllowed); @@ -89,8 +90,8 @@ public async Task Migrate_AllScriptOk_Rollback() var builder = new MigrationEngineBuilder(); - builder.UseCodeMigrations().FromAssembly(Assembly.GetExecutingAssembly()); - builder.UseScriptMigrations().FromDirectory(Path.Combine(Directory.GetCurrentDirectory(), "ScriptMigrations")); + builder.UseCodeMigrations().FromAssembly(Assembly.GetExecutingAssembly()); + builder.UseScriptMigrations().FromDirectory(Path.Combine(Directory.GetCurrentDirectory(), "TransactionsTests/TransactionScriptMigrations")); builder.ConfigureForPostgreSql(connectionString); builder.UseUpgradeMigrationPolicy(MigrationPolicy.AllAllowed); diff --git a/tests/IntegrationTests/Curiosity.Migrations.TransactionTests/config.yml b/tests/IntegrationTests/Curiosity.Migrations.IntegrationTests/config.yml similarity index 100% rename from tests/IntegrationTests/Curiosity.Migrations.TransactionTests/config.yml rename to tests/IntegrationTests/Curiosity.Migrations.IntegrationTests/config.yml diff --git a/tests/UnitTests/Curiosity.Migrations.UnitTests/DbMigrator_Should.cs b/tests/UnitTests/Curiosity.Migrations.UnitTests/DbMigrator_Should.cs index 0190f83..2a8d177 100644 --- a/tests/UnitTests/Curiosity.Migrations.UnitTests/DbMigrator_Should.cs +++ b/tests/UnitTests/Curiosity.Migrations.UnitTests/DbMigrator_Should.cs @@ -83,15 +83,21 @@ public async Task UpgradeToSpecifiedTarget_On_MigrateAsync() firstMigration .Setup(x => x.Version) .Returns(new MigrationVersion(1)); + firstMigration.Setup(x => x.Dependencies) + .Returns(new List()); var secondMigration = new Mock(); secondMigration .Setup(x => x.Version) .Returns(new MigrationVersion(1, 1)); + secondMigration.Setup(x => x.Dependencies) + .Returns(new List()); var thirdMigration = new Mock(); thirdMigration .Setup(x => x.Version) .Returns(new MigrationVersion(1, 2)); - + thirdMigration.Setup(x => x.Dependencies) + .Returns(new List()); + var migrations = new List { firstMigration.Object, @@ -145,15 +151,21 @@ public async Task UpgradeWithNotSpecifiedTarget_On_MigrateAsync() firstMigration .Setup(x => x.Version) .Returns(new MigrationVersion(1)); + firstMigration.Setup(x => x.Dependencies) + .Returns(new List()); var secondMigration = new Mock(); secondMigration .Setup(x => x.Version) .Returns(new MigrationVersion(1, 1)); + secondMigration.Setup(x => x.Dependencies) + .Returns(new List()); var thirdMigration = new Mock(); thirdMigration .Setup(x => x.Version) .Returns(new MigrationVersion(1, 2)); - + thirdMigration.Setup(x => x.Dependencies) + .Returns(new List()); + var migrations = new List { firstMigration.Object, @@ -257,6 +269,8 @@ public async Task SkipLongRunningMigration_On_MigrateAsync() firstMigration .Setup(x => x.Version) .Returns(new MigrationVersion(1)); + firstMigration.Setup(x => x.Dependencies) + .Returns(new List()); var secondMigration = new Mock(); secondMigration .Setup(x => x.Version) @@ -264,6 +278,8 @@ public async Task SkipLongRunningMigration_On_MigrateAsync() secondMigration .Setup(x => x.IsLongRunning) .Returns(true); + secondMigration.Setup(x => x.Dependencies) + .Returns(new List()); var thirdMigration = new Mock(); thirdMigration .Setup(x => x.Version) @@ -271,11 +287,15 @@ public async Task SkipLongRunningMigration_On_MigrateAsync() thirdMigration .Setup(x => x.IsLongRunning) .Returns(true); + thirdMigration.Setup(x => x.Dependencies) + .Returns(new List()); var fourthMigration = new Mock(); fourthMigration .Setup(x => x.Version) .Returns(new MigrationVersion(2)); - + fourthMigration.Setup(x => x.Dependencies) + .Returns(new List()); + var migrations = new List { firstMigration.Object, @@ -328,6 +348,8 @@ public async Task SkipShortRunningMigration_On_MigrateAsync() firstMigration .Setup(x => x.Version) .Returns(new MigrationVersion(1)); + firstMigration.Setup(x => x.Dependencies) + .Returns(new List()); var secondMigration = new Mock(); secondMigration .Setup(x => x.Version) @@ -335,6 +357,8 @@ public async Task SkipShortRunningMigration_On_MigrateAsync() secondMigration .Setup(x => x.IsLongRunning) .Returns(true); + secondMigration.Setup(x => x.Dependencies) + .Returns(new List()); var thirdMigration = new Mock(); thirdMigration .Setup(x => x.Version) @@ -342,11 +366,15 @@ public async Task SkipShortRunningMigration_On_MigrateAsync() thirdMigration .Setup(x => x.IsLongRunning) .Returns(true); + thirdMigration.Setup(x => x.Dependencies) + .Returns(new List()); var fourthMigration = new Mock(); fourthMigration .Setup(x => x.Version) .Returns(new MigrationVersion(2)); - + fourthMigration.Setup(x => x.Dependencies) + .Returns(new List()); + var migrations = new List { firstMigration.Object, @@ -399,6 +427,8 @@ public async Task ExecutesOnlyTargetLongRunning_On_MigrateAsync() firstMigration .Setup(x => x.Version) .Returns(new MigrationVersion(1)); + firstMigration.Setup(x => x.Dependencies) + .Returns(new List()); var secondMigration = new Mock(); secondMigration .Setup(x => x.Version) @@ -406,6 +436,8 @@ public async Task ExecutesOnlyTargetLongRunning_On_MigrateAsync() secondMigration .Setup(x => x.IsLongRunning) .Returns(true); + secondMigration.Setup(x => x.Dependencies) + .Returns(new List()); var thirdMigration = new Mock(); thirdMigration .Setup(x => x.Version) @@ -413,11 +445,15 @@ public async Task ExecutesOnlyTargetLongRunning_On_MigrateAsync() thirdMigration .Setup(x => x.IsLongRunning) .Returns(true); + thirdMigration.Setup(x => x.Dependencies) + .Returns(new List()); var fourthMigration = new Mock(); fourthMigration .Setup(x => x.Version) .Returns(new MigrationVersion(2)); - + fourthMigration.Setup(x => x.Dependencies) + .Returns(new List()); + var migrations = new List { firstMigration.Object, @@ -681,6 +717,9 @@ private IMigration GetIMigrationMock(string version) .Setup(x => x.Version) .Returns(new MigrationVersion(version)); + migration.Setup(x => x.Dependencies) + .Returns(new List()); + return migration.Object; } @@ -711,14 +750,20 @@ public async Task MigrateAsync_Downgrade_Ok() firstMigration .Setup(x => x.Version) .Returns(new MigrationVersion(1)); + firstMigration.Setup(x => x.Dependencies) + .Returns(new List()); var secondMigration = new Mock(); secondMigration .Setup(x => x.Version) .Returns(new MigrationVersion(1, 1)); + secondMigration.Setup(x => x.Dependencies) + .Returns(new List()); var thirdMigration = new Mock(); thirdMigration .Setup(x => x.Version) .Returns(new MigrationVersion(1, 2)); + thirdMigration.Setup(x => x.Dependencies) + .Returns(new List()); var migrations = new List { @@ -770,19 +815,27 @@ public async Task MigrateAsync_DowngradeForbidden_Error() firstMigration .Setup(x => x.Version) .Returns(new MigrationVersion(1)); + firstMigration.Setup(x => x.Dependencies) + .Returns(new List()); var secondMigration = new Mock(); secondMigration .Setup(x => x.Version) .Returns(new MigrationVersion(1, 1)); + secondMigration.Setup(x => x.Dependencies) + .Returns(new List()); var thirdMigration = new Mock(); thirdMigration .Setup(x => x.Version) .Returns(new MigrationVersion(1, 2)); + thirdMigration.Setup(x => x.Dependencies) + .Returns(new List()); var fourthMigration = new Mock(); fourthMigration .Setup(x => x.Version) .Returns(new MigrationVersion(2)); - + fourthMigration.Setup(x => x.Dependencies) + .Returns(new List()); + var migrations = new List { firstMigration.Object,