using Infrastructure.StockData; namespace Application.Tests; [TestClass] public sealed class KrxSriBondParserTests { [TestMethod] public void ParseDaily_MapsIssuanceInfo_FromSampleJson() { // KRX OpenAPI(esg/sri_bond_info) 실검증 샘플 — 발행정보 스냅샷: ISU_CD 12자리 ISIN, 날짜 yyyyMMdd, 금액 원 단위, 표면이자율 % const string json = """ {"OutBlock_1":[ {"BAS_DD":"20250102","ISUR_NM":"신한은행","ISU_CD":"KR6000011B58","SRI_BND_TP_NM":"녹색채권","ISU_NM":"신한은행25-05-이10갑후(녹)","LIST_DD":"20210507","ISU_DD":"20210506","REDMPT_DD":"20310506","ISU_RT":"2.58000","ISU_AMT":"400000000000","LIST_AMT":"400000000000","BND_TP_NM":"회사채"} ]} """; var rows = KrxSriBondParser.ParseDaily(json); Assert.AreEqual(1, rows.Count); var r = rows[0]; Assert.AreEqual("KR6000011B58", r.Code, "12자리 ISIN 원문 유지"); Assert.AreEqual(new DateOnly(2025, 1, 2), r.TradeDate, "BAS_DD 스냅샷일"); Assert.AreEqual("신한은행", r.IssuerName); Assert.AreEqual("녹색채권", r.SriBondType); Assert.AreEqual("신한은행25-05-이10갑후(녹)", r.Name); Assert.AreEqual(new DateOnly(2021, 5, 7), r.ListDate, "LIST_DD"); Assert.AreEqual(new DateOnly(2021, 5, 6), r.IssueDate, "ISU_DD"); Assert.AreEqual(new DateOnly(2031, 5, 6), r.RedemptionDate, "REDMPT_DD"); Assert.AreEqual(2.58000m, r.CouponRate, "ISU_RT 표면이자율 %"); Assert.AreEqual(400000000000L, r.IssueAmount, "ISU_AMT 발행금액(원)"); Assert.AreEqual(400000000000L, r.ListAmount, "LIST_AMT 상장금액(원)"); Assert.AreEqual("회사채", r.BondType); } [TestMethod] public void ParseDaily_MissingDatesAndRate_AreNull() { // 결측 방어 — 날짜/표면이자율/채권유형이 ""/"-" 이면 null. 천단위 콤마 금액 방어. const string json = """ {"OutBlock_1":[ {"BAS_DD":"20250102","ISUR_NM":"한국주택금융공사","ISU_CD":"KR350102GA30","SRI_BND_TP_NM":"사회적채권","ISU_NM":"주금공MBS","LIST_DD":"","ISU_DD":"-","REDMPT_DD":"","ISU_RT":"","ISU_AMT":"1,000,000,000","LIST_AMT":"-","BND_TP_NM":"-"} ]} """; var rows = KrxSriBondParser.ParseDaily(json); Assert.AreEqual(1, rows.Count); var r = rows[0]; Assert.AreEqual("KR350102GA30", r.Code); Assert.IsNull(r.ListDate, "빈 LIST_DD 는 null"); Assert.IsNull(r.IssueDate, "\"-\" ISU_DD 는 null"); Assert.IsNull(r.RedemptionDate); Assert.IsNull(r.CouponRate, "빈 ISU_RT 는 null (0% 과 구분)"); Assert.AreEqual(1000000000L, r.IssueAmount, "발행금액 콤마 제거"); Assert.AreEqual(0L, r.ListAmount, "\"-\" LIST_AMT 는 0"); Assert.IsNull(r.BondType, "\"-\" BND_TP_NM 은 null"); } [TestMethod] public void ParseDaily_MissingCode_IsSkipped() { // 표준코드(ISU_CD) 없으면 UQ 키를 만들 수 없어 행 제외 const string json = """ {"OutBlock_1":[ {"BAS_DD":"20250102","ISUR_NM":"발행사","ISU_CD":"","SRI_BND_TP_NM":"녹색채권","ISU_NM":"코드없음채권","ISU_AMT":"100","LIST_AMT":"100"} ]} """; Assert.AreEqual(0, KrxSriBondParser.ParseDaily(json).Count); } [TestMethod] public void ParseDaily_EmptyOrMissingBlock_ReturnsEmpty() { Assert.AreEqual(0, KrxSriBondParser.ParseDaily("""{"OutBlock_1":[]}""").Count); Assert.AreEqual(0, KrxSriBondParser.ParseDaily("""{"respMsg":"Unauthorized API Call","respCode":"401"}""").Count); } }