DartDisclosureParserTests.cs 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. using Infrastructure.StockData;
  2. namespace Application.Tests;
  3. [TestClass]
  4. public sealed class DartDisclosureParserTests
  5. {
  6. [TestMethod]
  7. public void Parse_Success_MapsAllFieldsAndMeta()
  8. {
  9. // OpenDART list.json 실검증 shape — status "000", 상장(stock_code 6자리) 항목
  10. const string json = """
  11. {"status":"000","message":"정상","page_no":1,"page_count":100,"total_count":1178,"total_page":12,
  12. "list":[
  13. {"corp_code":"00126380","corp_name":"삼성전자","stock_code":"005930","corp_cls":"Y","report_nm":"주요사항보고서(자기주식취득결정)","rcept_no":"20250103000123","flr_nm":"삼성전자","rcept_dt":"20250103","rm":""}
  14. ]}
  15. """;
  16. var result = DartDisclosureParser.Parse(json);
  17. Assert.AreEqual("000", result.Status);
  18. Assert.AreEqual("정상", result.Message);
  19. Assert.AreEqual(1, result.PageNo);
  20. Assert.AreEqual(12, result.TotalPage);
  21. Assert.AreEqual(1178, result.TotalCount);
  22. Assert.AreEqual(1, result.Rows.Count);
  23. var d = result.Rows[0];
  24. Assert.AreEqual("20250103000123", d.RceptNo, "접수번호 14자리");
  25. Assert.AreEqual("00126380", d.CorpCode, "고유번호 8자리");
  26. Assert.AreEqual("삼성전자", d.CorpName);
  27. Assert.AreEqual("005930", d.StockCode, "상장 종목코드 6자리");
  28. Assert.AreEqual("Y", d.CorpCls, "유가증권시장");
  29. Assert.AreEqual("주요사항보고서(자기주식취득결정)", d.ReportNm);
  30. Assert.AreEqual("삼성전자", d.FlrNm);
  31. Assert.AreEqual(new DateOnly(2025, 1, 3), d.RceptDt, "rcept_dt YYYYMMDD → DateOnly");
  32. Assert.IsNull(d.Rm, "빈 rm 은 null");
  33. }
  34. [TestMethod]
  35. public void Parse_EmptyStockCode_BecomesNull()
  36. {
  37. // 비상장(자산운용 등) — stock_code "" → null, corp_cls "E"(기타), rm "정"(정정)
  38. const string json = """
  39. {"status":"000","message":"정상","page_no":1,"page_count":100,"total_count":1,"total_page":1,
  40. "list":[
  41. {"corp_code":"00380942","corp_name":"카디안자산운용","stock_code":"","corp_cls":"E","report_nm":"[기재정정]일괄신고서(집합투자증권-신탁형)","rcept_no":"20250103000294","flr_nm":"카디안자산운용","rcept_dt":"20250103","rm":"정"}
  42. ]}
  43. """;
  44. var result = DartDisclosureParser.Parse(json);
  45. Assert.AreEqual(1, result.Rows.Count);
  46. var d = result.Rows[0];
  47. Assert.IsNull(d.StockCode, "비상장 stock_code \"\" 는 null");
  48. Assert.AreEqual("E", d.CorpCls);
  49. Assert.AreEqual("정", d.Rm, "비고(rm) 유지");
  50. }
  51. [TestMethod]
  52. public void Parse_Status013_NoData_ReturnsEmptyRows()
  53. {
  54. // 013 = 조회된 데이터 없음 — list 노드 없이 status/message 만
  55. const string json = """
  56. {"status":"013","message":"조회된 데이터가 없습니다."}
  57. """;
  58. var result = DartDisclosureParser.Parse(json);
  59. Assert.AreEqual("013", result.Status);
  60. Assert.AreEqual(0, result.Rows.Count, "013 은 빈 목록");
  61. }
  62. [TestMethod]
  63. public void Parse_QuotaExceeded_Status020_ReturnsEmptyRows()
  64. {
  65. // 020 = 요청 제한(쿼터) 초과 (키 오류는 010/011) — status != "000" 이면 rows 비움
  66. const string json = """
  67. {"status":"020","message":"요청 제한을 초과하였습니다."}
  68. """;
  69. var result = DartDisclosureParser.Parse(json);
  70. Assert.AreEqual("020", result.Status);
  71. Assert.AreEqual(0, result.Rows.Count);
  72. }
  73. [TestMethod]
  74. public void DescribeStatus_KnownCodes_CorrectLabels()
  75. {
  76. // 020 은 쿼터 초과 — '등록되지 않은 키'(010)와 혼동 금지 (감사 P3 라벨 오기 수정)
  77. Assert.AreEqual("요청 제한(쿼터) 초과", DartDisclosureParser.DescribeStatus("020"));
  78. Assert.AreEqual("등록되지 않은 키", DartDisclosureParser.DescribeStatus("010"));
  79. Assert.AreEqual("조회된 데이터 없음", DartDisclosureParser.DescribeStatus("013"));
  80. Assert.AreEqual("정상", DartDisclosureParser.DescribeStatus("000"));
  81. Assert.AreEqual("알 수 없는 상태", DartDisclosureParser.DescribeStatus("999"));
  82. }
  83. [TestMethod]
  84. public void Parse_MultipleRows_DateParsing()
  85. {
  86. // 여러 행 — 각기 다른 접수일 파싱 확인
  87. const string json = """
  88. {"status":"000","message":"정상","page_no":1,"page_count":100,"total_count":2,"total_page":1,
  89. "list":[
  90. {"corp_code":"00111111","corp_name":"에이","stock_code":"111111","corp_cls":"K","report_nm":"분기보고서","rcept_no":"20250102000001","flr_nm":"에이","rcept_dt":"20250102","rm":""},
  91. {"corp_code":"00222222","corp_name":"비","stock_code":"222222","corp_cls":"N","report_nm":"사업보고서","rcept_no":"20250103000002","flr_nm":"비","rcept_dt":"20250103","rm":"첨부추가"}
  92. ]}
  93. """;
  94. var result = DartDisclosureParser.Parse(json);
  95. Assert.AreEqual(2, result.Rows.Count);
  96. Assert.AreEqual(new DateOnly(2025, 1, 2), result.Rows[0].RceptDt);
  97. Assert.AreEqual("K", result.Rows[0].CorpCls, "코스닥");
  98. Assert.AreEqual(new DateOnly(2025, 1, 3), result.Rows[1].RceptDt);
  99. Assert.AreEqual("N", result.Rows[1].CorpCls, "코넥스");
  100. Assert.AreEqual("첨부추가", result.Rows[1].Rm);
  101. }
  102. [TestMethod]
  103. public void Parse_MissingRequiredField_RowSkipped()
  104. {
  105. // 필수(rcept_no/corp_code/report_nm/rcept_dt) 결측 행은 건너뜀 — 방어
  106. const string json = """
  107. {"status":"000","message":"정상","page_no":1,"page_count":100,"total_count":2,"total_page":1,
  108. "list":[
  109. {"corp_code":"00333333","corp_name":"씨","stock_code":"333333","corp_cls":"Y","report_nm":"","rcept_no":"20250103000003","flr_nm":"씨","rcept_dt":"20250103","rm":""},
  110. {"corp_code":"00444444","corp_name":"디","stock_code":"444444","corp_cls":"Y","report_nm":"정기보고서","rcept_no":"20250103000004","flr_nm":"디","rcept_dt":"20250103","rm":""}
  111. ]}
  112. """;
  113. var result = DartDisclosureParser.Parse(json);
  114. Assert.AreEqual(1, result.Rows.Count, "report_nm 빈 행은 skip");
  115. Assert.AreEqual("20250103000004", result.Rows[0].RceptNo);
  116. }
  117. }