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 _parsingStrategy = new(Array.Empty); private readonly FakeMigrationObserver _observer = new(); private readonly FakeDocumentFilterFactory _filterFactory = new(); public AttributeMigrationTest() { _migration = new AttributeMigration ( _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() .FindAsync(Builders.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 ( _collectionFactory, _attributeName, _parsingStrategy, Guid.NewGuid().ToString(), _observer, _filterFactory ); Assert.ThrowsAsync(() => migration.Migrate(new CancellationToken())); } [Test] public void Migrate_DocumentInformationMissingDestination_NotifiesObserver() { var migration = new AttributeMigration ( _collectionFactory, _attributeName, _parsingStrategy, Guid.NewGuid().ToString(), _observer, _filterFactory ); var thrown = Assert.ThrowsAsync(() => migration.Migrate(new CancellationToken())); Assert.Contains(thrown, _observer.Errors); } [Test] public async Task Migrate_DocumentInformationMissingDestination_DoesNotMigrate() { var document = await MigrateableDocumentFactory(); var migration = new AttributeMigration ( _collectionFactory, _attributeName, _parsingStrategy, Guid.NewGuid().ToString(), _observer, _filterFactory ); Assert.ThrowsAsync(() => migration.Migrate(new CancellationToken())); var documentRecord = (await _collectionFactory .GetCollection() .FindAsync(Builders.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() .FindAsync(Builders.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() .FindAsync(Builders.Filter.Eq(d => d.DocumentId, document.DocumentId))) .Single(); var migratedDestinationProperty = GetDestinationProperty(documentRecord); CollectionAssert.AreEquivalent(parsedOwners, migratedDestinationProperty); } private async Task MigrateableDocumentFactory() { var document = DocumentInformationFactory.CreateDocumentInformation(); document.Attributes[_attributeName] = Guid.NewGuid().ToString(); _filterFactory.ApplicableDocuments[(_attributeName, DestinationProperty)] = new[] { document }; await _collectionFactory .GetCollection() .InsertOneAsync(document); return document; } public void Dispose() { _collectionFactory.Dispose(); } }