namespace MissionService.UnitTests.Domain; using FluentAssertions; using MissionService.Domain.AggregatesModel.CheckInAggregate; using Xunit; /// /// EN: Tests for UserCheckIn aggregate root invariants and behavior. /// VI: Kiểm thử các quy tắc bất biến và hành vi của UserCheckIn aggregate root. /// public class UserCheckInAggregateTests { private readonly StreakBonusConfig _defaultConfig = StreakBonusConfig.Default(); #region Constructor Tests [Fact] public void Constructor_ValidUserId_CreatesCheckInWithZeroStreak() { // Arrange var userId = Guid.NewGuid(); // Act var checkIn = new UserCheckIn(userId); // Assert checkIn.UserId.Should().Be(userId); checkIn.CurrentStreak.Should().Be(0); checkIn.LongestStreak.Should().Be(0); checkIn.TotalCheckIns.Should().Be(0); checkIn.LastCheckInDate.Should().BeNull(); checkIn.CheckInDays.Should().BeEmpty(); } [Fact] public void Constructor_EmptyUserId_ThrowsArgumentException() { // Arrange & Act var act = () => new UserCheckIn(Guid.Empty); // Assert act.Should().Throw() .WithMessage("*User ID is required*"); } #endregion #region CanCheckInToday Tests [Fact] public void CanCheckInToday_NewUser_ReturnsTrue() { // Arrange var checkIn = new UserCheckIn(Guid.NewGuid()); // Act var result = checkIn.CanCheckInToday(); // Assert result.Should().BeTrue(); } [Fact] public void CanCheckInToday_AfterCheckIn_ReturnsFalse() { // Arrange var checkIn = new UserCheckIn(Guid.NewGuid()); checkIn.CheckIn(_defaultConfig); // Act var result = checkIn.CanCheckInToday(); // Assert result.Should().BeFalse(); } #endregion #region CheckIn Tests [Fact] public void CheckIn_FirstTime_SetsStreakToOne() { // Arrange var checkIn = new UserCheckIn(Guid.NewGuid()); // Act var result = checkIn.CheckIn(_defaultConfig); // Assert checkIn.CurrentStreak.Should().Be(1); checkIn.LongestStreak.Should().Be(1); checkIn.TotalCheckIns.Should().Be(1); result.CurrentStreak.Should().Be(1); } [Fact] public void CheckIn_FirstTime_AwardsDailyPoints() { // Arrange var checkIn = new UserCheckIn(Guid.NewGuid()); // Act var result = checkIn.CheckIn(_defaultConfig); // Assert result.DailyPoints.Should().BeGreaterThan(0); result.TotalPoints.Should().BeGreaterThanOrEqualTo(result.DailyPoints); } [Fact] public void CheckIn_AlreadyCheckedInToday_ThrowsInvalidOperationException() { // Arrange var checkIn = new UserCheckIn(Guid.NewGuid()); checkIn.CheckIn(_defaultConfig); // Act var act = () => checkIn.CheckIn(_defaultConfig); // Assert act.Should().Throw() .WithMessage("*Already checked in today*"); } [Fact] public void CheckIn_AddsCheckInDayRecord() { // Arrange var checkIn = new UserCheckIn(Guid.NewGuid()); // Act var result = checkIn.CheckIn(_defaultConfig); // Assert checkIn.CheckInDays.Should().HaveCount(1); var dayRecord = checkIn.CheckInDays.First(); dayRecord.PointsEarned.Should().Be(result.TotalPoints); dayRecord.StreakOnDay.Should().Be(1); } #endregion #region ResetStreak Tests [Fact] public void ResetStreak_WithActiveStreak_ResetsCurrentStreakToZero() { // Arrange var checkIn = new UserCheckIn(Guid.NewGuid()); checkIn.CheckIn(_defaultConfig); checkIn.CurrentStreak.Should().Be(1); // Act checkIn.ResetStreak(); // Assert checkIn.CurrentStreak.Should().Be(0); // EN: LongestStreak should remain unchanged // VI: LongestStreak nên giữ nguyên checkIn.LongestStreak.Should().Be(1); } #endregion #region GetMonthlyCheckIns Tests [Fact] public void GetMonthlyCheckIns_NoCheckIns_ReturnsEmptyList() { // Arrange var checkIn = new UserCheckIn(Guid.NewGuid()); var now = DateTime.UtcNow; // Act var result = checkIn.GetMonthlyCheckIns(now.Year, now.Month); // Assert result.Should().BeEmpty(); } [Fact] public void GetMonthlyCheckIns_WithCheckIns_ReturnsCurrentMonthCheckIns() { // Arrange var checkIn = new UserCheckIn(Guid.NewGuid()); checkIn.CheckIn(_defaultConfig); var now = DateTime.UtcNow; // Act var result = checkIn.GetMonthlyCheckIns(now.Year, now.Month); // Assert result.Should().HaveCount(1); } #endregion }