25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

174 lines
6.6KB

  1. using CartWise.Application.Services;
  2. using CartWise.Domain.Entities;
  3. using CartWise.Domain.Enums;
  4. using CartWise.Infrastructure.Data;
  5. using Microsoft.EntityFrameworkCore;
  6. namespace CartWise.Application.Tests;
  7. public class PurchaseServiceTests : IDisposable
  8. {
  9. private static readonly DateTime Now = new(2026, 8, 10, 12, 0, 0, DateTimeKind.Utc);
  10. private readonly CartWiseDbContext _db;
  11. private readonly PurchaseService _sut;
  12. public PurchaseServiceTests()
  13. {
  14. var options = new DbContextOptionsBuilder<CartWiseDbContext>()
  15. .UseInMemoryDatabase(Guid.NewGuid().ToString())
  16. .Options;
  17. _db = new CartWiseDbContext(options);
  18. _sut = new PurchaseService(_db, new PriceService(_db, TimeProvider.System), TimeProvider.System);
  19. }
  20. public void Dispose()
  21. {
  22. _db.Dispose();
  23. GC.SuppressFinalize(this);
  24. }
  25. [Fact]
  26. public async Task StartPurchaseAsync_CreatesPurchase()
  27. {
  28. var householdId = Guid.NewGuid();
  29. var result = await _sut.StartPurchaseAsync(householdId, "user-1", Now);
  30. Assert.True(result.IsSuccess);
  31. Assert.Equal(householdId, result.Value!.HouseholdId);
  32. Assert.False(result.Value.IsCompleted);
  33. }
  34. [Fact]
  35. public async Task AddPurchaseItemAsync_AddsItemAndPersistsIt()
  36. {
  37. var started = await _sut.StartPurchaseAsync(Guid.NewGuid(), "user-1", Now);
  38. var result = await _sut.AddPurchaseItemAsync(started.Value!.HouseholdId, started.Value.PurchaseId, "Milk", 3.99m);
  39. Assert.True(result.IsSuccess);
  40. Assert.Equal("Milk", result.Value!.Description);
  41. var reloaded = await _sut.GetPurchaseAsync(started.Value.HouseholdId, started.Value.PurchaseId);
  42. Assert.Single(reloaded!.Items);
  43. }
  44. [Fact]
  45. public async Task AddPurchaseItemAsync_FailsForPurchaseInAnotherHousehold()
  46. {
  47. var started = await _sut.StartPurchaseAsync(Guid.NewGuid(), "user-1", Now);
  48. var result = await _sut.AddPurchaseItemAsync(Guid.NewGuid(), started.Value!.PurchaseId, "Milk", 3.99m);
  49. Assert.False(result.IsSuccess);
  50. }
  51. [Fact]
  52. public async Task AddPurchaseItemAsync_FailsWhenPurchaseAlreadyCompleted()
  53. {
  54. var started = await _sut.StartPurchaseAsync(Guid.NewGuid(), "user-1", Now);
  55. await _sut.AddPurchaseItemAsync(started.Value!.HouseholdId, started.Value.PurchaseId, "Milk", 3.99m);
  56. await _sut.CompletePurchaseAsync(started.Value.HouseholdId, started.Value.PurchaseId);
  57. var result = await _sut.AddPurchaseItemAsync(started.Value.HouseholdId, started.Value.PurchaseId, "Bread", 2.5m);
  58. Assert.False(result.IsSuccess);
  59. }
  60. [Fact]
  61. public async Task CompletePurchaseAsync_ComputesSubtotalAndTotal()
  62. {
  63. var started = await _sut.StartPurchaseAsync(Guid.NewGuid(), "user-1", Now);
  64. await _sut.AddPurchaseItemAsync(started.Value!.HouseholdId, started.Value.PurchaseId, "Milk", 3.99m);
  65. await _sut.AddPurchaseItemAsync(started.Value.HouseholdId, started.Value.PurchaseId, "Bread", 2.50m);
  66. var result = await _sut.CompletePurchaseAsync(started.Value.HouseholdId, started.Value.PurchaseId, tax: 0.50m);
  67. Assert.True(result.IsSuccess);
  68. Assert.Equal(6.49m, result.Value!.Subtotal);
  69. Assert.Equal(0.50m, result.Value.Tax);
  70. Assert.Equal(6.99m, result.Value.Total);
  71. }
  72. [Fact]
  73. public async Task CompletePurchaseAsync_FailsForPurchaseInAnotherHousehold()
  74. {
  75. var started = await _sut.StartPurchaseAsync(Guid.NewGuid(), "user-1", Now);
  76. var result = await _sut.CompletePurchaseAsync(Guid.NewGuid(), started.Value!.PurchaseId);
  77. Assert.False(result.IsSuccess);
  78. }
  79. [Fact]
  80. public async Task CompletePurchaseAsync_DoesNotCreateObservationForItemWithoutProduct()
  81. {
  82. var started = await _sut.StartPurchaseAsync(Guid.NewGuid(), "user-1", Now);
  83. await _sut.AddPurchaseItemAsync(started.Value!.HouseholdId, started.Value.PurchaseId, "Unresolved item", 3.99m);
  84. await _sut.CompletePurchaseAsync(started.Value.HouseholdId, started.Value.PurchaseId);
  85. Assert.Empty(await _db.PriceObservations.ToListAsync());
  86. }
  87. [Fact]
  88. public async Task CompletePurchaseAsync_CreatesObservationWithUnitPriceForResolvedProduct()
  89. {
  90. var product = new Product("Peanut Butter 16 oz", ProductSourceType.Manual, Now, sizeValue: 16m, sizeUnit: "oz");
  91. _db.Products.Add(product);
  92. await _db.SaveChangesAsync();
  93. var started = await _sut.StartPurchaseAsync(Guid.NewGuid(), "user-1", Now);
  94. await _sut.AddPurchaseItemAsync(
  95. started.Value!.HouseholdId,
  96. started.Value.PurchaseId,
  97. "Peanut Butter",
  98. 4.00m,
  99. quantity: 1m,
  100. productId: product.ProductId);
  101. await _sut.CompletePurchaseAsync(started.Value.HouseholdId, started.Value.PurchaseId);
  102. var observation = Assert.Single(await _db.PriceObservations.ToListAsync());
  103. Assert.Equal(product.ProductId, observation.ProductId);
  104. Assert.Equal(started.Value.HouseholdId, observation.HouseholdId);
  105. Assert.Equal(4.00m, observation.Price);
  106. Assert.Equal(0.25m, observation.UnitPrice);
  107. Assert.Equal(PriceObservationSourceType.UserEntered, observation.SourceType);
  108. Assert.Equal(1m, observation.ConfidenceScore);
  109. }
  110. [Fact]
  111. public async Task CompletePurchaseAsync_CreatesObservationWithoutUnitPriceWhenProductHasNoSize()
  112. {
  113. var product = new Product("Mystery Item", ProductSourceType.Manual, Now);
  114. _db.Products.Add(product);
  115. await _db.SaveChangesAsync();
  116. var started = await _sut.StartPurchaseAsync(Guid.NewGuid(), "user-1", Now);
  117. await _sut.AddPurchaseItemAsync(started.Value!.HouseholdId, started.Value.PurchaseId, "Mystery Item", 2.00m, productId: product.ProductId);
  118. await _sut.CompletePurchaseAsync(started.Value.HouseholdId, started.Value.PurchaseId);
  119. var observation = Assert.Single(await _db.PriceObservations.ToListAsync());
  120. Assert.Null(observation.UnitPrice);
  121. }
  122. [Fact]
  123. public async Task GetHouseholdPurchasesAsync_ReturnsOnlyThatHouseholdsPurchasesNewestFirst()
  124. {
  125. var householdA = Guid.NewGuid();
  126. var householdB = Guid.NewGuid();
  127. await _sut.StartPurchaseAsync(householdA, "user-1", Now.AddDays(-1));
  128. await _sut.StartPurchaseAsync(householdA, "user-1", Now);
  129. await _sut.StartPurchaseAsync(householdB, "user-2", Now);
  130. var results = await _sut.GetHouseholdPurchasesAsync(householdA);
  131. Assert.Equal(2, results.Count);
  132. Assert.True(results[0].PurchasedUtc >= results[1].PurchasedUtc);
  133. }
  134. }

Powered by TurnKey Linux.