using System.Security.Claims; using audit_logs.Models; using audit_logs.Models.DTO; using audit_logs.Utils; using MongoDB.Bson; namespace audit_test.Repository; using audit_logs.Repositories; using audit.Model; using audit_logs.Services; public class AuditServiceIntegrationTests { private AuditService _service; // private AuditRecordRepository _repository; [Order(1)] [SetUp] public void Setup() { IntegrationDb.Database.DropCollectionAsync("AuditRecords"); //_repository = new AuditRecordRepository(IntegrationDb.Database); _service = new AuditService(IntegrationDb.Database); } [Test] public async Task RecordAuditAsync_CreatesAndRetrievesAuditRecord() { // Arrange var changes = new List { new AuditRecord.Change { Field = "Username", OldValue = "oldUser", NewValue = "newUser", ValueType = "String" } }; AuditRecord auditLogData = new(); auditLogData.EntityType = "User"; auditLogData.RecordId = "1"; auditLogData.UserId = "123"; auditLogData.ActionType = "Update"; auditLogData.ActionTime = DateTime.UtcNow; auditLogData.Reason = "Testing change detection"; auditLogData.UserIpAddress = "127.0.0.1"; auditLogData.Changes = changes; // Act await _service.RecordAuditAsync(auditLogData); // Retrieve all records to validate insertion var allRecords = await _service.AuditLogRepository.GetAllAuditRecordsAsync(); // Assert Assert.AreEqual(1, allRecords.Count, "There should be exactly three audit record in the database."); var storedRecord = allRecords[0]; Assert.AreEqual(auditLogData.EntityType, storedRecord.EntityType); Assert.AreEqual(auditLogData.RecordId, storedRecord.RecordId); Assert.AreEqual(auditLogData.UserId, storedRecord.UserId); Assert.AreEqual(auditLogData.ActionType, storedRecord.ActionType); Assert.AreEqual(auditLogData.Reason, storedRecord.Reason); // Assert.That(storedRecord.Changes, Is.EquivalentTo(changes).Using(new ChangeComparer()), "Changes should match the input changes."); } [Test] public async Task GetAllAuditRecords_ReturnsAllRecords() { // Prepare multiple records var records = new List { new AuditRecord { EntityType = "User", RecordId = "1", UserId = "123", ActionType = "Create", ActionTime = DateTime.UtcNow, Reason = "Test Reason 1" }, new AuditRecord { EntityType = "Admin", RecordId = "2", UserId = "456", ActionType = "Delete", ActionTime = DateTime.UtcNow, Reason = "Test Reason 2" } }; foreach (var rec in records) { await _service.AuditLogRepository.InsertOneAsync(rec); } // Test retrieval method var allRecords = await _service.AuditLogRepository.GetAllAuditRecordsAsync(); // Assert Assert.AreEqual(2, allRecords.Count, "All records should be retrieved."); Assert.IsTrue(allRecords.Exists(r => r.RecordId == "1" && r.EntityType == "User")); Assert.IsTrue(allRecords.Exists(r => r.RecordId == "2" && r.EntityType == "Admin")); } [Test] public async Task DetectChangesAsync_DetectsAndRecordsChanges() //este es el que da problemas { // Arrange // Arrange var user = new ClaimsPrincipal(new ClaimsIdentity(new[] { new Claim(ClaimTypes.Name, "testUser"), new Claim("IpAddress", "127.0.0.1") })); // Variables de auditoría AuditLogData auditLogData = new(); auditLogData.OriginalJson = "{\"nhc\":\"C469\",\"person\":{\"admTime\":\"2024-11-26T12:03:36.276Z\",\"birthDate\":\"2024-11-02\",\"firstName\":\"Carlos Vi\",\"secondName\":\"Rey\",\"gender\":\"Male\",\"allergies\":\"\",\"ids\":{\"MR\":\"C469\"},\"historicalIds\":{}},\"unitId\":\"662760a96bdc7150b49fe29d\",\"admissionDate\":\"2024-11-26T12:03:36.276Z\",\"allergies\":\"\",\"diagnosis\":{\"optionType\":null,\"name\":\"Absceso amebiano del cerebro\",\"iconDefault\":null,\"iconLight\":null,\"iconDark\":null,\"color\":null,\"bgColor\":null,\"description\":\"A06.6\",\"initDate\":null,\"endDate\":null},\"origin\":{\"optionType\":null,\"name\":\"3A ucip\",\"iconDefault\":null,\"iconLight\":null,\"iconDark\":null,\"color\":null,\"bgColor\":null,\"description\":null,\"initDate\":null,\"endDate\":null},\"pointOfCareId\":null,\"languageBarrier\":\"\",\"originAux\":\"\",\"diagnosisAux\":\"Absceso amebiano del cerebro\"}"; // auditLogData.ModifiedJson = "{\"nhc\":\"H987541\",\"patientLocation\":{\"unitName\":\"SMACST\",\"bed\":\"SMT 2\",\"room\":\"SMT 2\"},\"person\":{\"admTime\":\"2024-11-26T10:31:37.018Z\",\"birthDate\":\"2024-11-01\",\"firstName\":\"Carlos\",\"secondName\":\"Borja\",\"gender\":\"Male\",\"allergies\":[{\"optionType\":null,\"name\":\"Sí\",\"iconDefault\":\"icCheck\",\"iconLight\":null,\"iconDark\":null,\"color\":null,\"bgColor\":null,\"description\":null,\"initDate\":null,\"endDate\":null}],\"ids\":{\"MR\":\"H987541\"},\"historicalIds\":{}},\"unitId\":\"662760a96bdc7150b49fe29d\",\"admissionDate\":\"2024-11-26T10:31:37.018Z\",\"allergies\":[{\"optionType\":null,\"name\":\"Sí\",\"iconDefault\":\"icCheck\",\"iconLight\":null,\"iconDark\":null,\"color\":null,\"bgColor\":null,\"description\":null,\"initDate\":null,\"endDate\":null}],\"diagnosis\":{\"optionType\":null,\"name\":\"Absceso amebiano del cerebro\",\"iconDefault\":null,\"iconLight\":null,\"iconDark\":null,\"color\":null,\"bgColor\":null,\"description\":\"A06.6\",\"initDate\":null,\"endDate\":null},\"origin\":{\"optionType\":null,\"name\":\"3A ucip\",\"iconDefault\":null,\"iconLight\":null,\"iconDark\":null,\"color\":null,\"bgColor\":null,\"description\":null,\"initDate\":null,\"endDate\":null},\"pointOfCareId\":\"662761fc6bdc7150b49fe29f\",\"languageBarrier\":\"\",\"originAux\":\"\",\"diagnosisAux\":\"Absceso amebiano del cerebro\"}"; auditLogData.ModifiedJson = null;//"{\"nhc\":\"C469\",\"person\":{\"admTime\":\"2024-11-26T12:03:36.276Z\",\"birthDate\":\"2024-11-01\",\"firstName\":\"Carlos V\",\"secondName\":\"Rey\",\"gender\":\"Male\",\"allergies\":\"\",\"ids\":{\"MR\":\"C469\"},\"historicalIds\":{}},\"unitId\":\"662760a96bdc7150b49fe29d\",\"admissionDate\":\"2024-11-26T12:03:36.276Z\",\"allergies\":\"\",\"diagnosis\":{\"optionType\":null,\"name\":\"Absceso amebiano del cerebro\",\"iconDefault\":null,\"iconLight\":null,\"iconDark\":null,\"color\":null,\"bgColor\":null,\"description\":\"A06.6\",\"initDate\":null,\"endDate\":null},\"origin\":{\"optionType\":null,\"name\":\"3A ucip\",\"iconDefault\":null,\"iconLight\":null,\"iconDark\":null,\"color\":null,\"bgColor\":null,\"description\":null,\"initDate\":null,\"endDate\":null},\"pointOfCareId\":null,\"languageBarrier\":\"\",\"originAux\":\"\",\"diagnosisAux\":\"Absceso amebiano del cerebro\"}"; auditLogData.EntityType = "User"; auditLogData.RecordId = "1"; auditLogData.UserId = "123"; auditLogData.ActionType = "Update"; auditLogData.ActionTime = DateTime.UtcNow; auditLogData.Reason = "Testing change detection"; auditLogData.UserIpAddress = "127.0.0.1"; // Act - Llamar a DetectChangesAsync para que identifique cambios await _service.CreateAuditLogAsync(auditLogData); // Retrieve all records to validate insertion var allRecords = await _service.AuditLogRepository.GetAllAuditRecordsAsync(); // Assert Assert.AreEqual(1, allRecords.Count, "Debe haber exactamente un registro de auditoría en la base de datos."); var storedRecord = allRecords[0]; Assert.AreEqual(auditLogData.EntityType, storedRecord.EntityType); Assert.AreEqual(auditLogData.RecordId, storedRecord.RecordId); Assert.AreEqual(auditLogData.UserId, storedRecord.UserId); Assert.AreEqual(auditLogData.ActionType, storedRecord.ActionType); Assert.AreEqual(auditLogData.Reason, storedRecord.Reason); // Verificar que los cambios detectados sean correctos Assert.That(storedRecord.Changes.Count, Is.EqualTo(10), "Debería detectar tres cambios."); } [Test] public async Task GetAuditLogsAsync_ReturnsPaginatedRecords() { // Arrange - Insert multiple audit records to test pagination var records = new List(); for (int i = 1; i <= 20; i++) { records.Add(new AuditRecord { EntityType = "TestEntity", RecordId = i.ToString(), UserId = "User" + i, ActionType = "Create", ActionTime = DateTime.UtcNow, Reason = "Test Reason " + i, Changes = new List { new AuditRecord.Change { Field = "Field" + i, OldValue = "OldValue" + i, NewValue = "NewValue" + i, ValueType = "String" } } }); } foreach (var record in records) { await _service.AuditLogRepository.InsertOneAsync(record); } // Act - Retrieve the first page with a page size of 5 int pageNumber = 1; int pageSize = 5; SpecificAuditLogFilterDto auditLogParametersDto = new SpecificAuditLogFilterDto(); auditLogParametersDto.pageNumber=1; auditLogParametersDto.pageSize = 5; var paginatedResult = await _service.GetAuditLogsBySpecificFilterAsync(auditLogParametersDto); // Assert - Check that the pagination returns the correct number of records and page information Assert.AreEqual(pageSize, paginatedResult.Records.Count(), "The number of records should match the page size."); Assert.AreEqual(20, paginatedResult.TotalRecords,"The total number of records should match the inserted records."); Assert.AreEqual(4, paginatedResult.TotalPages, "The total pages should be calculated correctly."); Assert.AreEqual(pageNumber, paginatedResult.CurrentPage, "The current page should be correct."); // Further assertions to check that the records and changes are the expected ones var firstRecord = paginatedResult.Records.First(); Assert.AreEqual("TestEntity", firstRecord.EntityType); Assert.AreEqual("1", firstRecord.RecordId); Assert.AreEqual("User1", firstRecord.UserId); Assert.AreEqual("Create", firstRecord.ActionType); // Verify the changes for the first record Assert.IsNotNull(firstRecord.Changes); Assert.AreEqual(1, firstRecord.Changes.Count, "There should be exactly one change in the first record."); var firstChange = firstRecord.Changes.First(); Assert.AreEqual("Field1", firstChange.Field); Assert.AreEqual("OldValue1", firstChange.OldValue); Assert.AreEqual("NewValue1", firstChange.NewValue); Assert.AreEqual("String", firstChange.ValueType); } [Test] public async Task CreateAuditLogAsync_CreatesCorrectAuditLog() { // Arrange var user = new ClaimsPrincipal(new ClaimsIdentity(new[] { new Claim(ClaimTypes.Name, "testUser"), new Claim("IpAddress", "127.0.0.1") })); var dataOriginal = new { Id = ObjectId.GenerateNewId(), Name = "OldName", Email = "old@example.com" }; var dataModified = new { Id = dataOriginal.Id, Name = "NewName", Email = "new@example.com" }; string reason = "Updating user details"; // Act await _service.CreateAuditLogAsync(user, dataOriginal, dataModified, reason); // Assert - Retrieve audit logs to validate var allRecords = await _service.AuditLogRepository.GetAllAuditRecordsAsync(); Assert.AreEqual(1, allRecords.Count, "There should be exactly one audit log entry."); var storedRecord = allRecords[0]; // Validate stored record Assert.AreEqual("AnonymousType", storedRecord.EntityType); Assert.AreEqual(dataOriginal.Id.ToString(), storedRecord.RecordId); Assert.AreEqual("testUser", storedRecord.UserId); Assert.AreEqual("update", storedRecord.ActionType); Assert.AreEqual(reason, storedRecord.Reason); Assert.AreEqual("127.0.0.1", storedRecord.UserIpAddress); // Validate changes Assert.AreEqual(2, storedRecord.Changes.Count, "There should be exactly two detected changes."); var nameChange = storedRecord.Changes.Find(c => c.Field == "Name"); Assert.NotNull(nameChange, "There should be a change in the 'Name' field."); Assert.AreEqual("OldName", nameChange.OldValue.AsString); Assert.AreEqual("NewName", nameChange.NewValue.AsString); Assert.AreEqual("string", nameChange.ValueType); var emailChange = storedRecord.Changes.Find(c => c.Field == "Email"); Assert.NotNull(emailChange, "There should be a change in the 'Email' field."); Assert.AreEqual("old@example.com", emailChange.OldValue.AsString); Assert.AreEqual("new@example.com", emailChange.NewValue.AsString); Assert.AreEqual("string", emailChange.ValueType); } [Test] public async Task CreateAuditLogAsync_WithNullIpAndDataOriginal_CreatesCorrectAuditLog() { // Arrange var user = new ClaimsPrincipal(new ClaimsIdentity(new[] { new Claim(ClaimTypes.Name, "testUser"), // IP Address claim is not included to simulate a null IP scenario })); // Set dataOriginal to null to simulate scenario object dataOriginal = null; var dataModified = new { Id = ObjectId.GenerateNewId(), Name = "NewName", Email = "new@example.com" }; string reason = "Creating new user"; // Act await _service.CreateAuditLogAsync(user, dataOriginal, dataModified, reason); // Assert - Retrieve audit logs to validate var allRecords = await _service.AuditLogRepository.GetAllAuditRecordsAsync(); Assert.AreEqual(1, allRecords.Count, "There should be exactly one audit log entry."); var storedRecord = allRecords[0]; // Validate stored record Assert.AreEqual("AnonymousType", storedRecord.EntityType); Assert.AreEqual(dataModified.Id.ToString(), storedRecord.RecordId); Assert.AreEqual("testUser", storedRecord.UserId); Assert.AreEqual("create", storedRecord.ActionType); // Action type should be 'create' as original data is null Assert.AreEqual(reason, storedRecord.Reason); // Validate changes - expecting changes in name and email as it's a creation Assert.AreEqual(3, storedRecord.Changes.Count, "There should be exactly two detected changes."); var nameChange = storedRecord.Changes.Find(c => c.Field == "Name"); Assert.NotNull(nameChange, "There should be a change in the 'Name' field."); Assert.IsTrue(nameChange.OldValue.IsBsonNull, "Old value should be null for new creations."); Assert.AreEqual("NewName", nameChange.NewValue.AsString); Assert.AreEqual("string", nameChange.ValueType); var emailChange = storedRecord.Changes.Find(c => c.Field == "Email"); Assert.NotNull(emailChange, "There should be a change in the 'Email' field."); Assert.IsTrue(emailChange.OldValue.IsBsonNull, "Old value should be null for new creations."); Assert.AreEqual("new@example.com", emailChange.NewValue.AsString); Assert.AreEqual("string", emailChange.ValueType); } [Test] public async Task CreateAuditLogAsync_WithNullIpAndDataModified_CreatesCorrectAuditLog() { // Arrange var user = new ClaimsPrincipal(new ClaimsIdentity(new[] { new Claim(ClaimTypes.Name, "testUser"), // IP Address claim is not included to simulate a null IP scenario })); // Set dataModified to null to simulate the scenario object? dataModified = null; var dataOriginal = new { Id = ObjectId.GenerateNewId(), Name = "OldName", Email = "old@example.com" }; string reason = "Deleting user"; // Act await _service.CreateAuditLogAsync(user, dataOriginal, dataModified, reason); // Assert - Retrieve audit logs to validate var allRecords = await _service.AuditLogRepository.GetAllAuditRecordsAsync(); Assert.AreEqual(1, allRecords.Count, "There should be exactly one audit log entry."); var storedRecord = allRecords[0]; // Validate stored record Assert.AreEqual("AnonymousType", storedRecord.EntityType); Assert.AreEqual(dataOriginal.Id.ToString(), storedRecord.RecordId); Assert.AreEqual("testUser", storedRecord.UserId); Assert.AreEqual("delete", storedRecord.ActionType); // Action type should be 'delete' as modified data is null Assert.AreEqual(reason, storedRecord.Reason); // Validate changes - expecting no changes as it's a deletion Assert.AreEqual(3, storedRecord.Changes.Count, "There should be exactly two detected changes."); var nameChange = storedRecord.Changes.Find(c => c.Field == "Name"); Assert.NotNull(nameChange, "There should be a change in the 'Name' field."); Assert.AreEqual("OldName", nameChange.OldValue.AsString, "Old value should be 'OldName'."); Assert.IsTrue(nameChange.NewValue.IsBsonNull, "New value should be null for deletions."); Assert.AreEqual("string", nameChange.ValueType); var emailChange = storedRecord.Changes.Find(c => c.Field == "Email"); Assert.NotNull(emailChange, "There should be a change in the 'Email' field."); Assert.AreEqual("old@example.com", emailChange.OldValue.AsString, "Old value should be 'old@example.com'."); Assert.IsTrue(emailChange.NewValue.IsBsonNull, "New value should be null for deletions."); Assert.AreEqual("string", emailChange.ValueType); } }