Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 17 additions & 20 deletions src/Goodtocode.Domain.Tests/Entities/DomainEntityTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,10 @@ public TestEntity(Guid id, string partitionKey, DateTime createdOn, DateTimeOffs
: base(id, partitionKey, createdOn, timestamp) { }
}

private sealed class TestEvent : IDomainEvent<TestEntity>
private sealed class TestEvent(DomainEntityTests.TestEntity item) : IDomainEvent<TestEntity>
{
public TestEntity Item { get; set; }
public TestEntity Item { get; set; } = item;
public DateTime OccurredOn { get; set; } = DateTime.UtcNow;

public TestEvent(TestEntity item)
{
Item = item;
}
}

[TestMethod]
Expand Down Expand Up @@ -138,8 +133,8 @@ public void TimestampIsSetByDefault()
var afterCreation = DateTimeOffset.UtcNow;

// Assert
Assert.IsTrue(entity.Timestamp >= beforeCreation);
Assert.IsTrue(entity.Timestamp <= afterCreation);
Assert.IsGreaterThanOrEqualTo(beforeCreation, entity.Timestamp);
Assert.IsLessThanOrEqualTo(afterCreation, entity.Timestamp);
}

[TestMethod]
Expand Down Expand Up @@ -170,14 +165,14 @@ public void DomainEventsAddAndClearWorks()
entity.AddDomainEvent(evt);

// Assert
Assert.AreEqual(1, entity.DomainEvents.Count);
Assert.HasCount(1, entity.DomainEvents);
Assert.AreSame(evt, entity.DomainEvents[0]);

// Act
entity.ClearDomainEvents();

// Assert
Assert.AreEqual(0, entity.DomainEvents.Count);
Assert.IsEmpty(entity.DomainEvents);
}

[TestMethod]
Expand All @@ -193,7 +188,7 @@ public void DomainEventsCanAddMultipleEvents()
entity.AddDomainEvent(evt2);

// Assert
Assert.AreEqual(2, entity.DomainEvents.Count);
Assert.HasCount(2, entity.DomainEvents);
Assert.AreSame(evt1, entity.DomainEvents[0]);
Assert.AreSame(evt2, entity.DomainEvents[1]);
}
Expand All @@ -216,7 +211,7 @@ public void DomainEventsCollectionStartsEmpty()

// Assert
Assert.IsNotNull(entity.DomainEvents);
Assert.AreEqual(0, entity.DomainEvents.Count);
Assert.IsEmpty(entity.DomainEvents);
}

[TestMethod]
Expand All @@ -229,15 +224,15 @@ public void DomainEventsCanBeAddedAndClearedMultipleTimes()

// Act & Assert - First round
entity.AddDomainEvent(evt1);
Assert.AreEqual(1, entity.DomainEvents.Count);
Assert.HasCount(1, entity.DomainEvents);
entity.ClearDomainEvents();
Assert.AreEqual(0, entity.DomainEvents.Count);
Assert.IsEmpty(entity.DomainEvents);

// Act & Assert - Second round
entity.AddDomainEvent(evt2);
Assert.AreEqual(1, entity.DomainEvents.Count);
Assert.HasCount(1, entity.DomainEvents);
entity.ClearDomainEvents();
Assert.AreEqual(0, entity.DomainEvents.Count);
Assert.IsEmpty(entity.DomainEvents);
}

[TestMethod]
Expand All @@ -255,7 +250,7 @@ public void DomainEventsMaintainOrderOfAddition()
entity.AddDomainEvent(evt3);

// Assert
Assert.AreEqual(3, entity.DomainEvents.Count);
Assert.HasCount(3, entity.DomainEvents);
Assert.AreSame(evt1, entity.DomainEvents[0]);
Assert.AreSame(evt2, entity.DomainEvents[1]);
Assert.AreSame(evt3, entity.DomainEvents[2]);
Expand All @@ -274,7 +269,7 @@ public void DomainEventsNotAffectedByAuditFieldUpdates()
entity.MarkDeleted();

// Assert
Assert.AreEqual(1, entity.DomainEvents.Count, "Domain events should not be affected by audit field updates");
Assert.HasCount(1, entity.DomainEvents, "Domain events should not be affected by audit field updates");
Assert.AreSame(evt, entity.DomainEvents[0]);
}

Expand Down Expand Up @@ -400,7 +395,7 @@ public void DomainEventPropertiesAreCorrectlySet()
entity.AddDomainEvent(domainEvent);

// Assert
Assert.AreEqual(1, entity.DomainEvents.Count);
Assert.HasCount(1, entity.DomainEvents);
var eventFromEntity = entity.DomainEvents[0];
Assert.AreEqual(entity, eventFromEntity.Item);
Assert.AreEqual(occurredOn, eventFromEntity.OccurredOn, "Domain event OccurredOn should match the set value");
Expand Down Expand Up @@ -452,7 +447,9 @@ public void MarkModifiedUpdatesModifiedOnToMoreRecentValue()

// Assert
Assert.IsNotNull(secondModified);
#pragma warning disable MSTEST0037 // Use proper 'Assert' methods
Assert.IsTrue(secondModified > firstModified, "ModifiedOn should be updated to a more recent value");
#pragma warning restore MSTEST0037 // Use proper 'Assert' methods
}

