|
|
@ -50,33 +50,81 @@ namespace Squidex.Infrastructure.Migrations |
|
|
return Task.FromResult(lockAcquired); |
|
|
return Task.FromResult(lockAcquired); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
public Task UnlockAsync(int newVersion) |
|
|
public Task CompleteAsync(int newVersion) |
|
|
{ |
|
|
{ |
|
|
lock (lockObject) |
|
|
lock (lockObject) |
|
|
{ |
|
|
{ |
|
|
isLocked = false; |
|
|
|
|
|
|
|
|
|
|
|
version = newVersion; |
|
|
version = newVersion; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
return Task.CompletedTask; |
|
|
return Task.CompletedTask; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public Task UnlockAsync() |
|
|
|
|
|
{ |
|
|
|
|
|
lock (lockObject) |
|
|
|
|
|
{ |
|
|
|
|
|
isLocked = false; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return Task.CompletedTask; |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
public MigratorTests() |
|
|
public MigratorTests() |
|
|
{ |
|
|
{ |
|
|
A.CallTo(() => path.GetNext(A<int>._)) |
|
|
A.CallTo(() => path.GetNext(A<int>._)) |
|
|
.ReturnsLazily((int v) => |
|
|
.ReturnsLazily((int version) => |
|
|
{ |
|
|
{ |
|
|
var m = migrations.Where(x => x.From == v).ToList(); |
|
|
var selected = migrations.Where(x => x.From == version).ToList(); |
|
|
|
|
|
|
|
|
|
|
|
if (selected.Count == 0) |
|
|
|
|
|
{ |
|
|
|
|
|
return (0, null); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
return m.Count == 0 ? (0, null) : (migrations.Max(x => x.To), migrations.Select(x => x.Migration)); |
|
|
var newVersion = selected.Max(x => x.To); |
|
|
|
|
|
|
|
|
|
|
|
return (newVersion, migrations.Select(x => x.Migration)); |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
A.CallTo(() => status.GetVersionAsync()).Returns(0); |
|
|
A.CallTo(() => status.GetVersionAsync()).Returns(0); |
|
|
A.CallTo(() => status.TryLockAsync()).Returns(true); |
|
|
A.CallTo(() => status.TryLockAsync()).Returns(true); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
[Fact] |
|
|
|
|
|
public async Task Should_migrate_in_one_step() |
|
|
|
|
|
{ |
|
|
|
|
|
var migrator_0_1 = BuildMigration(0, 1); |
|
|
|
|
|
var migrator_1_2 = BuildMigration(0, 2); |
|
|
|
|
|
var migrator_2_3 = BuildMigration(0, 3); |
|
|
|
|
|
|
|
|
|
|
|
var sut = new Migrator(status, path, log); |
|
|
|
|
|
|
|
|
|
|
|
await sut.MigrateAsync(); |
|
|
|
|
|
|
|
|
|
|
|
A.CallTo(() => migrator_0_1.UpdateAsync()) |
|
|
|
|
|
.MustHaveHappened(); |
|
|
|
|
|
|
|
|
|
|
|
A.CallTo(() => migrator_1_2.UpdateAsync()) |
|
|
|
|
|
.MustHaveHappened(); |
|
|
|
|
|
|
|
|
|
|
|
A.CallTo(() => migrator_2_3.UpdateAsync()) |
|
|
|
|
|
.MustHaveHappened(); |
|
|
|
|
|
|
|
|
|
|
|
A.CallTo(() => status.CompleteAsync(1)) |
|
|
|
|
|
.MustNotHaveHappened(); |
|
|
|
|
|
|
|
|
|
|
|
A.CallTo(() => status.CompleteAsync(2)) |
|
|
|
|
|
.MustNotHaveHappened(); |
|
|
|
|
|
|
|
|
|
|
|
A.CallTo(() => status.CompleteAsync(3)) |
|
|
|
|
|
.MustHaveHappened(); |
|
|
|
|
|
|
|
|
|
|
|
A.CallTo(() => status.UnlockAsync()) |
|
|
|
|
|
.MustHaveHappened(); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
[Fact] |
|
|
[Fact] |
|
|
public async Task Should_migrate_step_by_step() |
|
|
public async Task Should_migrate_step_by_step() |
|
|
{ |
|
|
{ |
|
|
@ -88,11 +136,25 @@ namespace Squidex.Infrastructure.Migrations |
|
|
|
|
|
|
|
|
await sut.MigrateAsync(); |
|
|
await sut.MigrateAsync(); |
|
|
|
|
|
|
|
|
A.CallTo(() => migrator_0_1.UpdateAsync()).MustHaveHappened(); |
|
|
A.CallTo(() => migrator_0_1.UpdateAsync()) |
|
|
A.CallTo(() => migrator_1_2.UpdateAsync()).MustHaveHappened(); |
|
|
.MustHaveHappened(); |
|
|
A.CallTo(() => migrator_2_3.UpdateAsync()).MustHaveHappened(); |
|
|
|
|
|
|
|
|
A.CallTo(() => migrator_1_2.UpdateAsync()) |
|
|
|
|
|
.MustHaveHappened(); |
|
|
|
|
|
|
|
|
|
|
|
A.CallTo(() => migrator_2_3.UpdateAsync()) |
|
|
|
|
|
.MustHaveHappened(); |
|
|
|
|
|
|
|
|
|
|
|
A.CallTo(() => status.CompleteAsync(1)) |
|
|
|
|
|
.MustHaveHappened(); |
|
|
|
|
|
|
|
|
|
|
|
A.CallTo(() => status.CompleteAsync(2)) |
|
|
|
|
|
.MustHaveHappened(); |
|
|
|
|
|
|
|
|
|
|
|
A.CallTo(() => status.CompleteAsync(3)) |
|
|
|
|
|
.MustHaveHappened(); |
|
|
|
|
|
|
|
|
A.CallTo(() => status.UnlockAsync(3)) |
|
|
A.CallTo(() => status.UnlockAsync()) |
|
|
.MustHaveHappened(); |
|
|
.MustHaveHappened(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@ -109,11 +171,26 @@ namespace Squidex.Infrastructure.Migrations |
|
|
|
|
|
|
|
|
await Assert.ThrowsAsync<MigrationFailedException>(() => sut.MigrateAsync()); |
|
|
await Assert.ThrowsAsync<MigrationFailedException>(() => sut.MigrateAsync()); |
|
|
|
|
|
|
|
|
A.CallTo(() => migrator_0_1.UpdateAsync()).MustHaveHappened(); |
|
|
A.CallTo(() => migrator_0_1.UpdateAsync()) |
|
|
A.CallTo(() => migrator_1_2.UpdateAsync()).MustHaveHappened(); |
|
|
.MustHaveHappened(); |
|
|
A.CallTo(() => migrator_2_3.UpdateAsync()).MustNotHaveHappened(); |
|
|
|
|
|
|
|
|
|
|
|
A.CallTo(() => status.UnlockAsync(0)).MustHaveHappened(); |
|
|
A.CallTo(() => migrator_1_2.UpdateAsync()) |
|
|
|
|
|
.MustHaveHappened(); |
|
|
|
|
|
|
|
|
|
|
|
A.CallTo(() => migrator_2_3.UpdateAsync()) |
|
|
|
|
|
.MustNotHaveHappened(); |
|
|
|
|
|
|
|
|
|
|
|
A.CallTo(() => status.CompleteAsync(1)) |
|
|
|
|
|
.MustNotHaveHappened(); |
|
|
|
|
|
|
|
|
|
|
|
A.CallTo(() => status.CompleteAsync(2)) |
|
|
|
|
|
.MustNotHaveHappened(); |
|
|
|
|
|
|
|
|
|
|
|
A.CallTo(() => status.CompleteAsync(3)) |
|
|
|
|
|
.MustNotHaveHappened(); |
|
|
|
|
|
|
|
|
|
|
|
A.CallTo(() => status.UnlockAsync()) |
|
|
|
|
|
.MustHaveHappened(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
[Fact] |
|
|
[Fact] |
|
|
@ -142,7 +219,7 @@ namespace Squidex.Infrastructure.Migrations |
|
|
public async Task Should_prevent_multiple_updates() |
|
|
public async Task Should_prevent_multiple_updates() |
|
|
{ |
|
|
{ |
|
|
var migrator_0_1 = BuildMigration(0, 1); |
|
|
var migrator_0_1 = BuildMigration(0, 1); |
|
|
var migrator_1_2 = BuildMigration(1, 2); |
|
|
var migrator_1_2 = BuildMigration(0, 2); |
|
|
|
|
|
|
|
|
var sut = new Migrator(new InMemoryStatus(), path, log) { LockWaitMs = 2 }; |
|
|
var sut = new Migrator(new InMemoryStatus(), path, log) { LockWaitMs = 2 }; |
|
|
|
|
|
|
|
|
@ -150,6 +227,7 @@ namespace Squidex.Infrastructure.Migrations |
|
|
|
|
|
|
|
|
A.CallTo(() => migrator_0_1.UpdateAsync()) |
|
|
A.CallTo(() => migrator_0_1.UpdateAsync()) |
|
|
.MustHaveHappenedOnceExactly(); |
|
|
.MustHaveHappenedOnceExactly(); |
|
|
|
|
|
|
|
|
A.CallTo(() => migrator_1_2.UpdateAsync()) |
|
|
A.CallTo(() => migrator_1_2.UpdateAsync()) |
|
|
.MustHaveHappenedOnceExactly(); |
|
|
.MustHaveHappenedOnceExactly(); |
|
|
} |
|
|
} |
|
|
|