| 12345678910111213141516171819202122232425262728293031323334353637 |
- using Domain.Entities.Common.ValueObject;
- using Domain.Entities.Wallets;
- using Microsoft.EntityFrameworkCore;
- namespace Application.Tests;
- [TestClass]
- public sealed class WalletBalanceConcurrencyTests
- {
- [TestMethod]
- public async Task ConcurrentUpdate_OnSameBalance_ThrowsConcurrencyException()
- {
- // P0 #8 재현: WalletBalance 에 동시성 토큰이 없어 동시 갱신이 last-write-wins 로
- // 조용히 덮어써졌다 (이중 지출 가능). 기대 행위: 두 번째 저장은 DbUpdateConcurrencyException.
- int memberID;
- using (var setup = TestDb.Create())
- {
- memberID = await TestDb.CreateMemberAsync(setup);
- var wallet = Wallet.Create(memberID);
- wallet.CreditPgCharge(Money.KRW(10000), "seed");
- await setup.Wallet.AddAsync(wallet);
- await setup.SaveChangesAsync(default);
- }
- using var db1 = TestDb.Create();
- using var db2 = TestDb.Create();
- var w1 = await db1.Wallet.Include(w => w.Balances).AsSplitQuery().SingleAsync(w => w.MemberID == memberID);
- var w2 = await db2.Wallet.Include(w => w.Balances).AsSplitQuery().SingleAsync(w => w.MemberID == memberID);
- w1.CreditPgCharge(Money.KRW(1000), "c1");
- w2.CreditPgCharge(Money.KRW(1000), "c2");
- await db1.SaveChangesAsync(default);
- await Assert.ThrowsExceptionAsync<DbUpdateConcurrencyException>(() => db2.SaveChangesAsync(default));
- }
- }
|