New Backoffice: Refactor migrations to allow for unscoped migrations (#13654)

* Remove PostMigrations

These should be replaced with Notification usage

* Remove outer scope from Upgrader

* Remove unececary null check

* Add marker base class for migrations

* Enable scopeless migrations

* Remove unnecessary state check

The final state of the migration is no longer necessarily the final state of the plan.

* Extend ExecutedMigrationPlan

* Ensure that MigrationPlanExecutor.Execute always returns a result.

* Always save final state, regardless of errors

* Remove obsolete Execute

* Add Umbraco specific migration notification

* Publish notification after umbraco migration

* Throw the exception that failed a migration after publishing notification

* Handle notification publishing in DatabaseBuilder

* Fix tests

* Remember to complete scope

* Clean up MigrationPlanExecutor

* Run each package migration in a separate scope

* Add PartialMigrationsTests

* Add unhappy path test

* Fix bug shown by test

* Move PartialMigrationsTests into the correct folder

* Comment out refresh cache in data type migration

Need to add this back again as a notification handler or something.

* Start working on a notification test

* Allow migrations to request a cache rebuild

* Set RebuildCache from MigrateDataTypeConfigurations

* Clean MigrationPlanExecutor

* Add comment explaining the need to partial migration success

* Fix tests

* Allow overriding DefinePlan of UmbracoPlan

This is needed to test the DatabaseBuilder

* Fix notification test

* Don't throw exception to be immediately re-caught

* Assert that scopes notification are always published

* Ensure that scopes are created when requested

* Make test classes internal.

It doesn't really matter, but this way it doesn't show up in intellisense

* Add notification handler for clearing cookies

* Add CompatibilitySuppressions

* Rename Execute to ExecutePlan

We have to do this to be able to obsolete :(

* Update CompatibilitySuppressions

* Update src/Umbraco.Infrastructure/Migrations/MigrationPlanExecutor.cs

Co-authored-by: Bjarke Berg <mail@bergmania.dk>

Co-authored-by: Bjarke Berg <mail@bergmania.dk>
This commit is contained in:
Mole
2023-01-16 14:52:51 +01:00
committed by GitHub
parent 01224fce89
commit 13fb2a4f52
30 changed files with 790 additions and 424 deletions

View File

@@ -9,9 +9,12 @@ using Microsoft.Extensions.Options;
using Moq;
using NPoco;
using NUnit.Framework;
using Umbraco.Cms.Core.Cache;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.Events;
using Umbraco.Cms.Core.PublishedCache;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Sync;
using Umbraco.Cms.Infrastructure.Migrations;
using Umbraco.Cms.Infrastructure.Migrations.Upgrade;
using Umbraco.Cms.Infrastructure.Persistence;
@@ -37,6 +40,11 @@ public class MigrationPlanTests
.Setup(x => x.Database)
.Returns(database);
var databaseFactory = Mock.Of<IUmbracoDatabaseFactory>();
Mock.Get(databaseFactory)
.Setup(x => x.CreateDatabase())
.Returns(database);
var sqlContext = new SqlContext(
new SqlServerSyntaxProvider(Options.Create(new GlobalSettings())),
DatabaseType.SQLCe,
@@ -59,7 +67,11 @@ public class MigrationPlanTests
}
});
var executor = new MigrationPlanExecutor(scopeProvider, scopeProvider, loggerFactory, migrationBuilder);
var distributedCache = new DistributedCache(
Mock.Of<IServerMessenger>(),
new CacheRefresherCollection(() => Enumerable.Empty<ICacheRefresher>()));
var executor = new MigrationPlanExecutor(scopeProvider, scopeProvider, loggerFactory, migrationBuilder, databaseFactory, Mock.Of<IPublishedSnapshotService>(), distributedCache);
var plan = new MigrationPlan("default")
.From(string.Empty)
@@ -77,7 +89,8 @@ public class MigrationPlanTests
var sourceState = kvs.GetValue("Umbraco.Tests.MigrationPlan") ?? string.Empty;
// execute plan
state = executor.Execute(plan, sourceState);
var result = executor.ExecutePlan(plan, sourceState);
state = result.FinalState;
// save new state
kvs.SetValue("Umbraco.Tests.MigrationPlan", sourceState, state);

View File