[TestMethod]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ namespace Goodtocode.Domain.Tests.Entities;
[TestClass]
public sealed class SecuredEntityExtensionsTests
{
#pragma warning disable CA1822
private sealed class TestSecuredEntity : ISecurable
{
public Guid OwnerId { get; set; }
Expand All @@ -22,7 +23,10 @@ private sealed class TestSecuredEntity : ISecurable
public Guid? DeletedBy { get; set; }
public DateTimeOffset Timestamp { get; set; } = DateTimeOffset.UtcNow;


#pragma warning disable IDE0060 // Remove unused parameter
public void AddDomainEvent(IDomainEvent<TestSecuredEntity> domainEvent)
#pragma warning restore IDE0060 // Remove unused parameter
{
// No-op for test stub
}
Expand Down Expand Up @@ -92,7 +96,7 @@ public void IsOwnerFiltersEntitiesByOwnerId()
var result = entities.WhereOwner(ownerId).ToList();

// Assert
Assert.AreEqual(1, result.Count);
Assert.HasCount(1, result);
Assert.AreEqual(ownerId, result[0].OwnerId);
}

Expand All @@ -111,7 +115,7 @@ public void WhereOwnerFiltersEntitiesByOwnerId()
var result = entities.WhereOwner(ownerId).ToList();

// Assert
Assert.AreEqual(1, result.Count);
Assert.HasCount(1, result);
Assert.AreEqual(ownerId, result[0].OwnerId);
}

Expand Down Expand Up @@ -156,8 +160,9 @@ public void WhereAuthorizedFiltersEntitiesByTenantOrOwner()
var result = entities.WhereAuthorized(tenantId, ownerId).ToList();

// Assert
Assert.AreEqual(2, result.Count);
Assert.IsTrue(result.Any(x => x.OwnerId == ownerId));
Assert.IsTrue(result.Any(x => x.TenantId == tenantId));
Assert.HasCount(2, result);
Assert.Contains(x => x.OwnerId == ownerId, result);
Assert.Contains(x => x.TenantId == tenantId, result);
}
#pragma warning restore CA1822
}
17 changes: 6 additions & 11 deletions src/Goodtocode.Domain.Tests/Entities/SecuredEntityTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,10 @@ public TestSecuredEntity(Guid id) : base(id) { }
public TestSecuredEntity(Guid id, Guid ownerId, Guid tenantId) : base(id, ownerId, tenantId) { }
}

private sealed class TestSecuredEvent : IDomainEvent<TestSecuredEntity>
private sealed class TestSecuredEvent(SecuredEntityTests.TestSecuredEntity item) : IDomainEvent<TestSecuredEntity>
{
public TestSecuredEntity Item { get; set; }
public TestSecuredEntity Item { get; set; } = item;
public DateTime OccurredOn { get; set; } = DateTime.UtcNow;

public TestSecuredEvent(TestSecuredEntity item)
{
Item = item;
}
}

[TestMethod]
Expand Down Expand Up @@ -300,14 +295,14 @@ public void DomainEventsAddAndClearWorks()
entity.AddDomainEvent(evt);

// Assert
Assert.AreEqual(1, entity.DomainEvents.Count);
Assert.HasCount(1, entity.DomainEvents);
Assert.AreSame(evt, entity.DomainEvents[0]);

// Act
entity.ClearDomainEvents();

// Assert
Assert.AreEqual(0, entity.DomainEvents.Count);
Assert.IsEmpty(entity.DomainEvents);
}

[TestMethod]
Expand All @@ -323,7 +318,7 @@ public void DomainEventsCanAddMultipleEvents()
entity.AddDomainEvent(evt2);

// Assert
Assert.AreEqual(2, entity.DomainEvents.Count);
Assert.HasCount(2, entity.DomainEvents);
Assert.AreSame(evt1, entity.DomainEvents[0]);
Assert.AreSame(evt2, entity.DomainEvents[1]);
}
Expand All @@ -342,7 +337,7 @@ public void DomainEventsWorkWithSecurityContext()
entity.AddDomainEvent(evt);

// Assert
Assert.AreEqual(1, entity.DomainEvents.Count);
Assert.HasCount(1, entity.DomainEvents);
Assert.AreEqual(ownerId, entity.OwnerId);
Assert.AreEqual(tenantId, entity.TenantId);
Assert.AreSame(entity, entity.DomainEvents[0].Item);
Expand Down
4 changes: 2 additions & 2 deletions src/Goodtocode.Domain.Tests/Events/DomainEventTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public void DomainEventCanBeAddedToEntity()
entity.AddDomainEvent(evt);

// Assert
Assert.AreEqual(1, entity.DomainEvents.Count);
Assert.HasCount(1, entity.DomainEvents);
Assert.AreSame(evt, entity.DomainEvents[0]);
}

Expand All @@ -60,6 +60,6 @@ public void DomainEventCanBeClearedFromEntity()
entity.ClearDomainEvents();

