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,684 @@
1
+ """
2
+ Test suite for Job class
3
+ Tests cover initialization, lifecycle management, cache operations, and protocol compliance
4
+ """
5
+
6
+ from typing import Any, Dict
7
+ from unittest.mock import patch
8
+
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.metric import Metric
14
+ from xfintech.data.common.paginate import Paginate
15
+ from xfintech.data.common.params import Params
16
+ from xfintech.data.common.retry import Retry
17
+ from xfintech.data.job.job import Job
18
+ from xfintech.data.job.joblike import JobLike
19
+
20
+ # ============================================================================
21
+ # Helper Classes for Testing
22
+ # ============================================================================
23
+
24
+
25
+ class ConcreteJob(Job):
26
+ """Concrete implementation for testing Job"""
27
+
28
+ def _run(self) -> Dict[str, Any]:
29
+ return {"status": "success", "data": [1, 2, 3]}
30
+
31
+
32
+ class FailingJob(Job):
33
+ """Job that fails for testing retry logic"""
34
+
35
+ def __init__(self, *args, fail_count=2, **kwargs):
36
+ super().__init__(*args, **kwargs)
37
+ self.attempts = 0
38
+ self.fail_count = fail_count
39
+
40
+ def _run(self) -> Dict[str, Any]:
41
+ self.attempts += 1
42
+ if self.attempts <= self.fail_count:
43
+ raise RuntimeError(f"Attempt {self.attempts} failed")
44
+ return {"status": "success", "attempts": self.attempts}
45
+
46
+
47
+ class CachedJob(Job):
48
+ """Job that uses cache"""
49
+
50
+ def _run(self) -> Dict[str, Any]:
51
+ # Check cache first
52
+ cached = self.get_cache("result")
53
+ if cached:
54
+ return {"status": "from_cache", "data": cached}
55
+
56
+ # Compute result
57
+ result = {"computed": True, "value": 42}
58
+ self.set_cache("result", result)
59
+ return {"status": "computed", "data": result}
60
+
61
+
62
+ # ============================================================================
63
+ # Job Initialization Tests
64
+ # ============================================================================
65
+
66
+
67
+ def test_job_init_basic():
68
+ """Test Job initialization with minimal parameters"""
69
+ job = ConcreteJob(name="test_job", key="test_key")
70
+
71
+ assert job.name == "test_job"
72
+ assert job.key == "test_key"
73
+ assert isinstance(job.params, Params)
74
+ assert isinstance(job.coolant, Coolant)
75
+ assert isinstance(job.paginate, Paginate)
76
+ assert isinstance(job.retry, Retry)
77
+ assert isinstance(job.metric, Metric)
78
+ assert job.cache is None
79
+
80
+
81
+ def test_job_init_with_params_dict():
82
+ """Test Job initialization with params as dict"""
83
+ job = ConcreteJob(name="test_job", key="test_key", params={"symbol": "AAPL", "limit": 100})
84
+
85
+ assert isinstance(job.params, Params)
86
+ assert job.params.symbol == "AAPL"
87
+ assert job.params.limit == 100
88
+
89
+
90
+ def test_job_init_with_params_object():
91
+ """Test Job initialization with Params object"""
92
+ params = Params(symbol="TSLA", date="20240115")
93
+ job = ConcreteJob(name="test_job", key="test_key", params=params)
94
+
95
+ assert job.params is params
96
+ assert job.params.symbol == "TSLA"
97
+
98
+
99
+ def test_job_init_with_coolant_dict():
100
+ """Test Job initialization with coolant as dict"""
101
+ job = ConcreteJob(name="test_job", key="test_key", coolant={"interval": 2, "use_jitter": True})
102
+
103
+ assert isinstance(job.coolant, Coolant)
104
+ assert job.coolant.interval == 2
105
+ assert job.coolant.use_jitter is True
106
+
107
+
108
+ def test_job_init_with_coolant_object():
109
+ """Test Job initialization with Coolant object"""
110
+ coolant = Coolant(interval=5, use_jitter=False)
111
+ job = ConcreteJob(name="test_job", key="test_key", coolant=coolant)
112
+
113
+ assert job.coolant is coolant
114
+
115
+
116
+ def test_job_init_with_paginate_dict():
117
+ """Test Job initialization with paginate as dict"""
118
+ job = ConcreteJob(name="test_job", key="test_key", paginate={"pagesize": 1000, "pagelimit": 5000})
119
+
120
+ assert isinstance(job.paginate, Paginate)
121
+ assert job.paginate.pagesize == 1000
122
+ assert job.paginate.pagelimit == 5000
123
+
124
+
125
+ def test_job_init_with_paginate_object():
126
+ """Test Job initialization with Paginate object"""
127
+ paginate = Paginate(pagesize=2000, pagelimit=10000)
128
+ job = ConcreteJob(name="test_job", key="test_key", paginate=paginate)
129
+
130
+ assert job.paginate is paginate
131
+
132
+
133
+ def test_job_init_with_retry_dict():
134
+ """Test Job initialization with retry as dict"""
135
+ job = ConcreteJob(name="test_job", key="test_key", retry={"retry": 5, "wait": 10})
136
+
137
+ assert isinstance(job.retry, Retry)
138
+ assert job.retry.retry == 5
139
+ assert job.retry.wait == 10
140
+
141
+
142
+ def test_job_init_with_retry_object():
143
+ """Test Job initialization with Retry object"""
144
+ retry = Retry(retry=3, wait=5)
145
+ job = ConcreteJob(name="test_job", key="test_key", retry=retry)
146
+
147
+ assert job.retry is retry
148
+
149
+
150
+ def test_job_init_with_cache_true():
151
+ """Test Job initialization with cache=True"""
152
+ job = ConcreteJob(name="test_job", key="test_key", cache=True)
153
+
154
+ assert isinstance(job.cache, Cache)
155
+ assert job.cache is not None
156
+
157
+
158
+ def test_job_init_with_cache_false():
159
+ """Test Job initialization with cache=False"""
160
+ job = ConcreteJob(name="test_job", key="test_key", cache=False)
161
+
162
+ assert job.cache is None
163
+
164
+
165
+ def test_job_init_with_cache_dict():
166
+ """Test Job initialization with cache as dict"""
167
+ job = ConcreteJob(name="test_job", key="test_key", cache={"name": "custom_cache"})
168
+
169
+ assert isinstance(job.cache, Cache)
170
+
171
+
172
+ def test_job_init_with_cache_object():
173
+ """Test Job initialization with Cache object"""
174
+ cache = Cache()
175
+ job = ConcreteJob(name="test_job", key="test_key", cache=cache)
176
+
177
+ assert job.cache is cache
178
+
179
+
180
+ def test_job_init_marks_init_point():
181
+ """Test Job initialization marks 'init' checkpoint"""
182
+ job = ConcreteJob(name="test_job", key="test_key")
183
+
184
+ assert "init[OK]" in job.metric.marks
185
+
186
+
187
+ # ============================================================================
188
+ # Job Resolve Methods Tests
189
+ # ============================================================================
190
+
191
+
192
+ def test_job_resolve_params_none():
193
+ """Test _resolve_params with None returns default Params"""
194
+ job = ConcreteJob(name="test_job", key="test_key")
195
+ result = job._resolve_params(None)
196
+
197
+ assert isinstance(result, Params)
198
+ assert result.to_dict() == {}
199
+
200
+
201
+ def test_job_resolve_params_dict():
202
+ """Test _resolve_params converts dict to Params"""
203
+ job = ConcreteJob(name="test_job", key="test_key")
204
+ result = job._resolve_params({"key": "value"})
205
+
206
+ assert isinstance(result, Params)
207
+ assert result.key == "value"
208
+
209
+
210
+ def test_job_resolve_params_object():
211
+ """Test _resolve_params returns Params object as-is"""
212
+ job = ConcreteJob(name="test_job", key="test_key")
213
+ params = Params(test="data")
214
+ result = job._resolve_params(params)
215
+
216
+ assert result is params
217
+
218
+
219
+ def test_job_resolve_coolant_none():
220
+ """Test _resolve_coolant with None returns default Coolant"""
221
+ job = ConcreteJob(name="test_job", key="test_key")
222
+ result = job._resolve_coolant(None)
223
+
224
+ assert isinstance(result, Coolant)
225
+
226
+
227
+ def test_job_resolve_paginate_none():
228
+ """Test _resolve_paginate with None returns default Paginate"""
229
+ job = ConcreteJob(name="test_job", key="test_key")
230
+ result = job._resolve_paginate(None)
231
+
232
+ assert isinstance(result, Paginate)
233
+
234
+
235
+ def test_job_resolve_retry_none():
236
+ """Test _resolve_retry with None returns default Retry"""
237
+ job = ConcreteJob(name="test_job", key="test_key")
238
+ result = job._resolve_retry(None)
239
+
240
+ assert isinstance(result, Retry)
241
+
242
+
243
+ def test_job_resolve_cache_none():
244
+ """Test _resolve_cache with None returns None"""
245
+ job = ConcreteJob(name="test_job", key="test_key")
246
+ result = job._resolve_cache(None)
247
+
248
+ assert result is None
249
+
250
+
251
+ def test_job_resolve_cache_true():
252
+ """Test _resolve_cache with True creates Cache"""
253
+ job = ConcreteJob(name="test_job", key="test_key")
254
+ result = job._resolve_cache(True)
255
+
256
+ assert isinstance(result, Cache)
257
+
258
+
259
+ def test_job_resolve_cache_false():
260
+ """Test _resolve_cache with False returns None"""
261
+ job = ConcreteJob(name="test_job", key="test_key")
262
+ result = job._resolve_cache(False)
263
+
264
+ assert result is None
265
+
266
+
267
+ # ============================================================================
268
+ # Job Run Method Tests
269
+ # ============================================================================
270
+
271
+
272
+ def test_job_run_basic():
273
+ """Test Job run method executes successfully"""
274
+ job = ConcreteJob(name="test_job", key="test_key")
275
+ result = job.run()
276
+
277
+ assert result["status"] == "success"
278
+ assert result["data"] == [1, 2, 3]
279
+
280
+
281
+ def test_job_run_tracks_duration():
282
+ """Test Job run tracks execution duration"""
283
+ job = ConcreteJob(name="test_job", key="test_key")
284
+ job.run()
285
+
286
+ assert job.metric.duration > 0
287
+
288
+
289
+ def test_job_run_not_implemented():
290
+ """Test base Job raises NotImplementedError if _run not implemented"""
291
+ job = Job(name="test_job", key="test_key")
292
+
293
+ with pytest.raises(NotImplementedError, match="Subclasses must implement"):
294
+ job.run()
295
+
296
+
297
+ def test_job_run_with_retry_on_failure():
298
+ """Test Job retries on failure"""
299
+ job = FailingJob(name="test_job", key="test_key", retry={"retry": 5, "wait": 0}, fail_count=2)
300
+
301
+ result = job.run()
302
+
303
+ assert result["status"] == "success"
304
+ assert job.attempts == 3 # Failed 2 times, succeeded on 3rd
305
+
306
+
307
+ def test_job_run_exhausts_retries():
308
+ """Test Job exhausts retries and raises exception"""
309
+ job = FailingJob(
310
+ name="test_job",
311
+ key="test_key",
312
+ retry={"max_retries": 2, "interval": 0},
313
+ fail_count=5, # Will keep failing
314
+ )
315
+
316
+ with pytest.raises(RuntimeError):
317
+ job.run()
318
+
319
+
320
+ def test_job_run_captures_error_in_metric():
321
+ """Test Job captures error in metric when run fails"""
322
+ job = FailingJob(name="test_job", key="test_key", retry={"retry": 1, "wait": 0}, fail_count=5)
323
+
324
+ try:
325
+ job.run()
326
+ except RuntimeError:
327
+ pass
328
+
329
+ assert len(job.metric.errors) > 0
330
+
331
+
332
+ # ============================================================================
333
+ # Job Markpoint Tests
334
+ # ============================================================================
335
+
336
+
337
+ def test_job_markpoint():
338
+ """Test Job markpoint adds checkpoint to metric"""
339
+ job = ConcreteJob(name="test_job", key="test_key")
340
+ job.markpoint("checkpoint1")
341
+ job.markpoint("checkpoint2")
342
+
343
+ assert "checkpoint1" in job.metric.marks
344
+ assert "checkpoint2" in job.metric.marks
345
+
346
+
347
+ # ============================================================================
348
+ # Job Cool Tests
349
+ # ============================================================================
350
+
351
+
352
+ @patch("time.sleep")
353
+ def test_job_cool(mock_sleep):
354
+ """Test Job cool method calls coolant"""
355
+ job = ConcreteJob(name="test_job", key="test_key", coolant={"interval": 5})
356
+
357
+ job.cool()
358
+
359
+ mock_sleep.assert_called_once()
360
+
361
+
362
+ # ============================================================================
363
+ # Job Get Params Tests
364
+ # ============================================================================
365
+
366
+
367
+ def test_job_get_params():
368
+ """Test Job get_params returns params dict"""
369
+ job = ConcreteJob(name="test_job", key="test_key", params={"symbol": "AAPL", "limit": 100})
370
+
371
+ result = job.get_params()
372
+
373
+ assert result["symbol"] == "AAPL"
374
+ assert result["limit"] == 100
375
+
376
+
377
+ def test_job_get_params_empty():
378
+ """Test Job get_params with no params"""
379
+ job = ConcreteJob(name="test_job", key="test_key")
380
+ result = job.get_params()
381
+
382
+ assert result == {}
383
+
384
+
385
+ # ============================================================================
386
+ # Job Cache Operations Tests
387
+ # ============================================================================
388
+
389
+
390
+ def test_job_get_cache_without_cache():
391
+ """Test get_cache returns default when cache is disabled"""
392
+ job = ConcreteJob(name="test_job", key="test_key", cache=False)
393
+ result = job.get_cache("key", default="default_value")
394
+
395
+ assert result == "default_value"
396
+
397
+
398
+ def test_job_get_cache_with_cache():
399
+ """Test get_cache retrieves data from cache"""
400
+ job = ConcreteJob(name="test_job", key="test_key", cache=True)
401
+ job.set_cache("test_key", {"data": "value"})
402
+
403
+ result = job.get_cache("test_key")
404
+
405
+ assert result == {"data": "value"}
406
+
407
+
408
+ def test_job_get_cache_nonexistent_key():
409
+ """Test get_cache returns default for nonexistent key"""
410
+ job = ConcreteJob(name="test_job", key="test_key", cache=True)
411
+ result = job.get_cache("nonexistent", default="not_found")
412
+
413
+ assert result == "not_found"
414
+
415
+
416
+ def test_job_set_cache_without_cache():
417
+ """Test set_cache does nothing when cache is disabled"""
418
+ job = ConcreteJob(name="test_job", key="test_key", cache=False)
419
+ job.set_cache("key", "value") # Should not raise error
420
+
421
+ result = job.get_cache("key", default="default")
422
+ assert result == "default"
423
+
424
+
425
+ def test_job_set_cache_with_cache():
426
+ """Test set_cache stores data in cache"""
427
+ job = ConcreteJob(name="test_job", key="test_key", cache=True)
428
+ job.set_cache("test_key", [1, 2, 3])
429
+
430
+ result = job.get_cache("test_key")
431
+
432
+ assert result == [1, 2, 3]
433
+
434
+
435
+ def test_job_cache_workflow():
436
+ """Test complete cache workflow in CachedJob"""
437
+ job = CachedJob(name="test_job", key="test_key", cache=True)
438
+
439
+ # First run computes result
440
+ result1 = job.run()
441
+ assert result1["status"] == "computed"
442
+
443
+ # Second run uses cache
444
+ result2 = job.run()
445
+ assert result2["status"] == "from_cache"
446
+
447
+
448
+ # ============================================================================
449
+ # Job Reset Tests
450
+ # ============================================================================
451
+
452
+
453
+ def test_job_reset_metric():
454
+ """Test Job reset clears metric"""
455
+ job = ConcreteJob(name="test_job", key="test_key")
456
+ job.run()
457
+
458
+ assert job.metric.duration > 0
459
+
460
+ job.reset()
461
+
462
+ assert job.metric.duration == 0
463
+
464
+
465
+ def test_job_reset_cache():
466
+ """Test Job reset clears cache"""
467
+ job = ConcreteJob(name="test_job", key="test_key", cache=True)
468
+ job.set_cache("key", "value")
469
+
470
+ assert job.get_cache("key") == "value"
471
+
472
+ job.reset()
473
+
474
+ assert job.get_cache("key") is None
475
+
476
+
477
+ def test_job_reset_without_cache():
478
+ """Test Job reset works when cache is disabled"""
479
+ job = ConcreteJob(name="test_job", key="test_key", cache=False)
480
+ job.run()
481
+
482
+ job.reset() # Should not raise error
483
+
484
+ assert job.metric.duration == 0
485
+
486
+
487
+ # ============================================================================
488
+ # Job Describe Tests
489
+ # ============================================================================
490
+
491
+
492
+ def test_job_describe_basic():
493
+ """Test Job describe returns basic info"""
494
+ job = ConcreteJob(name="test_job", key="test_key")
495
+ result = job.describe()
496
+
497
+ assert result["name"] == "test_job"
498
+ assert result["key"] == "test_key"
499
+ assert "params" in result
500
+ assert "coolant" in result
501
+ assert "paginate" in result
502
+ assert "retry" in result
503
+ assert "metric" in result
504
+
505
+
506
+ def test_job_describe_with_cache():
507
+ """Test Job describe includes cache when enabled"""
508
+ job = ConcreteJob(name="test_job", key="test_key", cache=True)
509
+ result = job.describe()
510
+
511
+ assert "cache" in result
512
+
513
+
514
+ def test_job_describe_without_cache():
515
+ """Test Job describe excludes cache when disabled"""
516
+ job = ConcreteJob(name="test_job", key="test_key", cache=False)
517
+ result = job.describe()
518
+
519
+ assert "cache" not in result
520
+
521
+
522
+ def test_job_describe_with_custom_params():
523
+ """Test Job describe includes custom params"""
524
+ job = ConcreteJob(name="test_job", key="test_key", params={"symbol": "AAPL", "date": "20240115"})
525
+ result = job.describe()
526
+
527
+ assert result["params"]["symbol"] == "AAPL"
528
+ assert result["params"]["date"] == "20240115"
529
+
530
+
531
+ # ============================================================================
532
+ # Job To Dict Tests
533
+ # ============================================================================
534
+
535
+
536
+ def test_job_to_dict_basic():
537
+ """Test Job to_dict returns complete structure"""
538
+ job = ConcreteJob(name="test_job", key="test_key")
539
+ result = job.to_dict()
540
+
541
+ assert result["name"] == "test_job"
542
+ assert result["key"] == "test_key"
543
+ assert "params" in result
544
+ assert "coolant" in result
545
+ assert "paginate" in result
546
+ assert "retry" in result
547
+ assert "metric" in result
548
+
549
+
550
+ def test_job_to_dict_with_cache():
551
+ """Test Job to_dict includes cache"""
552
+ job = ConcreteJob(name="test_job", key="test_key", cache=True)
553
+ result = job.to_dict()
554
+
555
+ assert result["cache"] is not None
556
+ assert isinstance(result["cache"], dict)
557
+
558
+
559
+ def test_job_to_dict_structure():
560
+ """Test Job to_dict has expected structure"""
561
+ job = ConcreteJob(
562
+ name="test_job",
563
+ key="test_key",
564
+ params={"symbol": "AAPL"},
565
+ coolant={"interval": 5},
566
+ paginate={"pagesize": 1000},
567
+ retry={"max_retries": 3},
568
+ cache=True,
569
+ )
570
+ result = job.to_dict()
571
+
572
+ assert isinstance(result["params"], dict)
573
+ assert isinstance(result["coolant"], dict)
574
+ assert isinstance(result["paginate"], dict)
575
+ assert isinstance(result["retry"], dict)
576
+ assert isinstance(result["cache"], dict)
577
+ assert isinstance(result["metric"], dict)
578
+
579
+
580
+ # ============================================================================
581
+ # JobLike Protocol Compliance Tests
582
+ # ============================================================================
583
+
584
+
585
+ def test_job_implements_joblike_protocol():
586
+ """Test Job implements JobLike protocol"""
587
+ job = ConcreteJob(name="test_job", key="test_key")
588
+
589
+ assert isinstance(job, JobLike)
590
+
591
+
592
+ def test_job_has_required_methods():
593
+ """Test Job has all required protocol methods"""
594
+ job = ConcreteJob(name="test_job", key="test_key")
595
+
596
+ assert hasattr(job, "run")
597
+ assert callable(job.run)
598
+ assert hasattr(job, "_run")
599
+ assert callable(job._run)
600
+ assert hasattr(job, "describe")
601
+ assert callable(job.describe)
602
+ assert hasattr(job, "to_dict")
603
+ assert callable(job.to_dict)
604
+
605
+
606
+ # ============================================================================
607
+ # Integration Tests
608
+ # ============================================================================
609
+
610
+
611
+ def test_job_full_lifecycle():
612
+ """Test complete Job lifecycle"""
613
+ job = ConcreteJob(
614
+ name="test_job", key="test_key", params={"symbol": "AAPL"}, retry={"retry": 2, "wait": 0}, cache=True
615
+ )
616
+
617
+ # Run job
618
+ result = job.run()
619
+ assert result["status"] == "success"
620
+
621
+ # Check metrics
622
+ assert job.metric.duration > 0
623
+
624
+ # Check params
625
+ params = job.get_params()
626
+ assert params["symbol"] == "AAPL"
627
+
628
+ # Get description
629
+ desc = job.describe()
630
+ assert desc["name"] == "test_job"
631
+
632
+ # Reset
633
+ job.reset()
634
+ assert job.metric.duration == 0
635
+
636
+
637
+ def test_job_multiple_runs():
638
+ """Test Job can be run multiple times"""
639
+ job = ConcreteJob(name="test_job", key="test_key")
640
+
641
+ result1 = job.run()
642
+ result2 = job.run()
643
+
644
+ assert result1 == result2
645
+
646
+
647
+ def test_job_with_all_features():
648
+ """Test Job with all features enabled"""
649
+ job = CachedJob(
650
+ name="complex_job",
651
+ key="complex_key",
652
+ params={"symbol": "AAPL", "date": "20240115"},
653
+ coolant={"interval": 1, "use_jitter": True},
654
+ paginate={"pagesize": 1000, "pagelimit": 5000},
655
+ retry={"retry": 3, "wait": 1},
656
+ cache=True,
657
+ )
658
+
659
+ # First run
660
+ job.run()
661
+ result2 = job.run()
662
+ assert result2["status"] == "from_cache"
663
+
664
+ # Verify all components
665
+ assert isinstance(job.params, Params)
666
+ assert isinstance(job.coolant, Coolant)
667
+ assert isinstance(job.paginate, Paginate)
668
+ assert isinstance(job.retry, Retry)
669
+ assert isinstance(job.cache, Cache)
670
+ assert isinstance(job.metric, Metric)
671
+
672
+
673
+ def test_job_different_instances_independent():
674
+ """Test different Job instances are independent"""
675
+ job1 = ConcreteJob(name="job1", key="key1", params={"value": 1})
676
+ job2 = ConcreteJob(name="job2", key="key2", params={"value": 2})
677
+
678
+ assert job1.name != job2.name
679
+ assert job1.params.value != job2.params.value
680
+
681
+ job1.run()
682
+
683
+ assert job1.metric.duration > 0
684
+ assert job2.metric.duration == 0