@@ -1,164 +0,0 @@
// Copyright (c) Umbraco.
// See LICENSE for more details.
using System;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Moq;
using NPoco;
using NUnit.Framework;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.Events;
using Umbraco.Cms.Core.Migrations;
using Umbraco.Cms.Core.Scoping;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Infrastructure.Migrations;
using Umbraco.Cms.Infrastructure.Migrations.Upgrade;
using Umbraco.Cms.Infrastructure.Persistence;
using Umbraco.Cms.Infrastructure.Scoping;
using Umbraco.Cms.Persistence.SqlServer.Services;
using Umbraco.Cms.Tests.Common.TestHelpers;
using IScope = Umbraco.Cms.Infrastructure.Scoping.IScope;
namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Migrations;
[TestFixture]
public class PostMigrationTests
{
private static readonly ILoggerFactory s_loggerFactory = NullLoggerFactory.Instance;
private IMigrationPlanExecutor GetMigrationPlanExecutor(
ICoreScopeProvider scopeProvider,
IScopeAccessor scopeAccessor,
IMigrationBuilder builder)
=> new MigrationPlanExecutor(scopeProvider, scopeAccessor, s_loggerFactory, builder);
[Test]
public void ExecutesPlanPostMigration()
{
var builder = Mock.Of<IMigrationBuilder>();
Mock.Get(builder)
.Setup(x => x.Build(It.IsAny<Type>(), It.IsAny<IMigrationContext>()))
.Returns<Type, IMigrationContext>((t, c) =>
{
switch (t.Name)
{
case nameof(NoopMigration):
return new NoopMigration(c);
case nameof(TestPostMigration):
return new TestPostMigration(c);
default:
throw new NotSupportedException();
}
});
var database = new TestDatabase();
var scope = Mock.Of<IScope>(x => x.Notifications == Mock.Of<IScopedNotificationPublisher>());
Mock.Get(scope)
.Setup(x => x.Database)
.Returns(database);
var sqlContext = new SqlContext(
new SqlServerSyntaxProvider(Options.Create(new GlobalSettings())),
DatabaseType.SQLCe,
Mock.Of<IPocoDataFactory>());
var scopeProvider = new MigrationTests.TestScopeProvider(scope) { SqlContext = sqlContext };
var plan = new MigrationPlan("Test")
.From(string.Empty).To("done");
plan.AddPostMigration<TestPostMigration>();
TestPostMigration.MigrateCount = 0;
var upgrader = new Upgrader(plan);
var executor = GetMigrationPlanExecutor(scopeProvider, scopeProvider, builder);
upgrader.Execute(
executor,
scopeProvider,
Mock.Of<IKeyValueService>());
Assert.AreEqual(1, TestPostMigration.MigrateCount);
}
[Test]
public void MigrationCanAddPostMigration()
{
var builder = Mock.Of<IMigrationBuilder>();
Mock.Get(builder)
.Setup(x => x.Build(It.IsAny<Type>(), It.IsAny<IMigrationContext>()))
.Returns<Type, IMigrationContext>((t, c) =>
{
switch (t.Name)
{
case nameof(NoopMigration):
return new NoopMigration(c);
case nameof(TestMigration):
return new TestMigration(c);
case nameof(TestPostMigration):
return new TestPostMigration(c);
default:
throw new NotSupportedException();
}
});
var database = new TestDatabase();
var scope = Mock.Of<IScope>(x => x.Notifications == Mock.Of<IScopedNotificationPublisher>());
Mock.Get(scope)
.Setup(x => x.Database)
.Returns(database);
var sqlContext = new SqlContext(
new SqlServerSyntaxProvider(Options.Create(new GlobalSettings())),
DatabaseType.SQLCe,
Mock.Of<IPocoDataFactory>());
var scopeProvider = new MigrationTests.TestScopeProvider(scope) { SqlContext = sqlContext };
var plan = new MigrationPlan("Test")
.From(string.Empty).To<TestMigration>("done");
TestMigration.MigrateCount = 0;
TestPostMigration.MigrateCount = 0;
new MigrationContext(plan, database, s_loggerFactory.CreateLogger<MigrationContext>());
var upgrader = new Upgrader(plan);
var executor = GetMigrationPlanExecutor(scopeProvider, scopeProvider, builder);
upgrader.Execute(
executor,
scopeProvider,
Mock.Of<IKeyValueService>());
Assert.AreEqual(1, TestMigration.MigrateCount);
Assert.AreEqual(1, TestPostMigration.MigrateCount);
}
public class TestMigration : MigrationBase
{
public TestMigration(IMigrationContext context)
: base(context)
{
}
public static int MigrateCount { get; set; }
protected override void Migrate()
{
MigrateCount++;
Context.AddPostMigration<TestPostMigration>();
}
}
public class TestPostMigration : MigrationBase
{
public TestPostMigration(IMigrationContext context)
: base(context)
{
}
public static int MigrateCount { get; set; }
protected override void Migrate() => MigrateCount++;
}
}