using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using FluentAssertions; using LCU.API.Internal.Document.Exceptions; using LCU.API.Internal.Document.Services; using LCU.API.Internal.DocumentLib.Containers; using LCU.API.Internal.DocumentLib.Repositories; using LCU.Library.Internal.DocumentModels.Requests; using Moq; using NUnit.Framework; namespace LCU.API.Internal.DocumentTest.Services; public class DocumentAuditServiceTest { private DocumentAuditService _documentAuditService; private Mock _mockDocumentAuditRepository; [SetUp] public void Setup() { _mockDocumentAuditRepository = new Mock(); _documentAuditService = new DocumentAuditService(_mockDocumentAuditRepository.Object); } [Test] public async Task GetDocumentAudits_RepositoryReturnsActionAudits_ReturnsAuditsTransformedToResponse() { var documentId = Guid.NewGuid(); var actionAudits = new List { new DocumentActionAudit ( documentId, Guid.NewGuid().ToString(), DateTime.Now, Guid.NewGuid().ToString(), Valid(), Guid.NewGuid().ToString() ) }; _mockDocumentAuditRepository .Setup(r => r.GetDocumentAudits(documentId)) .ReturnsAsync(actionAudits); var audits = await _documentAuditService.GetDocumentAudits(documentId); actionAudits.Should().HaveSameCount(audits); actionAudits.Should().AllSatisfy(a => audits.Should().ContainEquivalentOf(a)); } [Test] public async Task GetDocumentAudits_RepositoryReturnsFieldAudits_ReturnsAuditsTransformedToResponse() { var documentId = Guid.NewGuid(); var actionAudits = new List { new DocumentFieldAudit ( documentId, Guid.NewGuid().ToString(), DateTime.Now, Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid(), Guid.NewGuid() ) }; _mockDocumentAuditRepository .Setup(r => r.GetDocumentAudits(documentId)) .ReturnsAsync(actionAudits); var audits = await _documentAuditService.GetDocumentAudits(documentId); actionAudits.Should().HaveSameCount(audits); actionAudits.Should().AllSatisfy(a => audits.Should().ContainEquivalentOf(a)); } [Test] public async Task GetDocumentAudits_RepositoryReturnsAnyAuditsOfUnknownType_ThrowsDocumentAuditServiceException() { var documentId = Guid.NewGuid(); var actionAudits = new List { new UnknownAuditType ( documentId, Guid.NewGuid().ToString(), DateTime.Now, Guid.NewGuid().ToString() ) }; _mockDocumentAuditRepository .Setup(r => r.GetDocumentAudits(documentId)) .ReturnsAsync(actionAudits); Assert.ThrowsAsync(() => _documentAuditService.GetDocumentAudits(documentId)); } [Test] public void GetDocumentAudits_DocumentAuditRepositoryThrowsException_ThrowsDocumentAuditServiceExceptionThrown() { var docId = Guid.NewGuid(); _mockDocumentAuditRepository .Setup(x => x.GetDocumentAudits(docId)) .ThrowsAsync(new Exception("Thrown From Unit Test")) .Verifiable(); Assert.ThrowsAsync(async () => await _documentAuditService.GetDocumentAudits(docId)); _mockDocumentAuditRepository.Verify(); } [Test] public void GetDocumentAudits_Exception_DocumentAuditServiceExceptionThrown() { var docId = Guid.NewGuid(); _mockDocumentAuditRepository .Setup(x => x.GetDocumentAudits(docId)) .ThrowsAsync(new Exception("Thrown From Unit Test")) .Verifiable(); Assert.ThrowsAsync(async () => await _documentAuditService.GetDocumentAudits(docId)); _mockDocumentAuditRepository.Verify(); } [Test] public async Task CreateDocumentUploadedAudit_Success() { var uploadDate = DateTime.Parse("2022-04-28T17:32:35.093Z"); var documentInformation = GenerateDocumentInformation(); var audits = new DocumentAudit[] { new DocumentActionAudit ( documentInformation.DocumentId, "System", uploadDate, "System", DocumentAction.Upload, "Document Uploaded" ) }; _mockDocumentAuditRepository .Setup(x => x.SaveDocumentAudits(It.Is(list => list.Length == 1 && list.All(a => a is DocumentActionAudit) && ((DocumentActionAudit)list.FirstOrDefault()!).Action == DocumentAction.Upload && ((DocumentActionAudit)list.FirstOrDefault()!).UserId == "System" && ((DocumentActionAudit)list.FirstOrDefault()!).Source == "System" && ((DocumentActionAudit)list.FirstOrDefault()!).ActionDescription == "Document Uploaded"))) .ReturnsAsync(audits) .Verifiable(); var result = await _documentAuditService.CreateDocumentUploadedAudit(documentInformation.DocumentId, uploadDate); var resultArray = result.ToArray(); Assert.NotNull(result); audits.Should().HaveSameCount(resultArray); audits.Should().AllSatisfy(a => resultArray.Should().ContainEquivalentOf(a)); _mockDocumentAuditRepository.Verify(); } [Test] public void CreateDocumentUploadedAudit_DocumentAuditRepositoryThrowsException_ThrowsNewDocumentAuditServiceExceptionThrown() { var uploadDate = DateTime.Parse("2022-04-28T17:32:35.093Z"); var documentInformation = GenerateDocumentInformation(); _mockDocumentAuditRepository .Setup(x => x.SaveDocumentAudits(It.Is(list => list.Any()))) .ThrowsAsync(new DocumentAuditServiceException("Thrown From Unit Test")) .Verifiable(); Assert.ThrowsAsync(() => _documentAuditService.CreateDocumentUploadedAudit(documentInformation.DocumentId, uploadDate)); _mockDocumentAuditRepository.Verify(); } [Test] public void CreateDocumentUploadedAudit_Exception_DocumentAuditServiceExceptionThrown() { var uploadDate = DateTime.Parse("2022-04-28T17:32:35.093Z"); var documentInformation = GenerateDocumentInformation(); _mockDocumentAuditRepository .Setup(x => x.SaveDocumentAudits(It.Is(list => list.Any()))) .ThrowsAsync(new Exception("Thrown From Unit Test")) .Verifiable(); Assert.ThrowsAsync(() => _documentAuditService.CreateDocumentUploadedAudit(documentInformation.DocumentId, uploadDate)); _mockDocumentAuditRepository.Verify(); } [Test] public async Task CreateDocumentDeletedAudit_Success() { var deleteDate = DateTime.Parse("2022-04-28T17:32:35.093Z"); var documentInformation = GenerateDocumentInformation(); var audits = new DocumentAudit[] { new DocumentActionAudit ( documentInformation.DocumentId, "System", deleteDate, "System", DocumentAction.Delete, "Document Deleted" ) }; _mockDocumentAuditRepository .Setup(x => x.SaveDocumentAudits(It.Is(list => list.Length == 1 && list.All(a => a is DocumentActionAudit) && ((DocumentActionAudit)list.FirstOrDefault()!).Action == DocumentAction.Delete && ((DocumentActionAudit)list.FirstOrDefault()!).UserId == "System" && ((DocumentActionAudit)list.FirstOrDefault()!).Source == "System" && ((DocumentActionAudit)list.FirstOrDefault()!).ActionDescription == "Document Deleted"))) .ReturnsAsync(audits) .Verifiable(); var result = await _documentAuditService.CreateDocumentDeletedAudit(documentInformation.DocumentId, deleteDate); var resultArray = result.ToArray(); Assert.NotNull(result); audits.Should().HaveSameCount(resultArray); audits.Should().AllSatisfy(a => resultArray.Should().ContainEquivalentOf(a)); _mockDocumentAuditRepository.Verify(); } [Test] public void CreateDocumentDeletedAudit_DocumentAuditRepositoryThrowsException_ThrowsNewDocumentAuditServiceExceptionThrown() { var deleteDate = DateTime.Parse("2022-04-28T17:32:35.093Z"); var documentInformation = GenerateDocumentInformation(); _mockDocumentAuditRepository .Setup(x => x.SaveDocumentAudits(It.Is(list => list.Any()))) .ThrowsAsync(new DocumentAuditServiceException("Thrown From Unit Test")) .Verifiable(); Assert.ThrowsAsync(() => _documentAuditService.CreateDocumentDeletedAudit(documentInformation.DocumentId, deleteDate)); _mockDocumentAuditRepository.Verify(); } [Test] public void CreateDocumentDeletedAudit_Exception_DocumentAuditServiceExceptionThrown() { var deleteDate = DateTime.Parse("2022-04-28T17:32:35.093Z"); var documentInformation = GenerateDocumentInformation(); _mockDocumentAuditRepository .Setup(x => x.SaveDocumentAudits(It.Is(list => list.Any()))) .ThrowsAsync(new Exception("Thrown From Unit Test")) .Verifiable(); Assert.ThrowsAsync(() => _documentAuditService.CreateDocumentDeletedAudit(documentInformation.DocumentId, deleteDate)); _mockDocumentAuditRepository.Verify(); } [Test] public async Task CreateDocumentContentDeletedAudit_Success() { var deleteDate = DateTime.Parse("2022-04-28T17:32:35.093Z"); var documentInformation = GenerateDocumentInformation(); var reason = Guid.NewGuid().ToString(); var reasonFull = $"DocumentContent Deleted. Reason: {reason}"; var audits = new DocumentAudit[] { new DocumentActionAudit ( documentInformation.DocumentId, "System", deleteDate, "System", DocumentAction.DeleteContent, reasonFull ) }; _mockDocumentAuditRepository .Setup(x => x.SaveDocumentAudits(It.Is(list => list.Length == 1 && list.All(a => a is DocumentActionAudit) && ((DocumentActionAudit)list.FirstOrDefault()!).Action == DocumentAction.DeleteContent && ((DocumentActionAudit)list.FirstOrDefault()!).UserId == "System" && ((DocumentActionAudit)list.FirstOrDefault()!).Source == "System" && ((DocumentActionAudit)list.FirstOrDefault()!).ActionDescription == reasonFull))) .ReturnsAsync(audits) .Verifiable(); var result = await _documentAuditService.CreateDocumentContentDeletedAudit(documentInformation.DocumentId, deleteDate, reason); var resultArray = result.ToArray(); Assert.NotNull(result); audits.Should().HaveSameCount(resultArray); audits.Should().AllSatisfy(a => resultArray.Should().ContainEquivalentOf(a)); _mockDocumentAuditRepository.Verify(); } [Test] public void CreateDocumentContentDeletedAudit_DocumentAuditRepositoryThrowsException_ThrowsNewDocumentAuditServiceExceptionThrown() { var deleteDate = DateTime.Parse("2022-04-28T17:32:35.093Z"); var documentInformation = GenerateDocumentInformation(); var reason = Guid.NewGuid().ToString(); _mockDocumentAuditRepository .Setup(x => x.SaveDocumentAudits(It.Is(list => list.Any()))) .ThrowsAsync(new DocumentAuditServiceException("Thrown From Unit Test")) .Verifiable(); Assert.ThrowsAsync(() => _documentAuditService.CreateDocumentContentDeletedAudit(documentInformation.DocumentId, deleteDate, reason)); _mockDocumentAuditRepository.Verify(); } [Test] public void CreateDocumentContentDeletedAudit_Exception_DocumentAuditServiceExceptionThrown() { var deleteDate = DateTime.Parse("2022-04-28T17:32:35.093Z"); var documentInformation = GenerateDocumentInformation(); var reason = Guid.NewGuid().ToString(); _mockDocumentAuditRepository .Setup(x => x.SaveDocumentAudits(It.Is(list => list.Any()))) .ThrowsAsync(new Exception("Thrown From Unit Test")) .Verifiable(); Assert.ThrowsAsync(() => _documentAuditService.CreateDocumentContentDeletedAudit(documentInformation.DocumentId, deleteDate, reason)); _mockDocumentAuditRepository.Verify(); } [Test] public async Task CreateDocumentAudits_No_Differences_Success() { var updateDate = DateTime.Parse("2022-04-28T17:32:35.093Z"); var docInformationOld = GenerateDocumentInformation(); var docInformationNew = GenerateDocumentInformation(); var result = await _documentAuditService.CreateDocumentAudits(docInformationOld, docInformationNew, false, updateDate, "testUser"); Assert.NotNull(result); Assert.IsEmpty(result); _mockDocumentAuditRepository.Verify(x => x.SaveDocumentAudits(It.IsAny()), Times.Never); } [Test] public async Task CreateDocumentAudits_No_Differences_ContentChange_Success() { var updateDate = DateTime.Parse("2022-04-28T17:32:35.093Z"); var userId = Guid.NewGuid().ToString(); var docInformationOld = GenerateDocumentInformation(); var docInformationNew = GenerateDocumentInformation(); _mockDocumentAuditRepository .Setup(x => x.SaveDocumentAudits(It.Is(list => list.Length == 1 && list.All(a => a is DocumentFieldAudit) && ((DocumentFieldAudit)list.FirstOrDefault()!).Field == "Content" && list.All(audit => string.Equals(userId, ((DocumentFieldAudit)audit).UserId))))) .ReturnsAsync(new List()) .Verifiable(); await _documentAuditService.CreateDocumentAudits(docInformationOld, docInformationNew, true, updateDate, userId); _mockDocumentAuditRepository.Verify(); } [Test] public async Task CreateDocumentAudits_All_Differences_Success() { var updateDate = DateTime.Parse("2022-04-28T17:32:35.093Z"); var docInformationOld = GenerateDocumentInformation(); var docInformationEverythingChanges = GenerateDocumentInformation(); var userId = Guid.NewGuid().ToString(); docInformationEverythingChanges.Name = "BrandNewName"; docInformationEverythingChanges.Type = "BrandNewType"; docInformationEverythingChanges.ScanStatus = true; docInformationEverythingChanges.DataSource = "NEW SOURCE"; docInformationEverythingChanges.Attributes = new Dictionary { { "NEW_FIELD", "NEW_VALUE" } }; _mockDocumentAuditRepository .Setup(x => x.SaveDocumentAudits(It.Is(list => list.Length == 4 && list.All(a => a is DocumentFieldAudit) && list.Any(audit => string.Equals("Name", ((DocumentFieldAudit)audit).Field)) && list.Any(audit => string.Equals("Type", ((DocumentFieldAudit)audit).Field)) && list.Any(audit => string.Equals("ScanStatus", ((DocumentFieldAudit)audit).Field)) && list.Any(audit => string.Equals("Attributes", ((DocumentFieldAudit)audit).Field)) && list.All(audit => string.Equals(userId, ((DocumentFieldAudit)audit).UserId))))) .ReturnsAsync(new List()) .Verifiable(); await _documentAuditService.CreateDocumentAudits(docInformationOld, docInformationEverythingChanges, false, updateDate, userId); _mockDocumentAuditRepository.Verify(); } [Test] public void CreateDocumentAudits_DocumentAuditRepositoryThrowsException_ThrowsNewDocumentAuditServiceExceptionThrown() { var updateDate = DateTime.Parse("2022-04-28T17:32:35.093Z"); var docInformationOld = GenerateDocumentInformation(); var docInformationNew = GenerateDocumentInformation(); var userId = Guid.NewGuid().ToString(); docInformationNew.Name = "NewName.txt"; _mockDocumentAuditRepository .Setup(x => x.SaveDocumentAudits(It.Is(list => list.Any()))) .ThrowsAsync(new DocumentAuditServiceException("Thrown From Unit Test")) .Verifiable(); Assert.ThrowsAsync(() => _documentAuditService.CreateDocumentAudits(docInformationOld, docInformationNew, false, updateDate, userId)); _mockDocumentAuditRepository.Verify(); } [Test] public void CreateDocumentAudits_Exception_DocumentAuditServiceExceptionThrown() { var updateDate = DateTime.Parse("2022-04-28T17:32:35.093Z"); var docInformationOld = GenerateDocumentInformation(); var docInformationNew = GenerateDocumentInformation(); var userId = Guid.NewGuid().ToString(); docInformationNew.Name = "NewName.txt"; _mockDocumentAuditRepository .Setup(x => x.SaveDocumentAudits(It.Is(list => list.Any()))) .ThrowsAsync(new Exception("Thrown From Unit Test")) .Verifiable(); Assert.ThrowsAsync(() => _documentAuditService.CreateDocumentAudits(docInformationOld, docInformationNew, false, updateDate, userId)); _mockDocumentAuditRepository.Verify(); } [Test] public void AuditAction_RepositoryThrowsException_ThrowsDocumentAuditServiceException() { var documentId = Guid.NewGuid(); var userId = Guid.NewGuid().ToString(); var source = Guid.NewGuid().ToString(); var action = Valid(); _mockDocumentAuditRepository .Setup(r => r.SaveDocumentAudits(It.Is(a => a.DocumentId == documentId && a.UserId == userId && a.Source == source && a.Action == action))) .ThrowsAsync(new Exception()); Assert.ThrowsAsync(() => _documentAuditService.AuditAction(documentId, userId, source, action)); } [Test] public void AuditAction_UnknownActionType_ThrowsDocumentAuditServiceException() { var documentId = Guid.NewGuid(); var userId = Guid.NewGuid().ToString(); var source = Guid.NewGuid().ToString(); var action = Invalid(); Assert.ThrowsAsync(() => _documentAuditService.AuditAction(documentId, userId, source, action)); } private static DocumentAction[] AllDocumentActions => AllValid(); [TestCaseSource(nameof(AllDocumentActions))] public async Task AuditAction_ValidAction_SavesActionAudit(DocumentAction action) { var documentId = Guid.NewGuid(); var userId = Guid.NewGuid().ToString(); var source = Guid.NewGuid().ToString(); _mockDocumentAuditRepository .Setup(r => r.SaveDocumentAudits(It.Is(a => a.DocumentId == documentId && a.UserId == userId && a.Source == source && a.Action == action))) .Verifiable(); await _documentAuditService.AuditAction(documentId, userId, source, action); _mockDocumentAuditRepository.Verify(); } private static List GenerateDocumentAudits() { return new List { new DocumentFieldAudit(Guid.NewGuid(), "UnitTest", DateTime.Parse("2022-04-28T17:32:35.093Z"), "Test", "Test", "0", "1") }; } private static DocumentInformation GenerateDocumentInformation() { return new DocumentInformation { DocumentId = Guid.NewGuid(), Name = "TestFile.txt", Type = "Test", Source = "Developer's Laptop", ScanStatus = null, UploadDate = DateTime.Parse("2022-03-25T19:16:12.3665642Z"), LastModifiedDate = DateTime.Parse("0001-01-01T00:00:00"), Attributes = new Dictionary { { "IsTest", "true" }, { "Owner", "Me Myself" } } }; } private static T[] AllValid() where T : struct, Enum { return Enum.GetValues(); } private static T Valid() where T : struct, Enum { var values = Enum.GetValues(); return values[DateTime.Now.Millisecond % values.Length]; } private static T Invalid() where T : struct, Enum { return (T)Convert.ChangeType(Enum.GetValues().Length + 1, typeof(int)); } private class UnknownAuditType : DocumentAudit { public UnknownAuditType(Guid documentId, string source, DateTime date, string userId) : base(documentId, source, date, userId) { } } }