| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869 |
- using Application.Helpers;
- using Domain.Entities.Stocks;
- using Domain.Entities.Stocks.ValueObject;
- using Microsoft.EntityFrameworkCore;
- namespace Application.Tests;
- /// <summary>
- /// 백필 적재여부 판정의 유형별 완전성 검증 — 기존의 날짜 단위 AnyAsync 판정은 3개 엔드포인트 중 일부만 성공해도
- /// 그 날짜를 영구 skip(영구 결손)했다. BackfillCompleteness 는 기대 유형이 전부 있어야 skip 하고,
- /// 유형별 데이터 시작일 이전 날짜는 기대에서 제외한다 (과잉 재fetch 방지).
- /// </summary>
- [TestClass]
- public sealed class BackfillCompletenessTests
- {
- // KrxEtpSyncService 와 동일한 유형별 데이터 시작일 (docs/KRX/readme.md)
- private static readonly (EtpType Type, DateOnly DataStartDate)[] EtpStartDates =
- [
- (EtpType.ETF, new DateOnly(2010, 1, 4)),
- (EtpType.ETN, new DateOnly(2014, 11, 17)),
- (EtpType.ELW, new DateOnly(2010, 1, 4))
- ];
- [TestMethod]
- public void IsComplete_PartialKinds_ReturnsFalse()
- {
- // 기대 행위: 3개 유형 중 일부만 적재된 날짜는 불완전 → skip 하지 않고 재수집 대상.
- var day = new DateOnly(2026, 2, 10);
- Assert.IsFalse(BackfillCompleteness.IsComplete([EtpType.ETF], EtpStartDates, day), "ETF 만 있으면 불완전");
- Assert.IsFalse(BackfillCompleteness.IsComplete([EtpType.ETF, EtpType.ELW], EtpStartDates, day), "ETN 누락이면 불완전");
- Assert.IsFalse(BackfillCompleteness.IsComplete([], EtpStartDates, day), "아무것도 없으면 불완전");
- Assert.IsTrue(BackfillCompleteness.IsComplete([EtpType.ETF, EtpType.ETN, EtpType.ELW], EtpStartDates, day), "3개 유형 모두 있으면 완전 → skip");
- }
- [TestMethod]
- public void IsComplete_BeforeKindStartDate_KindNotRequired()
- {
- // 기대 행위: ETN 시작일(2014-11-17) 이전 날짜는 ETN 없이 ETF+ELW 만으로 완전 — 존재할 수 없는 유형을 영원히 재fetch 하지 않는다.
- var beforeEtn = new DateOnly(2014, 1, 15);
- Assert.IsTrue(BackfillCompleteness.IsComplete([EtpType.ETF, EtpType.ELW], EtpStartDates, beforeEtn), "ETN 시작일 이전엔 ETF+ELW 만으로 완전");
- Assert.IsFalse(BackfillCompleteness.IsComplete([EtpType.ETF], EtpStartDates, beforeEtn), "ELW 는 여전히 기대 대상");
- // 모든 유형의 시작일 이전 — 수집할 것이 없으므로 완전 취급
- Assert.IsTrue(BackfillCompleteness.IsComplete([], EtpStartDates, new DateOnly(2009, 1, 2)), "전 유형 시작일 이전은 skip");
- }
- [TestMethod]
- public async Task Etp_PartiallyLoadedDate_IsNotSkipped()
- {
- // 기대 행위: ETF 만 적재된 날짜는 skip 되지 않고(불완전), 3개 유형이 모두 적재되면 skip 된다 — KrxEtpSyncService existsForDate 와 동일 판정.
- using var db = TestDb.Create();
- var day = new DateOnly(2026, 2, 11);
- await db.EtpDailyTrade.AddAsync(EtpDailyTrade.Create(EtpType.ETF, "990901", "ZQE테스트ETF", day, 10000m, 9990m, 10010m, 9980m, 10m, 0.10m, 1000L, 10000000L));
- await db.SaveChangesAsync(default);
- var partial = await db.EtpDailyTrade.AsNoTracking().Where(c => c.TradeDate == day).Select(c => c.EtpType).Distinct().ToListAsync();
- Assert.IsFalse(BackfillCompleteness.IsComplete(partial, EtpStartDates, day), "ETF 만 적재된 부분 적재 날짜는 skip 되지 않는다");
- await db.EtpDailyTrade.AddAsync(EtpDailyTrade.Create(EtpType.ETN, "990902", "ZQE테스트ETN", day, 20000m, 19990m, 20010m, 19980m, 10m, 0.05m, 500L, 10000000L));
- await db.EtpDailyTrade.AddAsync(EtpDailyTrade.Create(EtpType.ELW, "99K901", "ZQE테스트ELW", day, 150m, 145m, 155m, 140m, 5m, 0m, 100L, 15000L));
- await db.SaveChangesAsync(default);
- var complete = await db.EtpDailyTrade.AsNoTracking().Where(c => c.TradeDate == day).Select(c => c.EtpType).Distinct().ToListAsync();
- Assert.IsTrue(BackfillCompleteness.IsComplete(complete, EtpStartDates, day), "3개 유형 모두 적재되면 skip 된다 (정상 skip 동작 유지)");
- }
- }
|