Skip to content

Commit

Permalink
Add fist version of Mongo.Extensions.Migration
Browse files Browse the repository at this point in the history
  • Loading branch information
TimHolzherr committed Jul 8, 2022
1 parent 7bdccc0 commit 99631b7
Show file tree
Hide file tree
Showing 25 changed files with 641 additions and 2 deletions.
89 changes: 89 additions & 0 deletions src/Migration.Tests/Integration/Scenario1/MigrateDownTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using MongoDB.Extensions.Migration;
using FluentAssertions;
using Microsoft.Extensions.Logging;
using MongoDB.Bson;
using MongoDB.Bson.Serialization;
using MongoDB.Driver;
using MongoDB.Driver.Linq;
using Squadron;
using Xunit;

namespace MongoMigrationTest.Integration.Scenario1;

[Collection("SharedMongoDbCollection")]
public class MigrateDownTests
{
readonly IMongoCollection<TestEntityForDown> _typedCollection;
readonly IMongoCollection<BsonDocument> _untypedCollection;

public MigrateDownTests(MongoResource resource)
{
RegisterMongoMigrations();
IMongoDatabase database = resource.Client.GetDatabase("Scenario1-down");
_typedCollection = database.GetCollection<TestEntityForDown>("TestEntityForDown");
_untypedCollection = database.GetCollection<BsonDocument>("TestEntityForDown");
}

static void RegisterMongoMigrations()
{
ILoggerFactory loggerFactory = LoggerFactory.Create(_ => { });

MigrationOption options = new MigrationOptionBuilder()
.ForEntity<TestEntityForDown>(o => o.AtVersion(0)
.WithMigration(new TestMigration1())
.WithMigration(new TestMigration2())
.WithMigration(new TestMigration3()))
.Build();
var context = new MigrationContext(options, loggerFactory);

BsonSerializer.RegisterSerializationProvider(new MigrationSerializerProvider(context));
}

[Fact]
public async Task Scenario1_AddRetrieve_NoMigration()
{
// Arrange
const string input = "Bar";
await _typedCollection.InsertOneAsync(new TestEntityForDown("1", input, 1));

// Act
TestEntityForDown result = await _typedCollection.AsQueryable()
.SingleOrDefaultAsync(c => c.Id == "1");

// Assert
result.Foo.Should().Be(input);
}

[Fact]
public async Task Scenario1_RetrieveAtVersion3_MigratedDownTo0()
{
// Arrange
await _untypedCollection.InsertOneAsync(new BsonDocument(new Dictionary<string, object>
{ ["_id"] = "id0", ["Foo"] = "Bar", ["Version"] = 3 }));

// Act
TestEntityForDown result = await _typedCollection.AsQueryable()
.SingleOrDefaultAsync(c => c.Id == "id0");

// Assert
result.Foo.Should().Be("Bar Migrated Down to 2 Migrated Down to 1 Migrated Down to 0");
}

[Fact]
public async Task Scenario1_RetrieveAtVersion2_MigratedToVersion3()
{
// Arrange
await _untypedCollection.InsertOneAsync(new BsonDocument(new Dictionary<string, object>
{ ["_id"] = "id1", ["Foo"] = "Bar", ["Version"] = 1 }));

// Act
TestEntityForDown result = await _typedCollection.AsQueryable()
.SingleOrDefaultAsync(c => c.Id == "id1");

// Assert
result.Foo.Should().Be("Bar Migrated Down to 0");
}

}
84 changes: 84 additions & 0 deletions src/Migration.Tests/Integration/Scenario1/MigrateUpTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using MongoDB.Extensions.Migration;
using FluentAssertions;
using Microsoft.Extensions.Logging;
using MongoDB.Bson;
using MongoDB.Bson.Serialization;
using MongoDB.Driver;
using Xunit;
using MongoDB.Driver.Linq;
using Squadron;

namespace MongoMigrationTest.Integration.Scenario1;

