adding
This commit is contained in:
@@ -0,0 +1,201 @@
|
||||
using LCU.API.Internal.DocumentLib.Containers;
|
||||
using MongoDB.Driver;
|
||||
using OwnerNumberMigration;
|
||||
using OwnerNumberMigrationTest.TestDataFactories;
|
||||
using OwnerNumberMigrationTest.TestDoubles;
|
||||
|
||||
namespace OwnerNumberMigrationTest;
|
||||
|
||||
public class AttributeMigrationTest : IDisposable
|
||||
{
|
||||
private readonly IAttributeMigration _migration;
|
||||
|
||||
private readonly string _attributeName = Guid.NewGuid().ToString();
|
||||
private const string DestinationProperty = nameof(DocumentInformation.PersonNumbers);
|
||||
private string[] GetDestinationProperty(DocumentInformation document) => (string[])typeof(DocumentInformation).GetProperty(DestinationProperty)!.GetValue(document)!;
|
||||
private readonly FakeMongoCollectionFactory _collectionFactory = new();
|
||||
private readonly FakeParsingStrategy<string[]> _parsingStrategy = new(Array.Empty<string>);
|
||||
private readonly FakeMigrationObserver _observer = new();
|
||||
private readonly FakeDocumentFilterFactory _filterFactory = new();
|
||||
|
||||
public AttributeMigrationTest()
|
||||
{
|
||||
_migration = new AttributeMigration<string[]>
|
||||
(
|
||||
_collectionFactory,
|
||||
_attributeName,
|
||||
_parsingStrategy,
|
||||
DestinationProperty,
|
||||
_observer,
|
||||
_filterFactory
|
||||
);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Migrate_AlreadyCancelled_ReturnsImmediately()
|
||||
{
|
||||
var cancellation = new CancellationTokenSource();
|
||||
cancellation.Cancel();
|
||||
var document = await MigrateableDocumentFactory();
|
||||
|
||||
await _migration.Migrate(cancellation.Token);
|
||||
|
||||
var documentRecord = (await _collectionFactory
|
||||
.GetCollection<DocumentInformation>()
|
||||
.FindAsync(Builders<DocumentInformation>.Filter.Eq(d => d.DocumentId, document.DocumentId)))
|
||||
.Single();
|
||||
var unmigratedDestinationProperty = GetDestinationProperty(document);
|
||||
var migratedDestinationProperty = GetDestinationProperty(documentRecord);
|
||||
CollectionAssert.AreEquivalent(unmigratedDestinationProperty, migratedDestinationProperty);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Migrate_DocumentInformationMissingDestination_ThrowsException()
|
||||
{
|
||||
var migration = new AttributeMigration<string[]>
|
||||
(
|
||||
_collectionFactory,
|
||||
_attributeName,
|
||||
_parsingStrategy,
|
||||
Guid.NewGuid().ToString(),
|
||||
_observer,
|
||||
_filterFactory
|
||||
);
|
||||
|
||||
Assert.ThrowsAsync<InvalidOperationException>(() => migration.Migrate(new CancellationToken()));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Migrate_DocumentInformationMissingDestination_NotifiesObserver()
|
||||
{
|
||||
var migration = new AttributeMigration<string[]>
|
||||
(
|
||||
_collectionFactory,
|
||||
_attributeName,
|
||||
_parsingStrategy,
|
||||
Guid.NewGuid().ToString(),
|
||||
_observer,
|
||||
_filterFactory
|
||||
);
|
||||
|
||||
var thrown = Assert.ThrowsAsync<InvalidOperationException>(() => migration.Migrate(new CancellationToken()));
|
||||
|
||||
Assert.Contains(thrown, _observer.Errors);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Migrate_DocumentInformationMissingDestination_DoesNotMigrate()
|
||||
{
|
||||
var document = await MigrateableDocumentFactory();
|
||||
var migration = new AttributeMigration<string[]>
|
||||
(
|
||||
_collectionFactory,
|
||||
_attributeName,
|
||||
_parsingStrategy,
|
||||
Guid.NewGuid().ToString(),
|
||||
_observer,
|
||||
_filterFactory
|
||||
);
|
||||
|
||||
Assert.ThrowsAsync<InvalidOperationException>(() => migration.Migrate(new CancellationToken()));
|
||||
|
||||
var documentRecord = (await _collectionFactory
|
||||
.GetCollection<DocumentInformation>()
|
||||
.FindAsync(Builders<DocumentInformation>.Filter.Eq(d => d.DocumentId, document.DocumentId)))
|
||||
.Single();
|
||||
var unmigratedDestinationProperty = GetDestinationProperty(document);
|
||||
var migratedDestinationProperty = GetDestinationProperty(documentRecord);
|
||||
CollectionAssert.AreEquivalent(unmigratedDestinationProperty, migratedDestinationProperty);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Migrate_NotCancelled_NotifiesObserverOfDocument()
|
||||
{
|
||||
var document = await MigrateableDocumentFactory();
|
||||
|
||||
await _migration.Migrate(new CancellationToken());
|
||||
|
||||
var documentsStartedMigrating = _observer.Documents.Select(d => d.doc.DocumentId);
|
||||
CollectionAssert.Contains(documentsStartedMigrating, document.DocumentId);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Migrate_NotCancelled_MovesValueFromParseStrategyIntoDestinationAndSaves()
|
||||
{
|
||||
var document = await MigrateableDocumentFactory();
|
||||
var parsedOwners = _parsingStrategy.Values[document.Attributes[_attributeName]] =
|
||||
new[]
|
||||
{
|
||||
Guid.NewGuid().ToString(),
|
||||
Guid.NewGuid().ToString()
|
||||
};
|
||||
|
||||
await _migration.Migrate(new CancellationToken());
|
||||
|
||||
var documentRecord = (await _collectionFactory
|
||||
.GetCollection<DocumentInformation>()
|
||||
.FindAsync(Builders<DocumentInformation>.Filter.Eq(d => d.DocumentId, document.DocumentId)))
|
||||
.Single();
|
||||
var migratedDestinationProperty = GetDestinationProperty(documentRecord);
|
||||
CollectionAssert.AreEquivalent(parsedOwners, migratedDestinationProperty);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Migrate_AfterDocumentMigrates_NotifiesObserver()
|
||||
{
|
||||
var document = await MigrateableDocumentFactory();
|
||||
|
||||
await _migration.Migrate(new CancellationToken());
|
||||
|
||||
var ended = DateTime.Now;
|
||||
var migrationStart = _observer.Documents
|
||||
.Where(d => d.doc.DocumentId == document.DocumentId)
|
||||
.Select(d => d.started)
|
||||
.Single();
|
||||
Assert.AreEqual(1, _observer.Completions.Count);
|
||||
var migrationCompleted = _observer.Completions.Single();
|
||||
Assert.That(migrationCompleted, Is.InRange(migrationStart, ended));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Migrate_TooManyRequests_SleepsForIndicatedTimeThenResumes()
|
||||
{
|
||||
var document = await MigrateableDocumentFactory();
|
||||
var parsedOwners = _parsingStrategy.Values[document.Attributes[_attributeName]] =
|
||||
new[]
|
||||
{
|
||||
Guid.NewGuid().ToString(),
|
||||
Guid.NewGuid().ToString()
|
||||
};
|
||||
_collectionFactory.TooManyRequestsOnWriteFor = TimeSpan.FromMilliseconds(25);
|
||||
|
||||
await _migration.Migrate(new CancellationToken());
|
||||
|
||||
_collectionFactory.TooManyRequestsOnWriteFor = null;
|
||||
var documentRecord = (await _collectionFactory
|
||||
.GetCollection<DocumentInformation>()
|
||||
.FindAsync(Builders<DocumentInformation>.Filter.Eq(d => d.DocumentId, document.DocumentId)))
|
||||
.Single();
|
||||
var migratedDestinationProperty = GetDestinationProperty(documentRecord);
|
||||
CollectionAssert.AreEquivalent(parsedOwners, migratedDestinationProperty);
|
||||
}
|
||||
|
||||
private async Task<DocumentInformation> MigrateableDocumentFactory()
|
||||
{
|
||||
var document = DocumentInformationFactory.CreateDocumentInformation();
|
||||
document.Attributes[_attributeName] = Guid.NewGuid().ToString();
|
||||
|
||||
_filterFactory.ApplicableDocuments[(_attributeName, DestinationProperty)] = new[] { document };
|
||||
|
||||
await _collectionFactory
|
||||
.GetCollection<DocumentInformation>()
|
||||
.InsertOneAsync(document);
|
||||
|
||||
return document;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_collectionFactory.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
using LCU.API.Internal.DocumentLib.Containers;
|
||||
using MongoDB.Driver;
|
||||
using OwnerNumberMigration;
|
||||
using OwnerNumberMigrationTest.TestDataFactories;
|
||||
using OwnerNumberMigrationTest.TestDoubles;
|
||||
|
||||
namespace OwnerNumberMigrationTest;
|
||||
|
||||
public class DocumentFilterFactoryTest : IDisposable
|
||||
{
|
||||
private readonly IDocumentFilterFactory _factory = new DocumentFilterFactory();
|
||||
|
||||
private readonly FakeMongoCollectionFactory _collectionFactory = new();
|
||||
private readonly string _attributeName = "PersonNumber";
|
||||
private readonly string _destinationAttributeName = nameof(DocumentInformation.PersonNumbers);
|
||||
|
||||
[Test]
|
||||
public async Task AppliesForMigrationFilterFactory_DocumentContainsDestinationAttribute_FilteredOut()
|
||||
{
|
||||
var document = DocumentInformationFactory.CreateDocumentInformation();
|
||||
typeof(DocumentInformation)
|
||||
.GetProperty(_destinationAttributeName)
|
||||
?.SetValue(document, new[]{ Guid.NewGuid().ToString() });
|
||||
_collectionFactory.DatabasePostFix = Guid.NewGuid().ToString();
|
||||
var collection = _collectionFactory.GetCollection<DocumentInformation>();
|
||||
await collection.InsertOneAsync(document);
|
||||
|
||||
var filter = _factory.AppliesForMigrationFilterFactory(_attributeName, _destinationAttributeName);
|
||||
|
||||
var documents = await collection.CountDocumentsAsync(filter);
|
||||
Assert.AreEqual(0, documents);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task AppliesForMigrationFilterFactory_DocumentMissingSourceAttributeAttribute_FilteredOut()
|
||||
{
|
||||
var document = DocumentInformationFactory.CreateDocumentInformation();
|
||||
document.Attributes.Remove(_attributeName);
|
||||
_collectionFactory.DatabasePostFix = Guid.NewGuid().ToString();
|
||||
var collection = _collectionFactory.GetCollection<DocumentInformation>();
|
||||
await collection.InsertOneAsync(document);
|
||||
|
||||
var filter = _factory.AppliesForMigrationFilterFactory(_attributeName, _destinationAttributeName);
|
||||
|
||||
var documents = await collection.CountDocumentsAsync(filter);
|
||||
Assert.AreEqual(0, documents);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task
|
||||
AppliesForMigrationFilterFactory_DocumentContainsSourceAttributeAttributeAndNotDestinationAttribute_Included()
|
||||
{
|
||||
var document = DocumentInformationFactory.CreateDocumentInformation();
|
||||
document.Attributes[_attributeName] = Guid.NewGuid().ToString();
|
||||
_collectionFactory.DatabasePostFix = Guid.NewGuid().ToString();
|
||||
var collection = _collectionFactory.GetCollection<DocumentInformation>();
|
||||
await collection.InsertOneAsync(document);
|
||||
await collection.UpdateOneAsync
|
||||
(
|
||||
d => d.DocumentId == document.DocumentId,
|
||||
Builders<DocumentInformation>.Update.Unset(_destinationAttributeName)
|
||||
);
|
||||
|
||||
var filter = _factory.AppliesForMigrationFilterFactory(_attributeName, _destinationAttributeName);
|
||||
|
||||
var documents = await collection.CountDocumentsAsync(filter);
|
||||
Assert.AreEqual(1, documents);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_collectionFactory.Dispose();
|
||||
}
|
||||
}
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
using System.Text.RegularExpressions;
|
||||
using LCU.API.Internal.DocumentLib.Containers;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Moq;
|
||||
using OwnerNumberMigration;
|
||||
using OwnerNumberMigration.Logging;
|
||||
using OwnerNumberMigrationTest.TestDataFactories;
|
||||
using OwnerNumberMigrationTest.TestDoubles;
|
||||
|
||||
namespace OwnerNumberMigrationTest.Logging;
|
||||
|
||||
public class AttributeMigrationLoggingDecoratorTest : IDisposable
|
||||
{
|
||||
private readonly IAttributeMigration _decoratedMigration;
|
||||
|
||||
private readonly string _attributeName = Guid.NewGuid().ToString();
|
||||
private readonly string _destinationAttributeName = Guid.NewGuid().ToString();
|
||||
private readonly Mock<IAttributeMigration> _rawMigration = new();
|
||||
private readonly FakeLogger _logger = new();
|
||||
private readonly FakeMongoCollectionFactory _collectionFactory = new();
|
||||
private readonly FakeDocumentFilterFactory _filterFactory = new();
|
||||
|
||||
public AttributeMigrationLoggingDecoratorTest()
|
||||
{
|
||||
_decoratedMigration = new AttributeMigrationLoggingDecorator
|
||||
(
|
||||
_rawMigration.Object,
|
||||
_logger,
|
||||
_collectionFactory,
|
||||
_attributeName,
|
||||
_destinationAttributeName,
|
||||
_filterFactory
|
||||
);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Migrate_AllCases_LogsCountToMigrate()
|
||||
{
|
||||
var documentCount = new Random(DateTime.Now.Millisecond).Next(3, 10);
|
||||
var documents = EnumerableDataSeriesFactory
|
||||
.CreateSeries(DocumentInformationFactory.CreateDocumentInformation, documentCount)
|
||||
.ToArray();
|
||||
await _collectionFactory.GetCollection<DocumentInformation>().InsertManyAsync(documents);
|
||||
_filterFactory.ApplicableDocuments[(_attributeName, _destinationAttributeName)] = documents;
|
||||
|
||||
await _decoratedMigration.Migrate(new CancellationToken());
|
||||
|
||||
Func<Log, bool> logMatch = l => l.LogLevel == LogLevel.Information
|
||||
&& l.Message.Contains(_attributeName)
|
||||
&& new Regex($"\\b{documentCount}\\b").IsMatch(l.Message);
|
||||
Assert.IsTrue(_logger.Logs.Count(logMatch) == 1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Migrate_AllCases_InvokesInnerMigration()
|
||||
{
|
||||
var cancellation = new CancellationToken();
|
||||
|
||||
await _decoratedMigration.Migrate(cancellation);
|
||||
|
||||
_rawMigration.Verify(m => m.Migrate(cancellation), Times.Once);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Migrate_InnerMigrationCompletes_LogsElapsedTime()
|
||||
{
|
||||
var migrationDuration = TimeSpan.FromMilliseconds(100);
|
||||
_rawMigration
|
||||
.Setup(m => m.Migrate(It.IsAny<CancellationToken>()))
|
||||
.Returns(() => Task.Delay(migrationDuration));
|
||||
var actionStart = DateTime.Now;
|
||||
|
||||
await _decoratedMigration.Migrate(new CancellationToken());
|
||||
|
||||
var elapsed = DateTime.Now - actionStart;
|
||||
Func<Log, bool> logMatch = l =>
|
||||
{
|
||||
var timespan = Regex.Match(l.Message, "\\d\\d:\\d\\d:\\d\\d\\.\\d\\d\\d\\d\\d\\d\\d");
|
||||
if (!timespan.Success) return false;
|
||||
var duration = TimeSpan.Parse(timespan.ValueSpan);
|
||||
|
||||
return l.LogLevel == LogLevel.Information
|
||||
&& l.Message.Contains(_attributeName)
|
||||
&& duration >= migrationDuration
|
||||
&& duration <= elapsed;
|
||||
};
|
||||
Assert.IsTrue(_logger.Logs.Count(logMatch) == 1);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_collectionFactory.Dispose();
|
||||
}
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
using LCU.API.Internal.DocumentLib.Containers;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using OwnerNumberMigration.Logging;
|
||||
using OwnerNumberMigrationTest.TestDataFactories;
|
||||
using OwnerNumberMigrationTest.TestDoubles;
|
||||
|
||||
namespace OwnerNumberMigrationTest.Logging;
|
||||
|
||||
public class AttributeMigrationLoggingObserverTest
|
||||
{
|
||||
private readonly IObserver<DocumentInformation> _observer;
|
||||
|
||||
private readonly FakeLogger _logger = new();
|
||||
private readonly string _attributeName = Guid.NewGuid().ToString();
|
||||
|
||||
public AttributeMigrationLoggingObserverTest()
|
||||
{
|
||||
_observer = new AttributeMigrationLoggingObserver(_logger, _attributeName);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void OnCompleted_AllCases_InfoLogsCompletedAttribute()
|
||||
{
|
||||
_observer.OnCompleted();
|
||||
|
||||
Func<Log, bool> logMatch = l => l.LogLevel == LogLevel.Information
|
||||
&& l.Message.Contains("completed", StringComparison.OrdinalIgnoreCase)
|
||||
&& l.Message.Contains(_attributeName);
|
||||
Assert.IsTrue(_logger.Logs.Count(logMatch) == 1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void OnError_AllCases_ErrorLogsExceptionAndAttribute()
|
||||
{
|
||||
var error = new Exception(Guid.NewGuid().ToString());
|
||||
|
||||
_observer.OnError(error);
|
||||
|
||||
Func<Log, bool> logMatch = l => l.LogLevel == LogLevel.Error
|
||||
&& l.Message.Contains(error.Message)
|
||||
&& l.Message.Contains(_attributeName);
|
||||
Assert.IsTrue(_logger.Logs.Count(logMatch) == 1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void OnNext_AllCases_InfoLogsStartingAttributeAndDocumentId()
|
||||
{
|
||||
var document = DocumentInformationFactory.CreateDocumentInformation();
|
||||
|
||||
_observer.OnNext(document);
|
||||
|
||||
Func<Log, bool> logMatch = l => l.LogLevel == LogLevel.Information
|
||||
&& l.Message.Contains(document.DocumentId.ToString())
|
||||
&& l.Message.Contains(_attributeName);
|
||||
Assert.IsTrue(_logger.Logs.Count(logMatch) == 1);
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
using LCU.API.Internal.DocumentLib.Containers;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using OwnerNumberMigration.Migrations;
|
||||
using OwnerNumberMigration.ValueParsing;
|
||||
using OwnerNumberMigration;
|
||||
|
||||
namespace OwnerNumberMigrationTest.Migrations;
|
||||
|
||||
public class AccountNumberMigrationTest : AttributeMigrationTaskTest<string>
|
||||
{
|
||||
protected override string SourceAttribute => "AccountNumber";
|
||||
|
||||
protected override string DestinationAttribute => nameof(DocumentInformation.AccountNumber);
|
||||
|
||||
protected override IHostedService MigrationTaskFactory
|
||||
(
|
||||
AttributeMigrationFactory<string> factory,
|
||||
IParsingStrategyFactory strategyFactory,
|
||||
IMigrationManager manager
|
||||
) => new AccountNumberMigration(factory, strategyFactory, manager);
|
||||
|
||||
public AccountNumberMigrationTest() : base(() => string.Empty)
|
||||
{
|
||||
}
|
||||
}
|
||||
+170
@@ -0,0 +1,170 @@
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Moq;
|
||||
using OwnerNumberMigration;
|
||||
using OwnerNumberMigration.Migrations;
|
||||
using OwnerNumberMigration.ValueParsing;
|
||||
using OwnerNumberMigrationTest.TestDoubles;
|
||||
|
||||
namespace OwnerNumberMigrationTest.Migrations;
|
||||
|
||||
public abstract class AttributeMigrationTaskTest<T>
|
||||
{
|
||||
protected abstract IHostedService MigrationTaskFactory
|
||||
(
|
||||
AttributeMigrationFactory<T> factory,
|
||||
IParsingStrategyFactory strategyFactory,
|
||||
IMigrationManager manager
|
||||
);
|
||||
private IHostedService MigrationServiceFactory() => MigrationTaskFactory
|
||||
(
|
||||
(_, _, _) => _migration.Object,
|
||||
StrategyFactory.Object,
|
||||
_manager.Object
|
||||
);
|
||||
|
||||
protected abstract string SourceAttribute { get; }
|
||||
protected abstract string DestinationAttribute { get; }
|
||||
|
||||
protected readonly Mock<IParsingStrategyFactory> StrategyFactory = new();
|
||||
private readonly FakeParsingStrategy<T> _parsingStrategy;
|
||||
private readonly Mock<IAttributeMigration> _migration = new();
|
||||
private readonly Mock<IMigrationManager> _manager = new();
|
||||
|
||||
protected AttributeMigrationTaskTest(Func<T> newT)
|
||||
{
|
||||
_parsingStrategy = new FakeParsingStrategy<T>(newT);
|
||||
}
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
StrategyFactory
|
||||
.Setup(f => f.CreateParsingStrategy<T>(SourceAttribute))
|
||||
.Returns((IValueParsingStrategy<T>)_parsingStrategy);
|
||||
|
||||
_migration.Reset();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Constructor_AllCases_CreatesMigrationWithJobTypeAttributes()
|
||||
{
|
||||
string? sourceAttribute = null;
|
||||
string? destinationAttribute = null;
|
||||
IValueParsingStrategy<T>? parsingStrategy = null;
|
||||
|
||||
MigrationTaskFactory
|
||||
(
|
||||
(source, strategy, destination) =>
|
||||
{
|
||||
sourceAttribute = source;
|
||||
parsingStrategy = strategy;
|
||||
destinationAttribute = destination;
|
||||
|
||||
return _migration.Object;
|
||||
},
|
||||
StrategyFactory.Object,
|
||||
_manager.Object
|
||||
);
|
||||
|
||||
Assert.AreEqual(SourceAttribute, sourceAttribute);
|
||||
Assert.AreSame(_parsingStrategy, parsingStrategy);
|
||||
Assert.AreEqual(DestinationAttribute, destinationAttribute);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task StartAsync_TokenCancelled_DoesNotStartMigration()
|
||||
{
|
||||
var cancellation = new CancellationToken(true);
|
||||
var migration = MigrationServiceFactory();
|
||||
|
||||
await migration.StartAsync(cancellation);
|
||||
|
||||
_migration.Verify(m => m.Migrate(It.IsAny<CancellationToken>()), Times.Never);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task StartAsync_NotCanceled_StartsMigrationTaskAndReturns()
|
||||
{
|
||||
var cancellation = new CancellationToken();
|
||||
_migration
|
||||
.Setup(m => m.Migrate(It.IsAny<CancellationToken>()))
|
||||
.Returns<CancellationToken>(async token =>
|
||||
{
|
||||
await Task.Delay(TimeSpan.MaxValue, token);
|
||||
}
|
||||
);
|
||||
var migration = MigrationServiceFactory();
|
||||
|
||||
await migration.StartAsync(cancellation);
|
||||
|
||||
_migration.Verify(m => m.Migrate(It.IsAny<CancellationToken>()), Times.Once);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task StartAsync_NotCanceled_RegistersMigrationTaskWithManager()
|
||||
{
|
||||
var cancellation = new CancellationToken();
|
||||
var migrationTask = Task.Run(() => { });
|
||||
_migration
|
||||
.Setup(m => m.Migrate(It.IsAny<CancellationToken>()))
|
||||
.Returns(migrationTask);
|
||||
var migration = MigrationServiceFactory();
|
||||
|
||||
await migration.StartAsync(cancellation);
|
||||
|
||||
_manager.Verify(m => m.Start(migrationTask), Times.Once);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task StopAsync_NotStarted_ReturnsWithoutError()
|
||||
{
|
||||
var cancellation = new CancellationToken();
|
||||
var migration = MigrationServiceFactory();
|
||||
|
||||
Assert.DoesNotThrowAsync(() => migration.StopAsync(cancellation));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task StopAsync_AlreadyCompleted_CleansUpMigrationTask()
|
||||
{
|
||||
var cancellation = new CancellationToken();
|
||||
Task? migrationTask = null;
|
||||
_migration
|
||||
.Setup(m => m.Migrate(It.IsAny<CancellationToken>()))
|
||||
.Returns<CancellationToken>(token => migrationTask = Task.Run
|
||||
(
|
||||
() => { },
|
||||
token
|
||||
));
|
||||
var migration = MigrationServiceFactory();
|
||||
|
||||
await migration.StartAsync(cancellation);
|
||||
await migrationTask;
|
||||
await migration.StopAsync(cancellation);
|
||||
|
||||
Assert.AreEqual(TaskStatus.RanToCompletion, migrationTask.Status);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task StopAsync_NotCompleted_CancelsMigrationTaskThenCleansUp()
|
||||
{
|
||||
var cancellation = new CancellationToken();
|
||||
Task? migrationTask = null;
|
||||
_migration
|
||||
.Setup(m => m.Migrate(It.IsAny<CancellationToken>()))
|
||||
.Returns<CancellationToken>(token => migrationTask = Task.Run
|
||||
(
|
||||
async () =>
|
||||
{
|
||||
await Task.Delay(-1, token);
|
||||
},
|
||||
token
|
||||
));
|
||||
var migration = MigrationServiceFactory();
|
||||
|
||||
await migration.StartAsync(cancellation);
|
||||
await migration.StopAsync(cancellation);
|
||||
|
||||
Assert.AreEqual(TaskStatus.Canceled, migrationTask.Status);
|
||||
}
|
||||
}
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Moq;
|
||||
using OwnerNumberMigration.Migrations;
|
||||
using OwnerNumberMigrationTest.TestDataFactories;
|
||||
|
||||
namespace OwnerNumberMigrationTest.Migrations;
|
||||
|
||||
public class MigrationManagerTest
|
||||
{
|
||||
private readonly IMigrationManager _manager;
|
||||
|
||||
private readonly Mock<IHostApplicationLifetime> _lifetime = new();
|
||||
|
||||
public MigrationManagerTest()
|
||||
{
|
||||
_manager = new MigrationManager(_lifetime.Object);
|
||||
}
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
_lifetime.Reset();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Start_OneTaskThatFinishes_EndsApplication()
|
||||
{
|
||||
var applicationStopped = new SemaphoreSlim(0);
|
||||
_lifetime
|
||||
.Setup(l => l.StopApplication())
|
||||
.Callback(() => applicationStopped.Release());
|
||||
var task = Task.CompletedTask;
|
||||
|
||||
_manager.Start(task);
|
||||
|
||||
await applicationStopped.WaitAsync(TimeSpan.FromMilliseconds(50));
|
||||
_lifetime.Verify(l => l.StopApplication(), Times.AtLeastOnce);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Start_OneTaskThatCancels_EndsApplication()
|
||||
{
|
||||
var applicationStopped = new SemaphoreSlim(0);
|
||||
_lifetime
|
||||
.Setup(l => l.StopApplication())
|
||||
.Callback(() => applicationStopped.Release());
|
||||
var task = Task.FromCanceled(new CancellationToken(true));
|
||||
|
||||
_manager.Start(task);
|
||||
|
||||
await applicationStopped.WaitAsync(TimeSpan.FromMilliseconds(50));
|
||||
_lifetime.Verify(l => l.StopApplication(), Times.AtLeastOnce);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Start_OneTaskThatErrors_EndsApplication()
|
||||
{
|
||||
var applicationStopped = new SemaphoreSlim(0);
|
||||
_lifetime
|
||||
.Setup(l => l.StopApplication())
|
||||
.Callback(() => applicationStopped.Release());
|
||||
_lifetime
|
||||
.Setup(l => l.StopApplication())
|
||||
.Callback(() => applicationStopped.Release());
|
||||
var task = Task.FromException(new Exception());
|
||||
|
||||
_manager.Start(task);
|
||||
|
||||
await applicationStopped.WaitAsync(TimeSpan.FromMilliseconds(50));
|
||||
_lifetime.Verify(l => l.StopApplication(), Times.AtLeastOnce);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Start_MultipleTasksThatFinish_EndsApplicationAfterAll()
|
||||
{
|
||||
var applicationStopped = new SemaphoreSlim(0);
|
||||
_lifetime
|
||||
.Setup(l => l.StopApplication())
|
||||
.Callback(() => applicationStopped.Release());
|
||||
var semaphores = EnumerableDataSeriesFactory
|
||||
.CreateSeries(() => new SemaphoreSlim(0), 5)
|
||||
.ToArray();
|
||||
var tasks = semaphores
|
||||
.Select(s => Task.Run(async () => await s.WaitAsync()))
|
||||
.ToArray();
|
||||
|
||||
foreach(var task in tasks) _manager.Start(task);
|
||||
|
||||
_lifetime.Verify(l => l.StopApplication(), Times.Never);
|
||||
foreach (var semaphore in semaphores[..^1])
|
||||
{
|
||||
semaphore.Release();
|
||||
_lifetime.Verify(l => l.StopApplication(), Times.Never);
|
||||
}
|
||||
foreach (var semaphore in semaphores[^1..])
|
||||
{
|
||||
semaphore.Release();
|
||||
}
|
||||
await applicationStopped.WaitAsync(TimeSpan.FromMilliseconds(50));
|
||||
_lifetime.Verify(l => l.StopApplication(), Times.AtLeastOnce);
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
using LCU.API.Internal.DocumentLib.Containers;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using OwnerNumberMigration;
|
||||
using OwnerNumberMigration.Migrations;
|
||||
using OwnerNumberMigration.ValueParsing;
|
||||
|
||||
namespace OwnerNumberMigrationTest.Migrations;
|
||||
|
||||
public class OrganizationNumberMigrationTest : AttributeMigrationTaskTest<string[]>
|
||||
{
|
||||
protected override string SourceAttribute => "OrganizationNumber";
|
||||
|
||||
protected override string DestinationAttribute => nameof(DocumentInformation.OrganizationNumbers);
|
||||
|
||||
protected override IHostedService MigrationTaskFactory
|
||||
(
|
||||
AttributeMigrationFactory<string[]> factory,
|
||||
IParsingStrategyFactory strategyFactory,
|
||||
IMigrationManager manager
|
||||
) => new OrganizationNumberMigration(factory, strategyFactory, manager);
|
||||
|
||||
public OrganizationNumberMigrationTest() : base(Array.Empty<string>)
|
||||
{
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using OwnerNumberMigration.Migrations;
|
||||
using OwnerNumberMigration;
|
||||
using OwnerNumberMigration.ValueParsing;
|
||||
using LCU.API.Internal.DocumentLib.Containers;
|
||||
|
||||
namespace OwnerNumberMigrationTest.Migrations;
|
||||
|
||||
public class OrganizationNumbersMigrationTest : AttributeMigrationTaskTest<string[]>
|
||||
{
|
||||
protected override string SourceAttribute => "OrganizationNumbers";
|
||||
|
||||
protected override string DestinationAttribute => nameof(DocumentInformation.OrganizationNumbers);
|
||||
|
||||
protected override IHostedService MigrationTaskFactory
|
||||
(
|
||||
AttributeMigrationFactory<string[]> factory,
|
||||
IParsingStrategyFactory strategyFactory,
|
||||
IMigrationManager manager
|
||||
) => new OrganizationNumbersMigration(factory, strategyFactory, manager);
|
||||
|
||||
public OrganizationNumbersMigrationTest() : base(Array.Empty<string>)
|
||||
{
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using OwnerNumberMigration.Migrations;
|
||||
using OwnerNumberMigration;
|
||||
using OwnerNumberMigration.ValueParsing;
|
||||
using LCU.API.Internal.DocumentLib.Containers;
|
||||
|
||||
namespace OwnerNumberMigrationTest.Migrations;
|
||||
|
||||
public class PersonNumberMigrationTest : AttributeMigrationTaskTest<string[]>
|
||||
{
|
||||
protected override string SourceAttribute => "PersonNumber";
|
||||
|
||||
protected override string DestinationAttribute => nameof(DocumentInformation.PersonNumbers);
|
||||
|
||||
protected override IHostedService MigrationTaskFactory
|
||||
(
|
||||
AttributeMigrationFactory<string[]> factory,
|
||||
IParsingStrategyFactory strategyFactory,
|
||||
IMigrationManager manager
|
||||
) => new PersonNumberMigration(factory, strategyFactory, manager);
|
||||
|
||||
public PersonNumberMigrationTest() : base(Array.Empty<string>)
|
||||
{
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using OwnerNumberMigration.Migrations;
|
||||
using OwnerNumberMigration;
|
||||
using OwnerNumberMigration.ValueParsing;
|
||||
using LCU.API.Internal.DocumentLib.Containers;
|
||||
|
||||
namespace OwnerNumberMigrationTest.Migrations;
|
||||
|
||||
public class PersonNumbersMigrationTest : AttributeMigrationTaskTest<string[]>
|
||||
{
|
||||
protected override string SourceAttribute => "PersonNumbers";
|
||||
|
||||
protected override string DestinationAttribute => nameof(DocumentInformation.PersonNumbers);
|
||||
|
||||
protected override IHostedService MigrationTaskFactory
|
||||
(
|
||||
AttributeMigrationFactory<string[]> factory,
|
||||
IParsingStrategyFactory strategyFactory,
|
||||
IMigrationManager manager
|
||||
) => new PersonNumbersMigration(factory, strategyFactory, manager);
|
||||
|
||||
public PersonNumbersMigrationTest() : base(Array.Empty<string>)
|
||||
{
|
||||
}
|
||||
}
|
||||
+197
@@ -0,0 +1,197 @@
|
||||
using LCU.API.Internal.DocumentLib.Configurations;
|
||||
using LCU.API.Internal.DocumentLib.Containers;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using MongoDB.Driver;
|
||||
using OwnerNumberMigrationTest.TestDataFactories;
|
||||
using OwnerNumberMigrationTest.TestDoubles;
|
||||
|
||||
namespace OwnerNumberMigrationTest;
|
||||
|
||||
[Category("Integration")]
|
||||
public class OwnerNumberMigrationIntegrationTest : IDisposable
|
||||
{
|
||||
private readonly FakeMongoCollectionFactory _collectionFactory = new();
|
||||
|
||||
private readonly DocumentInformation _personNumber;
|
||||
private readonly DocumentInformation _personNumbers;
|
||||
private readonly DocumentInformation _organizationNumber;
|
||||
private readonly DocumentInformation _organizationNumbers;
|
||||
private readonly DocumentInformation _accountNumber;
|
||||
|
||||
private readonly DocumentInformation _personNumberMigrated;
|
||||
private readonly DocumentInformation _personNumbersMigrated;
|
||||
private readonly DocumentInformation _organizationNumberMigrated;
|
||||
private readonly DocumentInformation _organizationNumbersMigrated;
|
||||
private readonly DocumentInformation _accountNumberMigrated;
|
||||
|
||||
public OwnerNumberMigrationIntegrationTest()
|
||||
{
|
||||
static string SingleOwnerFactory() => Guid.NewGuid().ToString();
|
||||
|
||||
static string MultipleOwnersFactory() => string.Join
|
||||
(
|
||||
",",
|
||||
EnumerableDataSeriesFactory.CreateSeries(SingleOwnerFactory, 3)
|
||||
);
|
||||
|
||||
_personNumber = DocumentInformationFactory.CreateDocumentInformation();
|
||||
_personNumber.Attributes.Clear();
|
||||
_personNumber.Attributes["PersonNumber"] = SingleOwnerFactory();
|
||||
_personNumbers = DocumentInformationFactory.CreateDocumentInformation();
|
||||
_personNumbers.Attributes.Clear();
|
||||
_personNumbers.Attributes["PersonNumbers"] = MultipleOwnersFactory();
|
||||
_organizationNumber = DocumentInformationFactory.CreateDocumentInformation();
|
||||
_organizationNumber.Attributes.Clear();
|
||||
_organizationNumber.Attributes["OrganizationNumber"] = SingleOwnerFactory();
|
||||
_organizationNumbers = DocumentInformationFactory.CreateDocumentInformation();
|
||||
_organizationNumbers.Attributes.Clear();
|
||||
_organizationNumbers.Attributes["OrganizationNumbers"] = MultipleOwnersFactory();
|
||||
_accountNumber = DocumentInformationFactory.CreateDocumentInformation();
|
||||
_accountNumber.Attributes.Clear();
|
||||
_accountNumber.Attributes["AccountNumber"] = SingleOwnerFactory();
|
||||
|
||||
_personNumberMigrated = DocumentInformationFactory.CreateDocumentInformation();
|
||||
_personNumberMigrated.Attributes.Clear();
|
||||
_personNumberMigrated.Attributes["PersonNumber"] = SingleOwnerFactory();
|
||||
_personNumberMigrated.PersonNumbers = _personNumberMigrated.Attributes["PersonNumber"].Split(",");
|
||||
_personNumbersMigrated = DocumentInformationFactory.CreateDocumentInformation();
|
||||
_personNumbersMigrated.Attributes.Clear();
|
||||
_personNumbersMigrated.Attributes["PersonNumbers"] = MultipleOwnersFactory();
|
||||
_personNumbersMigrated.PersonNumbers = _personNumbersMigrated.Attributes["PersonNumbers"].Split(",");
|
||||
_organizationNumberMigrated = DocumentInformationFactory.CreateDocumentInformation();
|
||||
_organizationNumberMigrated.Attributes.Clear();
|
||||
_organizationNumberMigrated.Attributes["OrganizationNumber"] = SingleOwnerFactory();
|
||||
_organizationNumberMigrated.OrganizationNumbers =
|
||||
_organizationNumberMigrated.Attributes["OrganizationNumber"].Split(",");
|
||||
_organizationNumbersMigrated = DocumentInformationFactory.CreateDocumentInformation();
|
||||
_organizationNumbersMigrated.Attributes.Clear();
|
||||
_organizationNumbersMigrated.Attributes["OrganizationNumbers"] = MultipleOwnersFactory();
|
||||
_organizationNumbersMigrated.OrganizationNumbers =
|
||||
_organizationNumbersMigrated.Attributes["OrganizationNumbers"].Split(",");
|
||||
_accountNumberMigrated = DocumentInformationFactory.CreateDocumentInformation();
|
||||
_accountNumberMigrated.Attributes.Clear();
|
||||
_accountNumberMigrated.Attributes["AccountNumber"] = SingleOwnerFactory();
|
||||
_accountNumberMigrated.AccountNumber = _accountNumberMigrated.Attributes["AccountNumber"];
|
||||
}
|
||||
|
||||
[SetUp]
|
||||
public async Task Setup()
|
||||
{
|
||||
var collection = _collectionFactory.GetCollection<DocumentInformation>();
|
||||
|
||||
await collection.InsertManyAsync(new[]
|
||||
{
|
||||
_personNumber,
|
||||
_personNumbers,
|
||||
_organizationNumber,
|
||||
_organizationNumbers,
|
||||
_accountNumber,
|
||||
_personNumberMigrated,
|
||||
_personNumbersMigrated,
|
||||
_organizationNumberMigrated,
|
||||
_organizationNumbersMigrated,
|
||||
_accountNumberMigrated
|
||||
});
|
||||
|
||||
await collection.UpdateManyAsync
|
||||
(
|
||||
Builders<DocumentInformation>.Filter.In
|
||||
(
|
||||
d => d.DocumentId,
|
||||
new[]
|
||||
{
|
||||
_personNumber.DocumentId,
|
||||
_personNumbers.DocumentId,
|
||||
_organizationNumber.DocumentId,
|
||||
_organizationNumbers.DocumentId,
|
||||
_accountNumber.DocumentId
|
||||
}
|
||||
),
|
||||
Builders<DocumentInformation>.Update
|
||||
.Unset(nameof(DocumentInformation.PersonNumbers))
|
||||
.Unset(nameof(DocumentInformation.OrganizationNumbers))
|
||||
.Unset(nameof(DocumentInformation.AccountNumber))
|
||||
);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Program_AllCases_MigratesDocuments()
|
||||
{
|
||||
await Host.CreateDefaultBuilder()
|
||||
.ConfigureServices(services =>
|
||||
{
|
||||
services.RegisterOwnerMigrationServices();
|
||||
|
||||
services.AddSingleton<ILogger>(NullLogger.Instance);
|
||||
|
||||
services.AddSingleton(new DatabaseConfiguration
|
||||
{
|
||||
ConnectionString = _collectionFactory.ConnectionString,
|
||||
DatabaseName = _collectionFactory.DatabaseName<DocumentInformation>(),
|
||||
DocumentContainerName = _collectionFactory.CollectionName<DocumentInformation>()
|
||||
});
|
||||
})
|
||||
.Build()
|
||||
.RunAsync();
|
||||
|
||||
var migratedDocuments = (await
|
||||
(await
|
||||
_collectionFactory
|
||||
.GetCollection<DocumentInformation>()
|
||||
.FindAsync(Builders<DocumentInformation>.Filter.Empty)
|
||||
).ToListAsync())
|
||||
.ToDictionary(d => d.DocumentId);
|
||||
|
||||
CollectionAssert.AreEqual(_personNumber.Attributes["PersonNumber"].Split(","),
|
||||
migratedDocuments[_personNumber.DocumentId].PersonNumbers);
|
||||
CollectionAssert.AreEqual(_personNumbers.Attributes["PersonNumbers"].Split(","),
|
||||
migratedDocuments[_personNumbers.DocumentId].PersonNumbers);
|
||||
CollectionAssert.AreEqual(_organizationNumber.Attributes["OrganizationNumber"].Split(","),
|
||||
migratedDocuments[_organizationNumber.DocumentId].OrganizationNumbers);
|
||||
CollectionAssert.AreEqual(_organizationNumbers.Attributes["OrganizationNumbers"].Split(","),
|
||||
migratedDocuments[_organizationNumbers.DocumentId].OrganizationNumbers);
|
||||
Assert.AreEqual(_accountNumber.Attributes["AccountNumber"],
|
||||
migratedDocuments[_accountNumber.DocumentId].AccountNumber);
|
||||
|
||||
CollectionAssert.AreEqual(_personNumber.Attributes["PersonNumber"],
|
||||
migratedDocuments[_personNumber.DocumentId].Attributes["PersonNumber"]);
|
||||
CollectionAssert.AreEqual(_personNumbers.Attributes["PersonNumbers"],
|
||||
migratedDocuments[_personNumbers.DocumentId].Attributes["PersonNumbers"]);
|
||||
CollectionAssert.AreEqual(_organizationNumber.Attributes["OrganizationNumber"],
|
||||
migratedDocuments[_organizationNumber.DocumentId].Attributes["OrganizationNumber"]);
|
||||
CollectionAssert.AreEqual(_organizationNumbers.Attributes["OrganizationNumbers"],
|
||||
migratedDocuments[_organizationNumbers.DocumentId].Attributes["OrganizationNumbers"]);
|
||||
Assert.AreEqual(_accountNumber.Attributes["AccountNumber"],
|
||||
migratedDocuments[_accountNumber.DocumentId].Attributes["AccountNumber"]);
|
||||
|
||||
CollectionAssert.AreEqual(_personNumberMigrated.PersonNumbers,
|
||||
migratedDocuments[_personNumberMigrated.DocumentId].PersonNumbers);
|
||||
CollectionAssert.AreEqual(_personNumbersMigrated.PersonNumbers,
|
||||
migratedDocuments[_personNumbersMigrated.DocumentId].PersonNumbers);
|
||||
CollectionAssert.AreEqual(_organizationNumberMigrated.OrganizationNumbers,
|
||||
migratedDocuments[_organizationNumberMigrated.DocumentId].OrganizationNumbers);
|
||||
CollectionAssert.AreEqual(_organizationNumbersMigrated.OrganizationNumbers,
|
||||
migratedDocuments[_organizationNumbersMigrated.DocumentId].OrganizationNumbers);
|
||||
CollectionAssert.AreEqual(_accountNumberMigrated.OrganizationNumbers,
|
||||
migratedDocuments[_accountNumberMigrated.DocumentId].OrganizationNumbers);
|
||||
|
||||
CollectionAssert.AreEqual(_personNumberMigrated.Attributes["PersonNumber"],
|
||||
migratedDocuments[_personNumberMigrated.DocumentId].Attributes["PersonNumber"]);
|
||||
CollectionAssert.AreEqual(_personNumbersMigrated.Attributes["PersonNumbers"],
|
||||
migratedDocuments[_personNumbersMigrated.DocumentId].Attributes["PersonNumbers"]);
|
||||
CollectionAssert.AreEqual(_organizationNumberMigrated.Attributes["OrganizationNumber"],
|
||||
migratedDocuments[_organizationNumberMigrated.DocumentId].Attributes["OrganizationNumber"]);
|
||||
CollectionAssert.AreEqual(_organizationNumbersMigrated.Attributes["OrganizationNumbers"],
|
||||
migratedDocuments[_organizationNumbersMigrated.DocumentId].Attributes["OrganizationNumbers"]);
|
||||
CollectionAssert.AreEqual(_accountNumberMigrated.Attributes["AccountNumber"],
|
||||
migratedDocuments[_accountNumberMigrated.DocumentId].Attributes["AccountNumber"]);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_collectionFactory.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0" />
|
||||
<PackageReference Include="Mongo2Go" Version="3.1.3" />
|
||||
<PackageReference Include="Moq" Version="4.17.2" />
|
||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.2.1" />
|
||||
<PackageReference Include="NUnit.Analyzers" Version="3.3.0" />
|
||||
<PackageReference Include="coverlet.collector" Version="3.1.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Logging\" />
|
||||
<Folder Include="TestDataFactories\" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\OwnerNumberMigration\OwnerNumberMigration.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
using LCU.API.Internal.DocumentLib.Containers;
|
||||
|
||||
namespace OwnerNumberMigrationTest.TestDataFactories;
|
||||
|
||||
public static class DocumentInformationFactory
|
||||
{
|
||||
public static DocumentInformation CreateDocumentInformation() => new()
|
||||
{
|
||||
DocumentId = Guid.NewGuid(),
|
||||
DocumentBlobId = Guid.NewGuid(),
|
||||
TraceId = Guid.NewGuid(),
|
||||
Name = Guid.NewGuid().ToString(),
|
||||
Type = Guid.NewGuid().ToString(),
|
||||
Source = Guid.NewGuid().ToString(),
|
||||
DataSource = Guid.NewGuid().ToString(),
|
||||
FileSize = DateTime.Now.Millisecond,
|
||||
ScanStatus = DateTime.Now.Millisecond % 2 == 0,
|
||||
UploadDate = DateTime.Now,
|
||||
LastModifiedDate = DateTime.Now,
|
||||
CreatedDate = DateTime.Now,
|
||||
PersonNumbers = new[] { Guid.NewGuid().ToString(), Guid.NewGuid().ToString() },
|
||||
OrganizationNumbers = new []{ Guid.NewGuid().ToString() , Guid.NewGuid().ToString() },
|
||||
AccountNumber = Guid.NewGuid().ToString(),
|
||||
Attributes = new Dictionary<string, string>
|
||||
{
|
||||
["PersonNumber"] = Guid.NewGuid().ToString(),
|
||||
["PersonNumbers"] = $"{Guid.NewGuid()},{Guid.NewGuid()}",
|
||||
["OrganizationNumber"] = Guid.NewGuid().ToString(),
|
||||
["OrganizationNumbers"] = $"{Guid.NewGuid()},{Guid.NewGuid()}",
|
||||
["AccountNumber"] = Guid.NewGuid().ToString()
|
||||
},
|
||||
};
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
namespace OwnerNumberMigrationTest.TestDataFactories;
|
||||
|
||||
public static class EnumerableDataSeriesFactory
|
||||
{
|
||||
public static IEnumerable<T> CreateSeries<T>(Func<T> singleFactory, int count) => Enumerable
|
||||
.Repeat(singleFactory, count)
|
||||
.Select(f => f());
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
using LCU.API.Internal.DocumentLib.Containers;
|
||||
using MongoDB.Driver;
|
||||
using OwnerNumberMigration;
|
||||
|
||||
namespace OwnerNumberMigrationTest.TestDoubles;
|
||||
|
||||
public class FakeDocumentFilterFactory : IDocumentFilterFactory
|
||||
{
|
||||
public IDictionary<(string attribute, string destination), DocumentInformation[]> ApplicableDocuments { get; } =
|
||||
new Dictionary<(string, string), DocumentInformation[]>();
|
||||
|
||||
public FilterDefinition<DocumentInformation> AppliesForMigrationFilterFactory
|
||||
(
|
||||
string attributeName,
|
||||
string destinationAttributeName
|
||||
)
|
||||
{
|
||||
var applicable = ApplicableDocuments.TryGetValue((attributeName, destinationAttributeName), out var documents)
|
||||
? documents
|
||||
: Array.Empty<DocumentInformation>();
|
||||
|
||||
return Builders<DocumentInformation>.Filter.In
|
||||
(
|
||||
d => d.DocumentId,
|
||||
applicable.Select(d => d.DocumentId)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace OwnerNumberMigrationTest.TestDoubles;
|
||||
|
||||
public record Log(LogLevel LogLevel, Exception? Exception, string Message);
|
||||
|
||||
public class FakeLogger : ILogger
|
||||
{
|
||||
public IList<Log> Logs { get; }= new List<Log>();
|
||||
|
||||
public IDisposable BeginScope<TState>(TState state)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public bool IsEnabled(LogLevel logLevel)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
|
||||
{
|
||||
Logs.Add(new Log(logLevel, exception, formatter(state, exception)));
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
using LCU.API.Internal.DocumentLib.Containers;
|
||||
|
||||
namespace OwnerNumberMigrationTest.TestDoubles;
|
||||
|
||||
public class FakeMigrationObserver : IObserver<DocumentInformation>
|
||||
{
|
||||
public List<(DocumentInformation doc, DateTime started)> Documents { get; } = new();
|
||||
|
||||
public List<Exception> Errors { get; } = new();
|
||||
|
||||
public List<DateTime> Completions { get; } = new();
|
||||
|
||||
public void OnCompleted()
|
||||
{
|
||||
Completions.Add(DateTime.Now);
|
||||
}
|
||||
|
||||
public void OnError(Exception error)
|
||||
{
|
||||
Errors.Add(error);
|
||||
}
|
||||
|
||||
public void OnNext(DocumentInformation value)
|
||||
{
|
||||
Documents.Add((value, DateTime.Now));
|
||||
}
|
||||
}
|
||||
+739
@@ -0,0 +1,739 @@
|
||||
using System.Net;
|
||||
using LCU.API.Internal.DocumentLib.Utilities;
|
||||
using Mongo2Go;
|
||||
using MongoDB.Bson;
|
||||
using MongoDB.Bson.Serialization;
|
||||
using MongoDB.Driver;
|
||||
using MongoDB.Driver.Core.Connections;
|
||||
using MongoDB.Driver.Core.Servers;
|
||||
|
||||
namespace OwnerNumberMigrationTest.TestDoubles;
|
||||
|
||||
public class FakeMongoCollectionFactory : IMongoCollectionFactory, IDisposable
|
||||
{
|
||||
private readonly MongoDbRunner _runner = MongoDbRunner.Start();
|
||||
public string ConnectionString => _runner.ConnectionString;
|
||||
public string DatabaseName<T>() => typeof(T).Name + DatabasePostFix;
|
||||
public string CollectionName<T>() => typeof(T).Name;
|
||||
public string DatabasePostFix { get; set; } = string.Empty;
|
||||
public TimeSpan? TooManyRequestsOnWriteFor { get; set; }
|
||||
|
||||
public IMongoCollection<T> GetCollection<T>()
|
||||
{
|
||||
var collection = new MongoClient(_runner.ConnectionString)
|
||||
.GetDatabase(DatabaseName<T>())
|
||||
.GetCollection<T>(CollectionName<T>());
|
||||
|
||||
if (TooManyRequestsOnWriteFor is not null)
|
||||
{
|
||||
collection = new TooManyRequestsOnWriteCollectionDecorator<T>(collection, TooManyRequestsOnWriteFor.Value);
|
||||
}
|
||||
|
||||
return collection;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_runner.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public class TooManyRequestsOnWriteCollectionDecorator<T> : IMongoCollection<T>
|
||||
{
|
||||
private IMongoCollection<T> _collection;
|
||||
|
||||
private readonly TimeSpan _tooManyRequestsDuring;
|
||||
private DateTime? _firstRequest = null;
|
||||
|
||||
public TooManyRequestsOnWriteCollectionDecorator(IMongoCollection<T> collection, TimeSpan tooManyRequestsDuring)
|
||||
{
|
||||
_collection = collection;
|
||||
_tooManyRequestsDuring = tooManyRequestsDuring;
|
||||
}
|
||||
|
||||
private void CheckTimout()
|
||||
{
|
||||
if (_firstRequest is null)
|
||||
{
|
||||
_firstRequest = DateTime.Now;
|
||||
|
||||
var dns = new DnsEndPoint("localhost", 27017);
|
||||
var cluster = _collection.Database.Client.Cluster.ClusterId;
|
||||
var server = new ServerId(cluster, dns);
|
||||
var connection = new ConnectionId(server);
|
||||
|
||||
var result = new BsonDocument();
|
||||
result["code"] = 16500;
|
||||
result["errmsg"] = $"{Guid.NewGuid()} RetryAfterMs={_tooManyRequestsDuring.TotalMilliseconds} {Guid.NewGuid()}";
|
||||
|
||||
throw new MongoCommandException(connection, "", new BsonDocument(), result);
|
||||
}
|
||||
|
||||
if (DateTime.Now - _firstRequest < _tooManyRequestsDuring)
|
||||
{
|
||||
throw new Exception("cool it, mate");
|
||||
}
|
||||
}
|
||||
|
||||
public IAsyncCursor<TResult> Aggregate<TResult>(PipelineDefinition<T, TResult> pipeline, AggregateOptions options = null,
|
||||
CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
return _collection.Aggregate(pipeline, options, cancellationToken);
|
||||
}
|
||||
|
||||
public IAsyncCursor<TResult> Aggregate<TResult>(IClientSessionHandle session, PipelineDefinition<T, TResult> pipeline,
|
||||
AggregateOptions options = null, CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
return _collection.Aggregate(session, pipeline, options, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<IAsyncCursor<TResult>> AggregateAsync<TResult>(PipelineDefinition<T, TResult> pipeline, AggregateOptions options = null,
|
||||
CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
return _collection.AggregateAsync(pipeline, options, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<IAsyncCursor<TResult>> AggregateAsync<TResult>(IClientSessionHandle session, PipelineDefinition<T, TResult> pipeline, AggregateOptions options = null,
|
||||
CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
return _collection.AggregateAsync(session, pipeline, options, cancellationToken);
|
||||
}
|
||||
|
||||
public void AggregateToCollection<TResult>(PipelineDefinition<T, TResult> pipeline, AggregateOptions options = null,
|
||||
CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
CheckTimout();
|
||||
|
||||
_collection.AggregateToCollection(pipeline, options, cancellationToken);
|
||||
}
|
||||
|
||||
public void AggregateToCollection<TResult>(IClientSessionHandle session, PipelineDefinition<T, TResult> pipeline,
|
||||
AggregateOptions options = null, CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
CheckTimout();
|
||||
|
||||
_collection.AggregateToCollection(session, pipeline, options, cancellationToken);
|
||||
}
|
||||
|
||||
public Task AggregateToCollectionAsync<TResult>(PipelineDefinition<T, TResult> pipeline, AggregateOptions options = null,
|
||||
CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
CheckTimout();
|
||||
|
||||
return _collection.AggregateToCollectionAsync(pipeline, options, cancellationToken);
|
||||
}
|
||||
|
||||
public Task AggregateToCollectionAsync<TResult>(IClientSessionHandle session, PipelineDefinition<T, TResult> pipeline,
|
||||
AggregateOptions options = null, CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
CheckTimout();
|
||||
|
||||
return _collection.AggregateToCollectionAsync(session, pipeline, options, cancellationToken);
|
||||
}
|
||||
|
||||
public BulkWriteResult<T> BulkWrite(IEnumerable<WriteModel<T>> requests, BulkWriteOptions options = null,
|
||||
CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
CheckTimout();
|
||||
|
||||
return _collection.BulkWrite(requests, options, cancellationToken);
|
||||
}
|
||||
|
||||
public BulkWriteResult<T> BulkWrite(IClientSessionHandle session, IEnumerable<WriteModel<T>> requests, BulkWriteOptions options = null,
|
||||
CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
CheckTimout();
|
||||
|
||||
return _collection.BulkWrite(session, requests, options, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<BulkWriteResult<T>> BulkWriteAsync(IEnumerable<WriteModel<T>> requests, BulkWriteOptions options = null,
|
||||
CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
CheckTimout();
|
||||
|
||||
return _collection.BulkWriteAsync(requests, options, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<BulkWriteResult<T>> BulkWriteAsync(IClientSessionHandle session, IEnumerable<WriteModel<T>> requests, BulkWriteOptions options = null,
|
||||
CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
CheckTimout();
|
||||
|
||||
return _collection.BulkWriteAsync(session, requests, options, cancellationToken);
|
||||
}
|
||||
|
||||
public long Count(FilterDefinition<T> filter, CountOptions options = null,
|
||||
CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
return _collection.Count(filter, options, cancellationToken);
|
||||
}
|
||||
|
||||
public long Count(IClientSessionHandle session, FilterDefinition<T> filter, CountOptions options = null,
|
||||
CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
return _collection.Count(session, filter, options, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<long> CountAsync(FilterDefinition<T> filter, CountOptions options = null,
|
||||
CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
return _collection.CountAsync(filter, options, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<long> CountAsync(IClientSessionHandle session, FilterDefinition<T> filter, CountOptions options = null,
|
||||
CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
return _collection.CountAsync(session, filter, options, cancellationToken);
|
||||
}
|
||||
|
||||
public long CountDocuments(FilterDefinition<T> filter, CountOptions options = null,
|
||||
CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
return _collection.CountDocuments(filter, options, cancellationToken);
|
||||
}
|
||||
|
||||
public long CountDocuments(IClientSessionHandle session, FilterDefinition<T> filter, CountOptions options = null,
|
||||
CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
return _collection.CountDocuments(session, filter, options, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<long> CountDocumentsAsync(FilterDefinition<T> filter, CountOptions options = null,
|
||||
CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
return _collection.CountDocumentsAsync(filter, options, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<long> CountDocumentsAsync(IClientSessionHandle session, FilterDefinition<T> filter, CountOptions options = null,
|
||||
CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
return _collection.CountDocumentsAsync(session, filter, options, cancellationToken);
|
||||
}
|
||||
|
||||
public DeleteResult DeleteMany(FilterDefinition<T> filter, CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
CheckTimout();
|
||||
|
||||
return _collection.DeleteMany(filter, cancellationToken);
|
||||
}
|
||||
|
||||
public DeleteResult DeleteMany(FilterDefinition<T> filter, DeleteOptions options,
|
||||
CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
CheckTimout();
|
||||
|
||||
return _collection.DeleteMany(filter, options, cancellationToken);
|
||||
}
|
||||
|
||||
public DeleteResult DeleteMany(IClientSessionHandle session, FilterDefinition<T> filter, DeleteOptions options = null,
|
||||
CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
CheckTimout();
|
||||
|
||||
return _collection.DeleteMany(session, filter, options, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<DeleteResult> DeleteManyAsync(FilterDefinition<T> filter, CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
CheckTimout();
|
||||
|
||||
return _collection.DeleteManyAsync(filter, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<DeleteResult> DeleteManyAsync(FilterDefinition<T> filter, DeleteOptions options,
|
||||
CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
CheckTimout();
|
||||
|
||||
return _collection.DeleteManyAsync(filter, options, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<DeleteResult> DeleteManyAsync(IClientSessionHandle session, FilterDefinition<T> filter, DeleteOptions options = null,
|
||||
CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
CheckTimout();
|
||||
|
||||
return _collection.DeleteManyAsync(session, filter, options, cancellationToken);
|
||||
}
|
||||
|
||||
public DeleteResult DeleteOne(FilterDefinition<T> filter, CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
CheckTimout();
|
||||
|
||||
return _collection.DeleteOne(filter, cancellationToken);
|
||||
}
|
||||
|
||||
public DeleteResult DeleteOne(FilterDefinition<T> filter, DeleteOptions options,
|
||||
CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
CheckTimout();
|
||||
|
||||
return _collection.DeleteOne(filter, options, cancellationToken);
|
||||
}
|
||||
|
||||
public DeleteResult DeleteOne(IClientSessionHandle session, FilterDefinition<T> filter, DeleteOptions options = null,
|
||||
CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
CheckTimout();
|
||||
|
||||
return _collection.DeleteOne(session, filter, options, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<DeleteResult> DeleteOneAsync(FilterDefinition<T> filter, CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
CheckTimout();
|
||||
|
||||
return _collection.DeleteOneAsync(filter, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<DeleteResult> DeleteOneAsync(FilterDefinition<T> filter, DeleteOptions options,
|
||||
CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
CheckTimout();
|
||||
|
||||
return _collection.DeleteOneAsync(filter, options, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<DeleteResult> DeleteOneAsync(IClientSessionHandle session, FilterDefinition<T> filter, DeleteOptions options = null,
|
||||
CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
CheckTimout();
|
||||
|
||||
return _collection.DeleteOneAsync(session, filter, options, cancellationToken);
|
||||
}
|
||||
|
||||
public IAsyncCursor<TField> Distinct<TField>(FieldDefinition<T, TField> field, FilterDefinition<T> filter, DistinctOptions options = null,
|
||||
CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
return _collection.Distinct(field, filter, options, cancellationToken);
|
||||
}
|
||||
|
||||
public IAsyncCursor<TField> Distinct<TField>(IClientSessionHandle session, FieldDefinition<T, TField> field, FilterDefinition<T> filter,
|
||||
DistinctOptions options = null, CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
return _collection.Distinct(session, field, filter, options, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<IAsyncCursor<TField>> DistinctAsync<TField>(FieldDefinition<T, TField> field, FilterDefinition<T> filter, DistinctOptions options = null,
|
||||
CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
return _collection.DistinctAsync(field, filter, options, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<IAsyncCursor<TField>> DistinctAsync<TField>(IClientSessionHandle session, FieldDefinition<T, TField> field, FilterDefinition<T> filter,
|
||||
DistinctOptions options = null, CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
return _collection.DistinctAsync(session, field, filter, options, cancellationToken);
|
||||
}
|
||||
|
||||
public long EstimatedDocumentCount(EstimatedDocumentCountOptions options = null,
|
||||
CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
return _collection.EstimatedDocumentCount(options, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<long> EstimatedDocumentCountAsync(EstimatedDocumentCountOptions options = null,
|
||||
CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
return _collection.EstimatedDocumentCountAsync(options, cancellationToken);
|
||||
}
|
||||
|
||||
public IAsyncCursor<TProjection> FindSync<TProjection>(FilterDefinition<T> filter, FindOptions<T, TProjection> options = null,
|
||||
CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
return _collection.FindSync(filter, options, cancellationToken);
|
||||
}
|
||||
|
||||
public IAsyncCursor<TProjection> FindSync<TProjection>(IClientSessionHandle session, FilterDefinition<T> filter, FindOptions<T, TProjection> options = null,
|
||||
CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
return _collection.FindSync(session, filter, options, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<IAsyncCursor<TProjection>> FindAsync<TProjection>(FilterDefinition<T> filter, FindOptions<T, TProjection> options = null,
|
||||
CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
return _collection.FindAsync(filter, options, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<IAsyncCursor<TProjection>> FindAsync<TProjection>(IClientSessionHandle session, FilterDefinition<T> filter, FindOptions<T, TProjection> options = null,
|
||||
CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
return _collection.FindAsync(session, filter, options, cancellationToken);
|
||||
}
|
||||
|
||||
public TProjection FindOneAndDelete<TProjection>(FilterDefinition<T> filter, FindOneAndDeleteOptions<T, TProjection> options = null,
|
||||
CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
CheckTimout();
|
||||
|
||||
return _collection.FindOneAndDelete(filter, options, cancellationToken);
|
||||
}
|
||||
|
||||
public TProjection FindOneAndDelete<TProjection>(IClientSessionHandle session, FilterDefinition<T> filter,
|
||||
FindOneAndDeleteOptions<T, TProjection> options = null, CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
CheckTimout();
|
||||
|
||||
return _collection.FindOneAndDelete(session, filter, options, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<TProjection> FindOneAndDeleteAsync<TProjection>(FilterDefinition<T> filter, FindOneAndDeleteOptions<T, TProjection> options = null,
|
||||
CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
CheckTimout();
|
||||
|
||||
return _collection.FindOneAndDeleteAsync(filter, options, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<TProjection> FindOneAndDeleteAsync<TProjection>(IClientSessionHandle session, FilterDefinition<T> filter,
|
||||
FindOneAndDeleteOptions<T, TProjection> options = null, CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
CheckTimout();
|
||||
|
||||
return _collection.FindOneAndDeleteAsync(session, filter, options, cancellationToken);
|
||||
}
|
||||
|
||||
public TProjection FindOneAndReplace<TProjection>(FilterDefinition<T> filter, T replacement,
|
||||
FindOneAndReplaceOptions<T, TProjection> options = null, CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
CheckTimout();
|
||||
|
||||
return _collection.FindOneAndReplace(filter, replacement, options, cancellationToken);
|
||||
}
|
||||
|
||||
public TProjection FindOneAndReplace<TProjection>(IClientSessionHandle session, FilterDefinition<T> filter, T replacement,
|
||||
FindOneAndReplaceOptions<T, TProjection> options = null, CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
CheckTimout();
|
||||
|
||||
return _collection.FindOneAndReplace(session, filter, replacement, options, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<TProjection> FindOneAndReplaceAsync<TProjection>(FilterDefinition<T> filter, T replacement,
|
||||
FindOneAndReplaceOptions<T, TProjection> options = null, CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
CheckTimout();
|
||||
|
||||
return _collection.FindOneAndReplaceAsync(filter, replacement, options, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<TProjection> FindOneAndReplaceAsync<TProjection>(IClientSessionHandle session, FilterDefinition<T> filter, T replacement,
|
||||
FindOneAndReplaceOptions<T, TProjection> options = null, CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
CheckTimout();
|
||||
|
||||
return _collection.FindOneAndReplaceAsync(session, filter, replacement, options, cancellationToken);
|
||||
}
|
||||
|
||||
public TProjection FindOneAndUpdate<TProjection>(FilterDefinition<T> filter, UpdateDefinition<T> update,
|
||||
FindOneAndUpdateOptions<T, TProjection> options = null, CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
CheckTimout();
|
||||
|
||||
return _collection.FindOneAndUpdate(filter, update, options, cancellationToken);
|
||||
}
|
||||
|
||||
public TProjection FindOneAndUpdate<TProjection>(IClientSessionHandle session, FilterDefinition<T> filter,
|
||||
UpdateDefinition<T> update, FindOneAndUpdateOptions<T, TProjection> options = null,
|
||||
CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
CheckTimout();
|
||||
|
||||
return _collection.FindOneAndUpdate(session, filter, update, options, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<TProjection> FindOneAndUpdateAsync<TProjection>(FilterDefinition<T> filter, UpdateDefinition<T> update,
|
||||
FindOneAndUpdateOptions<T, TProjection> options = null, CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
CheckTimout();
|
||||
|
||||
return _collection.FindOneAndUpdateAsync(filter, update, options, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<TProjection> FindOneAndUpdateAsync<TProjection>(IClientSessionHandle session, FilterDefinition<T> filter, UpdateDefinition<T> update,
|
||||
FindOneAndUpdateOptions<T, TProjection> options = null, CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
CheckTimout();
|
||||
|
||||
return _collection.FindOneAndUpdateAsync(session, filter, update, options, cancellationToken);
|
||||
}
|
||||
|
||||
public void InsertOne(T document, InsertOneOptions options = null,
|
||||
CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
CheckTimout();
|
||||
|
||||
_collection.InsertOne(document, options, cancellationToken);
|
||||
}
|
||||
|
||||
public void InsertOne(IClientSessionHandle session, T document, InsertOneOptions options = null,
|
||||
CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
CheckTimout();
|
||||
|
||||
_collection.InsertOne(session, document, options, cancellationToken);
|
||||
}
|
||||
|
||||
public Task InsertOneAsync(T document, CancellationToken _cancellationToken)
|
||||
{
|
||||
CheckTimout();
|
||||
|
||||
return _collection.InsertOneAsync(document, _cancellationToken);
|
||||
}
|
||||
|
||||
public Task InsertOneAsync(T document, InsertOneOptions options = null,
|
||||
CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
CheckTimout();
|
||||
|
||||
return _collection.InsertOneAsync(document, options, cancellationToken);
|
||||
}
|
||||
|
||||
public Task InsertOneAsync(IClientSessionHandle session, T document, InsertOneOptions options = null,
|
||||
CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
CheckTimout();
|
||||
|
||||
return _collection.InsertOneAsync(session, document, options, cancellationToken);
|
||||
}
|
||||
|
||||
public void InsertMany(IEnumerable<T> documents, InsertManyOptions options = null,
|
||||
CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
CheckTimout();
|
||||
|
||||
_collection.InsertMany(documents, options, cancellationToken);
|
||||
}
|
||||
|
||||
public void InsertMany(IClientSessionHandle session, IEnumerable<T> documents, InsertManyOptions options = null,
|
||||
CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
CheckTimout();
|
||||
|
||||
_collection.InsertMany(session, documents, options, cancellationToken);
|
||||
}
|
||||
|
||||
public Task InsertManyAsync(IEnumerable<T> documents, InsertManyOptions options = null,
|
||||
CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
CheckTimout();
|
||||
|
||||
return _collection.InsertManyAsync(documents, options, cancellationToken);
|
||||
}
|
||||
|
||||
public Task InsertManyAsync(IClientSessionHandle session, IEnumerable<T> documents, InsertManyOptions options = null,
|
||||
CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
CheckTimout();
|
||||
|
||||
return _collection.InsertManyAsync(session, documents, options, cancellationToken);
|
||||
}
|
||||
|
||||
public IAsyncCursor<TResult> MapReduce<TResult>(BsonJavaScript map, BsonJavaScript reduce, MapReduceOptions<T, TResult> options = null,
|
||||
CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
return _collection.MapReduce(map, reduce, options, cancellationToken);
|
||||
}
|
||||
|
||||
public IAsyncCursor<TResult> MapReduce<TResult>(IClientSessionHandle session, BsonJavaScript map, BsonJavaScript reduce,
|
||||
MapReduceOptions<T, TResult> options = null, CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
return _collection.MapReduce(session, map, reduce, options, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<IAsyncCursor<TResult>> MapReduceAsync<TResult>(BsonJavaScript map, BsonJavaScript reduce, MapReduceOptions<T, TResult> options = null,
|
||||
CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
return _collection.MapReduceAsync(map, reduce, options, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<IAsyncCursor<TResult>> MapReduceAsync<TResult>(IClientSessionHandle session, BsonJavaScript map, BsonJavaScript reduce,
|
||||
MapReduceOptions<T, TResult> options = null, CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
return _collection.MapReduceAsync(session, map, reduce, options, cancellationToken);
|
||||
}
|
||||
|
||||
public IFilteredMongoCollection<TDerivedDocument> OfType<TDerivedDocument>() where TDerivedDocument : T
|
||||
{
|
||||
return _collection.OfType<TDerivedDocument>();
|
||||
}
|
||||
|
||||
public ReplaceOneResult ReplaceOne(FilterDefinition<T> filter, T replacement, ReplaceOptions options = null,
|
||||
CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
CheckTimout();
|
||||
|
||||
return _collection.ReplaceOne(filter, replacement, options, cancellationToken);
|
||||
}
|
||||
|
||||
public ReplaceOneResult ReplaceOne(FilterDefinition<T> filter, T replacement, UpdateOptions options,
|
||||
CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
CheckTimout();
|
||||
|
||||
return _collection.ReplaceOne(filter, replacement, options, cancellationToken);
|
||||
}
|
||||
|
||||
public ReplaceOneResult ReplaceOne(IClientSessionHandle session, FilterDefinition<T> filter, T replacement,
|
||||
ReplaceOptions options = null, CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
CheckTimout();
|
||||
|
||||
return _collection.ReplaceOne(session, filter, replacement, options, cancellationToken);
|
||||
}
|
||||
|
||||
public ReplaceOneResult ReplaceOne(IClientSessionHandle session, FilterDefinition<T> filter, T replacement, UpdateOptions options,
|
||||
CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
CheckTimout();
|
||||
|
||||
return _collection.ReplaceOne(session, filter, replacement, options, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<ReplaceOneResult> ReplaceOneAsync(FilterDefinition<T> filter, T replacement, ReplaceOptions options = null,
|
||||
CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
CheckTimout();
|
||||
|
||||
return _collection.ReplaceOneAsync(filter, replacement, options, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<ReplaceOneResult> ReplaceOneAsync(FilterDefinition<T> filter, T replacement, UpdateOptions options,
|
||||
CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
CheckTimout();
|
||||
|
||||
return _collection.ReplaceOneAsync(filter, replacement, options, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<ReplaceOneResult> ReplaceOneAsync(IClientSessionHandle session, FilterDefinition<T> filter, T replacement,
|
||||
ReplaceOptions options = null, CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
CheckTimout();
|
||||
|
||||
return _collection.ReplaceOneAsync(session, filter, replacement, options, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<ReplaceOneResult> ReplaceOneAsync(IClientSessionHandle session, FilterDefinition<T> filter, T replacement, UpdateOptions options,
|
||||
CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
CheckTimout();
|
||||
|
||||
return _collection.ReplaceOneAsync(session, filter, replacement, options, cancellationToken);
|
||||
}
|
||||
|
||||
public UpdateResult UpdateMany(FilterDefinition<T> filter, UpdateDefinition<T> update, UpdateOptions options = null,
|
||||
CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
CheckTimout();
|
||||
|
||||
return _collection.UpdateMany(filter, update, options, cancellationToken);
|
||||
}
|
||||
|
||||
public UpdateResult UpdateMany(IClientSessionHandle session, FilterDefinition<T> filter, UpdateDefinition<T> update,
|
||||
UpdateOptions options = null, CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
CheckTimout();
|
||||
|
||||
return _collection.UpdateMany(session, filter, update, options, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<UpdateResult> UpdateManyAsync(FilterDefinition<T> filter, UpdateDefinition<T> update, UpdateOptions options = null,
|
||||
CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
CheckTimout();
|
||||
|
||||
return _collection.UpdateManyAsync(filter, update, options, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<UpdateResult> UpdateManyAsync(IClientSessionHandle session, FilterDefinition<T> filter, UpdateDefinition<T> update,
|
||||
UpdateOptions options = null, CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
CheckTimout();
|
||||
|
||||
return _collection.UpdateManyAsync(session, filter, update, options, cancellationToken);
|
||||
}
|
||||
|
||||
public UpdateResult UpdateOne(FilterDefinition<T> filter, UpdateDefinition<T> update, UpdateOptions options = null,
|
||||
CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
CheckTimout();
|
||||
|
||||
return _collection.UpdateOne(filter, update, options, cancellationToken);
|
||||
}
|
||||
|
||||
public UpdateResult UpdateOne(IClientSessionHandle session, FilterDefinition<T> filter, UpdateDefinition<T> update,
|
||||
UpdateOptions options = null, CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
CheckTimout();
|
||||
|
||||
return _collection.UpdateOne(session, filter, update, options, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<UpdateResult> UpdateOneAsync(FilterDefinition<T> filter, UpdateDefinition<T> update, UpdateOptions options = null,
|
||||
CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
CheckTimout();
|
||||
|
||||
return _collection.UpdateOneAsync(filter, update, options, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<UpdateResult> UpdateOneAsync(IClientSessionHandle session, FilterDefinition<T> filter, UpdateDefinition<T> update,
|
||||
UpdateOptions options = null, CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
CheckTimout();
|
||||
|
||||
return _collection.UpdateOneAsync(session, filter, update, options, cancellationToken);
|
||||
}
|
||||
|
||||
public IChangeStreamCursor<TResult> Watch<TResult>(PipelineDefinition<ChangeStreamDocument<T>, TResult> pipeline, ChangeStreamOptions options = null,
|
||||
CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
return _collection.Watch(pipeline, options, cancellationToken);
|
||||
}
|
||||
|
||||
public IChangeStreamCursor<TResult> Watch<TResult>(IClientSessionHandle session, PipelineDefinition<ChangeStreamDocument<T>, TResult> pipeline,
|
||||
ChangeStreamOptions options = null, CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
return _collection.Watch(session, pipeline, options, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<IChangeStreamCursor<TResult>> WatchAsync<TResult>(PipelineDefinition<ChangeStreamDocument<T>, TResult> pipeline, ChangeStreamOptions options = null,
|
||||
CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
return _collection.WatchAsync(pipeline, options, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<IChangeStreamCursor<TResult>> WatchAsync<TResult>(IClientSessionHandle session, PipelineDefinition<ChangeStreamDocument<T>, TResult> pipeline, ChangeStreamOptions options = null,
|
||||
CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
return _collection.WatchAsync(session, pipeline, options, cancellationToken);
|
||||
}
|
||||
|
||||
public IMongoCollection<T> WithReadConcern(ReadConcern readConcern)
|
||||
{
|
||||
return _collection.WithReadConcern(readConcern);
|
||||
}
|
||||
|
||||
public IMongoCollection<T> WithReadPreference(ReadPreference readPreference)
|
||||
{
|
||||
return _collection.WithReadPreference(readPreference);
|
||||
}
|
||||
|
||||
public IMongoCollection<T> WithWriteConcern(WriteConcern writeConcern)
|
||||
{
|
||||
return _collection.WithWriteConcern(writeConcern);
|
||||
}
|
||||
|
||||
public CollectionNamespace CollectionNamespace => _collection.CollectionNamespace;
|
||||
|
||||
public IMongoDatabase Database => _collection.Database;
|
||||
|
||||
public IBsonSerializer<T> DocumentSerializer => _collection.DocumentSerializer;
|
||||
|
||||
public IMongoIndexManager<T> Indexes => _collection.Indexes;
|
||||
|
||||
public MongoCollectionSettings Settings => _collection.Settings;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using OwnerNumberMigration.ValueParsing;
|
||||
|
||||
namespace OwnerNumberMigrationTest.TestDoubles;
|
||||
|
||||
public class FakeParsingStrategy<T> : IValueParsingStrategy<T>
|
||||
{
|
||||
public IDictionary<string, T> Values { get; } = new Dictionary<string, T>();
|
||||
|
||||
private readonly Func<T> _newT;
|
||||
|
||||
public FakeParsingStrategy(Func<T> newT)
|
||||
{
|
||||
this._newT = newT;
|
||||
}
|
||||
|
||||
public T Parse(string value) => Values.TryGetValue(value, out var parsed)
|
||||
? parsed
|
||||
: _newT();
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
global using NUnit.Framework;
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
using OwnerNumberMigration.ValueParsing;
|
||||
using OwnerNumberMigrationTest.TestDataFactories;
|
||||
|
||||
namespace OwnerNumberMigrationTest.ValueParsing;
|
||||
|
||||
public class CommaStringArrayStrategyTest
|
||||
{
|
||||
private readonly IValueParsingStrategy<string[]> _parsingStrategy = new CommaStringArrayStrategy();
|
||||
|
||||
[Test]
|
||||
public void Parse_StringContainsSingleValue_ReturnsAnArrayContainingOnlyTheValue()
|
||||
{
|
||||
var owners = Guid.NewGuid().ToString();
|
||||
|
||||
var parsedOwners = _parsingStrategy.Parse(owners);
|
||||
|
||||
Assert.AreEqual(1, parsedOwners.Length);
|
||||
CollectionAssert.Contains(parsedOwners, owners);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Parse_StringContainsCommas_ReturnsAnArrayContainingTheValueSplitOnComma()
|
||||
{
|
||||
var ownerCount = new Random(DateTime.Now.Millisecond).Next(3, 10);
|
||||
var owners = string.Join(",", EnumerableDataSeriesFactory.CreateSeries(() => Guid.NewGuid().ToString(), ownerCount));
|
||||
|
||||
var parsedOwners = _parsingStrategy.Parse(owners);
|
||||
|
||||
Assert.AreEqual(ownerCount, parsedOwners.Length);
|
||||
CollectionAssert.AreEquivalent(parsedOwners, owners.Split(","));
|
||||
}
|
||||
}
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using OwnerNumberMigration.ValueParsing;
|
||||
|
||||
namespace OwnerNumberMigrationTest.ValueParsing;
|
||||
|
||||
public class ParsingStrategyFactoryTest
|
||||
{
|
||||
private readonly IParsingStrategyFactory _factory;
|
||||
|
||||
private readonly SingleValueArrayStrategy _singleArray = new();
|
||||
private readonly CommaStringArrayStrategy _comma = new();
|
||||
private readonly SingleValueStrategy _single = new();
|
||||
|
||||
public ParsingStrategyFactoryTest()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
services.AddSingleton(_singleArray);
|
||||
services.AddSingleton(_comma);
|
||||
services.AddSingleton(_single);
|
||||
|
||||
_factory = new ParsingStrategyFactory(services.BuildServiceProvider());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CreateParsingStrategy_PersonNumber_ReturnsSingleArrayStrategy()
|
||||
{
|
||||
const string Attribute = "PersonNumber";
|
||||
|
||||
var strategy = _factory.CreateParsingStrategy<string[]>(Attribute);
|
||||
|
||||
Assert.AreSame(_singleArray, strategy);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CreateParsingStrategy_PersonNumbers_ReturnsCommaStrategy()
|
||||
{
|
||||
const string Attribute = "PersonNumbers";
|
||||
|
||||
var strategy = _factory.CreateParsingStrategy<string[]>(Attribute);
|
||||
|
||||
Assert.AreSame(_comma, strategy);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CreateParsingStrategy_OrganizationNumber_ReturnsSingleArrayStrategy()
|
||||
{
|
||||
const string Attribute = "OrganizationNumber";
|
||||
|
||||
var strategy = _factory.CreateParsingStrategy<string[]>(Attribute);
|
||||
|
||||
Assert.AreSame(_singleArray, strategy);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CreateParsingStrategy_OrganizationNumbers_ReturnsCommaStrategy()
|
||||
{
|
||||
const string Attribute = "OrganizationNumbers";
|
||||
|
||||
var strategy = _factory.CreateParsingStrategy<string[]>(Attribute);
|
||||
|
||||
Assert.AreSame(_comma, strategy);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CreateParsingStrategy_AccountNumber_ReturnsSingleValueStrategy()
|
||||
{
|
||||
const string Attribute = "AccountNumber";
|
||||
|
||||
var strategy = _factory.CreateParsingStrategy<string>(Attribute);
|
||||
|
||||
Assert.AreSame(_single, strategy);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CreateParsingStrategy_UnkownValue_ThrowsException()
|
||||
{
|
||||
var attribute = Guid.NewGuid().ToString();
|
||||
|
||||
Assert.Throws<NotImplementedException>(() => _factory.CreateParsingStrategy<dynamic>(attribute));
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
using OwnerNumberMigration.ValueParsing;
|
||||
|
||||
namespace OwnerNumberMigrationTest.ValueParsing;
|
||||
|
||||
public class SingleValueArrayStrategyTest
|
||||
{
|
||||
private readonly IValueParsingStrategy<string[]> _parsingStrategy = new SingleValueArrayStrategy();
|
||||
|
||||
[Test]
|
||||
public void ParseOwners_AllCases_ReturnsAnArrayContainingOnlyTheValue()
|
||||
{
|
||||
var owners = Guid.NewGuid().ToString();
|
||||
|
||||
var parsedOwners = _parsingStrategy.Parse(owners);
|
||||
|
||||
Assert.AreEqual(1, parsedOwners.Length);
|
||||
CollectionAssert.Contains(parsedOwners, owners);
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
using OwnerNumberMigration.ValueParsing;
|
||||
|
||||
namespace OwnerNumberMigrationTest.ValueParsing;
|
||||
|
||||
public class SingleValueStrategyTest
|
||||
{
|
||||
private readonly IValueParsingStrategy<string> _parsingStrategy = new SingleValueStrategy();
|
||||
|
||||
[Test]
|
||||
public void ParseOwners_AllCases_ReturnsAnArrayContainingOnlyTheValue()
|
||||
{
|
||||
var account = Guid.NewGuid().ToString();
|
||||
|
||||
var parsedAccount = _parsingStrategy.Parse(account);
|
||||
|
||||
Assert.AreEqual(account, parsedAccount);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user