BackfillCompletenessTests.cs 4.1 KB

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