This commit is contained in:
Zeeshaun
2026-03-13 09:11:35 -05:00
parent f004c8205a
commit c2d6bda458
197 changed files with 14970 additions and 0 deletions
@@ -0,0 +1,185 @@
using FluentAssertions;
using LCU.API.Internal.DocumentLib.Containers;
using LCU.API.Internal.DocumentLibTest.TestDoubles;
using LCU.Library.Internal.DocumentModels.Requests;
using MongoDB.Bson.Serialization;
using MongoDB.Driver;
namespace LCU.API.Internal.DocumentLibTest.Containers;
public class DocumentAuditDiscriminatorConventionTest : IDisposable
{
private readonly FakeMongoCollectionFactory _collectionFactory = new();
private readonly IMongoCollection<DocumentAudit> _collection;
public DocumentAuditDiscriminatorConventionTest()
{
_collection = _collectionFactory.GetCollection<DocumentAudit>();
}
private static bool _registered = false;
[SetUp]
public void Setup()
{
if (_registered) return;
BsonSerializer.RegisterDiscriminatorConvention
(
typeof(DocumentAudit),
new DocumentAuditDiscriminatorConvention()
);
_registered = true;
}
[Test]
public async Task FindAuditDocuments_DocumentMissingDiscriminator_DeserializesAsFieldAudit()
{
var audit = FieldAuditFactory();
await _collection.InsertOneAsync(audit);
await _collection.FindOneAndUpdateAsync
(
a => a.DocumentId == audit.DocumentId,
Builders<DocumentAudit>.Update.Unset(DocumentAuditDiscriminatorConvention.Discriminator)
);
var retrievedAudit = await
(await _collection.FindAsync(a => a.DocumentId == audit.DocumentId))
.SingleOrDefaultAsync();
Assert.IsInstanceOf<DocumentFieldAudit>(retrievedAudit);
retrievedAudit.Should().BeEquivalentTo(audit, a => a.Using(DateTimeComparison.Truncated));
}
[Test]
public async Task FindAuditDocuments_DiscriminatorIsFieldAudit_DeserializesAsFieldAudit()
{
var audit = FieldAuditFactory();
await _collection.InsertOneAsync(audit);
await _collection.FindOneAndUpdateAsync
(
a => a.DocumentId == audit.DocumentId,
Builders<DocumentAudit>.Update.Set
(
DocumentAuditDiscriminatorConvention.Discriminator,
nameof(DocumentFieldAudit)
)
);
var retrievedAudit = await
(await _collection.FindAsync(a => a.DocumentId == audit.DocumentId))
.SingleOrDefaultAsync();
Assert.IsInstanceOf<DocumentFieldAudit>(retrievedAudit);
retrievedAudit.Should().BeEquivalentTo(audit, a => a.Using(DateTimeComparison.Truncated));
}
[Test]
public async Task InsertFieldAuditDocuments_NewDocumentWithConventionRegistered_DiscriminatorIsFieldAudit()
{
var audit = FieldAuditFactory();
await _collection.InsertOneAsync(audit);
var audits = await
(await _collection.FindAsync
(
Builders<DocumentAudit>.Filter.Eq
(
DocumentAuditDiscriminatorConvention.Discriminator,
nameof(DocumentFieldAudit)
)
)
)
.ToListAsync();
audits.Should().ContainEquivalentOf(audit, a => a.Using(DateTimeComparison.Truncated));
}
[Test]
public async Task FindAuditDocuments_DiscriminatorIsActionAudit_DeserializesAsActionAudit()
{
var audit = ActionAuditFactory();
await _collection.InsertOneAsync(audit);
await _collection.FindOneAndUpdateAsync
(
a => a.DocumentId == audit.DocumentId,
Builders<DocumentAudit>.Update.Set
(
DocumentAuditDiscriminatorConvention.Discriminator,
nameof(DocumentActionAudit)
)
);
var retrievedAudit = await
(await _collection.FindAsync(a => a.DocumentId == audit.DocumentId))
.SingleOrDefaultAsync();
Assert.IsInstanceOf<DocumentActionAudit>(retrievedAudit);
retrievedAudit.Should().BeEquivalentTo(audit, a => a.Using(DateTimeComparison.Truncated));
}
[Test]
public async Task InsertActionAuditDocuments_NewDocumentWithConventionRegistered_DiscriminatorIsActionAudit()
{
var audit = ActionAuditFactory();
await _collection.InsertOneAsync(audit);
var audits = await
(await _collection.FindAsync
(
Builders<DocumentAudit>.Filter.Eq
(
DocumentAuditDiscriminatorConvention.Discriminator,
nameof(DocumentActionAudit)
)
)
)
.ToListAsync();
audits.Should().ContainEquivalentOf(audit, a => a.Using(DateTimeComparison.Truncated));
}
[Test]
public async Task FindAuditDocuments_UnknownDiscriminator_ThrowsException()
{
var audit = FieldAuditFactory();
await _collection.InsertOneAsync(audit);
await _collection.FindOneAndUpdateAsync
(
a => a.DocumentId == audit.DocumentId,
Builders<DocumentAudit>.Update.Set
(
DocumentAuditDiscriminatorConvention.Discriminator,
Guid.NewGuid().ToString()
)
);
Assert.ThrowsAsync<NotSupportedException>(() => _collection.FindAsync(a => a.DocumentId == audit.DocumentId));
}
private static DocumentFieldAudit FieldAuditFactory() => new
(
Guid.NewGuid(),
Guid.NewGuid().ToString(),
DateTime.UtcNow,
Guid.NewGuid().ToString(),
Guid.NewGuid().ToString(),
Guid.NewGuid(),
Guid.NewGuid()
);
private static DocumentActionAudit ActionAuditFactory() => new
(
Guid.NewGuid(),
Guid.NewGuid().ToString(),
DateTime.UtcNow,
Guid.NewGuid().ToString(),
DocumentAction.Download,
Guid.NewGuid().ToString()
);
public void Dispose()
{
_collectionFactory.Dispose();
}
}
@@ -0,0 +1,31 @@
using FluentAssertions;
using FluentAssertions.Equivalency;
namespace LCU.API.Internal.DocumentLibTest;
public static class DateTimeComparison
{
public static IEquivalencyStep Truncated { get; } = new TruncatedDateTimeStep();
public static DateTime Truncate(this DateTime date) => date.AddTicks(-(date.Ticks % 10000));
private class TruncatedDateTimeStep : IEquivalencyStep
{
public EquivalencyResult Handle
(
Comparands comparands,
IEquivalencyValidationContext context,
IEquivalencyValidator nestedValidator
)
{
if (comparands.Subject is not DateTime subject || comparands.Expectation is not DateTime expectation)
return EquivalencyResult.ContinueWithNext;
subject.Truncate()
.Should()
.Be(expectation.Truncate(), context.Reason.FormattedMessage, context.Reason.Arguments);
return EquivalencyResult.AssertionCompleted;
}
}
}
@@ -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="FluentAssertions" Version="6.7.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
<PackageReference Include="Mongo2Go" Version="3.1.3" />
<PackageReference Include="Moq" Version="4.18.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>
<ProjectReference Include="..\LCU.API.Internal.DocumentLib\LCU.API.Internal.DocumentLib.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="Repositories\" />
</ItemGroup>
</Project>
@@ -0,0 +1,124 @@
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.Internal.DocumentModels.Requests;
using MongoDB.Driver;
namespace LCU.API.Internal.DocumentLibTest.Repositories;
public class DocumentAuditRepositoryTest : IDisposable
{
private readonly IDocumentAuditRepository _repository;
private readonly FakeMongoCollectionFactory _collectionFactory = new();
public DocumentAuditRepositoryTest()
{
_repository = new DocumentAuditRepository(_collectionFactory);
}
[Test]
public async Task GetDocumentAudits_FieldAuditRecordsExistForId_ReturnsMatchedLogsById()
{
var audits = EnumerableDataSeriesFactory.CreateSeries(() => FieldAuditFactory(), 5).ToArray();
await _collectionFactory.GetCollection<DocumentAudit>().InsertManyAsync(audits);
var expectedAudits = new[]{ audits.First(), audits.Last() };
var documentIds = expectedAudits.Select(a => a.DocumentId).ToArray();
var retrievedAudits = await _repository.GetDocumentAudits(documentIds);
retrievedAudits.Should().HaveSameCount(documentIds);
expectedAudits.Should().BeEquivalentTo(retrievedAudits, a => a.Using(DateTimeComparison.Truncated));
}
[Test]
public async Task GetDocumentAudits_ActionAuditRecordsExistForId_ReturnsMatchedLogsById()
{
var audits = EnumerableDataSeriesFactory.CreateSeries(() => ActionAuditFactory(), 5).ToArray();
await _collectionFactory.GetCollection<DocumentAudit>().InsertManyAsync(audits);
var expectedAudits = new[] { audits.First(), audits.Last() };
var documentIds = expectedAudits.Select(a => a.DocumentId).ToArray();
var retrievedAudits = await _repository.GetDocumentAudits(documentIds);
retrievedAudits.Should().HaveSameCount(documentIds);
expectedAudits.Should().BeEquivalentTo(retrievedAudits, a => a.Using(DateTimeComparison.Truncated));
}
[Test]
public async Task SaveDocumentAudits_FieldAuditsSubmitted_SavesAll()
{
var audits = EnumerableDataSeriesFactory.CreateSeries(() => FieldAuditFactory(), 5).ToArray();
await _repository.SaveDocumentAudits(audits);
var savedAudits = await (
await _collectionFactory
.GetCollection<DocumentAudit>()
.FindAsync(Builders<DocumentAudit>.Filter.In
(
nameof(DocumentAudit.DocumentId),
audits.Select(a => a.DocumentId))
)
).ToListAsync();
audits.Should().BeEquivalentTo(savedAudits, a => a.Using(DateTimeComparison.Truncated));
}
[Test]
public async Task SaveDocumentAudits_ActionAuditsSubmitted_SavesAll()
{
var audits = EnumerableDataSeriesFactory.CreateSeries(() => ActionAuditFactory(), 5).ToArray();
await _repository.SaveDocumentAudits(audits);
var savedAudits = await (
await _collectionFactory
.GetCollection<DocumentAudit>()
.FindAsync(Builders<DocumentAudit>.Filter.In(nameof(DocumentAudit.DocumentId),
audits.Select(a => a.DocumentId)))
).ToListAsync();
audits.Should().BeEquivalentTo(savedAudits, a => a.Using(DateTimeComparison.Truncated));
}
[Test]
public async Task SaveDocumentAudits_MultipleTypesOfAudits_CanRetrieveAll()
{
var documentId = Guid.NewGuid();
var fieldAudits = EnumerableDataSeriesFactory.CreateSeries<DocumentAudit>(() => FieldAuditFactory(documentId), 5);
var actionAudits = EnumerableDataSeriesFactory.CreateSeries<DocumentAudit>(() => ActionAuditFactory(documentId), 5);
var audits = fieldAudits.Concat(actionAudits).ToArray();
await _repository.SaveDocumentAudits(audits);
var savedAudits = await _repository.GetDocumentAudits(documentId);
audits.Should().BeEquivalentTo(savedAudits, a => a.Using(DateTimeComparison.Truncated));
}
private static DocumentFieldAudit FieldAuditFactory(Guid? documentId = null) => new
(
documentId ?? Guid.NewGuid(),
Guid.NewGuid().ToString(),
DateTime.UtcNow,
Guid.NewGuid().ToString(),
Guid.NewGuid().ToString(),
Guid.NewGuid().ToString(),
Guid.NewGuid().ToString()
);
private static DocumentActionAudit ActionAuditFactory(Guid? documentId = null) => new
(
documentId ?? Guid.NewGuid(),
Guid.NewGuid().ToString(),
DateTime.UtcNow,
Guid.NewGuid().ToString(),
(DocumentAction)DateTime.Now.Millisecond,
Guid.NewGuid().ToString()
);
public void Dispose()
{
_collectionFactory.Dispose();
}
}
@@ -0,0 +1,33 @@
using LCU.API.Internal.DocumentLib.Containers;
namespace LCU.API.Internal.DocumentLibTest.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.UtcNow,
LastModifiedDate = DateTime.UtcNow,
CreatedDate = DateTime.UtcNow,
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()
},
};
}
@@ -0,0 +1,8 @@
namespace LCU.API.Internal.DocumentLibTest.TestDataFactories;
public static class EnumerableDataSeriesFactory
{
public static IEnumerable<T> CreateSeries<T>(Func<T> singleFactory, int count) => Enumerable
.Repeat(singleFactory, count)
.Select(f => f());
}
@@ -0,0 +1,19 @@
using LCU.API.Internal.DocumentLib.Utilities;
using Mongo2Go;
using MongoDB.Driver;
namespace LCU.API.Internal.DocumentLibTest.TestDoubles;
public class FakeMongoCollectionFactory : IMongoCollectionFactory, IDisposable
{
private readonly MongoDbRunner _runner = MongoDbRunner.Start();
public IMongoCollection<T> GetCollection<T>() => new MongoClient(_runner.ConnectionString)
.GetDatabase(typeof(T).Name)
.GetCollection<T>(typeof(T).Name);
public void Dispose()
{
_runner.Dispose();
}
}
@@ -0,0 +1 @@
global using NUnit.Framework;