diff --git a/BusinessLayer.Tests/Services/ReviewAggregateResultService.cs b/BusinessLayer.Tests/Services/ReviewAggregateResultService.cs new file mode 100644 index 0000000000000000000000000000000000000000..aa6c0da73088d5e24fb499dcef755a529aa91c42 --- /dev/null +++ b/BusinessLayer.Tests/Services/ReviewAggregateResultService.cs @@ -0,0 +1,109 @@ +using BusinessLayer.Services.ReviewAggregateService; +using DAL.Data; +using Microsoft.EntityFrameworkCore; +using TestUtilities.Constants; +using TestUtilities.Mocks; + +namespace BusinessLayer.Tests.Services +{ + public class ReviewAggregateResultServiceTests + { + private readonly DbContextOptions<RestaurantDBContext> _dbContextOptions; + private readonly RestaurantDBContext _dbContext; + private readonly ReviewAggregateResultService _service; + + public ReviewAggregateResultServiceTests() + { + _dbContextOptions = MockedDbContext.CreateInMemoryContextOptions(); + _dbContext = MockedDbContext.CreateContext(_dbContextOptions); + _service = new ReviewAggregateResultService(_dbContext); + } + + [Fact] + public async Task DoesAggregateExist_ExistingId_ReturnsTrue() + { + // Arrange + var ids = new Guid[] { FakeSeedingValues.Restaurant1Id }; + + // Act + var result = await _service.DoesAggregateExistAsync(ids); + + // Assert + Assert.True(result); + } + + [Fact] + public async Task DoesAggregateExist_NonExistingId_ReturnsFalse() + { + // Arrange + var ids = new Guid[] { Guid.NewGuid() }; + + // Act + var result = await _service.DoesAggregateExistAsync(ids); + + // Assert + Assert.False(result); + } + + [Fact] + public async Task GetAggregateByIdAsync_ExistingAggregate_ReturnsAggregate() + { + // Arrange + var id = FakeSeedingValues.Restaurant1Id; + + // Act + var result = await _service.GetAggregateByIdAsync(id); + + // Assert + Assert.NotNull(result); + Assert.Equal(id, result.RestaurantId); + Assert.InRange(result.FoodRating, (uint)1, (uint)10); + Assert.InRange(result.ServiceRating, (uint)1, (uint)10); + Assert.InRange(result.EnvironmentRating, (uint)1, (uint)10); + } + + [Fact] + public async Task GetAggregateByIdAsync_NonExistingAggregate_ReturnsNull() + { + // Arrange + var id = Guid.NewGuid(); + + // Act + var result = await _service.GetAggregateByIdAsync(id); + + // Assert + Assert.Null(result); + } + + [Fact] + public async Task GetAggregatesAsync_Limit_AppliesCorrectly() + { + // Arrange + int limit = 1; + + // Act + var result = await _service.GetAggregatesAsync(limit: limit); + + // Assert + Assert.Equal(limit, result.Count); + Assert.NotEmpty(result); + } + + [Theory] + [InlineData("food", true)] + [InlineData("service_desc", true)] + [InlineData("environment", true)] + [InlineData("", true)] + public async Task GetAggregatesAsync_OrderByOption_ReturnsOrderedResults(string orderBy, bool includeRestaurant) + { + // Act + var result = await _service.GetAggregatesAsync(orderBy: orderBy, includeRestaurant: includeRestaurant); + + // Assert + Assert.NotEmpty(result); + Assert.All(result, r => Assert.InRange(r.FoodRating, (uint)1, (uint)10)); + Assert.All(result, r => Assert.InRange(r.ServiceRating, (uint)1, (uint)10)); + Assert.All(result, r => Assert.InRange(r.EnvironmentRating, (uint)1, (uint)10)); + } + } +} diff --git a/BusinessLayer.Tests/Services/ReviewCommentServiceTests.cs b/BusinessLayer.Tests/Services/ReviewCommentServiceTests.cs new file mode 100644 index 0000000000000000000000000000000000000000..54464af0585823d28003a5c2e64c90fbd0b82a3a --- /dev/null +++ b/BusinessLayer.Tests/Services/ReviewCommentServiceTests.cs @@ -0,0 +1,155 @@ +using BusinessLayer.DTOs.ReviewComment; +using BusinessLayer.Services.ReviewCommentService; +using BusinessLayer.Utils.Filters; +using DAL.Data; +using Microsoft.EntityFrameworkCore; +using TestUtilities.Constants; +using TestUtilities.Mocks; + +namespace BusinessLayer.Tests.Services +{ + public class ReviewCommentServiceTests + { + private readonly DbContextOptions<RestaurantDBContext> _dbContextOptions; + private readonly RestaurantDBContext _dbContext; + private readonly ReviewCommentService _service; + + public ReviewCommentServiceTests() + { + _dbContextOptions = MockedDbContext.CreateInMemoryContextOptions(); + _dbContext = MockedDbContext.CreateContext(_dbContextOptions); + _service = new ReviewCommentService(_dbContext); + } + + [Fact] + public async Task CreateCommentAsync_ValidData_Succeeds() + { + // Arrange + var data = new ReviewCommentCreateDTO + { + PosterId = FakeSeedingValues.User1Id, + ReviewId = FakeSeedingValues.Review1Id, + Content = "This is a test comment." + }; + + // Act + var result = await _service.CreateCommentAsync(data); + + // Assert + Assert.NotNull(result); + Assert.Equal(data.Content, result.Content); + Assert.Null(result.DeletedAt); + } + + [Fact] + public async Task CreateCommentAsync_NonExistentUserOrReview_ReturnsNull() + { + // Arrange + var data = new ReviewCommentCreateDTO + { + PosterId = Guid.NewGuid(), // Non-existent user + ReviewId = Guid.NewGuid(), // Non-existent review + Content = "Invalid comment due to missing user/review." + }; + + // Act + var result = await _service.CreateCommentAsync(data); + + // Assert + Assert.Null(result); + } + + [Fact] + public async Task DeleteCommentAsync_CommentExists_Succeeds() + { + // Arrange + var commentId = FakeSeedingValues.ReviewComment1Id; + + // Act + var result = await _service.DeleteCommentAsync(commentId); + + // Assert + Assert.True(result); + } + + [Fact] + public async Task DeleteCommentAsync_CommentNotExists_ReturnsFalse() + { + // Arrange + var commentId = Guid.NewGuid(); + + // Act + var result = await _service.DeleteCommentAsync(commentId); + + // Assert + Assert.False(result); + } + + [Fact] + public async Task GetByIdAsync_CommentExists_Succeeds() + { + // Arrange + var commentId = FakeSeedingValues.ReviewComment1Id; + + // Act + var result = await _service.GetByIdAsync(commentId); + + // Assert + Assert.NotNull(result); + Assert.Equal(commentId, result.Id); + Assert.Null(result.DeletedAt); + } + + [Fact] + public async Task GetCommentsAsync_Filtered_ReturnsExpectedResults() + { + // Arrange + var filter = new ReviewCommentFilter + { + ReviewId = FakeSeedingValues.Review1Id + }; + + // Act + var result = await _service.GetCommentsAsync(filter); + + // Assert + Assert.NotEmpty(result); + Assert.All(result, r => Assert.Equal(FakeSeedingValues.Review1Id, r.ReviewId)); + } + + [Fact] + public async Task UpdateCommentAsync_CommentExists_Succeeds() + { + // Arrange + var commentId = FakeSeedingValues.ReviewComment1Id; + var updateData = new ReviewCommentUpdateDTO + { + Content = "Updated comment content." + }; + + // Act + var result = await _service.UpdateCommentAsync(commentId, updateData); + + // Assert + Assert.NotNull(result); + Assert.Equal(updateData.Content, result.Content); + } + + [Fact] + public async Task UpdateCommentAsync_CommentNotExists_ReturnsNull() + { + // Arrange + var commentId = Guid.NewGuid(); + var updateData = new ReviewCommentUpdateDTO + { + Content = "Non-existent comment update." + }; + + // Act + var result = await _service.UpdateCommentAsync(commentId, updateData); + + // Assert + Assert.Null(result); + } + } +} diff --git a/BusinessLayer.Tests/Services/ReviewServiceTests.cs b/BusinessLayer.Tests/Services/ReviewServiceTests.cs new file mode 100644 index 0000000000000000000000000000000000000000..f71bb6a3cfafa0246337bf457e21b44a04c7873a --- /dev/null +++ b/BusinessLayer.Tests/Services/ReviewServiceTests.cs @@ -0,0 +1,213 @@ +using BusinessLayer.DTOs.Review; +using BusinessLayer.Services.ReviewService; +using BusinessLayer.Utils.Filters; +using BusinessLayer.Utils.Ordering; +using DAL.Data; +using Microsoft.EntityFrameworkCore; +using TestUtilities.Constants; +using TestUtilities.Mocks; + + +namespace BusinessLayer.Tests.Services +{ + public class ReviewServiceTests + { + private readonly DbContextOptions<RestaurantDBContext> _dbContextOptions; + private readonly RestaurantDBContext _dbContext; + private readonly ReviewService _service; + + public ReviewServiceTests() + { + _dbContextOptions = MockedDbContext.CreateInMemoryContextOptions(); + _dbContext = MockedDbContext.CreateContext(_dbContextOptions); + _service = new ReviewService(_dbContext); + } + + [Fact] + public async Task CreateReviewAsync_ValidData_Succeeds() + { + // Arrange + var data = new ReviewCreateDTO + { + PosterId = FakeSeedingValues.User1Id, + RestaurantId = FakeSeedingValues.Restaurant1Id, + Content = "Great restaurant!", + FoodRating = 9, + ServiceRating = 8, + EnvironmentRating = 7, + ServesFreeWater = true, + TimeSpent = 1.5f, + LeftTip = true + }; + + // Act + var result = await _service.CreateReviewAsync(data); + + // Assert + Assert.NotNull(result); + Assert.Equal(data.Content, result.Content); + Assert.Equal(data.FoodRating, result.FoodRating); + Assert.Null(result.DeletedAt); + } + + [Fact] + public async Task CreateReviewAsync_UserOrRestaurantNotFound_ReturnsNull() + { + // Arrange + var data = new ReviewCreateDTO + { + PosterId = Guid.NewGuid(), // Non-existent user + RestaurantId = Guid.NewGuid(), // Non-existent restaurant + Content = "Great restaurant!", + FoodRating = 9, + ServiceRating = 8, + EnvironmentRating = 7 + }; + + // Act + var result = await _service.CreateReviewAsync(data); + + // Assert + Assert.Null(result); + } + + [Fact] + public async Task DeleteReviewByIdAsync_ReviewExists_Succeeds() + { + // Arrange + var reviewId = FakeSeedingValues.Review1Id; + + // Act + var result = await _service.DeleteReviewByIdAsync(reviewId); + + // Assert + Assert.True(result); + } + + [Fact] + public async Task DeleteReviewByIdAsync_ReviewNotExists_ReturnsFalse() + { + // Arrange + var reviewId = Guid.NewGuid(); + + // Act + var result = await _service.DeleteReviewByIdAsync(reviewId); + + // Assert + Assert.False(result); + } + + [Fact] + public async Task DeleteReviewByIdAsync_DeletedReview_ReturnsFalse() + { + // Arrange + var reviewId = FakeSeedingValues.DeletedReviewId; + + // Act + var result = await _service.DeleteReviewByIdAsync(reviewId); + + // Assert + Assert.False(result); + } + + [Fact] + public async Task GetReviewAsync_ReviewExists_Succeeds() + { + // Arrange + var reviewId = FakeSeedingValues.Review1Id; + + // Act + var result = await _service.GetReviewAsync(reviewId); + + // Assert + Assert.NotNull(result); + Assert.Equal(reviewId, result.Id); + Assert.Null(result.DeletedAt); + } + + [Fact] + public async Task GetReviewsAsync_Filtered_ReturnsExpectedResults() + { + // Arrange + var filter = new ReviewFilter + { + RestaurantId = FakeSeedingValues.Restaurant1Id + }; + + // Act + var result = await _service.GetReviewsAsync(filter, ReviewOrdering.CreatedAtDesc); + + // Assert + Assert.NotEmpty(result); + Assert.All(result, r => Assert.Equal(FakeSeedingValues.Restaurant1Id, r.RestaurantId)); + } + + [Fact] + public async Task GetReviewAsync_DeletedReview_ReturnsNull() + { + // Arrange + var reviewId = FakeSeedingValues.DeletedReviewId; + + // Act + var result = await _service.GetReviewAsync(reviewId); + + // Assert + Assert.Null(result); + } + + [Fact] + public async Task UpdateReviewAsync_ReviewExists_Succeeds() + { + // Arrange + var reviewId = FakeSeedingValues.Review1Id; + var updateData = new ReviewUpdateDTO + { + Content = "Updated review content", + FoodRating = 10 + }; + + // Act + var result = await _service.UpdateReviewAsync(reviewId, updateData); + + // Assert + Assert.NotNull(result); + Assert.Equal(updateData.Content, result.Content); + Assert.Equal(updateData.FoodRating, result.FoodRating); + } + + [Fact] + public async Task UpdateReviewAsync_ReviewNotExists_ReturnsNull() + { + // Arrange + var reviewId = Guid.NewGuid(); + var updateData = new ReviewUpdateDTO + { + Content = "Non-existent review content" + }; + + // Act + var result = await _service.UpdateReviewAsync(reviewId, updateData); + + // Assert + Assert.Null(result); + } + + [Fact] + public async Task UpdateReviewAsync_DeletedReview_ReturnsNull() + { + // Arrange + var reviewId = FakeSeedingValues.DeletedReviewId; + var updateData = new ReviewUpdateDTO + { + Content = "Updated content for a deleted review" + }; + + // Act + var result = await _service.UpdateReviewAsync(reviewId, updateData); + + // Assert + Assert.Null(result); + } + + } +} diff --git a/BusinessLayer/DTOs/Review/ReviewDTO.cs b/BusinessLayer/DTOs/Review/ReviewDTO.cs index e3483cbb81fb372cf9f262b84df3edecd67e1a77..9385f536917048b7374de4d99ed3d0d1417dee4c 100644 --- a/BusinessLayer/DTOs/Review/ReviewDTO.cs +++ b/BusinessLayer/DTOs/Review/ReviewDTO.cs @@ -10,6 +10,7 @@ namespace BusinessLayer.DTOs.Review public uint FoodRating { get; set; } public uint ServiceRating { get; set; } public uint EnvironmentRating { get; set; } + public Guid RestaurantId { get; set; } public DateTime CreatedAt { get; set; } public DateTime UpdatedAt { get; set; } public DateTime? DeletedAt { get; set; } diff --git a/BusinessLayer/Services/ReviewAggregateResultService/IReviewAggregateResultService.cs b/BusinessLayer/Services/ReviewAggregateResultService/IReviewAggregateResultService.cs index 892d4d9781c63b24f7efb04a38120bbb1d040341..564803c8cc4dbfd3534c447c5897f2cf14e31f44 100644 --- a/BusinessLayer/Services/ReviewAggregateResultService/IReviewAggregateResultService.cs +++ b/BusinessLayer/Services/ReviewAggregateResultService/IReviewAggregateResultService.cs @@ -4,7 +4,7 @@ namespace BusinessLayer.Services.ReviewAggregateService { public interface IReviewAggregateResultService : IBaseService { - public Task<bool> DoesAggregateExist(params Guid[] ids); + public Task<bool> DoesAggregateExistAsync(params Guid[] ids); public Task<List<ReviewAggregateDTO>> GetAggregatesAsync( string orderBy = "", diff --git a/BusinessLayer/Services/ReviewAggregateResultService/ReviewAggregateResultService.cs b/BusinessLayer/Services/ReviewAggregateResultService/ReviewAggregateResultService.cs index 134c17fb4c625f2f5a4bce1e843c1a0d92a25a86..28cb3c82450b5c31727ea6a136b6f63292207b32 100644 --- a/BusinessLayer/Services/ReviewAggregateResultService/ReviewAggregateResultService.cs +++ b/BusinessLayer/Services/ReviewAggregateResultService/ReviewAggregateResultService.cs @@ -14,7 +14,7 @@ namespace BusinessLayer.Services.ReviewAggregateService _dbContext = dbContext; } - public async Task<bool> DoesAggregateExist(params Guid[] ids) + public async Task<bool> DoesAggregateExistAsync(params Guid[] ids) { return await _dbContext.ReviewAggregateResults.AnyAsync(r => ids.Contains(r.RestaurantId)); } diff --git a/BusinessLayer/Services/ReviewService/ReviewService.cs b/BusinessLayer/Services/ReviewService/ReviewService.cs index 1334ed57a22c8b5c4c0e3d0ced4f2fc6ca558ec4..ff27b8590d9a4b1e7408262c658f1dec4b754ef2 100644 --- a/BusinessLayer/Services/ReviewService/ReviewService.cs +++ b/BusinessLayer/Services/ReviewService/ReviewService.cs @@ -50,7 +50,7 @@ namespace BusinessLayer.Services.ReviewService { var review = await _dbContext.Reviews.FindAsync(id); - if (review is null) + if (review is null || review.DeletedAt is not null) { return false; } diff --git a/TestUtilities/Constants/FakeSeedingValues.cs b/TestUtilities/Constants/FakeSeedingValues.cs index c01fbb757ec4ab3eb9b5d23957b665327f68b68a..48d7183322b3dd2d62338d0b7dd39b06ad100b2c 100644 --- a/TestUtilities/Constants/FakeSeedingValues.cs +++ b/TestUtilities/Constants/FakeSeedingValues.cs @@ -12,6 +12,7 @@ public static readonly Guid Review1Id = new("32669bac-a1bd-4e9d-9580-ee4a7764cda3"); public static readonly Guid Review2Id = new("e8f210bd-5666-40c7-bc61-4d8ef17c5191"); public static readonly Guid Review3Id = new("d748b6c9-8d9b-4809-b425-766cfc705b01"); + public static readonly Guid DeletedReviewId = new("bd2d0401-0000-4988-b683-7927c1a5c66d"); // Review Comment IDs public static readonly Guid ReviewComment1Id = new("af16972e-d611-4c1c-8ce3-a93d667c1b9a"); diff --git a/TestUtilities/FakeData/FakeDataInitializer.cs b/TestUtilities/FakeData/FakeDataInitializer.cs index 2089868dcdf3c483027d10de4a75fd3f166b62c6..56c4bc2a565bf4bb4aa3093e6734095c7568b133 100644 --- a/TestUtilities/FakeData/FakeDataInitializer.cs +++ b/TestUtilities/FakeData/FakeDataInitializer.cs @@ -118,6 +118,19 @@ namespace TestUtilities.FakeData LeftTip = true, CreatedAt = new DateTime(2024, 10, 10, 16, 41, 2), UpdatedAt = new DateTime(2024, 10, 10, 16, 41, 2) + }, + new Review + { + Id = FakeSeedingValues.DeletedReviewId, + PosterId = FakeSeedingValues.User1Id, + RestaurantId = FakeSeedingValues.Restaurant1Id, + Content = "This is a deleted review.", + FoodRating = 7, + ServiceRating = 7, + EnvironmentRating = 7, + CreatedAt = DateTime.UtcNow, + UpdatedAt = DateTime.UtcNow, + DeletedAt = DateTime.UtcNow } ]; }