SeibroXmlTests.cs 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. using System.Text.RegularExpressions;
  2. using System.Xml;
  3. using Infrastructure.StockData;
  4. namespace Application.Tests;
  5. [TestClass]
  6. public sealed class SeibroXmlTests
  7. {
  8. [TestMethod]
  9. public void Parse_All37Fixtures_SmokeTest_RowsMatchActualDataElements()
  10. {
  11. // 샘플 37종 전수 스모크 — 전부 예외 없이 파싱되고, rows 수는 파일에 실제 존재하는 <data> 수와 일치해야 한다
  12. // (vector@result 는 원문 행수라 truncate 된 샘플에선 rows 보다 클 수 있음 → ResultCount 는 음수 아님만 확인)
  13. var files = SeibroFixtures.AllXmlFiles();
  14. Assert.AreEqual(37, files.Length, "SEIBro fixture 는 37종 전부 복사되어야 한다");
  15. foreach (var file in files)
  16. {
  17. var xml = File.ReadAllText(file);
  18. var envelope = SeibroXml.Parse(xml);
  19. // 파서와 독립적인 검증용 카운트 — <data ...> 시작 태그 수 (셀프클로징 없음, 래퍼/필드 element 는 data 로 시작하지 않음)
  20. var actualDataCount = Regex.Matches(xml, @"<data[\s>]", RegexOptions.IgnoreCase).Count;
  21. Assert.AreEqual(actualDataCount, envelope.Rows.Count, $"{Path.GetFileName(file)}: rows 수는 실제 data element 수와 일치");
  22. Assert.IsTrue(envelope.ResultCount >= 0, $"{Path.GetFileName(file)}: result 행수 속성 파싱");
  23. Assert.IsTrue(envelope.ResultCount >= envelope.Rows.Count, $"{Path.GetFileName(file)}: result 는 원문 행수 (truncate 샘플은 rows 이상)");
  24. }
  25. }
  26. [TestMethod]
  27. public void Parse_UppercaseResultWrapper_GetAssetInfo_MapsFields()
  28. {
  29. // 파생결합 getAssetInfo 는 래퍼가 대문자 <RESULT> — 대소문자 무시 파싱 검증 (docs/SEIBro/readme.md 규칙 4)
  30. var envelope = SeibroXml.Parse(SeibroFixtures.Read("getAssetInfo.xml"));
  31. Assert.AreEqual(3, envelope.ResultCount);
  32. var row = envelope.Rows[0];
  33. Assert.AreEqual("KR6539335879", SeibroXml.GetOrNull(row, "ISIN"));
  34. Assert.AreEqual("1", SeibroXml.GetOrNull(row, "ASSET_SEQ"));
  35. Assert.AreEqual("Hang Seng China Enterprises Index", SeibroXml.GetOrNull(row, "STND_BASSET_NM"));
  36. Assert.AreEqual("6090.15", SeibroXml.GetOrNull(row, "XRC_STOP_LOLMT_PRICE"));
  37. }
  38. [TestMethod]
  39. public void Parse_LowercaseResultWrapper_GetShotnByMart_MapsFields()
  40. {
  41. // 주식 getShotnByMart 는 래퍼가 소문자 <result> — 필드값 = value 속성
  42. var envelope = SeibroXml.Parse(SeibroFixtures.Read("getShotnByMart.xml"));
  43. Assert.AreEqual(126, envelope.ResultCount, "vector@result = 원문 행수");
  44. var row = envelope.Rows[0];
  45. Assert.AreEqual("000800", SeibroXml.GetOrNull(row, "SHOTN_ISIN"));
  46. Assert.AreEqual("경남기업", SeibroXml.GetOrNull(row, "KOR_SECN_NM"));
  47. Assert.AreEqual("80", SeibroXml.GetOrNull(row, "ISSUCO_CUSTNO"));
  48. }
  49. [TestMethod]
  50. public void Parse_EmptyResult_ZeroRows()
  51. {
  52. // result="0" + data 없음 = 정상 빈 결과 (getXrcStkStatInfo 샘플이 실제 이 형태)
  53. var envelope = SeibroXml.Parse(SeibroFixtures.Read("getXrcStkStatInfo.xml"));
  54. Assert.AreEqual(0, envelope.ResultCount);
  55. Assert.AreEqual(0, envelope.Rows.Count);
  56. }
  57. [TestMethod]
  58. public void Parse_EmptyValueAttribute_TreatedAsNull()
  59. {
  60. // value="" 는 null 취급 — 행 딕셔너리에서 키 자체가 빠진다 (element 생략과 동일 의미)
  61. const string xml = """
  62. <vector result="1">
  63. <data vectorkey="0" type="Document">
  64. <result>
  65. <ISIN value="KR7005930003"/>
  66. <EMPTY_FIELD value=""/>
  67. </result>
  68. </data>
  69. </vector>
  70. """;
  71. var envelope = SeibroXml.Parse(xml);
  72. Assert.AreEqual(1, envelope.Rows.Count);
  73. Assert.AreEqual("KR7005930003", SeibroXml.GetOrNull(envelope.Rows[0], "ISIN"));
  74. Assert.IsNull(SeibroXml.GetOrNull(envelope.Rows[0], "EMPTY_FIELD"), "value=\"\" → null");
  75. Assert.IsNull(SeibroXml.GetOrNull(envelope.Rows[0], "OMITTED_FIELD"), "element 생략 → null");
  76. }
  77. [TestMethod]
  78. public void Parse_FieldLookup_IsCaseInsensitive()
  79. {
  80. const string xml = """
  81. <vector result="1">
  82. <data vectorkey="0" type="Document">
  83. <RESULT>
  84. <ISSUCO_CUSTNO value="593"/>
  85. </RESULT>
  86. </data>
  87. </vector>
  88. """;
  89. var envelope = SeibroXml.Parse(xml);
  90. Assert.AreEqual("593", SeibroXml.GetOrNull(envelope.Rows[0], "issuco_custno"), "필드명 조회는 대소문자 무시");
  91. }
  92. [TestMethod]
  93. public void Parse_NonXmlInput_Throws()
  94. {
  95. // 비XML 응답(에러 페이지 등)은 명시적 예외 — silent 빈 결과 금지
  96. Assert.ThrowsExactly<XmlException>(() => SeibroXml.Parse("Unauthorized Key"));
  97. }
  98. [TestMethod]
  99. public void Parse_NonVectorRoot_Throws()
  100. {
  101. Assert.ThrowsExactly<FormatException>(() => SeibroXml.Parse("<error message=\"invalid key\"/>"));
  102. }
  103. [TestMethod]
  104. public void Parse_MissingResultAttribute_Throws()
  105. {
  106. Assert.ThrowsExactly<FormatException>(() => SeibroXml.Parse("<vector><data vectorkey=\"0\"><result/></data></vector>"));
  107. }
  108. }