| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138 |
- using Domain.Entities.Stocks;
- using Infrastructure.StockData;
- using Microsoft.EntityFrameworkCore;
- using SharedKernel;
- namespace Application.Tests;
- /// <summary>
- /// KosisImport 의 DB 반영 로직(config ClassCode 필터 + Code+Period upsert) 통합 검증 — HTTP 는 파서 입력을 fixture/합성으로 대체
- /// (SeibroDividendSyncTests 방식 미러링). 실제 SQL Server(LocalDB)로 UQ upsert·idempotency 확인.
- /// </summary>
- [TestClass]
- public sealed class KosisSyncTests
- {
- private static string FixtureRoot => Path.Combine(AppContext.BaseDirectory, "Fixtures", "Kosis");
- private static AppSettings.KosisSection.KosisIndicator Cpi(string classCode = "T10") => new()
- {
- Code = $"CPI-{Guid.NewGuid():N}",
- Name = "소비자물가지수",
- OrgId = "101",
- TblId = "DT_1J22003",
- ItmId = "T",
- ObjL1 = "ALL",
- Cycle = "M",
- ClassCode = classCode
- };
- private static AppSettings.KosisSection.KosisIndicator Unemployment(string classCode = "00") => new()
- {
- // Code 컬럼 MaxLength(40) — 테스트 고유화 접미사(GUID 32자)를 붙여도 넘지 않도록 짧은 접두사 사용 (운영 Code "UNEMPLOYMENT_RATE"=17자는 여유).
- Code = $"UNEMP-{Guid.NewGuid():N}",
- Name = "실업률",
- OrgId = "101",
- TblId = "DT_1DA7004S",
- ItmId = "T80",
- ObjL1 = "00",
- Cycle = "M",
- ClassCode = classCode
- };
- [TestMethod]
- public async Task Upsert_CpiFixture_FiltersToNationalSeries_ThenIdempotent()
- {
- // 기대: CPI fixture(전국 T10 2행 + 서울 11 1행 + 빈DT 1행)에서 ClassCode=T10 필터로 전국 2행만 적재. 재실행은 무변경.
- var rows = KosisParser.Parse(File.ReadAllText(Path.Combine(FixtureRoot, "cpi.json")));
- var indicator = Cpi();
- using (var db = TestDb.Create())
- {
- var (inserted, updated) = await KosisImport.UpsertAsync(db, indicator, rows, CancellationToken.None);
- Assert.AreEqual(2, inserted, "ClassCode=T10(전국) 계열만, DT 빈값 제외 → 2행");
- Assert.AreEqual(0, updated);
- }
- using (var db = TestDb.Create())
- {
- var (inserted, updated) = await KosisImport.UpsertAsync(db, indicator, rows, CancellationToken.None);
- Assert.AreEqual(0, inserted, "동일 스냅샷 재실행은 insert 없음");
- Assert.AreEqual(0, updated, "무변경 행은 update 로 세지 않는다");
- var saved = await db.MacroIndicator.AsNoTracking().Where(c => c.Code == indicator.Code && c.Period == "202401").SingleAsync();
- Assert.AreEqual(113.17m, saved.Value);
- Assert.AreEqual("2020=100", saved.Unit);
- var count = await db.MacroIndicator.AsNoTracking().CountAsync(c => c.Code == indicator.Code);
- Assert.AreEqual(2, count, "서울(11) 계열은 적재되지 않음");
- }
- }
- [TestMethod]
- public async Task Upsert_EmploymentFixture_FiltersToNationalSeries_ByC1()
- {
- // 고용통계(DT_1DA7004S)는 고용률/실업률/취업자수가 한 표에 있고 itmId(T90/T80/T30)로 요청단에서 계열을 가른다.
- // 지역(C1)은 ClassCode 로 고른다. fixture = 실업률(T80) 전국(00) 2행 + 서울(11) 1행 → ClassCode=00 이면 전국 2행만 적재.
- var rows = KosisParser.Parse(File.ReadAllText(Path.Combine(FixtureRoot, "employment.json")));
- var indicator = Unemployment();
- using var db = TestDb.Create();
- var (inserted, updated) = await KosisImport.UpsertAsync(db, indicator, rows, CancellationToken.None);
- Assert.AreEqual(2, inserted, "ClassCode=00(전국) 계열만 → 2행 (서울 11 제외)");
- Assert.AreEqual(0, updated);
- var saved = await db.MacroIndicator.AsNoTracking().Where(c => c.Code == indicator.Code && c.Period == "202505").SingleAsync();
- Assert.AreEqual(2.8m, saved.Value);
- Assert.AreEqual("%", saved.Unit);
- var count = await db.MacroIndicator.AsNoTracking().CountAsync(c => c.Code == indicator.Code);
- Assert.AreEqual(2, count, "서울(11) 계열은 적재되지 않음");
- }
- [TestMethod]
- public async Task Upsert_EmptyClassCode_AutoLocksToFirstSeries()
- {
- // ClassCode 미지정이면 응답 첫 계열(첫 등장 C1=T10)로 자동 고정 — 서울(11) 혼입 배제
- var rows = KosisParser.Parse(File.ReadAllText(Path.Combine(FixtureRoot, "cpi.json")));
- var indicator = Cpi(classCode: "");
- using var db = TestDb.Create();
- var (inserted, _) = await KosisImport.UpsertAsync(db, indicator, rows, CancellationToken.None);
- Assert.AreEqual(2, inserted, "첫 계열(T10)만 2행");
- }
- [TestMethod]
- public async Task Upsert_ChangedValue_UpdatesRow()
- {
- var indicator = Cpi();
- var original = new List<KosisParser.Row> { new("202501", 120.0m, "2020=100", "소비자물가지수(총지수)", "T10", "전국") };
- var changed = new List<KosisParser.Row> { new("202501", 121.5m, "2020=100", "소비자물가지수(총지수)", "T10", "전국") };
- using (var db = TestDb.Create())
- {
- await KosisImport.UpsertAsync(db, indicator, original, CancellationToken.None);
- }
- using (var db = TestDb.Create())
- {
- var (inserted, updated) = await KosisImport.UpsertAsync(db, indicator, changed, CancellationToken.None);
- Assert.AreEqual(0, inserted);
- Assert.AreEqual(1, updated, "수치 변경 → update");
- var saved = await db.MacroIndicator.AsNoTracking().Where(c => c.Code == indicator.Code && c.Period == "202501").SingleAsync();
- Assert.AreEqual(121.5m, saved.Value);
- }
- }
- [TestMethod]
- public async Task Upsert_NoMatchingClassCode_InsertsNothing()
- {
- var indicator = Cpi(classCode: "ZZZ");
- var rows = KosisParser.Parse(File.ReadAllText(Path.Combine(FixtureRoot, "cpi.json")));
- using var db = TestDb.Create();
- var (inserted, updated) = await KosisImport.UpsertAsync(db, indicator, rows, CancellationToken.None);
- Assert.AreEqual(0, inserted);
- Assert.AreEqual(0, updated);
- }
- }
|