| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172 |
- using Domain.Entities.Common.ValueObject;
- using Domain.Entities.Payments.Danal;
- using Domain.Entities.Payments.ValueObject;
- using Domain.Entities.Wallets;
- using Domain.Entities.Wallets.ValueObject;
- using Microsoft.EntityFrameworkCore;
- using AdminCancelCommand = Application.Features.Admin.Payment.Danal.Confirm.Cancel.Command;
- using AdminCancelHandler = Application.Features.Admin.Payment.Danal.Confirm.Cancel.Handler;
- namespace Application.Tests;
- [TestClass]
- public sealed class AdminDanalCancelHandlerTests
- {
- private static async Task<DanalConfirm> SeedConfirmAsync(Infrastructure.Persistence.AppDbContext db, Domain.Entities.Payments.PaymentOrder order)
- {
- var confirm = DanalConfirm.CreateRequest(order.MemberID, order.ID, order.PaymentMethod, order.TransactionID ?? "TX-SEED", order.OrderID, order.Amount, order.MerchantID);
- confirm.SetResponse("0000", "OK");
- await db.DanalConfirm.AddAsync(confirm);
- await db.SaveChangesAsync(default);
- return confirm;
- }
- [TestMethod]
- public async Task AdminForceCancel_RecoversPgChargedPoints()
- {
- // 리뷰 #3 재현: Admin 강제취소가 Adjusted 버킷 차감(항상 0) + 예외 흡수로,
- // PG 현금은 환불됐는데 회원 포인트가 회수되지 않았다.
- // 기대 행위: PgCharged 에서 회수하고 ChargeCancelled 원장을 남긴다.
- using var db = TestDb.Create();
- var memberID = await TestDb.CreateMemberAsync(db);
- var wallet = Wallet.Create(memberID);
- wallet.CreditPgCharge(Money.KRW(10000), "충전", "SEED");
- await db.Wallet.AddAsync(wallet);
- var order = await TestDb.CreateOrderAsync(db, memberID, 10000, paid: true);
- var confirm = await SeedConfirmAsync(db, order);
- var fake = new FakeDanalPayService();
- var handler = new AdminCancelHandler(db, fake, FakeAppDbContextFactory.Instance);
- var result = await handler.Handle(new AdminCancelCommand(confirm.ID, "admin", "운영자 강제취소"), default);
- Assert.IsTrue(result.IsSuccess, "운영자 강제취소가 성공해야 한다");
- using var verify = TestDb.Create();
- var savedOrder = await verify.PaymentOrder.SingleAsync(o => o.OrderID == order.OrderID);
- Assert.AreEqual(PaymentStatus.Cancelled, savedOrder.Status);
- var savedWallet = await verify.Wallet.Include(w => w.Balances).Include(w => w.Transactions).AsSplitQuery().SingleAsync(w => w.MemberID == memberID);
- Assert.AreEqual(0m, savedWallet.Balances.Single(b => b.Type == WalletBalanceType.PgCharged).Amount.Value, "환불 시 PgCharged 포인트가 회수되어야 한다");
- Assert.IsTrue(savedWallet.Transactions.Any(t => t.TxType == WalletTransactionType.ChargeCancelled && t.Amount.Value == 10000m), "ChargeCancelled 원장이 있어야 한다");
- }
- [TestMethod]
- public async Task AdminForceCancel_PointsSpent_Fails_BeforePgCall()
- {
- using var db = TestDb.Create();
- var memberID = await TestDb.CreateMemberAsync(db);
- var wallet = Wallet.Create(memberID);
- wallet.CreditPgCharge(Money.KRW(10000), "충전", "SEED");
- wallet.Spend(Money.KRW(6000), "사용");
- await db.Wallet.AddAsync(wallet);
- var order = await TestDb.CreateOrderAsync(db, memberID, 10000, paid: true);
- var confirm = await SeedConfirmAsync(db, order);
- var fake = new FakeDanalPayService();
- var handler = new AdminCancelHandler(db, fake, FakeAppDbContextFactory.Instance);
- var result = await handler.Handle(new AdminCancelCommand(confirm.ID, "admin", "강제취소"), default);
- Assert.IsFalse(result.IsSuccess, "사용분이 있으면 자동 취소를 거절(수동 조정 유도)해야 한다");
- Assert.AreEqual(0, fake.CancelCallCount, "거절은 PG 호출 전에 이뤄져야 한다");
- }
- }
|