lib-x17-fintech 2.1.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (282) hide show
  1. lib_x17_fintech-2.1.3.dist-info/METADATA +633 -0
  2. lib_x17_fintech-2.1.3.dist-info/RECORD +282 -0
  3. lib_x17_fintech-2.1.3.dist-info/WHEEL +5 -0
  4. lib_x17_fintech-2.1.3.dist-info/licenses/LICENSE +1 -0
  5. lib_x17_fintech-2.1.3.dist-info/top_level.txt +1 -0
  6. xfintech/__init__.py +0 -0
  7. xfintech/connect/__init__.py +18 -0
  8. xfintech/connect/artifact/__init__.py +5 -0
  9. xfintech/connect/artifact/artifact.py +168 -0
  10. xfintech/connect/artifact/tests/__init__.py +3 -0
  11. xfintech/connect/artifact/tests/test_class_artifact_all.py +564 -0
  12. xfintech/connect/common/__init__.py +12 -0
  13. xfintech/connect/common/connect.py +49 -0
  14. xfintech/connect/common/connectref.py +119 -0
  15. xfintech/connect/common/error.py +62 -0
  16. xfintech/connect/common/tests/__init__.py +1 -0
  17. xfintech/connect/common/tests/test_class_connectlike_all.py +544 -0
  18. xfintech/connect/common/tests/test_class_connectref_all.py +586 -0
  19. xfintech/connect/common/tests/test_class_errors_all.py +524 -0
  20. xfintech/connect/instance/__init__.py +7 -0
  21. xfintech/connect/instance/macos.py +121 -0
  22. xfintech/connect/instance/s3.py +176 -0
  23. xfintech/connect/instance/tests/__init__.py +1 -0
  24. xfintech/connect/instance/tests/test_class_macosconnect_all.py +692 -0
  25. xfintech/connect/instance/tests/test_class_s3connect_all.py +603 -0
  26. xfintech/data/__init__.py +20 -0
  27. xfintech/data/common/__init__.py +15 -0
  28. xfintech/data/common/cache.py +186 -0
  29. xfintech/data/common/coolant.py +171 -0
  30. xfintech/data/common/metric.py +138 -0
  31. xfintech/data/common/paginate.py +132 -0
  32. xfintech/data/common/params.py +162 -0
  33. xfintech/data/common/retry.py +201 -0
  34. xfintech/data/common/tests/__init__.py +1 -0
  35. xfintech/data/common/tests/test_class_cache_all.py +681 -0
  36. xfintech/data/common/tests/test_class_coolant_all.py +534 -0
  37. xfintech/data/common/tests/test_class_metric_all.py +705 -0
  38. xfintech/data/common/tests/test_class_paginate_all.py +508 -0
  39. xfintech/data/common/tests/test_class_params_all.py +891 -0
  40. xfintech/data/common/tests/test_class_retry_all.py +714 -0
  41. xfintech/data/job/__init__.py +17 -0
  42. xfintech/data/job/errors.py +112 -0
  43. xfintech/data/job/house.py +156 -0
  44. xfintech/data/job/job.py +247 -0
  45. xfintech/data/job/joblike.py +47 -0
  46. xfintech/data/job/tests/__init__.py +1 -0
  47. xfintech/data/job/tests/test_class_errors_all.py +275 -0
  48. xfintech/data/job/tests/test_class_house_all.py +801 -0
  49. xfintech/data/job/tests/test_class_job_all.py +684 -0
  50. xfintech/data/job/tests/test_class_joblike_all.py +482 -0
  51. xfintech/data/relay/__init__.py +7 -0
  52. xfintech/data/relay/client.py +114 -0
  53. xfintech/data/relay/clientlike.py +45 -0
  54. xfintech/data/relay/tests/test_class_relayclient_all.py +484 -0
  55. xfintech/data/relay/tests/test_class_relayclientlike_all.py +500 -0
  56. xfintech/data/source/__init__.py +7 -0
  57. xfintech/data/source/baostock/__init__.py +21 -0
  58. xfintech/data/source/baostock/job/__init__.py +5 -0
  59. xfintech/data/source/baostock/job/job.py +217 -0
  60. xfintech/data/source/baostock/job/tests/__init__.py +0 -0
  61. xfintech/data/source/baostock/job/tests/test_class_baostockjob_all.py +547 -0
  62. xfintech/data/source/baostock/session/__init__.py +8 -0
  63. xfintech/data/source/baostock/session/relay.py +223 -0
  64. xfintech/data/source/baostock/session/session.py +241 -0
  65. xfintech/data/source/baostock/session/tests/__init__.py +0 -0
  66. xfintech/data/source/baostock/session/tests/test_class_relay_all.py +694 -0
  67. xfintech/data/source/baostock/session/tests/test_class_session_all.py +505 -0
  68. xfintech/data/source/baostock/stock/__init__.py +0 -0
  69. xfintech/data/source/baostock/stock/hs300stock/__init__.py +3 -0
  70. xfintech/data/source/baostock/stock/hs300stock/constant.py +49 -0
  71. xfintech/data/source/baostock/stock/hs300stock/hs300stock.py +133 -0
  72. xfintech/data/source/baostock/stock/hs300stock/tests/__init__.py +1 -0
  73. xfintech/data/source/baostock/stock/hs300stock/tests/test_class_hs300index_all.py +413 -0
  74. xfintech/data/source/baostock/stock/minuteline/__init__.py +19 -0
  75. xfintech/data/source/baostock/stock/minuteline/constant.py +89 -0
  76. xfintech/data/source/baostock/stock/minuteline/minuteline.py +163 -0
  77. xfintech/data/source/baostock/stock/minuteline/tests/__init__.py +0 -0
  78. xfintech/data/source/baostock/stock/minuteline/tests/test_class_minuteline_all.py +582 -0
  79. xfintech/data/source/baostock/stock/stock/__init__.py +19 -0
  80. xfintech/data/source/baostock/stock/stock/constant.py +55 -0
  81. xfintech/data/source/baostock/stock/stock/stock.py +149 -0
  82. xfintech/data/source/baostock/stock/stock/tests/__init__.py +0 -0
  83. xfintech/data/source/baostock/stock/stock/tests/test_class_stock_all.py +508 -0
  84. xfintech/data/source/baostock/stock/stockinfo/__init__.py +5 -0
  85. xfintech/data/source/baostock/stock/stockinfo/constant.py +66 -0
  86. xfintech/data/source/baostock/stock/stockinfo/stockinfo.py +176 -0
  87. xfintech/data/source/baostock/stock/stockinfo/tests/__init__.py +1 -0
  88. xfintech/data/source/baostock/stock/stockinfo/tests/test_class_stockinfo_all.py +617 -0
  89. xfintech/data/source/baostock/stock/sz50stock/__init__.py +3 -0
  90. xfintech/data/source/baostock/stock/sz50stock/constant.py +49 -0
  91. xfintech/data/source/baostock/stock/sz50stock/sz50stock.py +133 -0
  92. xfintech/data/source/baostock/stock/sz50stock/tests/__init__.py +1 -0
  93. xfintech/data/source/baostock/stock/sz50stock/tests/test_class_sz50stock_all.py +397 -0
  94. xfintech/data/source/baostock/stock/tradedate/__init__.py +19 -0
  95. xfintech/data/source/baostock/stock/tradedate/constant.py +72 -0
  96. xfintech/data/source/baostock/stock/tradedate/tests/__init__.py +0 -0
  97. xfintech/data/source/baostock/stock/tradedate/tests/test_class_tradedate_all.py +695 -0
  98. xfintech/data/source/baostock/stock/tradedate/tradedate.py +208 -0
  99. xfintech/data/source/baostock/stock/zz500stock/__init__.py +3 -0
  100. xfintech/data/source/baostock/stock/zz500stock/constant.py +55 -0
  101. xfintech/data/source/baostock/stock/zz500stock/tests/__init__.py +1 -0
  102. xfintech/data/source/baostock/stock/zz500stock/tests/test_class_zz500stock_all.py +421 -0
  103. xfintech/data/source/baostock/stock/zz500stock/zz500stock.py +133 -0
  104. xfintech/data/source/tushare/__init__.py +61 -0
  105. xfintech/data/source/tushare/job/__init__.py +5 -0
  106. xfintech/data/source/tushare/job/job.py +257 -0
  107. xfintech/data/source/tushare/job/tests/test_class_tusharejob_all.py +589 -0
  108. xfintech/data/source/tushare/session/__init__.py +5 -0
  109. xfintech/data/source/tushare/session/relay.py +231 -0
  110. xfintech/data/source/tushare/session/session.py +239 -0
  111. xfintech/data/source/tushare/session/tests/test_class_relay_all.py +719 -0
  112. xfintech/data/source/tushare/session/tests/test_class_session_all.py +705 -0
  113. xfintech/data/source/tushare/stock/__init__.py +55 -0
  114. xfintech/data/source/tushare/stock/adjfactor/__init__.py +19 -0
  115. xfintech/data/source/tushare/stock/adjfactor/adjfactor.py +150 -0
  116. xfintech/data/source/tushare/stock/adjfactor/constant.py +71 -0
  117. xfintech/data/source/tushare/stock/adjfactor/tests/__init__.py +0 -0
  118. xfintech/data/source/tushare/stock/adjfactor/tests/test_class_adjfactor_all.py +372 -0
  119. xfintech/data/source/tushare/stock/capflow/__init__.py +19 -0
  120. xfintech/data/source/tushare/stock/capflow/capflow.py +171 -0
  121. xfintech/data/source/tushare/stock/capflow/constant.py +105 -0
  122. xfintech/data/source/tushare/stock/capflow/tests/__init__.py +0 -0
  123. xfintech/data/source/tushare/stock/capflow/tests/test_class_capflow_all.py +589 -0
  124. xfintech/data/source/tushare/stock/capflowdc/__init__.py +19 -0
  125. xfintech/data/source/tushare/stock/capflowdc/capflowdc.py +167 -0
  126. xfintech/data/source/tushare/stock/capflowdc/constant.py +95 -0
  127. xfintech/data/source/tushare/stock/capflowdc/tests/__init__.py +0 -0
  128. xfintech/data/source/tushare/stock/capflowdc/tests/test_class_capflowdc_all.py +814 -0
  129. xfintech/data/source/tushare/stock/capflowths/__init__.py +19 -0
  130. xfintech/data/source/tushare/stock/capflowths/capflowths.py +173 -0
  131. xfintech/data/source/tushare/stock/capflowths/constant.py +92 -0
  132. xfintech/data/source/tushare/stock/capflowths/tests/__init__.py +0 -0
  133. xfintech/data/source/tushare/stock/capflowths/tests/test_class_capflowths_all.py +551 -0
  134. xfintech/data/source/tushare/stock/company/__init__.py +19 -0
  135. xfintech/data/source/tushare/stock/company/company.py +188 -0
  136. xfintech/data/source/tushare/stock/company/constant.py +92 -0
  137. xfintech/data/source/tushare/stock/company/tests/__init__.py +1 -0
  138. xfintech/data/source/tushare/stock/company/tests/test_class_company_all.py +829 -0
  139. xfintech/data/source/tushare/stock/companybusiness/__init__.py +21 -0
  140. xfintech/data/source/tushare/stock/companybusiness/companybusiness.py +183 -0
  141. xfintech/data/source/tushare/stock/companybusiness/constant.py +91 -0
  142. xfintech/data/source/tushare/stock/companybusiness/tests/__init__.py +0 -0
  143. xfintech/data/source/tushare/stock/companybusiness/tests/test_class_companybusiness_all.py +633 -0
  144. xfintech/data/source/tushare/stock/companycashflow/__init__.py +21 -0
  145. xfintech/data/source/tushare/stock/companycashflow/companycashflow.py +277 -0
  146. xfintech/data/source/tushare/stock/companycashflow/constant.py +293 -0
  147. xfintech/data/source/tushare/stock/companycashflow/tests/__init__.py +0 -0
  148. xfintech/data/source/tushare/stock/companycashflow/tests/test_class_companycashflow_all.py +619 -0
  149. xfintech/data/source/tushare/stock/companydebt/__init__.py +19 -0
  150. xfintech/data/source/tushare/stock/companydebt/companydebt.py +339 -0
  151. xfintech/data/source/tushare/stock/companydebt/constant.py +403 -0
  152. xfintech/data/source/tushare/stock/companydebt/tests/__init__.py +0 -0
  153. xfintech/data/source/tushare/stock/companydebt/tests/test_class_companydebt_all.py +655 -0
  154. xfintech/data/source/tushare/stock/companyoverview/__init__.py +21 -0
  155. xfintech/data/source/tushare/stock/companyoverview/companyoverview.py +214 -0
  156. xfintech/data/source/tushare/stock/companyoverview/constant.py +152 -0
  157. xfintech/data/source/tushare/stock/companyoverview/tests/__init__.py +0 -0
  158. xfintech/data/source/tushare/stock/companyoverview/tests/test_class_companyoverview_all.py +647 -0
  159. xfintech/data/source/tushare/stock/companyprofit/__init__.py +21 -0
  160. xfintech/data/source/tushare/stock/companyprofit/companyprofit.py +272 -0
  161. xfintech/data/source/tushare/stock/companyprofit/constant.py +259 -0
  162. xfintech/data/source/tushare/stock/companyprofit/tests/__init__.py +0 -0
  163. xfintech/data/source/tushare/stock/companyprofit/tests/test_class_companyprofit_all.py +635 -0
  164. xfintech/data/source/tushare/stock/conceptcapflowdc/__init__.py +21 -0
  165. xfintech/data/source/tushare/stock/conceptcapflowdc/conceptcapflowdc.py +175 -0
  166. xfintech/data/source/tushare/stock/conceptcapflowdc/constant.py +106 -0
  167. xfintech/data/source/tushare/stock/conceptcapflowdc/tests/__init__.py +0 -0
  168. xfintech/data/source/tushare/stock/conceptcapflowdc/tests/test_class_conceptcapflowdc_all.py +568 -0
  169. xfintech/data/source/tushare/stock/conceptcapflowths/__init__.py +21 -0
  170. xfintech/data/source/tushare/stock/conceptcapflowths/conceptcapflowths.py +188 -0
  171. xfintech/data/source/tushare/stock/conceptcapflowths/constant.py +89 -0
  172. xfintech/data/source/tushare/stock/conceptcapflowths/tests/__init__.py +0 -0
  173. xfintech/data/source/tushare/stock/conceptcapflowths/tests/test_class_conceptcapflowths_all.py +516 -0
  174. xfintech/data/source/tushare/stock/dayline/__init__.py +19 -0
  175. xfintech/data/source/tushare/stock/dayline/constant.py +87 -0
  176. xfintech/data/source/tushare/stock/dayline/dayline.py +177 -0
  177. xfintech/data/source/tushare/stock/dayline/tests/__init__.py +0 -0
  178. xfintech/data/source/tushare/stock/dayline/tests/test_class_dayline_all.py +585 -0
  179. xfintech/data/source/tushare/stock/industrycapflowths/__init__.py +21 -0
  180. xfintech/data/source/tushare/stock/industrycapflowths/constant.py +89 -0
  181. xfintech/data/source/tushare/stock/industrycapflowths/industrycapflowths.py +192 -0
  182. xfintech/data/source/tushare/stock/industrycapflowths/tests/__init__.py +0 -0
  183. xfintech/data/source/tushare/stock/industrycapflowths/tests/test_class_industrycapflowths_all.py +683 -0
  184. xfintech/data/source/tushare/stock/marketindexcapflowdc/__init__.py +21 -0
  185. xfintech/data/source/tushare/stock/marketindexcapflowdc/constant.py +90 -0
  186. xfintech/data/source/tushare/stock/marketindexcapflowdc/marketindexcapflowdc.py +173 -0
  187. xfintech/data/source/tushare/stock/marketindexcapflowdc/tests/__init__.py +0 -0
  188. xfintech/data/source/tushare/stock/marketindexcapflowdc/tests/test_class_marketindexcapflowdc_all.py +793 -0
  189. xfintech/data/source/tushare/stock/monthline/__init__.py +19 -0
  190. xfintech/data/source/tushare/stock/monthline/constant.py +87 -0
  191. xfintech/data/source/tushare/stock/monthline/monthline.py +180 -0
  192. xfintech/data/source/tushare/stock/monthline/tests/__init__.py +0 -0
  193. xfintech/data/source/tushare/stock/monthline/tests/test_class_monthline_all.py +574 -0
  194. xfintech/data/source/tushare/stock/stock/__init__.py +19 -0
  195. xfintech/data/source/tushare/stock/stock/constant.py +105 -0
  196. xfintech/data/source/tushare/stock/stock/stock.py +193 -0
  197. xfintech/data/source/tushare/stock/stock/tests/__init__.py +0 -0
  198. xfintech/data/source/tushare/stock/stock/tests/test_class_stock_all.py +788 -0
  199. xfintech/data/source/tushare/stock/stockdividend/__init__.py +21 -0
  200. xfintech/data/source/tushare/stock/stockdividend/constant.py +111 -0
  201. xfintech/data/source/tushare/stock/stockdividend/stockdividend.py +180 -0
  202. xfintech/data/source/tushare/stock/stockdividend/tests/__init__.py +0 -0
  203. xfintech/data/source/tushare/stock/stockdividend/tests/test_class_stockdividend_all.py +725 -0
  204. xfintech/data/source/tushare/stock/stockinfo/__init__.py +19 -0
  205. xfintech/data/source/tushare/stock/stockinfo/constant.py +104 -0
  206. xfintech/data/source/tushare/stock/stockinfo/stockinfo.py +208 -0
  207. xfintech/data/source/tushare/stock/stockinfo/tests/__init__.py +0 -0
  208. xfintech/data/source/tushare/stock/stockinfo/tests/test_class_stockinfo_all.py +881 -0
  209. xfintech/data/source/tushare/stock/stockipo/__init__.py +19 -0
  210. xfintech/data/source/tushare/stock/stockipo/constant.py +90 -0
  211. xfintech/data/source/tushare/stock/stockipo/stockipo.py +234 -0
  212. xfintech/data/source/tushare/stock/stockipo/tests/__init__.py +1 -0
  213. xfintech/data/source/tushare/stock/stockipo/tests/test_class_stockipo_all.py +750 -0
  214. xfintech/data/source/tushare/stock/stockpledge/__init__.py +19 -0
  215. xfintech/data/source/tushare/stock/stockpledge/constant.py +72 -0
  216. xfintech/data/source/tushare/stock/stockpledge/stockpledge.py +158 -0
  217. xfintech/data/source/tushare/stock/stockpledge/tests/__init__.py +0 -0
  218. xfintech/data/source/tushare/stock/stockpledge/tests/test_class_stockpledge_all.py +664 -0
  219. xfintech/data/source/tushare/stock/stockpledgedetail/__init__.py +21 -0
  220. xfintech/data/source/tushare/stock/stockpledgedetail/constant.py +85 -0
  221. xfintech/data/source/tushare/stock/stockpledgedetail/stockpledgedetail.py +171 -0
  222. xfintech/data/source/tushare/stock/stockpledgedetail/tests/__init__.py +0 -0
  223. xfintech/data/source/tushare/stock/stockpledgedetail/tests/test_class_stockpledgedetail_all.py +112 -0
  224. xfintech/data/source/tushare/stock/stockst/__init__.py +19 -0
  225. xfintech/data/source/tushare/stock/stockst/constant.py +80 -0
  226. xfintech/data/source/tushare/stock/stockst/stockst.py +189 -0
  227. xfintech/data/source/tushare/stock/stockst/tests/__init__.py +0 -0
  228. xfintech/data/source/tushare/stock/stockst/tests/test_class_stockst_all.py +693 -0
  229. xfintech/data/source/tushare/stock/stocksuspend/__init__.py +21 -0
  230. xfintech/data/source/tushare/stock/stocksuspend/constant.py +75 -0
  231. xfintech/data/source/tushare/stock/stocksuspend/stocksuspend.py +151 -0
  232. xfintech/data/source/tushare/stock/stocksuspend/tests/__init__.py +0 -0
  233. xfintech/data/source/tushare/stock/stocksuspend/tests/test_class_stocksuspend_all.py +626 -0
  234. xfintech/data/source/tushare/stock/techindex/__init__.py +19 -0
  235. xfintech/data/source/tushare/stock/techindex/constant.py +600 -0
  236. xfintech/data/source/tushare/stock/techindex/techindex.py +314 -0
  237. xfintech/data/source/tushare/stock/techindex/tests/__init__.py +0 -0
  238. xfintech/data/source/tushare/stock/techindex/tests/test_class_techindex_all.py +576 -0
  239. xfintech/data/source/tushare/stock/tradedate/__init__.py +19 -0
  240. xfintech/data/source/tushare/stock/tradedate/constant.py +93 -0
  241. xfintech/data/source/tushare/stock/tradedate/tests/__init__.py +0 -0
  242. xfintech/data/source/tushare/stock/tradedate/tests/test_class_tradedate_all.py +947 -0
  243. xfintech/data/source/tushare/stock/tradedate/tradedate.py +234 -0
  244. xfintech/data/source/tushare/stock/weekline/__init__.py +19 -0
  245. xfintech/data/source/tushare/stock/weekline/constant.py +87 -0
  246. xfintech/data/source/tushare/stock/weekline/tests/__init__.py +0 -0
  247. xfintech/data/source/tushare/stock/weekline/tests/test_class_weekline_all.py +575 -0
  248. xfintech/data/source/tushare/stock/weekline/weekline.py +182 -0
  249. xfintech/fabric/__init__.py +18 -0
  250. xfintech/fabric/column/__init__.py +7 -0
  251. xfintech/fabric/column/info.py +202 -0
  252. xfintech/fabric/column/kind.py +102 -0
  253. xfintech/fabric/column/tests/__init__.py +0 -0
  254. xfintech/fabric/column/tests/test_class_info_all.py +207 -0
  255. xfintech/fabric/column/tests/test_class_kind_all.py +80 -0
  256. xfintech/fabric/table/__init__.py +5 -0
  257. xfintech/fabric/table/info.py +263 -0
  258. xfintech/fabric/table/tests/__init__.py +0 -0
  259. xfintech/fabric/table/tests/test_class_info_all.py +547 -0
  260. xfintech/serde/__init__.py +35 -0
  261. xfintech/serde/common/__init__.py +9 -0
  262. xfintech/serde/common/dataformat.py +78 -0
  263. xfintech/serde/common/deserialiserlike.py +38 -0
  264. xfintech/serde/common/error.py +182 -0
  265. xfintech/serde/common/serialiserlike.py +38 -0
  266. xfintech/serde/common/tests/__init__.py +1 -0
  267. xfintech/serde/common/tests/test_class_dataformat_all.py +694 -0
  268. xfintech/serde/common/tests/test_class_deserialiserlike_all.py +500 -0
  269. xfintech/serde/common/tests/test_class_errors_all.py +518 -0
  270. xfintech/serde/common/tests/test_class_serialiserlike_all.py +401 -0
  271. xfintech/serde/deserialiser/__init__.py +7 -0
  272. xfintech/serde/deserialiser/pandas.py +113 -0
  273. xfintech/serde/deserialiser/python.py +68 -0
  274. xfintech/serde/deserialiser/tests/__init__.py +1 -0
  275. xfintech/serde/deserialiser/tests/test_class_pandasdeserialiser_all.py +503 -0
  276. xfintech/serde/deserialiser/tests/test_class_pythondeserialiser_all.py +570 -0
  277. xfintech/serde/serialiser/__init__.py +7 -0
  278. xfintech/serde/serialiser/pandas.py +116 -0
  279. xfintech/serde/serialiser/python.py +71 -0
  280. xfintech/serde/serialiser/tests/__init__.py +1 -0
  281. xfintech/serde/serialiser/tests/test_class_pandasserialiser_all.py +474 -0
  282. xfintech/serde/serialiser/tests/test_class_pythonserialiser_all.py +508 -0
