| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111 |
- using Domain.Entities.Stocks;
- using Domain.Entities.Stocks.ValueObject;
- using LendingHandler = Application.Features.Api.Stocks.GetLendingHistory.Handler;
- using LendingQuery = Application.Features.Api.Stocks.GetLendingHistory.Query;
- using LockupHandler = Application.Features.Api.Stocks.GetLockupCalendar.Handler;
- using LockupQuery = Application.Features.Api.Stocks.GetLockupCalendar.Query;
- namespace Application.Tests;
- [TestClass]
- public sealed class SeibroSupplyQueryHandlerTests
- {
- [TestMethod]
- public async Task GetLendingHistory_ByShortCode_FiltersPeriod_OrdersDesc()
- {
- // 기대 행위: 단축코드 → Stock.ISIN 로 SecuritiesLending 조회 + from~to 필터 + 최신순
- var code = $"72{Random.Shared.Next(1000, 9999)}";
- var isin = $"KR7{code}003";
- using var db = TestDb.Create();
- var stock = Stock.Create(code, "대차종목", StockMarket.KOSPI, new DateOnly(2020, 1, 2), isin: isin);
- await db.Stock.AddAsync(stock);
- await db.SecuritiesLending.AddAsync(SecuritiesLending.Create(isin, new DateOnly(2025, 3, 3), 1000L, 100L, 50L, 10L, 500L, 300L, 1.5m, 100L, 0.3m, 400L, 1.2m, 90L, 0.2m));
- await db.SecuritiesLending.AddAsync(SecuritiesLending.Create(isin, new DateOnly(2025, 3, 4), 1000L, 120L, 60L, 20L, 540L, 320L, 1.6m, 110L, 0.35m, 410L, 1.25m, 95L, 0.22m));
- await db.SecuritiesLending.AddAsync(SecuritiesLending.Create(isin, new DateOnly(2025, 1, 2), 1000L, 80L, 40L, 5L, 480L, 280L, 1.4m, 90L, 0.28m, 380L, 1.1m, 85L, 0.18m));
- await db.SaveChangesAsync(default);
- var handler = new LendingHandler(db);
- var result = await handler.Handle(new LendingQuery(code, new DateOnly(2025, 3, 1), new DateOnly(2025, 3, 31), 1, 100), default);
- Assert.IsTrue(result.IsSuccess);
- Assert.AreEqual(isin, result.Value.Isin);
- Assert.AreEqual(2, result.Value.Total, "1월 행은 범위 밖 → 제외");
- Assert.AreEqual(new DateOnly(2025, 3, 4), result.Value.List[0].StdDt, "최신순");
- Assert.AreEqual(1.6m, result.Value.List[0].ForeignLendRatio);
- Assert.AreEqual(540L, result.Value.List[0].LendingBalanceQty);
- }
- [TestMethod]
- public async Task GetLendingHistory_ByIsin_WhenNoStockRow()
- {
- // 기대 행위: 6자리 단축코드가 Stock 에 없으면 입력을 ISIN 으로 간주
- var isin = $"KR7{Guid.NewGuid().ToString("N")[..9]}";
- using var db = TestDb.Create();
- await db.SecuritiesLending.AddAsync(SecuritiesLending.Create(isin, new DateOnly(2024, 6, 6), 2000L, 200L, 100L, 30L, 900L, 600L, 2.1m, 200L, 0.5m, 700L, 1.8m, 180L, 0.4m));
- await db.SaveChangesAsync(default);
- var handler = new LendingHandler(db);
- var result = await handler.Handle(new LendingQuery(isin, null, null, 1, 100), default);
- Assert.IsTrue(result.IsSuccess);
- Assert.AreEqual(isin, result.Value.Isin);
- Assert.AreEqual(1, result.Value.Total);
- Assert.AreEqual(2.1m, result.Value.List[0].ForeignLendRatio);
- }
- [TestMethod]
- public async Task GetLockupCalendar_FiltersByReturnOrSafedpDate()
- {
- // 기대 행위: ReturnDt 또는 SafedpDt 가 기간 안이면 포함. 범위 밖 예수 이벤트는 제외.
- var custno = 800100 + Random.Shared.Next(1, 800);
- var code = $"73{Random.Shared.Next(1000, 9999)}";
- using var db = TestDb.Create();
- // 반환일이 기간 안 (락업 해제)
- await db.LockupEvent.AddAsync(LockupEvent.Create(custno, "락업종목", "보통주", code, "11", "2", new DateOnly(2024, 3, 1), 100000L, "03", new DateOnly(2025, 3, 1), 100000L, 5000000L));
- // 예수일이 기간 안
- await db.LockupEvent.AddAsync(LockupEvent.Create(custno, "락업종목", "보통주", code, "11", "1", new DateOnly(2025, 5, 15), 50000L, "01", null, null, 5000000L));
- // 둘 다 기간 밖
- await db.LockupEvent.AddAsync(LockupEvent.Create(custno, "락업종목", "보통주", code, "11", "1", new DateOnly(2023, 1, 1), 30000L, "02", null, null, 5000000L));
- await db.SaveChangesAsync(default);
- var handler = new LockupHandler(db);
- var result = await handler.Handle(new LockupQuery(new DateOnly(2025, 1, 1), new DateOnly(2025, 12, 31), 1, 50), default);
- Assert.IsTrue(result.IsSuccess);
- var mine = result.Value.List.Where(c => c.IssucoCustno == custno).ToList();
- Assert.AreEqual(2, mine.Count, "반환일(2025-03) + 예수일(2025-05) 만. 2023 예수는 제외");
- Assert.IsTrue(mine.Any(c => c.ReturnDt == new DateOnly(2025, 3, 1) && c.OccrSeq == "2"));
- Assert.IsTrue(mine.Any(c => c.SafedpDt == new DateOnly(2025, 5, 15) && c.OccrSeq == "1"));
- }
- [TestMethod]
- public async Task GetLockupCalendar_SafedpBeforeFrom_ReturnAfterTo_IsExcluded()
- {
- // 회귀 (리뷰 결함 #2): SafedpDt<from & ReturnDt>to 인 행은 대표일(ReturnDt) 이 범위 밖이라 제외돼야 한다.
- // 기존 코드는 from/to 를 각각 (Safedp OR Return) 로 걸어 서로 다른 필드가 각 절을 따로 만족 → 범위 밖 이벤트 누출.
- var custno = 850100 + Random.Shared.Next(1, 800);
- var code = $"74{Random.Shared.Next(1000, 9999)}";
- using var db = TestDb.Create();
- // Safedp=2019(< from 2025) & Return=2030(> to 2025) — 대표일 2030 은 범위 밖 → 제외돼야 함
- await db.LockupEvent.AddAsync(LockupEvent.Create(custno, "누출종목", "보통주", code, "11", "1", new DateOnly(2019, 1, 1), 10000L, "01", new DateOnly(2030, 1, 1), 10000L, 5000000L));
- // 대조군: 대표일(Return=2025-06) 이 범위 안 → 포함
- await db.LockupEvent.AddAsync(LockupEvent.Create(custno, "정상종목", "보통주", code, "11", "2", new DateOnly(2024, 1, 1), 20000L, "03", new DateOnly(2025, 6, 1), 20000L, 5000000L));
- await db.SaveChangesAsync(default);
- var handler = new LockupHandler(db);
- var result = await handler.Handle(new LockupQuery(new DateOnly(2025, 1, 1), new DateOnly(2025, 12, 31), 1, 50), default);
- Assert.IsTrue(result.IsSuccess);
- var mine = result.Value.List.Where(c => c.IssucoCustno == custno).ToList();
- Assert.AreEqual(1, mine.Count, "대표일 범위 밖(2019~2030 걸침) 은 제외, 대표일 범위 안(2025-06) 만 포함");
- Assert.AreEqual(new DateOnly(2025, 6, 1), mine[0].ReturnDt);
- }
- }
|