using FluentAssertions; using LCU.API.Internal.DocumentLib.Containers; using LCU.API.Internal.DocumentLib.Repositories; using LCU.API.Internal.DocumentLibTest.TestDataFactories; using LCU.API.Internal.DocumentLibTest.TestDoubles; using LCU.Library.DocumentType.Models.Responses; using LCU.Library.Internal.DocumentIngestorModels.Models.Pagination; using LCU.Library.Internal.DocumentModels.Requests.Search; using MongoDB.Driver; namespace LCU.API.Internal.DocumentLibTest.Repositories; public class DocumentInformationRepositoryTest : IDisposable { private readonly IDocumentInformationRepository _repository; private readonly FakeMongoCollectionFactory _collectionFactory = new(); public DocumentInformationRepositoryTest() { _repository = new DocumentInformationRepository(_collectionFactory); } [SetUp] public async Task SetUp() { var collection = _collectionFactory.GetCollection(); await collection.DeleteManyAsync(Builders.Filter.Empty); } [Test] public async Task GetDocumentInformation_NoMatchingId_ReturnsNull() { await SearchableDocumentFactory(d => d.CreatedDate = DateTime.Now.AddDays(1)); var actualDocument = await _repository.GetDocumentInformation(Guid.NewGuid()); Assert.Null(actualDocument); } [Test] public async Task SearchDocumentInformation_NameMatchedExactly_ReturnsDocument() { var document = await SearchableDocumentFactory(); var filter = new DocumentInformationFilter { Name = document.Name }; var documents = await _repository.SearchDocumentInformation(filter); Assert.AreEqual(1, documents.Count); documents.Single().Should().BeEquivalentTo(document, d => d.Using(DateTimeComparison.Truncated)); } [Test] public async Task SearchDocumentInformation_NameNotMatched_DoesNotReturnDocument() { await SearchableDocumentFactory(); var filter = new DocumentInformationFilter { Name = Guid.NewGuid().ToString() }; var documents = await _repository.SearchDocumentInformation(filter); Assert.AreEqual(0, documents.Count); } [Test] public async Task SearchDocumentInformation_TypeMatchedExactly_ReturnsDocument() { var document = await SearchableDocumentFactory(); var filter = new DocumentInformationFilter { Type = document.Type }; var documents = await _repository.SearchDocumentInformation(filter); Assert.AreEqual(1, documents.Count); documents.Single().Should().BeEquivalentTo(document, d => d.Using(DateTimeComparison.Truncated)); } [Test] public async Task SearchDocumentInformation_TypeNotMatched_DoesNotReturnDocument() { await SearchableDocumentFactory(); var filter = new DocumentInformationFilter { Type = Guid.NewGuid().ToString() }; var documents = await _repository.SearchDocumentInformation(filter); Assert.AreEqual(0, documents.Count); } [Test] public async Task SearchDocumentInformation_ScanStatusMatchedExactly_ReturnsDocument() { var document = await SearchableDocumentFactory(); var filter = new DocumentInformationFilter { ScanStatus = document.ScanStatus }; var documents = await _repository.SearchDocumentInformation(filter); Assert.AreEqual(1, documents.Count); documents.Single().Should().BeEquivalentTo(document, d => d.Using(DateTimeComparison.Truncated)); } [Test] public async Task SearchDocumentInformation_ScanStatusNotMatched_DoesNotReturnDocument() { var document = await SearchableDocumentFactory(); var filter = new DocumentInformationFilter { ScanStatus = !document.ScanStatus }; var documents = await _repository.SearchDocumentInformation(filter); Assert.AreEqual(0, documents.Count); } [Test] public async Task SearchDocumentInformation_SourceMatchedExactly_ReturnsDocument() { var document = await SearchableDocumentFactory(); var filter = new DocumentInformationFilter { Source = document.Source }; var documents = await _repository.SearchDocumentInformation(filter); Assert.AreEqual(1, documents.Count); documents.Single().Should().BeEquivalentTo(document, d => d.Using(DateTimeComparison.Truncated)); } [Test] public async Task SearchDocumentInformation_SourceNotMatched_DoesNotReturnDocument() { await SearchableDocumentFactory(); var filter = new DocumentInformationFilter { Source = Guid.NewGuid().ToString() }; var documents = await _repository.SearchDocumentInformation(filter); Assert.AreEqual(0, documents.Count); } [Test] public async Task SearchDocumentInformation_DataSourceMatchedExactly_ReturnsDocument() { var document = await SearchableDocumentFactory(); var filter = new DocumentInformationFilter { DataSource = document.DataSource }; var documents = await _repository.SearchDocumentInformation(filter); Assert.AreEqual(1, documents.Count); documents.Single().Should().BeEquivalentTo(document, d => d.Using(DateTimeComparison.Truncated)); } [Test] public async Task SearchDocumentInformation_DataSourceNotMatched_DoesNotReturnDocument() { await SearchableDocumentFactory(); var filter = new DocumentInformationFilter { DataSource = Guid.NewGuid().ToString() }; var documents = await _repository.SearchDocumentInformation(filter); Assert.AreEqual(0, documents.Count); } [Test] public async Task SearchDocumentInformation_TraceIdMatchedExactly_ReturnsDocument() { var document = await SearchableDocumentFactory(); var filter = new DocumentInformationFilter { TraceId = document.TraceId }; var documents = await _repository.SearchDocumentInformation(filter); Assert.AreEqual(1, documents.Count); documents.Single().Should().BeEquivalentTo(document, d => d.Using(DateTimeComparison.Truncated)); } [Test] public async Task SearchDocumentInformation_TraceIdNotMatched_DoesNotReturnDocument() { await SearchableDocumentFactory(); var filter = new DocumentInformationFilter { TraceId = Guid.NewGuid() }; var documents = await _repository.SearchDocumentInformation(filter); Assert.AreEqual(0, documents.Count); } [Test] public async Task SearchDocumentInformation_PersonNumbersEqualsMatchedExactly_ReturnsDocument() { var document = await SearchableDocumentFactory(); var filter = new DocumentInformationFilter { PersonNumbersEquals = document.PersonNumbers.FirstOrDefault() }; var documents = await _repository.SearchDocumentInformation(filter); Assert.AreEqual(1, documents.Count); documents.Single().Should().BeEquivalentTo(document, d => d.Using(DateTimeComparison.Truncated)); } [Test] public async Task SearchDocumentInformation_PersonNumbersEqualsNotMatched_DoesNotReturnDocument() { await SearchableDocumentFactory(); var filter = new DocumentInformationFilter { PersonNumbersEquals = Guid.NewGuid().ToString() }; var documents = await _repository.SearchDocumentInformation(filter); Assert.AreEqual(0, documents.Count); } [Test] public async Task SearchDocumentInformation_PersonNumbersContainsMatchedExactly_ReturnsDocument() { var document = await SearchableDocumentFactory(); var filter = new DocumentInformationFilter { PersonNumbersContains = document.PersonNumbers }; var documents = await _repository.SearchDocumentInformation(filter); Assert.AreEqual(1, documents.Count); documents.Single().Should().BeEquivalentTo(document, d => d.Using(DateTimeComparison.Truncated)); } [Test] public async Task SearchDocumentInformation_PersonNumbersContainsPartiallyMatched_DoesNotReturnDocument() { var document = await SearchableDocumentFactory(); var filter = new DocumentInformationFilter { PersonNumbersContains = EnumerableDataSeriesFactory.CreateSeries(() => Guid.NewGuid().ToString(), 5).Concat(document.PersonNumbers).ToArray() }; var documents = await _repository.SearchDocumentInformation(filter); Assert.AreEqual(0, documents.Count); } [Test] public async Task SearchDocumentInformation_PersonNumbersContainsNotMatched_DoesNotReturnDocument() { await SearchableDocumentFactory(); var filter = new DocumentInformationFilter { PersonNumbersContains = EnumerableDataSeriesFactory.CreateSeries(() => Guid.NewGuid().ToString(), 5).ToArray() }; var documents = await _repository.SearchDocumentInformation(filter); Assert.AreEqual(0, documents.Count); } [Test] public async Task SearchDocumentInformation_PersonNumbersContainsAnyMatchedExactly_ReturnsDocument() { var document = await SearchableDocumentFactory(); var filter = new DocumentInformationFilter { PersonNumbersContainsAny = document.PersonNumbers }; var documents = await _repository.SearchDocumentInformation(filter); Assert.AreEqual(1, documents.Count); documents.Single().Should().BeEquivalentTo(document, d => d.Using(DateTimeComparison.Truncated)); } [Test] public async Task SearchDocumentInformation_PersonNumbersContainsAnyPartiallyMatched_ReturnsDocument() { var document = await SearchableDocumentFactory(); var filter = new DocumentInformationFilter { PersonNumbersContainsAny = EnumerableDataSeriesFactory.CreateSeries(() => Guid.NewGuid().ToString(), 5).Concat(document.PersonNumbers).ToArray() }; var documents = await _repository.SearchDocumentInformation(filter); Assert.AreEqual(1, documents.Count); documents.Single().Should().BeEquivalentTo(document, d => d.Using(DateTimeComparison.Truncated)); } [Test] public async Task SearchDocumentInformation_PersonNumbersContainsAnyNoneMatched_DoesNotReturnDocument() { await SearchableDocumentFactory(); var filter = new DocumentInformationFilter { PersonNumbersContainsAny = EnumerableDataSeriesFactory.CreateSeries(() => Guid.NewGuid().ToString(), 5).ToArray() }; var documents = await _repository.SearchDocumentInformation(filter); Assert.AreEqual(0, documents.Count); } [Test] public async Task SearchDocumentInformation_OrganizationNumbersEqualsMatchedExactly_ReturnsDocument() { var document = await SearchableDocumentFactory(); var filter = new DocumentInformationFilter { OrganizationNumbersEquals = document.OrganizationNumbers.FirstOrDefault() }; var documents = await _repository.SearchDocumentInformation(filter); Assert.AreEqual(1, documents.Count); documents.Single().Should().BeEquivalentTo(document, d => d.Using(DateTimeComparison.Truncated)); } [Test] public async Task SearchDocumentInformation_OrganizationNumbersEqualsNotMatched_DoesNotReturnDocument() { await SearchableDocumentFactory(); var filter = new DocumentInformationFilter { OrganizationNumbersEquals = Guid.NewGuid().ToString() }; var documents = await _repository.SearchDocumentInformation(filter); Assert.AreEqual(0, documents.Count); } [Test] public async Task SearchDocumentInformation_OrganizationNumbersContainsMatchedExactly_ReturnsDocument() { var document = await SearchableDocumentFactory(); var filter = new DocumentInformationFilter { OrganizationNumbersContains = new [] { document.OrganizationNumbers.First() } }; var documents = await _repository.SearchDocumentInformation(filter); Assert.AreEqual(1, documents.Count); documents.Single().Should().BeEquivalentTo(document, d => d.Using(DateTimeComparison.Truncated)); } [Test] public async Task SearchDocumentInformation_OrganizationNumbersContainsPartiallyMatched_DoesNotReturnDocument() { var document = await SearchableDocumentFactory(); var filter = new DocumentInformationFilter { OrganizationNumbersContains = EnumerableDataSeriesFactory.CreateSeries(() => Guid.NewGuid().ToString(), 5).Concat(document.OrganizationNumbers).ToArray() }; var documents = await _repository.SearchDocumentInformation(filter); Assert.AreEqual(0, documents.Count); } [Test] public async Task SearchDocumentInformation_OrganizationNumbersContainsNotMatched_DoesNotReturnDocument() { await SearchableDocumentFactory(); var filter = new DocumentInformationFilter { OrganizationNumbersContains = EnumerableDataSeriesFactory.CreateSeries(() => Guid.NewGuid().ToString(), 5).ToArray() }; var documents = await _repository.SearchDocumentInformation(filter); Assert.AreEqual(0, documents.Count); } [Test] public async Task SearchDocumentInformation_OrganizationNumbersContainsAnyMatchedExactly_ReturnsDocument() { var document = await SearchableDocumentFactory(); var filter = new DocumentInformationFilter { OrganizationNumbersContainsAny = document.OrganizationNumbers }; var documents = await _repository.SearchDocumentInformation(filter); Assert.AreEqual(1, documents.Count); documents.Single().Should().BeEquivalentTo(document, d => d.Using(DateTimeComparison.Truncated)); } [Test] public async Task SearchDocumentInformation_OrganizationNumbersContainsAnyPartiallyMatched_ReturnsDocument() { var document = await SearchableDocumentFactory(); var filter = new DocumentInformationFilter { OrganizationNumbersContainsAny = EnumerableDataSeriesFactory.CreateSeries(() => Guid.NewGuid().ToString(), 5).Concat(document.OrganizationNumbers).ToArray() }; var documents = await _repository.SearchDocumentInformation(filter); Assert.AreEqual(1, documents.Count); documents.Single().Should().BeEquivalentTo(document, d => d.Using(DateTimeComparison.Truncated)); } [Test] public async Task SearchDocumentInformation_OrganizationNumbersContainsAnyNoneMatched_DoesNotReturnDocument() { await SearchableDocumentFactory(); var filter = new DocumentInformationFilter { OrganizationNumbersContainsAny = EnumerableDataSeriesFactory.CreateSeries(() => Guid.NewGuid().ToString(), 5).ToArray() }; var documents = await _repository.SearchDocumentInformation(filter); Assert.AreEqual(0, documents.Count); } [Test] public async Task SearchDocumentInformation_AccountNumberEqualsMatchedExactly_ReturnsDocument() { var document = await SearchableDocumentFactory(); var filter = new DocumentInformationFilter { AccountNumberEquals = document.AccountNumber }; var documents = await _repository.SearchDocumentInformation(filter); Assert.AreEqual(1, documents.Count); documents.Single().Should().BeEquivalentTo(document, d => d.Using(DateTimeComparison.Truncated)); } [Test] public async Task SearchDocumentInformation_AccountNumberEqualsNotMatched_DoesNotReturnDocument() { await SearchableDocumentFactory(); var filter = new DocumentInformationFilter { AccountNumberEquals = Guid.NewGuid().ToString() }; var documents = await _repository.SearchDocumentInformation(filter); Assert.AreEqual(0, documents.Count); } [Test] public async Task SearchDocumentInformation_AccountNumberContainsAnyNumberInFilter_ReturnsDocument() { var document = await SearchableDocumentFactory(); var filter = new DocumentInformationFilter { AccountNumberContainsAny = EnumerableDataSeriesFactory.CreateSeries(() => Guid.NewGuid().ToString(), 5).Concat(new[]{ document.AccountNumber }).ToArray() }; var documents = await _repository.SearchDocumentInformation(filter); Assert.AreEqual(1, documents.Count); documents.Single().Should().BeEquivalentTo(document, d => d.Using(DateTimeComparison.Truncated)); } [Test] public async Task SearchDocumentInformation_AccountNumberContainsAnyNumberNotInFilter_DoesNotReturnDocument() { var document = await SearchableDocumentFactory(); var filter = new DocumentInformationFilter { AccountNumberContainsAny = EnumerableDataSeriesFactory.CreateSeries(() => Guid.NewGuid().ToString(), 5).Concat(document.OrganizationNumbers).ToArray() }; var documents = await _repository.SearchDocumentInformation(filter); Assert.AreEqual(0, documents.Count); } [Test] public async Task SearchDocumentInformation_AttributesEqualsMatchedExactly_ReturnsDocument() { var attribute = Guid.NewGuid().ToString(); var value = Guid.NewGuid().ToString(); var document = await SearchableDocumentFactory(d => d.Attributes[attribute] = value); var filter = new DocumentInformationFilter { AttributesEquals = new Dictionary { [attribute] = value } }; var documents = await _repository.SearchDocumentInformation(filter); Assert.AreEqual(1, documents.Count); documents.Single().Should().BeEquivalentTo(document, d => d.Using(DateTimeComparison.Truncated)); } [Test] public async Task SearchDocumentInformation_AttributesEqualsNotMatched_DoesNotReturnDocument() { var attribute = Guid.NewGuid().ToString(); var value = Guid.NewGuid().ToString(); await SearchableDocumentFactory(d => d.Attributes[attribute] = value); var filter = new DocumentInformationFilter { AttributesEquals = new Dictionary { [attribute] = Guid.NewGuid().ToString() } }; var documents = await _repository.SearchDocumentInformation(filter); Assert.AreEqual(0, documents.Count); } [Test] public async Task SearchDocumentInformation_AttributesContainsAndAttributeMatchesExactly_ReturnsDocument() { var attribute = Guid.NewGuid().ToString(); var value = Guid.NewGuid().ToString(); var document = await SearchableDocumentFactory(d => d.Attributes[attribute] = value); var filter = new DocumentInformationFilter { AttributesContains = new Dictionary { [attribute] = value } }; var documents = await _repository.SearchDocumentInformation(filter); Assert.AreEqual(1, documents.Count); documents.Single().Should().BeEquivalentTo(document, d => d.Using(DateTimeComparison.Truncated)); } [Test] public async Task SearchDocumentInformation_AttributesContainsAndAttributeMatchesDifferentCase_ReturnsDocument() { var attribute = Guid.NewGuid().ToString(); var value = Guid.NewGuid().ToString(); var document = await SearchableDocumentFactory(d => d.Attributes[attribute] = value); var filter = new DocumentInformationFilter { AttributesContains = new Dictionary { [attribute] = value.ToLower() } }; var documents = await _repository.SearchDocumentInformation(filter); Assert.AreEqual(1, documents.Count); documents.Single().Should().BeEquivalentTo(document, d => d.Using(DateTimeComparison.Truncated)); } [Test] public async Task SearchDocumentInformation_AttributesContainsAndAttributeMatchesOnWordBoundary_ReturnsDocument() { var attribute = Guid.NewGuid().ToString(); var value = Guid.NewGuid().ToString(); var document = await SearchableDocumentFactory(d => d.Attributes[attribute] = $"{Guid.NewGuid()} {value},{Guid.NewGuid()}"); var filter = new DocumentInformationFilter { AttributesContains = new Dictionary { [attribute] = value } }; var documents = await _repository.SearchDocumentInformation(filter); Assert.AreEqual(1, documents.Count); documents.Single().Should().BeEquivalentTo(document, d => d.Using(DateTimeComparison.Truncated)); } [Test] public async Task SearchDocumentInformation_AttributesContainsAndAttributeMatchesWithinAnotherWord_DoesNotReturnDocument() { var attribute = Guid.NewGuid().ToString(); var value = Guid.NewGuid().ToString(); await SearchableDocumentFactory(d => d.Attributes[attribute] = $"{Guid.NewGuid()}{value}{Guid.NewGuid()}"); var filter = new DocumentInformationFilter { AttributesContains = new Dictionary { [attribute] = value } }; var documents = await _repository.SearchDocumentInformation(filter); Assert.AreEqual(0, documents.Count); } [Test] public async Task SearchDocumentInformation_AttributeContainsAnyAttributeMatchesExactlyOne_ReturnsDocument() { var attribute = Guid.NewGuid().ToString(); var value = Guid.NewGuid().ToString(); var document = await SearchableDocumentFactory(d => d.Attributes[attribute] = value); var filter = new DocumentInformationFilter { AttributesContainsAny = new Dictionary { [attribute] = $"{Guid.NewGuid()},{value},{Guid.NewGuid()}" } }; var documents = await _repository.SearchDocumentInformation(filter); Assert.AreEqual(1, documents.Count); documents.Single().Should().BeEquivalentTo(document, d => d.Using(DateTimeComparison.Truncated)); } [Test] public async Task SearchDocumentInformation_AttributeContainsAnyAttributeMatchesOneDifferentCase_ReturnsDocument() { var attribute = Guid.NewGuid().ToString(); var value = Guid.NewGuid().ToString(); var document = await SearchableDocumentFactory(d => d.Attributes[attribute] = value); var filter = new DocumentInformationFilter { AttributesContainsAny = new Dictionary { [attribute] = $"{Guid.NewGuid()},{value.ToLower()},{Guid.NewGuid()}" } }; var documents = await _repository.SearchDocumentInformation(filter); Assert.AreEqual(1, documents.Count); documents.Single().Should().BeEquivalentTo(document, d => d.Using(DateTimeComparison.Truncated)); } [Test] public async Task SearchDocumentInformation_AttributeContainsAnyAttributeMatchesNone_DoesNotReturnDocument() { var attribute = Guid.NewGuid().ToString(); var value = Guid.NewGuid().ToString(); await SearchableDocumentFactory(d => d.Attributes[attribute] = value); var filter = new DocumentInformationFilter { AttributesContainsAny = new Dictionary { [attribute] = $"{Guid.NewGuid()},{Guid.NewGuid()},{Guid.NewGuid()}" } }; var documents = await _repository.SearchDocumentInformation(filter); Assert.AreEqual(0, documents.Count); } [Test] public async Task SearchDocumentInformation_AttributesContainsMatchAndAttributeContainsAnyMatch_ReturnsDocument() { var attributeContains = Guid.NewGuid().ToString(); var attributeContainsAny = Guid.NewGuid().ToString(); var value = Guid.NewGuid().ToString(); var document = await SearchableDocumentFactory(d => { d.Attributes[attributeContains] = $"{Guid.NewGuid()},{value},{Guid.NewGuid()}"; d.Attributes[attributeContainsAny] = value; }); var filter = new DocumentInformationFilter { AttributesContains = new Dictionary { [attributeContains] = value }, AttributesContainsAny = new Dictionary { [attributeContainsAny] = $"{Guid.NewGuid()},{value},{Guid.NewGuid()}" } }; var documents = await _repository.SearchDocumentInformation(filter); Assert.AreEqual(1, documents.Count); documents.Single().Should().BeEquivalentTo(document, d => d.Using(DateTimeComparison.Truncated)); } [Test] public async Task SearchDocumentInformation_AttributesContainsMatchAndAttributeContainsAnyNotMatched_DoesNotReturnDocument() { var attributeContains = Guid.NewGuid().ToString(); var attributeContainsAny = Guid.NewGuid().ToString(); var value = Guid.NewGuid().ToString(); await SearchableDocumentFactory(d => { d.Attributes[attributeContains] = $"{Guid.NewGuid()},{value},{Guid.NewGuid()}"; d.Attributes[attributeContainsAny] = value; }); var filter = new DocumentInformationFilter { AttributesContains = new Dictionary { [attributeContains] = value }, AttributesContainsAny = new Dictionary { [attributeContainsAny] = $"{Guid.NewGuid()},{Guid.NewGuid()},{Guid.NewGuid()}" } }; var documents = await _repository.SearchDocumentInformation(filter); Assert.AreEqual(0, documents.Count); } [Test] public async Task SearchDocumentInformation_AttributesContainsNotMatchedAndAttributeContainsAnyMatch_DoesNotReturnDocument() { var attributeContains = Guid.NewGuid().ToString(); var attributeContainsAny = Guid.NewGuid().ToString(); var value = Guid.NewGuid().ToString(); await SearchableDocumentFactory(d => { d.Attributes[attributeContains] = $"{Guid.NewGuid()},{value},{Guid.NewGuid()}"; d.Attributes[attributeContainsAny] = value; }); var filter = new DocumentInformationFilter { AttributesContains = new Dictionary { [attributeContains] = Guid.NewGuid().ToString() }, AttributesContainsAny = new Dictionary { [attributeContainsAny] = $"{Guid.NewGuid()},{value},{Guid.NewGuid()}" } }; var documents = await _repository.SearchDocumentInformation(filter); Assert.AreEqual(0, documents.Count); } [Test] public async Task SearchDocumentInformation_CreatedDateMinimumLessThanCreatedDate_ReturnsDocument() { var document = await SearchableDocumentFactory(); var filter = new DocumentInformationFilter { CreatedDateMinimum = document.CreatedDate.AddDays(-1) }; var documents = await _repository.SearchDocumentInformation(filter); Assert.AreEqual(1, documents.Count); documents.Single().Should().BeEquivalentTo(document, d => d.Using(DateTimeComparison.Truncated)); } [Test] public async Task SearchDocumentInformation_CreatedDateMinimumGreaterThanCreatedDate_DoesNotReturnDocument() { var document = await SearchableDocumentFactory(); var filter = new DocumentInformationFilter { CreatedDateMinimum = document.CreatedDate.AddDays(1) }; var documents = await _repository.SearchDocumentInformation(filter); Assert.AreEqual(0, documents.Count); } [Test] public async Task SearchDocumentInformation_CreatedDateMaximumGreaterThanCreatedDate_ReturnsDocument() { var document = await SearchableDocumentFactory(); var filter = new DocumentInformationFilter { CreatedDateMaximum = document.CreatedDate.AddDays(1) }; var documents = await _repository.SearchDocumentInformation(filter); Assert.AreEqual(1, documents.Count); documents.Single().Should().BeEquivalentTo(document, d => d.Using(DateTimeComparison.Truncated)); } [Test] public async Task SearchDocumentInformation_CreatedDateMaximumLessThanCreatedDate_DoesNotReturnDocument() { var document = await SearchableDocumentFactory(); var filter = new DocumentInformationFilter { CreatedDateMaximum = document.CreatedDate.AddDays(-1) }; var documents = await _repository.SearchDocumentInformation(filter); Assert.AreEqual(0, documents.Count); } [Test] public async Task SearchDocumentInformation_UploadDateMinimumLessThanCreatedDate_ReturnsDocument() { var document = await SearchableDocumentFactory(); var filter = new DocumentInformationFilter { UploadDateMinimum = document.CreatedDate.AddDays(-1) }; var documents = await _repository.SearchDocumentInformation(filter); Assert.AreEqual(1, documents.Count); documents.Single().Should().BeEquivalentTo(document, d => d.Using(DateTimeComparison.Truncated)); } [Test] public async Task SearchDocumentInformation_UploadDateMinimumGreaterThanCreatedDate_DoesNotReturnDocument() { var document = await SearchableDocumentFactory(); var filter = new DocumentInformationFilter { UploadDateMinimum = document.CreatedDate.AddDays(1) }; var documents = await _repository.SearchDocumentInformation(filter); Assert.AreEqual(0, documents.Count); } [Test] public async Task SearchDocumentInformation_UploadDateMaximumGreaterThanCreatedDate_ReturnsDocument() { var document = await SearchableDocumentFactory(); var filter = new DocumentInformationFilter { UploadDateMaximum = document.CreatedDate.AddDays(1) }; var documents = await _repository.SearchDocumentInformation(filter); Assert.AreEqual(1, documents.Count); documents.Single().Should().BeEquivalentTo(document, d => d.Using(DateTimeComparison.Truncated)); } [Test] public async Task SearchDocumentInformation_UploadDateMaximumLessThanCreatedDate_DoesNotReturnDocument() { var document = await SearchableDocumentFactory(); var filter = new DocumentInformationFilter { UploadDateMaximum = document.CreatedDate.AddDays(-1) }; var documents = await _repository.SearchDocumentInformation(filter); Assert.AreEqual(0, documents.Count); } [Test] public async Task SearchDocumentInformation_LastModifiedMinimumLessThanCreatedDate_ReturnsDocument() { var document = await SearchableDocumentFactory(); var filter = new DocumentInformationFilter { LastModifiedMinimum = document.CreatedDate.AddDays(-1) }; var documents = await _repository.SearchDocumentInformation(filter); Assert.AreEqual(1, documents.Count); documents.Single().Should().BeEquivalentTo(document, d => d.Using(DateTimeComparison.Truncated)); } [Test] public async Task SearchDocumentInformation_LastModifiedMinimumGreaterThanCreatedDate_DoesNotReturnDocument() { var document = await SearchableDocumentFactory(); var filter = new DocumentInformationFilter { LastModifiedMinimum = document.CreatedDate.AddDays(1) }; var documents = await _repository.SearchDocumentInformation(filter); Assert.AreEqual(0, documents.Count); } [Test] public async Task SearchDocumentInformation_LastModifiedDateMaximumGreaterThanCreatedDate_ReturnsDocument() { var document = await SearchableDocumentFactory(); var filter = new DocumentInformationFilter { LastModifiedDateMaximum = document.CreatedDate.AddDays(1) }; var documents = await _repository.SearchDocumentInformation(filter); Assert.AreEqual(1, documents.Count); documents.Single().Should().BeEquivalentTo(document, d => d.Using(DateTimeComparison.Truncated)); } [Test] public async Task SearchDocumentInformation_LastModifiedMaximumLessThanCreatedDate_DoesNotReturnDocument() { var document = await SearchableDocumentFactory(); var filter = new DocumentInformationFilter { LastModifiedDateMaximum = document.CreatedDate.AddDays(-1) }; var documents = await _repository.SearchDocumentInformation(filter); Assert.AreEqual(0, documents.Count); } [Test] public async Task SearchDocumentInformation_Skip_BypassesTheSkippedDocuments() { const int skip = 5; var documents = await Task.WhenAll(EnumerableDataSeriesFactory.CreateSeries(async () => await SearchableDocumentFactory(), skip * 2)); var filter = new DocumentInformationFilter { AccountNumberContainsAny = documents.Select(d => d.AccountNumber).ToArray() }; var searchResult = await _repository.SearchDocumentInformation(filter, skip); searchResult.Should().HaveSameCount(documents[skip..]); searchResult.Should().AllSatisfy(r => documents.Should().ContainEquivalentOf(r, d => d.Using(DateTimeComparison.Truncated))); } [Test] public async Task SearchDocumentInformation_Take_OnlyReturnsThatManyDocuments() { const int take = 5; var documents = await Task.WhenAll(EnumerableDataSeriesFactory.CreateSeries(async () => await SearchableDocumentFactory(), take * 2)); var filter = new DocumentInformationFilter { AccountNumberContainsAny = documents.Select(d => d.AccountNumber).ToArray() }; var searchResult = await _repository.SearchDocumentInformation(filter, null, take); searchResult.Should().HaveCount(take); searchResult.Should().AllSatisfy(r => documents.Should().ContainEquivalentOf(r, d => d.Using(DateTimeComparison.Truncated))); } [Test] public async Task GetDocumentInformationByBlobId_NoMatchingId_ReturnsNull() { await SearchableDocumentFactory(); var actualDocument = await _repository.GetDocumentInformationByBlobId(Guid.NewGuid()); Assert.Null(actualDocument); } [Test] public async Task SaveDocumentInformation_AllCases_InsertsDocumentAndReturnsIt() { var document = DocumentInformationFactory.CreateDocumentInformation(); var inserted = await _repository.SaveDocumentInformation(document); inserted.Should().BeEquivalentTo(document, d => d.Using(DateTimeComparison.Truncated)); var found = (await (await _collectionFactory .GetCollection() .FindAsync(d => d.DocumentId == document.DocumentId)) .ToListAsync() ).SingleOrDefault(); inserted.Should().BeEquivalentTo(found, d => d.Using(DateTimeComparison.Truncated)); } [Test] public async Task UpdateDocumentInformation_DocumentDoesNotExist_ThrowsException() { var id = Guid.NewGuid(); var document = DocumentInformationFactory.CreateDocumentInformation(); await _repository.UpdateDocumentInformation(id, document); var found = await _collectionFactory .GetCollection() .CountDocumentsAsync(d => d.DocumentId == id); Assert.AreEqual(0, found); } [Test] public async Task UpdateDocumentInformation_DocumentExists_FullyReplacesWithNewAndReturnsNew() { var existingDocument = await SearchableDocumentFactory(); var updatedDocument = DocumentInformationFactory.CreateDocumentInformation(); updatedDocument.DocumentId = existingDocument.DocumentId; var updated = await _repository.UpdateDocumentInformation(existingDocument.DocumentId, updatedDocument); updated.Should().BeEquivalentTo(updatedDocument, d => d.Using(DateTimeComparison.Truncated)); var found = (await (await _collectionFactory .GetCollection() .FindAsync(d => d.DocumentId == existingDocument.DocumentId)) .ToListAsync() ).SingleOrDefault(); updated.Should().BeEquivalentTo(found, d => d.Using(DateTimeComparison.Truncated)); } [Test] public async Task RemoveDocumentInformation_DocumentDoesNotExist_ReturnsNull() { var removed = await _repository.RemoveDocumentInformation(Guid.NewGuid()); Assert.IsNull(removed); } [Test] public async Task RemoveDocumentInformation_DocumentExists_DeletesAndReturnsDeleted() { var document = await SearchableDocumentFactory(); var removed = await _repository.RemoveDocumentInformation(document.DocumentId); removed.Should().BeEquivalentTo(document, d => d.Using(DateTimeComparison.Truncated)); var found = await _collectionFactory .GetCollection() .CountDocumentsAsync(d => d.DocumentId == document.DocumentId); Assert.AreEqual(0, found); } [Test] public async Task SearchDocumentInformation_SortsDocumentsWithOneSort() { var numberOfDocuments = 25; var documents = await SearchableMultiDocumentFactory(numberOfDocuments); // Using UploadDate as CreatedDate has AddDays(i % 3) to create duplicates. // If sorted by just CreatedDate, the order may be different when it is pulled from the collection causing the test to fail var sort = new List { new() { Field = "UploadDate", Position = 0, IsDescending = true } }; // Sort descending on upload date documents.Sort((a, b) => b.UploadDate.CompareTo(a.UploadDate)); var searchResult = await _repository.SearchDocumentInformation(new DocumentInformationFilter(), null, numberOfDocuments, sort); documents.Should().BeInDescendingOrder(a => a.UploadDate); searchResult.Should().BeInDescendingOrder(a => a.UploadDate); searchResult.Should().BeEquivalentTo(documents, d => d.Using(DateTimeComparison.Truncated).WithStrictOrdering()); } [Test] public async Task SearchDocumentInformation_SortsDocumentsWithMultipleSortsAndMaintainsPositionOrder() { var numberOfDocuments = 25; var documents = await SearchableMultiDocumentFactory(numberOfDocuments); var sort = new List { new() { Field = "Name", Position = 2, IsDescending = false }, new() { Field = "CreatedDate", Position = 0, IsDescending = false }, new() { Field = "UploadDate", Position = 1, IsDescending = false } }; // Sort ascending on created date, then ascending on upload date, then ascending on name documents = documents.OrderBy(x => x.CreatedDate).ThenBy(x => x.UploadDate).ThenBy(x => x.Name).ToList(); var searchResult = await _repository.SearchDocumentInformation(new DocumentInformationFilter(), null, numberOfDocuments, sort); searchResult.Should().BeEquivalentTo(documents, d => d.Using(DateTimeComparison.Truncated).WithStrictOrdering()); } [Test] public async Task GetDocumentsForCleanup_Success() { var documents = new List(); for (var i=0; i < 3; i++) { var document = DocumentInformationFactory.CreateDocumentInformation(); document.CreatedDate = document.CreatedDate.AddMonths(i % 3 * -1); document.UploadDate = document.UploadDate.AddDays(i); documents.Add(document); } documents[1].DocumentBlobId = null; documents = await SearchableMultiDocumentFactory(documents); var request = new GetDocumentCleanupRequest { DocumentCodeDetailInfos = new List { new() { DocumentCode = documents[0].Type, InternalRetentionMonths = 3 }, new() { DocumentCode = documents[1].Type, InternalRetentionMonths = 1 }, new() { DocumentCode = documents[2].Type, InternalRetentionMonths = 1 } }, Limit = 10 }; var expectedResults = documents.Where(document => { var docCodeDetail = request.DocumentCodeDetailInfos .FirstOrDefault(docCodeDetail => document.Type == docCodeDetail.DocumentCode); return DateTime.Now >= document.CreatedDate.AddMonths(docCodeDetail!.InternalRetentionMonths) && document.DocumentBlobId is not null; }).Select(document => document.DocumentId).ToList(); var result = await _repository.GetDocumentsForCleanup(request); result.Should().BeEquivalentTo(expectedResults); } [Test] public async Task GetDocumentsForCleanup_Success_NoDocumentsFound() { var request = new GetDocumentCleanupRequest { DocumentCodeDetailInfos = new List { new() { DocumentCode = Guid.NewGuid().ToString(), InternalRetentionMonths = 3 } }, Limit = 10 }; var result = await _repository.GetDocumentsForCleanup(request); Assert.IsEmpty(result); } private async Task SearchableDocumentFactory(Action? documentSetup = null) { var document = DocumentInformationFactory.CreateDocumentInformation(); documentSetup?.Invoke(document); var collection = _collectionFactory.GetCollection(); await collection.InsertOneAsync(document); return document; } private async Task> SearchableMultiDocumentFactory(int numberOfDocuments = 10, Action? documentSetup = null) { var documents = new List(); for(var i = 0; i < numberOfDocuments; i++) { var document = DocumentInformationFactory.CreateDocumentInformation(); document.CreatedDate = document.CreatedDate.AddMonths(i % 3 * -1); document.UploadDate = document.UploadDate.AddDays(i); documentSetup?.Invoke(document); documents.Add(document); } var collection = _collectionFactory.GetCollection(); await collection.InsertManyAsync(documents); return documents; } private async Task> SearchableMultiDocumentFactory(List documents) { var collection = _collectionFactory.GetCollection(); await collection.InsertManyAsync(documents); return documents; } public void Dispose() { _collectionFactory.Dispose(); } }