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,589 @@
1
+ """
2
+ Test suite for TushareJob class
3
+ Tests cover initialization, connection resolution, pagination, and data fetching
4
+ """
5
+
6
+ from unittest.mock import Mock, patch
7
+
8
+ import pandas as pd
9
+ import pytest
10
+
11
+ from xfintech.data.common.cache import Cache
12
+ from xfintech.data.common.coolant import Coolant
13
+ from xfintech.data.common.paginate import Paginate
14
+ from xfintech.data.common.params import Params
15
+ from xfintech.data.common.retry import Retry
16
+ from xfintech.data.source.tushare.job.job import TushareJob
17
+ from xfintech.data.source.tushare.session.session import Session
18
+
19
+ # ============================================================================
20
+ # Helper Classes for Testing
21
+ # ============================================================================
22
+
23
+
24
+ class ConcreteTushareJob(TushareJob):
25
+ """Concrete implementation for testing"""
26
+
27
+ def _run(self) -> pd.DataFrame:
28
+ return pd.DataFrame({"test": [1, 2, 3]})
29
+
30
+
31
+ class FetchAllJob(TushareJob):
32
+ """Job that uses _fetchall in _run"""
33
+
34
+ def _run(self) -> pd.DataFrame:
35
+ api = Mock(return_value=pd.DataFrame({"data": [1, 2, 3]}))
36
+ return self._fetchall(api, symbol="TEST")
37
+
38
+
39
+ # ============================================================================
40
+ # Initialization Tests
41
+ # ============================================================================
42
+
43
+
44
+ def test_tusharejob_init_basic():
45
+ """Test TushareJob basic initialization"""
46
+ mock_session = Mock(spec=Session)
47
+ mock_session.connection = Mock()
48
+
49
+ job = ConcreteTushareJob(name="test_job", key="test_key", session=mock_session)
50
+
51
+ assert job.name == "test_job"
52
+ assert job.key == "test_key"
53
+ assert job.connection is not None
54
+ assert isinstance(job.params, Params)
55
+ assert isinstance(job.coolant, Coolant)
56
+ assert isinstance(job.paginate, Paginate)
57
+ assert isinstance(job.retry, Retry)
58
+
59
+
60
+ def test_tusharejob_init_with_params():
61
+ """Test TushareJob initialization with params dict"""
62
+ mock_session = Mock(spec=Session)
63
+ mock_session.connection = Mock()
64
+
65
+ job = ConcreteTushareJob(
66
+ name="test_job", key="test_key", session=mock_session, params={"symbol": "AAPL", "date": "20240101"}
67
+ )
68
+
69
+ assert job.params.to_dict()["symbol"] == "AAPL"
70
+ assert job.params.to_dict()["date"] == "20240101"
71
+
72
+
73
+ def test_tusharejob_init_with_params_object():
74
+ """Test TushareJob initialization with Params object"""
75
+ mock_session = Mock(spec=Session)
76
+ mock_session.connection = Mock()
77
+ params = Params(symbol="TSLA", exchange="NASDAQ")
78
+
79
+ job = ConcreteTushareJob(name="test_job", key="test_key", session=mock_session, params=params)
80
+
81
+ assert job.params is params
82
+
83
+
84
+ def test_tusharejob_init_with_coolant_dict():
85
+ """Test TushareJob initialization with coolant dict"""
86
+ mock_session = Mock(spec=Session)
87
+ mock_session.connection = Mock()
88
+
89
+ job = ConcreteTushareJob(
90
+ name="test_job", key="test_key", session=mock_session, coolant={"interval": 2, "use_jitter": True}
91
+ )
92
+
93
+ assert job.coolant.interval == 2
94
+ assert job.coolant.use_jitter is True
95
+
96
+
97
+ def test_tusharejob_init_with_paginate_dict():
98
+ """Test TushareJob initialization with paginate dict"""
99
+ mock_session = Mock(spec=Session)
100
+ mock_session.connection = Mock()
101
+
102
+ job = ConcreteTushareJob(
103
+ name="test_job", key="test_key", session=mock_session, paginate={"pagesize": 500, "pagelimit": 5}
104
+ )
105
+
106
+ assert job.paginate.pagesize == 500
107
+ assert job.paginate.pagelimit == 5
108
+
109
+
110
+ def test_tusharejob_init_with_retry_dict():
111
+ """Test TushareJob initialization with retry dict"""
112
+ mock_session = Mock(spec=Session)
113
+ mock_session.connection = Mock()
114
+
115
+ job = ConcreteTushareJob(name="test_job", key="test_key", session=mock_session, retry={"retry": 5, "wait": 2})
116
+
117
+ assert job.retry.retry == 5
118
+ assert job.retry.wait == 2
119
+
120
+
121
+ def test_tusharejob_init_with_cache_true():
122
+ """Test TushareJob initialization with cache enabled"""
123
+ mock_session = Mock(spec=Session)
124
+ mock_session.connection = Mock()
125
+
126
+ job = ConcreteTushareJob(name="test_job", key="test_key", session=mock_session, cache=True)
127
+
128
+ assert job.cache is not None
129
+ assert isinstance(job.cache, Cache)
130
+
131
+
132
+ def test_tusharejob_init_with_cache_false():
133
+ """Test TushareJob initialization with cache disabled"""
134
+ mock_session = Mock(spec=Session)
135
+ mock_session.connection = Mock()
136
+
137
+ job = ConcreteTushareJob(name="test_job", key="test_key", session=mock_session, cache=False)
138
+
139
+ assert job.cache is None
140
+
141
+
142
+ def test_tusharejob_init_marks_checkpoints():
143
+ """Test TushareJob marks initialization checkpoints"""
144
+ mock_session = Mock(spec=Session)
145
+ mock_session.connection = Mock()
146
+
147
+ job = ConcreteTushareJob(name="test_job", key="test_key", session=mock_session)
148
+
149
+ # Should have marked both init[OK] and resolve_connection
150
+ marks = job.metric.marks
151
+ assert "init[OK]" in marks
152
+ assert "_resolve_connection[OK]" in marks
153
+
154
+
155
+ # ============================================================================
156
+ # Connection Resolution Tests
157
+ # ============================================================================
158
+
159
+
160
+ def test_resolve_connection_success():
161
+ """Test _resolve_connection with valid session"""
162
+ mock_session = Mock(spec=Session)
163
+ mock_connection = Mock()
164
+ mock_session.connection = mock_connection
165
+
166
+ job = ConcreteTushareJob(name="test_job", key="test_key", session=mock_session)
167
+
168
+ assert job.connection is mock_connection
169
+
170
+
171
+ def test_resolve_connection_no_connection_raises_error():
172
+ """Test _resolve_connection raises error when session has no connection"""
173
+ mock_session = Mock(spec=Session)
174
+ mock_session.connection = None
175
+
176
+ with pytest.raises(ConnectionError, match="No active connection found in session"):
177
+ ConcreteTushareJob(name="test_job", key="test_key", session=mock_session)
178
+
179
+
180
+ def test_resolve_connection_missing_attribute():
181
+ """Test _resolve_connection handles missing connection attribute"""
182
+ mock_session = Mock(spec=Session)
183
+ delattr(mock_session, "connection")
184
+
185
+ with pytest.raises(ConnectionError):
186
+ ConcreteTushareJob(name="test_job", key="test_key", session=mock_session)
187
+
188
+
189
+ def test_resolve_connection_marks_checkpoint():
190
+ """Test _resolve_connection marks checkpoint on success"""
191
+ mock_session = Mock(spec=Session)
192
+ mock_session.connection = Mock()
193
+
194
+ job = ConcreteTushareJob(name="test_job", key="test_key", session=mock_session)
195
+
196
+ assert "_resolve_connection[OK]" in job.metric.marks
197
+
198
+
199
+ # ============================================================================
200
+ # _fetchall Method Tests
201
+ # ============================================================================
202
+
203
+
204
+ def test_fetchall_single_page():
205
+ """Test _fetchall with data fitting in single page"""
206
+ mock_session = Mock(spec=Session)
207
+ mock_session.connection = Mock()
208
+
209
+ job = ConcreteTushareJob(name="test_job", key="test_key", session=mock_session, paginate={"pagesize": 100})
210
+
211
+ # Mock API that returns less than pagesize
212
+ mock_api = Mock(return_value=pd.DataFrame({"col": [1, 2, 3]}))
213
+
214
+ result = job._fetchall(mock_api, symbol="TEST")
215
+
216
+ assert isinstance(result, pd.DataFrame)
217
+ assert len(result) == 3
218
+ assert mock_api.call_count == 1
219
+ mock_api.assert_called_with(limit=100, offset=0, symbol="TEST")
220
+
221
+
222
+ def test_fetchall_multiple_pages():
223
+ """Test _fetchall with data spanning multiple pages"""
224
+ mock_session = Mock(spec=Session)
225
+ mock_session.connection = Mock()
226
+
227
+ job = ConcreteTushareJob(
228
+ name="test_job", key="test_key", session=mock_session, paginate={"pagesize": 10, "pagelimit": 5}
229
+ )
230
+
231
+ # Mock API that returns full pages then partial
232
+ mock_api = Mock(
233
+ side_effect=[
234
+ pd.DataFrame({"col": range(10)}), # Full page 1
235
+ pd.DataFrame({"col": range(10, 20)}), # Full page 2
236
+ pd.DataFrame({"col": range(20, 25)}), # Partial page 3
237
+ ]
238
+ )
239
+
240
+ result = job._fetchall(mock_api, symbol="TEST")
241
+
242
+ assert isinstance(result, pd.DataFrame)
243
+ assert len(result) == 25
244
+ assert mock_api.call_count == 3
245
+
246
+
247
+ def test_fetchall_empty_result():
248
+ """Test _fetchall with empty result"""
249
+ mock_session = Mock(spec=Session)
250
+ mock_session.connection = Mock()
251
+
252
+ job = ConcreteTushareJob(name="test_job", key="test_key", session=mock_session)
253
+
254
+ mock_api = Mock(return_value=pd.DataFrame())
255
+
256
+ result = job._fetchall(mock_api, symbol="TEST")
257
+
258
+ assert isinstance(result, pd.DataFrame)
259
+ assert len(result) == 0
260
+
261
+
262
+ def test_fetchall_none_result():
263
+ """Test _fetchall handles None result"""
264
+ mock_session = Mock(spec=Session)
265
+ mock_session.connection = Mock()
266
+
267
+ job = ConcreteTushareJob(name="test_job", key="test_key", session=mock_session)
268
+
269
+ mock_api = Mock(return_value=None)
270
+
271
+ result = job._fetchall(mock_api, symbol="TEST")
272
+
273
+ assert isinstance(result, pd.DataFrame)
274
+ assert len(result) == 0
275
+
276
+
277
+ def test_fetchall_respects_pagelimit():
278
+ """Test _fetchall respects pagelimit"""
279
+ mock_session = Mock(spec=Session)
280
+ mock_session.connection = Mock()
281
+
282
+ job = ConcreteTushareJob(
283
+ name="test_job", key="test_key", session=mock_session, paginate={"pagesize": 10, "pagelimit": 3}
284
+ )
285
+
286
+ # Mock API that always returns full pages
287
+ mock_api = Mock(return_value=pd.DataFrame({"col": range(10)}))
288
+
289
+ result = job._fetchall(mock_api)
290
+
291
+ # Should stop at pagelimit even though pages are full
292
+ assert mock_api.call_count == 3
293
+ assert len(result) == 30
294
+
295
+
296
+ def test_fetchall_calls_coolant():
297
+ """Test _fetchall calls coolant between pages"""
298
+ mock_session = Mock(spec=Session)
299
+ mock_session.connection = Mock()
300
+
301
+ job = ConcreteTushareJob(
302
+ name="test_job", key="test_key", session=mock_session, paginate={"pagesize": 10}, coolant={"interval": 1}
303
+ )
304
+
305
+ with patch.object(job.coolant, "cool") as mock_cool:
306
+ mock_api = Mock(
307
+ side_effect=[
308
+ pd.DataFrame({"col": range(10)}),
309
+ pd.DataFrame({"col": range(10, 20)}),
310
+ pd.DataFrame({"col": range(20, 25)}),
311
+ ]
312
+ )
313
+
314
+ job._fetchall(mock_api)
315
+
316
+ # cool() should be called between pages (2 times for 3 pages)
317
+ assert mock_cool.call_count == 2
318
+
319
+
320
+ def test_fetchall_updates_pagination_offset():
321
+ """Test _fetchall updates pagination offset correctly"""
322
+ mock_session = Mock(spec=Session)
323
+ mock_session.connection = Mock()
324
+
325
+ job = ConcreteTushareJob(name="test_job", key="test_key", session=mock_session, paginate={"pagesize": 100})
326
+
327
+ mock_api = Mock(
328
+ side_effect=[
329
+ pd.DataFrame({"col": range(100)}),
330
+ pd.DataFrame({"col": range(100, 200)}),
331
+ pd.DataFrame({"col": range(200, 250)}),
332
+ ]
333
+ )
334
+
335
+ job._fetchall(mock_api)
336
+
337
+ # Check that offset was updated in calls
338
+ calls = mock_api.call_args_list
339
+ assert calls[0][1]["offset"] == 0
340
+ assert calls[1][1]["offset"] == 100
341
+ assert calls[2][1]["offset"] == 200
342
+
343
+
344
+ def test_fetchall_marks_checkpoints():
345
+ """Test _fetchall marks checkpoints for each page"""
346
+ mock_session = Mock(spec=Session)
347
+ mock_session.connection = Mock()
348
+
349
+ job = ConcreteTushareJob(name="test_job", key="test_key", session=mock_session, paginate={"pagesize": 10})
350
+
351
+ mock_api = Mock(
352
+ side_effect=[
353
+ pd.DataFrame({"col": range(10)}),
354
+ pd.DataFrame({"col": range(10, 15)}),
355
+ ]
356
+ )
357
+
358
+ job._fetchall(mock_api)
359
+
360
+ marks = job.metric.marks
361
+ assert "_fetchall[pagenum=0, OK]" in marks
362
+ assert "_fetchall[pagenum=1, OK]" in marks
363
+ assert "_fetchall[OK]" in marks
364
+
365
+
366
+ def test_fetchall_passes_params_to_api():
367
+ """Test _fetchall passes all params to API"""
368
+ mock_session = Mock(spec=Session)
369
+ mock_session.connection = Mock()
370
+
371
+ job = ConcreteTushareJob(name="test_job", key="test_key", session=mock_session)
372
+
373
+ mock_api = Mock(return_value=pd.DataFrame({"col": [1, 2, 3]}))
374
+
375
+ job._fetchall(mock_api, symbol="AAPL", exchange="NASDAQ", date="20240101")
376
+
377
+ mock_api.assert_called_once_with(
378
+ limit=job.paginate.pagesize, offset=0, symbol="AAPL", exchange="NASDAQ", date="20240101"
379
+ )
380
+
381
+
382
+ def test_fetchall_concatenates_correctly():
383
+ """Test _fetchall concatenates DataFrames correctly"""
384
+ mock_session = Mock(spec=Session)
385
+ mock_session.connection = Mock()
386
+
387
+ job = ConcreteTushareJob(name="test_job", key="test_key", session=mock_session, paginate={"pagesize": 5})
388
+
389
+ # Full pages then partial to trigger multiple pages
390
+ mock_api = Mock(
391
+ side_effect=[
392
+ pd.DataFrame({"col1": [1, 2, 3, 4, 5], "col2": ["a", "b", "c", "d", "e"]}),
393
+ pd.DataFrame({"col1": [6, 7, 8, 9, 10], "col2": ["f", "g", "h", "i", "j"]}),
394
+ pd.DataFrame({"col1": [11, 12], "col2": ["k", "l"]}),
395
+ ]
396
+ )
397
+
398
+ result = job._fetchall(mock_api)
399
+
400
+ assert len(result) == 12
401
+ assert list(result.columns) == ["col1", "col2"]
402
+ assert result["col1"].tolist() == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
403
+
404
+
405
+ def test_fetchall_resets_index():
406
+ """Test _fetchall resets index in concatenated result"""
407
+ mock_session = Mock(spec=Session)
408
+ mock_session.connection = Mock()
409
+
410
+ job = ConcreteTushareJob(name="test_job", key="test_key", session=mock_session, paginate={"pagesize": 3})
411
+
412
+ # DataFrames with different indices, both full pages
413
+ df1 = pd.DataFrame({"col": [1, 2, 3]}, index=[10, 20, 30])
414
+ df2 = pd.DataFrame({"col": [4, 5]}, index=[40, 50])
415
+
416
+ mock_api = Mock(side_effect=[df1, df2])
417
+
418
+ result = job._fetchall(mock_api)
419
+
420
+ # Index should be reset to 0, 1, 2, 3, 4
421
+ assert result.index.tolist() == [0, 1, 2, 3, 4]
422
+
423
+
424
+ # ============================================================================
425
+ # Integration Tests
426
+ # ============================================================================
427
+
428
+
429
+ def test_tusharejob_full_workflow():
430
+ """Test complete TushareJob workflow"""
431
+ mock_session = Mock(spec=Session)
432
+ mock_api_method = Mock(
433
+ side_effect=[
434
+ pd.DataFrame({"symbol": ["AAPL", "GOOGL"], "price": [150.0, 2800.0]}),
435
+ pd.DataFrame({"symbol": ["MSFT"], "price": [300.0]}),
436
+ ]
437
+ )
438
+ mock_session.connection = Mock(stock_basic=mock_api_method)
439
+
440
+ class StockJob(TushareJob):
441
+ def _run(self):
442
+ return self._fetchall(api=self.connection.stock_basic, exchange="NASDAQ")
443
+
444
+ job = StockJob(
445
+ name="stock_job",
446
+ key="stock_001",
447
+ session=mock_session,
448
+ params={"exchange": "NASDAQ"},
449
+ paginate={"pagesize": 2, "pagelimit": 10},
450
+ retry={"retry": 2, "wait": 0},
451
+ )
452
+
453
+ result = job.run()
454
+
455
+ assert isinstance(result, pd.DataFrame)
456
+ assert len(result) == 3
457
+ assert "symbol" in result.columns
458
+ assert "price" in result.columns
459
+
460
+
461
+ def test_tusharejob_with_cache():
462
+ """Test TushareJob with caching enabled"""
463
+ mock_session = Mock(spec=Session)
464
+ mock_session.connection = Mock()
465
+
466
+ job = ConcreteTushareJob(name="cached_job", key="cached_001", session=mock_session, cache=True)
467
+
468
+ # Test cache operations
469
+ job.set_cache("test_key", {"data": "test_value"})
470
+ cached_value = job.get_cache("test_key")
471
+
472
+ assert cached_value == {"data": "test_value"}
473
+
474
+
475
+ def test_tusharejob_inherits_job_methods():
476
+ """Test TushareJob inherits all Job methods"""
477
+ mock_session = Mock(spec=Session)
478
+ mock_session.connection = Mock()
479
+
480
+ job = ConcreteTushareJob(name="test_job", key="test_key", session=mock_session, params={"symbol": "AAPL"})
481
+
482
+ # Test inherited methods
483
+ assert hasattr(job, "run")
484
+ assert hasattr(job, "markpoint")
485
+ assert hasattr(job, "cool")
486
+ assert hasattr(job, "get_params")
487
+ assert hasattr(job, "reset")
488
+ assert hasattr(job, "describe")
489
+ assert hasattr(job, "to_dict")
490
+
491
+ # Test get_params works
492
+ params = job.get_params()
493
+ assert params["symbol"] == "AAPL"
494
+
495
+
496
+ def test_tusharejob_metric_tracking():
497
+ """Test TushareJob tracks metrics correctly"""
498
+ mock_session = Mock(spec=Session)
499
+ mock_session.connection = Mock()
500
+
501
+ job = ConcreteTushareJob(name="test_job", key="test_key", session=mock_session)
502
+
503
+ # Marks are created during __init__
504
+ assert "init[OK]" in job.metric.marks
505
+ assert "_resolve_connection[OK]" in job.metric.marks
506
+
507
+ # Run job - note that metric context manager resets marks
508
+ job.run()
509
+
510
+ # After run, metric tracks duration
511
+ assert job.metric.duration is not None
512
+ assert job.metric.start_at is not None
513
+ assert job.metric.finish_at is not None
514
+
515
+
516
+ def test_tusharejob_multiple_instances_independent():
517
+ """Test multiple TushareJob instances are independent"""
518
+ mock_session1 = Mock(spec=Session)
519
+ mock_session1.connection = Mock(name="connection1")
520
+
521
+ mock_session2 = Mock(spec=Session)
522
+ mock_session2.connection = Mock(name="connection2")
523
+
524
+ job1 = ConcreteTushareJob(name="job1", key="key1", session=mock_session1, params={"symbol": "AAPL"})
525
+
526
+ job2 = ConcreteTushareJob(name="job2", key="key2", session=mock_session2, params={"symbol": "GOOGL"})
527
+
528
+ assert job1.name != job2.name
529
+ assert job1.connection != job2.connection
530
+ assert job1.get_params() != job2.get_params()
531
+
532
+
533
+ # ============================================================================
534
+ # Edge Case Tests
535
+ # ============================================================================
536
+
537
+
538
+ def test_fetchall_with_zero_pagelimit():
539
+ """Test _fetchall with pagelimit=0 iterates zero times"""
540
+ mock_session = Mock(spec=Session)
541
+ mock_session.connection = Mock()
542
+
543
+ job = ConcreteTushareJob(
544
+ name="test_job", key="test_key", session=mock_session, paginate={"pagesize": 100, "pagelimit": 1}
545
+ )
546
+
547
+ # API returns less than pagesize, should stop after one page
548
+ mock_api = Mock(return_value=pd.DataFrame({"col": [1, 2, 3]}))
549
+
550
+ result = job._fetchall(mock_api)
551
+
552
+ assert isinstance(result, pd.DataFrame)
553
+ assert len(result) == 3
554
+ assert mock_api.call_count == 1
555
+
556
+
557
+ def test_fetchall_api_raises_exception():
558
+ """Test _fetchall handles API exceptions"""
559
+ mock_session = Mock(spec=Session)
560
+ mock_session.connection = Mock()
561
+
562
+ job = ConcreteTushareJob(name="test_job", key="test_key", session=mock_session)
563
+
564
+ mock_api = Mock(side_effect=RuntimeError("API Error"))
565
+
566
+ with pytest.raises(RuntimeError, match="API Error"):
567
+ job._fetchall(mock_api)
568
+
569
+
570
+ def test_fetchall_with_varying_column_names():
571
+ """Test _fetchall handles DataFrames with different columns"""
572
+ mock_session = Mock(spec=Session)
573
+ mock_session.connection = Mock()
574
+
575
+ job = ConcreteTushareJob(name="test_job", key="test_key", session=mock_session, paginate={"pagesize": 10})
576
+
577
+ # First page has 2 rows (< pagesize), so loop stops after first page
578
+ mock_api = Mock(
579
+ side_effect=[
580
+ pd.DataFrame({"col1": [1, 2]}),
581
+ pd.DataFrame({"col2": [3, 4]}), # Won't be reached
582
+ ]
583
+ )
584
+
585
+ result = job._fetchall(mock_api)
586
+
587
+ assert isinstance(result, pd.DataFrame)
588
+ assert len(result) == 2
589
+ assert mock_api.call_count == 1
@@ -0,0 +1,5 @@
1
+ from xfintech.data.source.tushare.session.session import Session
2
+
3
+ __all__ = [
4
+ "Session",
5
+ ]