@@ -0,0 +1,750 @@
1
+ from unittest.mock import MagicMock, patch
2
+
3
+ import pandas as pd
4
+ import pytest
5
+
6
+ from xfintech.data.common.cache import Cache
7
+ from xfintech.data.common.coolant import Coolant
8
+ from xfintech.data.common.paginate import Paginate
9
+ from xfintech.data.common.params import Params
10
+ from xfintech.data.common.retry import Retry
11
+ from xfintech.data.source.tushare.session.session import Session
12
+ from xfintech.data.source.tushare.stock.stockipo.constant import (
13
+ KEY,
14
+ NAME,
15
+ PAGINATE,
16
+ SOURCE,
17
+ TARGET,
18
+ )
19
+ from xfintech.data.source.tushare.stock.stockipo.stockipo import StockIpo
20
+
21
+ # ============================================================================
22
+ # Fixtures
23
+ # ============================================================================
24
+
25
+
26
+ @pytest.fixture
27
+ def mock_session():
28
+ """Create a mock Tushare session"""
29
+ session = MagicMock(spec=Session)
30
+ session._credential = "test_token"
31
+ session.id = "test1234"
32
+ session.mode = "direct"
33
+ session.relay_url = None
34
+ session.relay_secret = None
35
+ session.connected = True
36
+
37
+ # Mock the connection object
38
+ mock_connection = MagicMock()
39
+ mock_connection.new_share = MagicMock()
40
+ session.connection = mock_connection
41
+
42
+ return session
43
+
44
+
45
+ @pytest.fixture
46
+ def sample_source_data():
47
+ """Create sample source data in Tushare format"""
48
+ return pd.DataFrame(
49
+ {
50
+ "ts_code": ["688001.SH", "688002.SH", "688003.SH"],
51
+ "sub_code": ["787001", "787002", "787003"],
52
+ "name": ["华兴源创", "睿创微纳", "天准科技"],
53
+ "ipo_date": ["20190627", "20190628", "20190710"],
54
+ "issue_date": ["20190710", "20190712", "20190722"],
55
+ "amount": [40100.0, 60000.0, 45360.0],
56
+ "market_amount": [10025.0, 15000.0, 11340.0],
57
+ "price": [24.26, 20.00, 25.50],
58
+ "pe": [41.08, 79.09, 58.62],
59
+ "limit_amount": [10.025, 15.0, 11.34],
60
+ "funds": [9.73, 12.0, 11.57],
61
+ "ballot": [0.0424, 0.0632, 0.0587],
62
+ }
63
+ )
64
+
65
+
66
+ @pytest.fixture
67
+ def expected_transformed_data():
68
+ """Create expected transformed data"""
69
+ return pd.DataFrame(
70
+ {
71
+ "code": ["688001.SH", "688002.SH", "688003.SH"],
72
+ "sub_code": ["787001", "787002", "787003"],
73
+ "name": ["华兴源创", "睿创微纳", "天准科技"],
74
+ "ipo_date": ["2019-06-27", "2019-06-28", "2019-07-10"],
75
+ "ipo_datecode": ["20190627", "20190628", "20190710"],
76
+ "issue_date": ["2019-07-10", "2019-07-12", "2019-07-22"],
77
+ "issue_datecode": ["20190710", "20190712", "20190722"],
78
+ "amount": [40100.0, 60000.0, 45360.0],
79
+ "market_amount": [10025.0, 15000.0, 11340.0],
80
+ "price": [24.26, 20.00, 25.50],
81
+ "pe": [41.08, 79.09, 58.62],
82
+ "limit_amount": [10.025, 15.0, 11.34],
83
+ "funds": [9.73, 12.0, 11.57],
84
+ "ballot": [0.0424, 0.0632, 0.0587],
85
+ }
86
+ )
87
+
88
+
89
+ # ============================================================================
90
+ # Initialization Tests
91
+ # ============================================================================
92
+
93
+
94
+ def test_ipo_init_basic(mock_session):
95
+ """Test StockIpo initialization with minimal parameters"""
96
+ ipo = StockIpo(session=mock_session)
97
+
98
+ assert ipo.name == NAME
99
+ assert ipo.key == KEY
100
+ assert ipo.source == SOURCE
101
+ assert ipo.target == TARGET
102
+ assert isinstance(ipo.params, Params)
103
+ assert isinstance(ipo.coolant, Coolant)
104
+ assert isinstance(ipo.paginate, Paginate)
105
+ assert isinstance(ipo.retry, Retry)
106
+
107
+
108
+ def test_ipo_init_with_params_dict(mock_session):
109
+ """Test StockIpo initialization with params as dict"""
110
+ params = {"start_date": "20230101", "end_date": "20231231"}
111
+ ipo = StockIpo(session=mock_session, params=params)
112
+
113
+ assert ipo.params.start_date == "20230101"
114
+ assert ipo.params.end_date == "20231231"
115
+
116
+
117
+ def test_ipo_init_with_params_object(mock_session):
118
+ """Test StockIpo initialization with Params object"""
119
+ params = Params(year="2023")
120
+ ipo = StockIpo(session=mock_session, params=params)
121
+
122
+ assert ipo.params.year == "2023"
123
+
124
+
125
+ def test_ipo_init_with_year_param(mock_session):
126
+ """Test StockIpo initialization with year parameter"""
127
+ ipo = StockIpo(session=mock_session, params={"year": "2023"})
128
+
129
+ assert ipo.params.year == "2023"
130
+
131
+
132
+ def test_ipo_init_with_trade_date_param(mock_session):
133
+ """Test StockIpo initialization with trade_date parameter"""
134
+ ipo = StockIpo(session=mock_session, params={"trade_date": "20230315"})
135
+
136
+ assert ipo.params.trade_date == "20230315"
137
+
138
+
139
+ def test_ipo_init_with_cache_bool_true(mock_session):
140
+ """Test StockIpo initialization with cache=True"""
141
+ ipo = StockIpo(session=mock_session, cache=True)
142
+
143
+ assert ipo.cache is not None
144
+ assert isinstance(ipo.cache, Cache)
145
+
146
+
147
+ def test_ipo_init_with_cache_bool_false(mock_session):
148
+ """Test StockIpo initialization with cache=False"""
149
+ ipo = StockIpo(session=mock_session, cache=False)
150
+
151
+ assert ipo.cache is None
152
+
153
+
154
+ def test_ipo_init_with_cache_dict(mock_session):
155
+ """Test StockIpo initialization with cache as dict"""
156
+ cache_config = {"directory": "/tmp/cache"}
157
+ ipo = StockIpo(session=mock_session, cache=cache_config)
158
+
159
+ assert ipo.cache is not None
160
+ assert isinstance(ipo.cache, Cache)
161
+
162
+
163
+ def test_ipo_init_default_paginate_limit(mock_session):
164
+ """Test StockIpo sets default paginate pagesize to 2000 and pagelimit to 5"""
165
+ ipo = StockIpo(session=mock_session)
166
+
167
+ assert ipo.paginate.pagesize == PAGINATE.pagesize
168
+ assert ipo.paginate.pagelimit == PAGINATE.pagelimit
169
+
170
+
171
+ def test_ipo_init_with_all_params(mock_session):
172
+ """Test StockIpo initialization with all parameters"""
173
+ ipo = StockIpo(
174
+ session=mock_session,
175
+ params={"year": "2023"},
176
+ coolant={"interval": 1.0},
177
+ retry={"max_retries": 3},
178
+ cache=True,
179
+ )
180
+
181
+ assert ipo.name == NAME
182
+ assert ipo.params.year == "2023"
183
+ assert ipo.cache is not None
184
+ assert ipo.paginate.pagesize == PAGINATE.pagesize
185
+ assert ipo.paginate.pagelimit == PAGINATE.pagelimit
186
+
187
+
188
+ def test_ipo_constants():
189
+ """Test that constants are properly defined"""
190
+ assert NAME == "stockipo"
191
+ assert KEY == "/tushare/stockipo"
192
+ assert SOURCE is not None
193
+ assert TARGET is not None
194
+
195
+
196
+ # ============================================================================
197
+ # Transform Method Tests
198
+ # ============================================================================
199
+
200
+
201
+ def test_ipo_transform_basic(mock_session, sample_source_data):
202
+ """Test basic data transformation"""
203
+ ipo = StockIpo(session=mock_session)
204
+ result = ipo.transform(sample_source_data)
205
+
206
+ assert not result.empty
207
+ assert len(result) == 3
208
+ assert "code" in result.columns
209
+ assert "name" in result.columns
210
+ assert "ipo_date" in result.columns
211
+
212
+
213
+ def test_ipo_transform_code_mapping(mock_session, sample_source_data):
214
+ """Test that ts_code is mapped to code"""
215
+ ipo = StockIpo(session=mock_session)
216
+ result = ipo.transform(sample_source_data)
217
+
218
+ assert result["code"].tolist() == ["688001.SH", "688002.SH", "688003.SH"]
219
+
220
+
221
+ def test_ipo_transform_name_mapping(mock_session, sample_source_data):
222
+ """Test that name is preserved"""
223
+ ipo = StockIpo(session=mock_session)
224
+ result = ipo.transform(sample_source_data)
225
+
226
+ assert "华兴源创" in result["name"].values
227
+
228
+
229
+ def test_ipo_transform_ipo_date_format(mock_session, sample_source_data):
230
+ """Test that ipo_date is converted from YYYYMMDD to YYYY-MM-DD"""
231
+ ipo = StockIpo(session=mock_session)
232
+ result = ipo.transform(sample_source_data)
233
+
234
+ assert result["ipo_date"].tolist() == ["2019-06-27", "2019-06-28", "2019-07-10"]
235
+
236
+
237
+ def test_ipo_transform_ipo_datecode_preserved(mock_session, sample_source_data):
238
+ """Test that ipo_datecode preserves original format"""
239
+ ipo = StockIpo(session=mock_session)
240
+ result = ipo.transform(sample_source_data)
241
+
242
+ assert result["ipo_datecode"].tolist() == ["20190627", "20190628", "20190710"]
243
+
244
+
245
+ def test_ipo_transform_issue_date_format(mock_session, sample_source_data):
246
+ """Test that issue_date is converted from YYYYMMDD to YYYY-MM-DD"""
247
+ ipo = StockIpo(session=mock_session)
248
+ result = ipo.transform(sample_source_data)
249
+
250
+ assert result["issue_date"].tolist() == ["2019-07-10", "2019-07-12", "2019-07-22"]
251
+
252
+
253
+ def test_ipo_transform_issue_datecode_preserved(mock_session, sample_source_data):
254
+ """Test that issue_datecode preserves original format"""
255
+ ipo = StockIpo(session=mock_session)
256
+ result = ipo.transform(sample_source_data)
257
+
258
+ assert result["issue_datecode"].tolist() == ["20190710", "20190712", "20190722"]
259
+
260
+
261
+ def test_ipo_transform_numeric_conversions(mock_session, sample_source_data):
262
+ """Test numeric field conversions"""
263
+ ipo = StockIpo(session=mock_session)
264
+ result = ipo.transform(sample_source_data)
265
+
266
+ assert result["amount"].dtype in [float, "float64"]
267
+ assert result["price"].dtype in [float, "float64"]
268
+ assert result["pe"].dtype in [float, "float64"]
269
+
270
+
271
+ def test_ipo_transform_empty_dataframe(mock_session):
272
+ """Test transform with empty DataFrame"""
273
+ ipo = StockIpo(session=mock_session)
274
+ empty_df = pd.DataFrame()
275
+ result = ipo.transform(empty_df)
276
+
277
+ assert result.empty
278
+ assert list(result.columns) == TARGET.list_column_names()
279
+
280
+
281
+ def test_ipo_transform_none_input(mock_session):
282
+ """Test transform with None input"""
283
+ ipo = StockIpo(session=mock_session)
284
+ result = ipo.transform(None)
285
+
286
+ assert result.empty
287
+ assert list(result.columns) == TARGET.list_column_names()
288
+
289
+
290
+ def test_ipo_transform_handles_invalid_dates(mock_session):
291
+ """Test transform handles invalid date formats"""
292
+ ipo = StockIpo(session=mock_session)
293
+ data = pd.DataFrame(
294
+ {
295
+ "ts_code": ["688001.SH"],
296
+ "sub_code": ["787001"],
297
+ "name": ["Test"],
298
+ "ipo_date": ["invalid"],
299
+ "issue_date": ["invalid"],
300
+ "amount": [1000.0],
301
+ "market_amount": [500.0],
302
+ "price": [10.0],
303
+ "pe": [20.0],
304
+ "limit_amount": [5.0],
305
+ "funds": [1.0],
306
+ "ballot": [0.05],
307
+ }
308
+ )
309
+
310
+ result = ipo.transform(data)
311
+ assert pd.isna(result["ipo_date"].iloc[0]) or result["ipo_date"].iloc[0] == "NaT"
312
+
313
+
314
+ def test_ipo_transform_removes_duplicates(mock_session):
315
+ """Test that transform removes duplicate rows"""
316
+ ipo = StockIpo(session=mock_session)
317
+ data = pd.DataFrame(
318
+ {
319
+ "ts_code": ["688001.SH", "688001.SH"], # Duplicate
320
+ "sub_code": ["787001", "787001"],
321
+ "name": ["Test", "Test"],
322
+ "ipo_date": ["20190627", "20190627"],
323
+ "issue_date": ["20190710", "20190710"],
324
+ "amount": [1000.0, 1000.0],
325
+ "market_amount": [500.0, 500.0],
326
+ "price": [10.0, 10.0],
327
+ "pe": [20.0, 20.0],
328
+ "limit_amount": [5.0, 5.0],
329
+ "funds": [1.0, 1.0],
330
+ "ballot": [0.05, 0.05],
331
+ }
332
+ )
333
+
334
+ result = ipo.transform(data)
335
+ assert len(result) == 1
336
+
337
+
338
+ def test_ipo_transform_sorts_by_code(mock_session, sample_source_data):
339
+ """Test that result is sorted by code"""
340
+ ipo = StockIpo(session=mock_session)
341
+ shuffled = sample_source_data.sample(frac=1).reset_index(drop=True)
342
+ result = ipo.transform(shuffled)
343
+
344
+ assert result["code"].tolist() == sorted(result["code"].tolist())
345
+
346
+
347
+ def test_ipo_transform_resets_index(mock_session, sample_source_data):
348
+ """Test that result has reset index"""
349
+ ipo = StockIpo(session=mock_session)
350
+ result = ipo.transform(sample_source_data)
351
+
352
+ assert result.index.tolist() == list(range(len(result)))
353
+
354
+
355
+ def test_ipo_transform_only_target_columns(mock_session, sample_source_data):
356
+ """Test that only target columns are in result"""
357
+ ipo = StockIpo(session=mock_session)
358
+ result = ipo.transform(sample_source_data)
359
+
360
+ expected_cols = set(TARGET.list_column_names())
361
+ actual_cols = set(result.columns)
362
+ assert actual_cols == expected_cols
363
+
364
+
365
+ # ============================================================================
366
+ # _run Method Tests
367
+ # ============================================================================
368
+
369
+
370
+ def test_ipo_run_with_cache_hit(mock_session):
371
+ """Test _run returns cached data when available"""
372
+ ipo = StockIpo(session=mock_session, cache=True)
373
+
374
+ cached_df = pd.DataFrame({"code": ["688001.SH"]})
375
+ ipo.cache.set(ipo.params.identifier, cached_df)
376
+
377
+ result = ipo._run()
378
+
379
+ assert result.equals(cached_df)
380
+
381
+
382
+ def test_ipo_run_basic_date_range(mock_session, sample_source_data):
383
+ """Test _run with start_date and end_date"""
384
+ ipo = StockIpo(session=mock_session, params={"start_date": "20190101", "end_date": "20191231"})
385
+
386
+ with patch.object(ipo, "_fetchall", return_value=sample_source_data):
387
+ result = ipo._run()
388
+
389
+ assert not result.empty
390
+ ipo._fetchall.assert_called_once()
391
+ call_kwargs = ipo._fetchall.call_args[1]
392
+ assert call_kwargs["start_date"] == "20190101"
393
+ assert call_kwargs["end_date"] == "20191231"
394
+
395
+
396
+ def test_ipo_run_with_year_param(mock_session, sample_source_data):
397
+ """Test _run converts year to start/end date"""
398
+ ipo = StockIpo(session=mock_session, params={"year": "2023"})
399
+
400
+ with patch.object(ipo, "_fetchall", return_value=sample_source_data):
401
+ ipo._run()
402
+
403
+ call_kwargs = ipo._fetchall.call_args[1]
404
+ assert call_kwargs["start_date"] == "20230101"
405
+ assert call_kwargs["end_date"] == "20231231"
406
+ assert "year" not in call_kwargs
407
+
408
+
409
+ def test_ipo_run_with_year_param_int(mock_session, sample_source_data):
410
+ """Test _run handles year as integer"""
411
+ ipo = StockIpo(session=mock_session, params={"year": 2023})
412
+
413
+ with patch.object(ipo, "_fetchall", return_value=sample_source_data):
414
+ ipo._run()
415
+
416
+ call_kwargs = ipo._fetchall.call_args[1]
417
+ assert call_kwargs["start_date"] == "20230101"
418
+ assert call_kwargs["end_date"] == "20231231"
419
+
420
+
421
+ def test_ipo_run_adds_fields_param(mock_session, sample_source_data):
422
+ """Test _run adds fields parameter if not provided"""
423
+ ipo = StockIpo(session=mock_session)
424
+
425
+ with patch.object(ipo, "_fetchall", return_value=sample_source_data):
426
+ ipo._run()
427
+
428
+ call_kwargs = ipo._fetchall.call_args[1]
429
+ assert "fields" in call_kwargs
430
+
431
+
432
+ def test_ipo_run_preserves_fields_param(mock_session, sample_source_data):
433
+ """Test _run preserves existing fields parameter"""
434
+ custom_fields = "ts_code,name,ipo_date"
435
+ ipo = StockIpo(session=mock_session, params={"fields": custom_fields})
436
+
437
+ with patch.object(ipo, "_fetchall", return_value=sample_source_data):
438
+ ipo._run()
439
+
440
+ # Fields should be in the call
441
+ call_kwargs = ipo._fetchall.call_args[1]
442
+ assert "fields" in call_kwargs
443
+
444
+
445
+ def test_ipo_run_sets_cache(mock_session, sample_source_data):
446
+ """Test _run saves result to cache"""
447
+ ipo = StockIpo(session=mock_session, cache=True)
448
+
449
+ with patch.object(ipo, "_fetchall", return_value=sample_source_data):
450
+ ipo._run()
451
+
452
+ cached = ipo.cache.get(ipo.params.identifier)
453
+ assert cached is not None
454
+
455
+
456
+ def test_ipo_run_calls_transform(mock_session, sample_source_data):
457
+ """Test _run calls transform method"""
458
+ ipo = StockIpo(session=mock_session)
459
+
460
+ with patch.object(ipo, "_fetchall", return_value=sample_source_data):
461
+ with patch.object(ipo, "transform", return_value=sample_source_data) as mock_transform:
462
+ ipo._run()
463
+
464
+ mock_transform.assert_called_once()
465
+
466
+
467
+ # ============================================================================
468
+ # list_codes Method Tests
469
+ # ============================================================================
470
+
471
+
472
+ def test_ipo_list_codes_basic(mock_session, sample_source_data):
473
+ """Test list_codes returns list of IPO codes"""
474
+ ipo = StockIpo(session=mock_session, cache=True)
475
+
476
+ transformed = ipo.transform(sample_source_data)
477
+ ipo.cache.set(ipo.params.identifier, transformed)
478
+
479
+ codes = ipo.list_codes()
480
+
481
+ assert isinstance(codes, list)
482
+ assert len(codes) == 3
483
+ assert "688001.SH" in codes
484
+
485
+
486
+ def test_ipo_list_codes_unique(mock_session):
487
+ """Test list_codes returns unique codes"""
488
+ ipo = StockIpo(session=mock_session, cache=True)
489
+
490
+ df = pd.DataFrame(
491
+ {
492
+ "code": ["688001.SH", "688001.SH", "688002.SH"],
493
+ }
494
+ )
495
+ ipo.cache.set(ipo.params.identifier, df)
496
+
497
+ codes = ipo.list_codes()
498
+
499
+ assert len(codes) == 2
500
+
501
+
502
+ def test_ipo_list_codes_sorted(mock_session, sample_source_data):
503
+ """Test list_codes returns sorted list"""
504
+ ipo = StockIpo(session=mock_session, cache=True)
505
+
506
+ transformed = ipo.transform(sample_source_data)
507
+ ipo.cache.set(ipo.params.identifier, transformed)
508
+
509
+ codes = ipo.list_codes()
510
+
511
+ assert codes == sorted(codes)
512
+
513
+
514
+ def test_ipo_list_codes_calls_run_when_not_cached(mock_session, sample_source_data):
515
+ """Test list_codes calls run() when data not in cache"""
516
+ ipo = StockIpo(session=mock_session)
517
+
518
+ with patch.object(ipo, "run", return_value=ipo.transform(sample_source_data)):
519
+ codes = ipo.list_codes()
520
+
521
+ ipo.run.assert_called_once()
522
+ assert len(codes) == 3
523
+
524
+
525
+ # ============================================================================
526
+ # list_names Method Tests
527
+ # ============================================================================
528
+
529
+
530
+ def test_ipo_list_names_basic(mock_session, sample_source_data):
531
+ """Test list_names returns list of IPO names"""
532
+ ipo = StockIpo(session=mock_session, cache=True)
533
+
534
+ transformed = ipo.transform(sample_source_data)
535
+ ipo.cache.set(ipo.params.identifier, transformed)
536
+
537
+ names = ipo.list_names()
538
+
539
+ assert isinstance(names, list)
540
+ assert len(names) == 3
541
+ assert "华兴源创" in names
542
+
543
+
544
+ def test_ipo_list_names_sorted(mock_session, sample_source_data):
545
+ """Test list_names returns sorted list"""
546
+ ipo = StockIpo(session=mock_session, cache=True)
547
+
548
+ transformed = ipo.transform(sample_source_data)
549
+ ipo.cache.set(ipo.params.identifier, transformed)
550
+
551
+ names = ipo.list_names()
552
+
553
+ assert names == sorted(names)
554
+
555
+
556
+ def test_ipo_list_names_unique(mock_session):
557
+ """Test list_names returns unique names"""
558
+ ipo = StockIpo(session=mock_session, cache=True)
559
+
560
+ df = pd.DataFrame(
561
+ {
562
+ "name": ["华兴源创", "华兴源创", "睿创微纳"],
563
+ }
564
+ )
565
+ ipo.cache.set(ipo.params.identifier, df)
566
+
567
+ names = ipo.list_names()
568
+
569
+ assert len(names) == 2
570
+
571
+
572
+ def test_ipo_list_names_calls_run_when_not_cached(mock_session, sample_source_data):
573
+ """Test list_names calls run() when data not in cache"""
574
+ ipo = StockIpo(session=mock_session)
575
+
576
+ with patch.object(ipo, "run", return_value=ipo.transform(sample_source_data)):
577
+ names = ipo.list_names()
578
+
579
+ ipo.run.assert_called_once()
580
+ assert len(names) == 3
581
+
582
+
583
+ # ============================================================================
584
+ # Integration Tests
585
+ # ============================================================================
586
+
587
+
588
+ def test_ipo_full_workflow(mock_session, sample_source_data):
589
+ """Test complete workflow from initialization to data retrieval"""
590
+ ipo = StockIpo(
591
+ session=mock_session,
592
+ params={"year": "2019"},
593
+ cache=True,
594
+ )
595
+
596
+ with patch.object(ipo, "_fetchall", return_value=sample_source_data):
597
+ result = ipo.run()
598
+
599
+ assert not result.empty
600
+ assert "code" in result.columns
601
+
602
+ codes = ipo.list_codes()
603
+ names = ipo.list_names()
604
+
605
+ assert len(codes) > 0
606
+ assert len(names) > 0
607
+
608
+
609
+ def test_ipo_cache_persistence(mock_session, sample_source_data):
610
+ """Test that cache persists across method calls"""
611
+ ipo = StockIpo(session=mock_session, cache=True)
612
+
613
+ with patch.object(ipo, "_load_cache", return_value=None) as mock_load:
614
+ with patch.object(ipo, "_fetchall", return_value=sample_source_data) as mock_fetch:
615
+ # First run - fetches data and caches it
616
+ result1 = ipo.run()
617
+ assert mock_fetch.call_count == 1
618
+ assert mock_load.call_count == 1
619
+
620
+ # Second run - _load_cache still returns None, so _fetchall called again
621
+ result2 = ipo.run()
622
+ assert mock_fetch.call_count == 2 # Called again
623
+ assert mock_load.call_count == 2
624
+
625
+ pd.testing.assert_frame_equal(result1, result2)
626
+
627
+
628
+ def test_ipo_params_identifier_uniqueness(mock_session):
629
+ """Test that different params produce different cache keys"""
630
+ ipo1 = StockIpo(session=mock_session, params={"year": "2022"}, cache=True)
631
+ ipo2 = StockIpo(session=mock_session, params={"year": "2023"}, cache=True)
632
+
633
+ assert ipo1.params.identifier != ipo2.params.identifier
634
+
635
+
636
+ # ============================================================================
637
+ # Edge Case Tests
638
+ # ============================================================================
639
+
640
+
641
+ def test_ipo_empty_result_handling(mock_session):
642
+ """Test handling of empty API results"""
643
+ ipo = StockIpo(session=mock_session)
644
+
645
+ empty_df = pd.DataFrame()
646
+ with patch.object(ipo, "_fetchall", return_value=empty_df):
647
+ result = ipo._run()
648
+
649
+ assert result.empty
650
+ assert list(result.columns) == TARGET.list_column_names()
651
+
652
+
653
+ def test_ipo_large_dataset_handling(mock_session):
654
+ """Test handling of large datasets"""
655
+ ipo = StockIpo(session=mock_session)
656
+
657
+ large_data = pd.DataFrame(
658
+ {
659
+ "ts_code": [f"{i:06d}.SH" for i in range(2000)],
660
+ "sub_code": [f"78{i:04d}" for i in range(2000)],
661
+ "name": [f"Stock {i}" for i in range(2000)],
662
+ "ipo_date": ["20230101"] * 2000,
663
+ "issue_date": ["20230115"] * 2000,
664
+ "amount": [1000.0] * 2000,
665
+ "market_amount": [500.0] * 2000,
666
+ "price": [10.0] * 2000,
667
+ "pe": [20.0] * 2000,
668
+ "limit_amount": [5.0] * 2000,
669
+ "funds": [1.0] * 2000,
670
+ "ballot": [0.05] * 2000,
671
+ }
672
+ )
673
+
674
+ result = ipo.transform(large_data)
675
+
676
+ assert len(result) == 2000
677
+ assert not result.empty
678
+
679
+
680
+ def test_ipo_special_characters_in_data(mock_session):
681
+ """Test handling of special characters in IPO data"""
682
+ ipo = StockIpo(session=mock_session)
683
+
684
+ data = pd.DataFrame(
685
+ {
686
+ "ts_code": ["688001.SH"],
687
+ "sub_code": ["787001"],
688
+ "name": ["股票名称(中文)& Special <Chars>"],
689
+ "ipo_date": ["20230101"],
690
+ "issue_date": ["20230115"],
691
+ "amount": [1000.0],
692
+ "market_amount": [500.0],
693
+ "price": [10.0],
694
+ "pe": [20.0],
695
+ "limit_amount": [5.0],
696
+ "funds": [1.0],
697
+ "ballot": [0.05],
698
+ }
699
+ )
700
+
701
+ result = ipo.transform(data)
702
+
703
+ assert len(result) == 1
704
+ assert "股" in result["name"].values[0] or "Special" in result["name"].values[0]
705
+
706
+
707
+ def test_ipo_without_cache(mock_session, sample_source_data):
708
+ """Test StockIpo works correctly without cache"""
709
+ ipo = StockIpo(session=mock_session, cache=False)
710
+
711
+ assert ipo.cache is None
712
+
713
+ with patch.object(ipo, "_fetchall", return_value=sample_source_data):
714
+ result = ipo.run()
715
+
716
+ assert not result.empty
717
+
718
+ codes = ipo.list_codes()
719
+ names = ipo.list_names()
720
+
721
+ assert len(codes) > 0
722
+ assert len(names) > 0
723
+
724
+
725
+ def test_ipo_missing_numeric_values(mock_session):
726
+ """Test handling of missing numeric values"""
727
+ ipo = StockIpo(session=mock_session)
728
+
729
+ data = pd.DataFrame(
730
+ {
731
+ "ts_code": ["688001.SH"],
732
+ "sub_code": ["787001"],
733
+ "name": ["Test Stock"],
734
+ "ipo_date": ["20230101"],
735
+ "issue_date": ["20230115"],
736
+ "amount": [None],
737
+ "market_amount": [None],
738
+ "price": [None],
739
+ "pe": [None],
740
+ "limit_amount": [None],
741
+ "funds": [None],
742
+ "ballot": [None],
743
+ }
744
+ )
745
+
746
+ result = ipo.transform(data)
747
+
748
+ assert len(result) == 1
749
+ assert pd.isna(result["amount"].iloc[0])
750
+ assert pd.isna(result["price"].iloc[0])