using Infrastructure.StockData; namespace Application.Tests; [TestClass] public sealed class DataGoKrParserTests { [TestMethod] public void ParseDailyPrices_MapsFields_FromSampleJson() { // 금융위 주식시세정보(getStockPriceInfo) 표준 응답 샘플 — 실호출 불가 전제의 고정 문자열 검증 const string json = """ {"response":{"header":{"resultCode":"00","resultMsg":"NORMAL SERVICE."},"body":{"numOfRows":2,"pageNo":1,"totalCount":2,"items":{"item":[ {"basDt":"20260702","srtnCd":"005930","isinCd":"KR7005930003","itmsNm":"삼성전자","mrktCtg":"KOSPI","clpr":"70500","vs":"-500","fltRt":"-.70","mkp":"71000","hipr":"71200","lopr":"70300","trqu":"12345678","trPrc":"870000000000","lstgStCnt":"5969782550","mrktTotAmt":"420870169275000"}, {"basDt":"20260702","srtnCd":"A000660","isinCd":"KR7000660001","itmsNm":"SK하이닉스","mrktCtg":"KOSPI","clpr":"210000","vs":"3000","fltRt":"1.45","mkp":"208000","hipr":"211500","lopr":"207000","trqu":"2345678","trPrc":"490000000000","lstgStCnt":"728002365","mrktTotAmt":"152880496650000"} ]}}}} """; var (items, totalCount) = DataGoKrStockParser.ParseDailyPrices(json); Assert.AreEqual(2, totalCount); Assert.AreEqual(2, items.Count); var samsung = items[0]; Assert.AreEqual("005930", samsung.Code); Assert.AreEqual(new DateOnly(2026, 7, 2), samsung.TradingDate); Assert.AreEqual(71000, samsung.Open); Assert.AreEqual(71200, samsung.High); Assert.AreEqual(70300, samsung.Low); Assert.AreEqual(70500, samsung.Close); Assert.AreEqual(12345678L, samsung.Volume); Assert.AreEqual(870000000000L, samsung.TradingValue); Assert.AreEqual(-500, samsung.ChangeAmount); Assert.AreEqual(-0.70m, samsung.ChangeRate, "-.70 형식 등락률을 파싱해야 한다"); Assert.AreEqual(420870169275000L, samsung.MarketCap); Assert.AreEqual(5969782550L, samsung.ListedShares); Assert.AreEqual("000660", items[1].Code, "'A' 프리픽스 단축코드는 뒤 6자리로 정규화한다"); } [TestMethod] public void ParseListedItems_SingleObjectItem_AndEmptyItems() { // 단건 응답은 item 이 배열이 아닌 객체로 온다 const string single = """ {"response":{"header":{"resultCode":"00","resultMsg":"NORMAL SERVICE."},"body":{"numOfRows":1,"pageNo":1,"totalCount":1,"items":{"item": {"basDt":"20260702","srtnCd":"005930","isinCd":"KR7005930003","itmsNm":"삼성전자","mrktCtg":"KOSPI","crno":"1301110006246","corpNm":"삼성전자(주)"} }}}} """; var (items, totalCount) = DataGoKrStockParser.ParseListedItems(single); Assert.AreEqual(1, totalCount); Assert.AreEqual(1, items.Count); Assert.AreEqual("005930", items[0].Code); Assert.AreEqual("KR7005930003", items[0].Isin); Assert.AreEqual("삼성전자", items[0].Name); Assert.AreEqual("KOSPI", items[0].MarketName); Assert.AreEqual(new DateOnly(2026, 7, 2), items[0].BaseDate); // 0건이면 items 가 빈 문자열로 온다 const string empty = """ {"response":{"header":{"resultCode":"00","resultMsg":"NORMAL SERVICE."},"body":{"numOfRows":0,"pageNo":1,"totalCount":0,"items":""}}} """; var (emptyItems, emptyTotal) = DataGoKrStockParser.ParseListedItems(empty); Assert.AreEqual(0, emptyTotal); Assert.AreEqual(0, emptyItems.Count); } [TestMethod] public void Parse_ErrorResultCode_Throws() { // 게이트웨이 정상 + 서비스 오류 코드 응답은 예외로 표면화한다 (조용한 빈 수집 방지) const string error = """ {"response":{"header":{"resultCode":"30","resultMsg":"SERVICE_KEY_IS_NOT_REGISTERED_ERROR"},"body":null}} """; Assert.ThrowsExactly(() => DataGoKrStockParser.ParseListedItems(error)); } }