// Assert
Assert.AreEqual(0, entity.DomainEvents.Count);
Assert.IsEmpty(entity.DomainEvents);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,50 +75,30 @@ public void UpdateEmail(string newEmail)
/// <summary>
/// Event raised when a person is created
/// </summary>
private sealed class PersonCreatedEvent : IDomainEvent<Person>
private sealed class PersonCreatedEvent(CommandHandlerWithEventsExample.Person person) : IDomainEvent<Person>
{
public Person Item { get; }
public DateTime OccurredOn { get; }

public PersonCreatedEvent(Person person)
{
Item = person;
OccurredOn = DateTime.UtcNow;
}
public Person Item { get; } = person;
public DateTime OccurredOn { get; } = DateTime.UtcNow;
}

/// <summary>
/// Event raised when a person is verified
/// </summary>
private sealed class PersonVerifiedEvent : IDomainEvent<Person>
private sealed class PersonVerifiedEvent(CommandHandlerWithEventsExample.Person person) : IDomainEvent<Person>
{
public Person Item { get; }
public DateTime OccurredOn { get; }

public PersonVerifiedEvent(Person person)
{
Item = person;
OccurredOn = DateTime.UtcNow;
}
public Person Item { get; } = person;
public DateTime OccurredOn { get; } = DateTime.UtcNow;
}

/// <summary>
/// Event raised when a person's email changes
/// </summary>
private sealed class PersonEmailChangedEvent : IDomainEvent<Person>
private sealed class PersonEmailChangedEvent(CommandHandlerWithEventsExample.Person person, string oldEmail, string newEmail) : IDomainEvent<Person>
{
public Person Item { get; }
public DateTime OccurredOn { get; }
public string OldEmail { get; }
public string NewEmail { get; }

public PersonEmailChangedEvent(Person person, string oldEmail, string newEmail)
{
Item = person;
OccurredOn = DateTime.UtcNow;
OldEmail = oldEmail;
NewEmail = newEmail;
}
public Person Item { get; } = person;
public DateTime OccurredOn { get; } = DateTime.UtcNow;
public string OldEmail { get; } = oldEmail;
public string NewEmail { get; } = newEmail;
}

#endregion
Expand Down Expand Up @@ -158,7 +138,7 @@ private interface IAppDbContext
/// </summary>
private sealed class InMemoryDbContext : IAppDbContext
{
public ICollection<Person> Persons { get; } = new List<Person>();
public ICollection<Person> Persons { get; } = [];

public Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
{
Expand Down Expand Up @@ -317,9 +297,7 @@ private sealed class VerifyPersonCommandHandler(
public async Task Handle(VerifyPersonCommand command)
{
// 1. Load entity from repository
var person = _dbContext.Persons.FirstOrDefault(p => p.Id == command.PersonId);
if (person == null)
throw new InvalidOperationException($"Person {command.PersonId} not found");
var person = _dbContext.Persons.FirstOrDefault(p => p.Id == command.PersonId) ?? throw new InvalidOperationException($"Person {command.PersonId} not found");

// 2. Execute domain logic (this adds PersonVerifiedEvent)
person.Verify();
Expand Down Expand Up @@ -376,7 +354,7 @@ public async Task CreatePersonAddsEntityToDbContextAndDispatchesEventToServiceBu
var personId = await handler.Handle(command);

// Assert - Verify entity was created
Assert.AreEqual(1, dbContext.Persons.Count);
Assert.HasCount(1, dbContext.Persons);
var person = dbContext.Persons.First();
Assert.AreEqual("John", person.FirstName);
Assert.AreEqual("Doe", person.LastName);
Expand All @@ -386,8 +364,8 @@ public async Task CreatePersonAddsEntityToDbContextAndDispatchesEventToServiceBu
Assert.AreNotEqual(Guid.Empty, person.TenantId);

// Assert - Verify event was published to service bus
Assert.AreEqual(1, serviceBus.PublishedMessages.Count);
Assert.IsTrue(serviceBus.PublishedMessages[0].Contains("PersonCreatedEvent"));
Assert.HasCount(1, serviceBus.PublishedMessages);
Assert.Contains("PersonCreatedEvent", serviceBus.PublishedMessages[0]);
}

[TestMethod]
Expand Down Expand Up @@ -436,9 +414,9 @@ public async Task VerifyPersonDispatchesMultipleEventsToServiceBus()
Assert.AreEqual("jane.smith@example.com", person.Email); // Email normalized

// Assert - Verify BOTH events were published to service bus
Assert.AreEqual(2, serviceBus.PublishedMessages.Count);
Assert.IsTrue(serviceBus.PublishedMessages.Any(m => m.Contains("PersonVerifiedEvent")));
Assert.IsTrue(serviceBus.PublishedMessages.Any(m => m.Contains("PersonEmailChangedEvent")));
Assert.HasCount(2, serviceBus.PublishedMessages);
Assert.Contains(m => m.Contains("PersonVerifiedEvent"), serviceBus.PublishedMessages);
Assert.Contains(m => m.Contains("PersonEmailChangedEvent"), serviceBus.PublishedMessages);
}

[TestMethod]
Expand Down
Loading
Loading