[Collection("SharedMongoDbCollection")]
public class MigrateUpTests
{
readonly IMongoCollection<TestEntityForUp> _typedCollection;
readonly IMongoCollection<BsonDocument> _untypedCollection;

public MigrateUpTests(MongoResource resource)
{
RegisterMongoMigrations();
var database = resource.Client.GetDatabase("Scenario1-up");
_typedCollection = database.GetCollection<TestEntityForUp>("TestEntityForUp");
_untypedCollection = database.GetCollection<BsonDocument>("TestEntityForUp");
}

static void RegisterMongoMigrations()
{
var loggerFactory = LoggerFactory.Create(_ => { });

var options = new MigrationOptionBuilder().ForEntity<TestEntityForUp>(o => o
.WithMigration(new TestMigration1())
.WithMigration(new TestMigration2())
.WithMigration(new TestMigration3()))
.Build();
var context = new MigrationContext(options, loggerFactory);

BsonSerializer.RegisterSerializationProvider(new MigrationSerializerProvider(context));
}

[Fact]
public async Task Scenario1_AddRetrieve_NoMigration()
{
// Arrange
const string input = "Bar";
await _typedCollection.InsertOneAsync(new TestEntityForUp("1", input, 1));

// Act
var result = await _typedCollection.AsQueryable().SingleOrDefaultAsync(c => c.Id == "1");

// Assert
result.Foo.Should().Be(input);
}

[Fact]
public async Task Scenario1_RetrieveAtVersion0_MigratedToNewestVersion()
{
// Arrange
await _untypedCollection.InsertOneAsync(new BsonDocument(new Dictionary<string, object>
{ ["_id"] = "id0", ["Foo"] = "Bar", ["Version"] = 0 }));

// Act
TestEntityForUp result = await _typedCollection.AsQueryable().SingleOrDefaultAsync(c => c.Id == "id0");

// Assert
result.Foo.Should().Be("Bar Migrated Up to 1 Migrated Up to 2 Migrated Up to 3");
}

[Fact]
public async Task Scenario1_RetrieveAtVersion2_MigratedToVersion3()
{
// Arrange
await _untypedCollection.InsertOneAsync(new BsonDocument(new Dictionary<string, object>
{ ["_id"] = "id1", ["Foo"] = "Bar", ["Version"] = 2 }));

// Act
TestEntityForUp result = await _typedCollection.AsQueryable().SingleOrDefaultAsync(c => c.Id == "id1");

// Assert
result.Foo.Should().Be("Bar Migrated Up to 3");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using MongoDB.Extensions.Migration;

namespace MongoMigrationTest.Integration.Scenario1;

public record TestEntityForDown(string Id, string Foo, int Version) : IVersioned
{
public int Version { get; set; }
}
8 changes: 8 additions & 0 deletions src/Migration.Tests/Integration/Scenario1/TestEntityForUp.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using MongoDB.Extensions.Migration;

namespace MongoMigrationTest.Integration.Scenario1;

public record TestEntityForUp(string Id, string Foo, int Version) : IVersioned
{
public int Version { get; set; }
}
19 changes: 19 additions & 0 deletions src/Migration.Tests/Integration/Scenario1/TestMigration1.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using MongoDB.Extensions.Migration;
using MongoDB.Bson;

namespace MongoMigrationTest.Integration.Scenario1;

public class TestMigration1 : IMigration
{
public int Version { get; } = 1;

public void Up(BsonDocument document)
{
document["Foo"] += " Migrated Up to 1";
}

public void Down(BsonDocument document)
{
document["Foo"] += " Migrated Down to 0";
}
}
19 changes: 19 additions & 0 deletions src/Migration.Tests/Integration/Scenario1/TestMigration2.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using MongoDB.Extensions.Migration;
using MongoDB.Bson;

namespace MongoMigrationTest.Integration.Scenario1;

public class TestMigration2 : IMigration
{
public int Version { get; } = 2;

public void Up(BsonDocument document)
{
document["Foo"] += " Migrated Up to 2";
}

public void Down(BsonDocument document)
{
document["Foo"] += " Migrated Down to 1";
}
}
19 changes: 19 additions & 0 deletions src/Migration.Tests/Integration/Scenario1/TestMigration3.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using MongoDB.Extensions.Migration;
using MongoDB.Bson;

namespace MongoMigrationTest.Integration.Scenario1;

public class TestMigration3 : IMigration
{
public int Version { get; } = 3;

public void Up(BsonDocument document)
{
document["Foo"] += " Migrated Up to 3";
}

public void Down(BsonDocument document)
{
document["Foo"] += " Migrated Down to 2";
}
}
9 changes: 9 additions & 0 deletions src/Migration.Tests/Integration/SharedMongoDbCollection.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using Squadron;
using Xunit;

namespace MongoMigrationTest.Integration;

[CollectionDefinition("SharedMongoDbCollection")]
public class SharedMongoDbCollection : ICollectionFixture<MongoResource>
{
}
14 changes: 14 additions & 0 deletions src/Migration.Tests/Migration.Tests.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="$(CCTestProjectProps)" Condition="Exists('$(CCTestProjectProps)')" />

<PropertyGroup>
<AssemblyName>Migration.Tests</AssemblyName>
<RootNamespace>Migration.Tests</RootNamespace>
<TargetFrameworks>net6.0</TargetFrameworks>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\Migration\Migration.csproj" />
</ItemGroup>

</Project>
66 changes: 66 additions & 0 deletions src/Migration/Builders/EntityOptionBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
using System.Collections.Generic;
using System.Linq;

namespace MongoDB.Extensions.Migration;

public class EntityOptionBuilder<T> where T : IVersioned
{
private int? _atVersion;
private readonly List<IMigration> _migrations = new();

/// <summary>
/// Set the current Version of the data which is suitable for the application.
/// If not set the newest Version is used.
/// </summary>
public EntityOptionBuilder<T> AtVersion(int atVersion)
{
_atVersion = atVersion;
return this;
}

/// <summary>
/// Register a migration for an entity. The versions of the migrations must start at 1 and be
/// continuously incremented without a gap
/// </summary>
public EntityOptionBuilder<T> WithMigration(IMigration migration)
{
_migrations.Add(migration);
return this;
}

public EntityOption Build()
{
if (_migrations.Count == 0)
{
throw new BuilderNotInitializedException(
"Please register at least one migration using the WithMigration method");
}

_migrations.Sort((x, y) => x.Version.CompareTo(y.Version));

_atVersion ??= _migrations.Last().Version;

// TODO: use IComparibel for this
if (_atVersion != 0 && _migrations.All(m => !Equals(m.Version, _atVersion)))
{
throw new InvalidConfigurationException(
$"There is no migration for version {_atVersion} for entity {typeof(T).Name}");
}

for (var i = 1; i < _migrations.Count; i++)
{
if (_migrations[i - 1].Version + 1 != _migrations[i].Version)
{
// TODO: Compare with IComparibel instead
throw new InvalidConfigurationException(
"The versions of the migrations must be continuously incremented");
}

}

return new EntityOption(
typeof(T),
_atVersion.Value,
_migrations);
}
}
21 changes: 21 additions & 0 deletions src/Migration/Builders/MigrationOptionBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;

namespace MongoDB.Extensions.Migration;

public class MigrationOptionBuilder
{
private readonly List<EntityOption> _entityOptions = new();

public MigrationOption Build()
{
return new MigrationOption(_entityOptions);
}

public MigrationOptionBuilder ForEntity<T>(
Func<EntityOptionBuilder<T>, EntityOptionBuilder<T>> builderAction) where T : IVersioned
{
_entityOptions.Add(builderAction(new EntityOptionBuilder<T>()).Build());
return this;
}
}
10 changes: 10 additions & 0 deletions src/Migration/Contracts/IMigration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using MongoDB.Bson;

namespace MongoDB.Extensions.Migration;

public interface IMigration
{
int Version { get; }
public void Up(BsonDocument document);
public void Down(BsonDocument document);
}
6 changes: 6 additions & 0 deletions src/Migration/Contracts/IVersioned.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace MongoDB.Extensions.Migration;

public interface IVersioned
{
int Version { get; set; }
}
Loading

0 comments on commit 99631b7

Please sign in to comment.