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.
- lib_x17_fintech-2.1.3.dist-info/METADATA +633 -0
- lib_x17_fintech-2.1.3.dist-info/RECORD +282 -0
- lib_x17_fintech-2.1.3.dist-info/WHEEL +5 -0
- lib_x17_fintech-2.1.3.dist-info/licenses/LICENSE +1 -0
- lib_x17_fintech-2.1.3.dist-info/top_level.txt +1 -0
- xfintech/__init__.py +0 -0
- xfintech/connect/__init__.py +18 -0
- xfintech/connect/artifact/__init__.py +5 -0
- xfintech/connect/artifact/artifact.py +168 -0
- xfintech/connect/artifact/tests/__init__.py +3 -0
- xfintech/connect/artifact/tests/test_class_artifact_all.py +564 -0
- xfintech/connect/common/__init__.py +12 -0
- xfintech/connect/common/connect.py +49 -0
- xfintech/connect/common/connectref.py +119 -0
- xfintech/connect/common/error.py +62 -0
- xfintech/connect/common/tests/__init__.py +1 -0
- xfintech/connect/common/tests/test_class_connectlike_all.py +544 -0
- xfintech/connect/common/tests/test_class_connectref_all.py +586 -0
- xfintech/connect/common/tests/test_class_errors_all.py +524 -0
- xfintech/connect/instance/__init__.py +7 -0
- xfintech/connect/instance/macos.py +121 -0
- xfintech/connect/instance/s3.py +176 -0
- xfintech/connect/instance/tests/__init__.py +1 -0
- xfintech/connect/instance/tests/test_class_macosconnect_all.py +692 -0
- xfintech/connect/instance/tests/test_class_s3connect_all.py +603 -0
- xfintech/data/__init__.py +20 -0
- xfintech/data/common/__init__.py +15 -0
- xfintech/data/common/cache.py +186 -0
- xfintech/data/common/coolant.py +171 -0
- xfintech/data/common/metric.py +138 -0
- xfintech/data/common/paginate.py +132 -0
- xfintech/data/common/params.py +162 -0
- xfintech/data/common/retry.py +201 -0
- xfintech/data/common/tests/__init__.py +1 -0
- xfintech/data/common/tests/test_class_cache_all.py +681 -0
- xfintech/data/common/tests/test_class_coolant_all.py +534 -0
- xfintech/data/common/tests/test_class_metric_all.py +705 -0
- xfintech/data/common/tests/test_class_paginate_all.py +508 -0
- xfintech/data/common/tests/test_class_params_all.py +891 -0
- xfintech/data/common/tests/test_class_retry_all.py +714 -0
- xfintech/data/job/__init__.py +17 -0
- xfintech/data/job/errors.py +112 -0
- xfintech/data/job/house.py +156 -0
- xfintech/data/job/job.py +247 -0
- xfintech/data/job/joblike.py +47 -0
- xfintech/data/job/tests/__init__.py +1 -0
- xfintech/data/job/tests/test_class_errors_all.py +275 -0
- xfintech/data/job/tests/test_class_house_all.py +801 -0
- xfintech/data/job/tests/test_class_job_all.py +684 -0
- xfintech/data/job/tests/test_class_joblike_all.py +482 -0
- xfintech/data/relay/__init__.py +7 -0
- xfintech/data/relay/client.py +114 -0
- xfintech/data/relay/clientlike.py +45 -0
- xfintech/data/relay/tests/test_class_relayclient_all.py +484 -0
- xfintech/data/relay/tests/test_class_relayclientlike_all.py +500 -0
- xfintech/data/source/__init__.py +7 -0
- xfintech/data/source/baostock/__init__.py +21 -0
- xfintech/data/source/baostock/job/__init__.py +5 -0
- xfintech/data/source/baostock/job/job.py +217 -0
- xfintech/data/source/baostock/job/tests/__init__.py +0 -0
- xfintech/data/source/baostock/job/tests/test_class_baostockjob_all.py +547 -0
- xfintech/data/source/baostock/session/__init__.py +8 -0
- xfintech/data/source/baostock/session/relay.py +223 -0
- xfintech/data/source/baostock/session/session.py +241 -0
- xfintech/data/source/baostock/session/tests/__init__.py +0 -0
- xfintech/data/source/baostock/session/tests/test_class_relay_all.py +694 -0
- xfintech/data/source/baostock/session/tests/test_class_session_all.py +505 -0
- xfintech/data/source/baostock/stock/__init__.py +0 -0
- xfintech/data/source/baostock/stock/hs300stock/__init__.py +3 -0
- xfintech/data/source/baostock/stock/hs300stock/constant.py +49 -0
- xfintech/data/source/baostock/stock/hs300stock/hs300stock.py +133 -0
- xfintech/data/source/baostock/stock/hs300stock/tests/__init__.py +1 -0
- xfintech/data/source/baostock/stock/hs300stock/tests/test_class_hs300index_all.py +413 -0
- xfintech/data/source/baostock/stock/minuteline/__init__.py +19 -0
- xfintech/data/source/baostock/stock/minuteline/constant.py +89 -0
- xfintech/data/source/baostock/stock/minuteline/minuteline.py +163 -0
- xfintech/data/source/baostock/stock/minuteline/tests/__init__.py +0 -0
- xfintech/data/source/baostock/stock/minuteline/tests/test_class_minuteline_all.py +582 -0
- xfintech/data/source/baostock/stock/stock/__init__.py +19 -0
- xfintech/data/source/baostock/stock/stock/constant.py +55 -0
- xfintech/data/source/baostock/stock/stock/stock.py +149 -0
- xfintech/data/source/baostock/stock/stock/tests/__init__.py +0 -0
- xfintech/data/source/baostock/stock/stock/tests/test_class_stock_all.py +508 -0
- xfintech/data/source/baostock/stock/stockinfo/__init__.py +5 -0
- xfintech/data/source/baostock/stock/stockinfo/constant.py +66 -0
- xfintech/data/source/baostock/stock/stockinfo/stockinfo.py +176 -0
- xfintech/data/source/baostock/stock/stockinfo/tests/__init__.py +1 -0
- xfintech/data/source/baostock/stock/stockinfo/tests/test_class_stockinfo_all.py +617 -0
- xfintech/data/source/baostock/stock/sz50stock/__init__.py +3 -0
- xfintech/data/source/baostock/stock/sz50stock/constant.py +49 -0
- xfintech/data/source/baostock/stock/sz50stock/sz50stock.py +133 -0
- xfintech/data/source/baostock/stock/sz50stock/tests/__init__.py +1 -0
- xfintech/data/source/baostock/stock/sz50stock/tests/test_class_sz50stock_all.py +397 -0
- xfintech/data/source/baostock/stock/tradedate/__init__.py +19 -0
- xfintech/data/source/baostock/stock/tradedate/constant.py +72 -0
- xfintech/data/source/baostock/stock/tradedate/tests/__init__.py +0 -0
- xfintech/data/source/baostock/stock/tradedate/tests/test_class_tradedate_all.py +695 -0
- xfintech/data/source/baostock/stock/tradedate/tradedate.py +208 -0
- xfintech/data/source/baostock/stock/zz500stock/__init__.py +3 -0
- xfintech/data/source/baostock/stock/zz500stock/constant.py +55 -0
- xfintech/data/source/baostock/stock/zz500stock/tests/__init__.py +1 -0
- xfintech/data/source/baostock/stock/zz500stock/tests/test_class_zz500stock_all.py +421 -0
- xfintech/data/source/baostock/stock/zz500stock/zz500stock.py +133 -0
- xfintech/data/source/tushare/__init__.py +61 -0
- xfintech/data/source/tushare/job/__init__.py +5 -0
- xfintech/data/source/tushare/job/job.py +257 -0
- xfintech/data/source/tushare/job/tests/test_class_tusharejob_all.py +589 -0
- xfintech/data/source/tushare/session/__init__.py +5 -0
- xfintech/data/source/tushare/session/relay.py +231 -0
- xfintech/data/source/tushare/session/session.py +239 -0
- xfintech/data/source/tushare/session/tests/test_class_relay_all.py +719 -0
- xfintech/data/source/tushare/session/tests/test_class_session_all.py +705 -0
- xfintech/data/source/tushare/stock/__init__.py +55 -0
- xfintech/data/source/tushare/stock/adjfactor/__init__.py +19 -0
- xfintech/data/source/tushare/stock/adjfactor/adjfactor.py +150 -0
- xfintech/data/source/tushare/stock/adjfactor/constant.py +71 -0
- xfintech/data/source/tushare/stock/adjfactor/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/adjfactor/tests/test_class_adjfactor_all.py +372 -0
- xfintech/data/source/tushare/stock/capflow/__init__.py +19 -0
- xfintech/data/source/tushare/stock/capflow/capflow.py +171 -0
- xfintech/data/source/tushare/stock/capflow/constant.py +105 -0
- xfintech/data/source/tushare/stock/capflow/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/capflow/tests/test_class_capflow_all.py +589 -0
- xfintech/data/source/tushare/stock/capflowdc/__init__.py +19 -0
- xfintech/data/source/tushare/stock/capflowdc/capflowdc.py +167 -0
- xfintech/data/source/tushare/stock/capflowdc/constant.py +95 -0
- xfintech/data/source/tushare/stock/capflowdc/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/capflowdc/tests/test_class_capflowdc_all.py +814 -0
- xfintech/data/source/tushare/stock/capflowths/__init__.py +19 -0
- xfintech/data/source/tushare/stock/capflowths/capflowths.py +173 -0
- xfintech/data/source/tushare/stock/capflowths/constant.py +92 -0
- xfintech/data/source/tushare/stock/capflowths/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/capflowths/tests/test_class_capflowths_all.py +551 -0
- xfintech/data/source/tushare/stock/company/__init__.py +19 -0
- xfintech/data/source/tushare/stock/company/company.py +188 -0
- xfintech/data/source/tushare/stock/company/constant.py +92 -0
- xfintech/data/source/tushare/stock/company/tests/__init__.py +1 -0
- xfintech/data/source/tushare/stock/company/tests/test_class_company_all.py +829 -0
- xfintech/data/source/tushare/stock/companybusiness/__init__.py +21 -0
- xfintech/data/source/tushare/stock/companybusiness/companybusiness.py +183 -0
- xfintech/data/source/tushare/stock/companybusiness/constant.py +91 -0
- xfintech/data/source/tushare/stock/companybusiness/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/companybusiness/tests/test_class_companybusiness_all.py +633 -0
- xfintech/data/source/tushare/stock/companycashflow/__init__.py +21 -0
- xfintech/data/source/tushare/stock/companycashflow/companycashflow.py +277 -0
- xfintech/data/source/tushare/stock/companycashflow/constant.py +293 -0
- xfintech/data/source/tushare/stock/companycashflow/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/companycashflow/tests/test_class_companycashflow_all.py +619 -0
- xfintech/data/source/tushare/stock/companydebt/__init__.py +19 -0
- xfintech/data/source/tushare/stock/companydebt/companydebt.py +339 -0
- xfintech/data/source/tushare/stock/companydebt/constant.py +403 -0
- xfintech/data/source/tushare/stock/companydebt/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/companydebt/tests/test_class_companydebt_all.py +655 -0
- xfintech/data/source/tushare/stock/companyoverview/__init__.py +21 -0
- xfintech/data/source/tushare/stock/companyoverview/companyoverview.py +214 -0
- xfintech/data/source/tushare/stock/companyoverview/constant.py +152 -0
- xfintech/data/source/tushare/stock/companyoverview/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/companyoverview/tests/test_class_companyoverview_all.py +647 -0
- xfintech/data/source/tushare/stock/companyprofit/__init__.py +21 -0
- xfintech/data/source/tushare/stock/companyprofit/companyprofit.py +272 -0
- xfintech/data/source/tushare/stock/companyprofit/constant.py +259 -0
- xfintech/data/source/tushare/stock/companyprofit/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/companyprofit/tests/test_class_companyprofit_all.py +635 -0
- xfintech/data/source/tushare/stock/conceptcapflowdc/__init__.py +21 -0
- xfintech/data/source/tushare/stock/conceptcapflowdc/conceptcapflowdc.py +175 -0
- xfintech/data/source/tushare/stock/conceptcapflowdc/constant.py +106 -0
- xfintech/data/source/tushare/stock/conceptcapflowdc/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/conceptcapflowdc/tests/test_class_conceptcapflowdc_all.py +568 -0
- xfintech/data/source/tushare/stock/conceptcapflowths/__init__.py +21 -0
- xfintech/data/source/tushare/stock/conceptcapflowths/conceptcapflowths.py +188 -0
- xfintech/data/source/tushare/stock/conceptcapflowths/constant.py +89 -0
- xfintech/data/source/tushare/stock/conceptcapflowths/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/conceptcapflowths/tests/test_class_conceptcapflowths_all.py +516 -0
- xfintech/data/source/tushare/stock/dayline/__init__.py +19 -0
- xfintech/data/source/tushare/stock/dayline/constant.py +87 -0
- xfintech/data/source/tushare/stock/dayline/dayline.py +177 -0
- xfintech/data/source/tushare/stock/dayline/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/dayline/tests/test_class_dayline_all.py +585 -0
- xfintech/data/source/tushare/stock/industrycapflowths/__init__.py +21 -0
- xfintech/data/source/tushare/stock/industrycapflowths/constant.py +89 -0
- xfintech/data/source/tushare/stock/industrycapflowths/industrycapflowths.py +192 -0
- xfintech/data/source/tushare/stock/industrycapflowths/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/industrycapflowths/tests/test_class_industrycapflowths_all.py +683 -0
- xfintech/data/source/tushare/stock/marketindexcapflowdc/__init__.py +21 -0
- xfintech/data/source/tushare/stock/marketindexcapflowdc/constant.py +90 -0
- xfintech/data/source/tushare/stock/marketindexcapflowdc/marketindexcapflowdc.py +173 -0
- xfintech/data/source/tushare/stock/marketindexcapflowdc/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/marketindexcapflowdc/tests/test_class_marketindexcapflowdc_all.py +793 -0
- xfintech/data/source/tushare/stock/monthline/__init__.py +19 -0
- xfintech/data/source/tushare/stock/monthline/constant.py +87 -0
- xfintech/data/source/tushare/stock/monthline/monthline.py +180 -0
- xfintech/data/source/tushare/stock/monthline/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/monthline/tests/test_class_monthline_all.py +574 -0
- xfintech/data/source/tushare/stock/stock/__init__.py +19 -0
- xfintech/data/source/tushare/stock/stock/constant.py +105 -0
- xfintech/data/source/tushare/stock/stock/stock.py +193 -0
- xfintech/data/source/tushare/stock/stock/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/stock/tests/test_class_stock_all.py +788 -0
- xfintech/data/source/tushare/stock/stockdividend/__init__.py +21 -0
- xfintech/data/source/tushare/stock/stockdividend/constant.py +111 -0
- xfintech/data/source/tushare/stock/stockdividend/stockdividend.py +180 -0
- xfintech/data/source/tushare/stock/stockdividend/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/stockdividend/tests/test_class_stockdividend_all.py +725 -0
- xfintech/data/source/tushare/stock/stockinfo/__init__.py +19 -0
- xfintech/data/source/tushare/stock/stockinfo/constant.py +104 -0
- xfintech/data/source/tushare/stock/stockinfo/stockinfo.py +208 -0
- xfintech/data/source/tushare/stock/stockinfo/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/stockinfo/tests/test_class_stockinfo_all.py +881 -0
- xfintech/data/source/tushare/stock/stockipo/__init__.py +19 -0
- xfintech/data/source/tushare/stock/stockipo/constant.py +90 -0
- xfintech/data/source/tushare/stock/stockipo/stockipo.py +234 -0
- xfintech/data/source/tushare/stock/stockipo/tests/__init__.py +1 -0
- xfintech/data/source/tushare/stock/stockipo/tests/test_class_stockipo_all.py +750 -0
- xfintech/data/source/tushare/stock/stockpledge/__init__.py +19 -0
- xfintech/data/source/tushare/stock/stockpledge/constant.py +72 -0
- xfintech/data/source/tushare/stock/stockpledge/stockpledge.py +158 -0
- xfintech/data/source/tushare/stock/stockpledge/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/stockpledge/tests/test_class_stockpledge_all.py +664 -0
- xfintech/data/source/tushare/stock/stockpledgedetail/__init__.py +21 -0
- xfintech/data/source/tushare/stock/stockpledgedetail/constant.py +85 -0
- xfintech/data/source/tushare/stock/stockpledgedetail/stockpledgedetail.py +171 -0
- xfintech/data/source/tushare/stock/stockpledgedetail/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/stockpledgedetail/tests/test_class_stockpledgedetail_all.py +112 -0
- xfintech/data/source/tushare/stock/stockst/__init__.py +19 -0
- xfintech/data/source/tushare/stock/stockst/constant.py +80 -0
- xfintech/data/source/tushare/stock/stockst/stockst.py +189 -0
- xfintech/data/source/tushare/stock/stockst/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/stockst/tests/test_class_stockst_all.py +693 -0
- xfintech/data/source/tushare/stock/stocksuspend/__init__.py +21 -0
- xfintech/data/source/tushare/stock/stocksuspend/constant.py +75 -0
- xfintech/data/source/tushare/stock/stocksuspend/stocksuspend.py +151 -0
- xfintech/data/source/tushare/stock/stocksuspend/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/stocksuspend/tests/test_class_stocksuspend_all.py +626 -0
- xfintech/data/source/tushare/stock/techindex/__init__.py +19 -0
- xfintech/data/source/tushare/stock/techindex/constant.py +600 -0
- xfintech/data/source/tushare/stock/techindex/techindex.py +314 -0
- xfintech/data/source/tushare/stock/techindex/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/techindex/tests/test_class_techindex_all.py +576 -0
- xfintech/data/source/tushare/stock/tradedate/__init__.py +19 -0
- xfintech/data/source/tushare/stock/tradedate/constant.py +93 -0
- xfintech/data/source/tushare/stock/tradedate/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/tradedate/tests/test_class_tradedate_all.py +947 -0
- xfintech/data/source/tushare/stock/tradedate/tradedate.py +234 -0
- xfintech/data/source/tushare/stock/weekline/__init__.py +19 -0
- xfintech/data/source/tushare/stock/weekline/constant.py +87 -0
- xfintech/data/source/tushare/stock/weekline/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/weekline/tests/test_class_weekline_all.py +575 -0
- xfintech/data/source/tushare/stock/weekline/weekline.py +182 -0
- xfintech/fabric/__init__.py +18 -0
- xfintech/fabric/column/__init__.py +7 -0
- xfintech/fabric/column/info.py +202 -0
- xfintech/fabric/column/kind.py +102 -0
- xfintech/fabric/column/tests/__init__.py +0 -0
- xfintech/fabric/column/tests/test_class_info_all.py +207 -0
- xfintech/fabric/column/tests/test_class_kind_all.py +80 -0
- xfintech/fabric/table/__init__.py +5 -0
- xfintech/fabric/table/info.py +263 -0
- xfintech/fabric/table/tests/__init__.py +0 -0
- xfintech/fabric/table/tests/test_class_info_all.py +547 -0
- xfintech/serde/__init__.py +35 -0
- xfintech/serde/common/__init__.py +9 -0
- xfintech/serde/common/dataformat.py +78 -0
- xfintech/serde/common/deserialiserlike.py +38 -0
- xfintech/serde/common/error.py +182 -0
- xfintech/serde/common/serialiserlike.py +38 -0
- xfintech/serde/common/tests/__init__.py +1 -0
- xfintech/serde/common/tests/test_class_dataformat_all.py +694 -0
- xfintech/serde/common/tests/test_class_deserialiserlike_all.py +500 -0
- xfintech/serde/common/tests/test_class_errors_all.py +518 -0
- xfintech/serde/common/tests/test_class_serialiserlike_all.py +401 -0
- xfintech/serde/deserialiser/__init__.py +7 -0
- xfintech/serde/deserialiser/pandas.py +113 -0
- xfintech/serde/deserialiser/python.py +68 -0
- xfintech/serde/deserialiser/tests/__init__.py +1 -0
- xfintech/serde/deserialiser/tests/test_class_pandasdeserialiser_all.py +503 -0
- xfintech/serde/deserialiser/tests/test_class_pythondeserialiser_all.py +570 -0
- xfintech/serde/serialiser/__init__.py +7 -0
- xfintech/serde/serialiser/pandas.py +116 -0
- xfintech/serde/serialiser/python.py +71 -0
- xfintech/serde/serialiser/tests/__init__.py +1 -0
- xfintech/serde/serialiser/tests/test_class_pandasserialiser_all.py +474 -0
- xfintech/serde/serialiser/tests/test_class_pythonserialiser_all.py +508 -0
|
@@ -0,0 +1,635 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
from unittest.mock import MagicMock, patch
|
|
3
|
+
|
|
4
|
+
import pandas as pd
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
from xfintech.data.common.cache import Cache
|
|
8
|
+
from xfintech.data.common.params import Params
|
|
9
|
+
from xfintech.data.source.tushare.session.session import Session
|
|
10
|
+
from xfintech.data.source.tushare.stock.companyprofit.companyprofit import CompanyProfit
|
|
11
|
+
from xfintech.data.source.tushare.stock.companyprofit.constant import (
|
|
12
|
+
KEY,
|
|
13
|
+
NAME,
|
|
14
|
+
PAGINATE,
|
|
15
|
+
SOURCE,
|
|
16
|
+
TARGET,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
# ============================================================================
|
|
20
|
+
# Fixtures
|
|
21
|
+
# ============================================================================
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@pytest.fixture
|
|
25
|
+
def mock_session():
|
|
26
|
+
"""Create a mock Tushare session"""
|
|
27
|
+
session = MagicMock(spec=Session)
|
|
28
|
+
session._credential = "test_token"
|
|
29
|
+
session.id = "test1234"
|
|
30
|
+
session.mode = "direct"
|
|
31
|
+
session.relay_url = None
|
|
32
|
+
session.relay_secret = None
|
|
33
|
+
session.connected = True
|
|
34
|
+
|
|
35
|
+
# Mock the connection object
|
|
36
|
+
mock_connection = MagicMock()
|
|
37
|
+
mock_connection.income_vip = MagicMock()
|
|
38
|
+
session.connection = mock_connection
|
|
39
|
+
|
|
40
|
+
return session
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@pytest.fixture
|
|
44
|
+
def sample_source_data():
|
|
45
|
+
"""Create sample source data in Tushare format"""
|
|
46
|
+
return pd.DataFrame(
|
|
47
|
+
{
|
|
48
|
+
"ts_code": ["000001.SZ", "000001.SZ", "000001.SZ"],
|
|
49
|
+
"ann_date": ["20200425", "20200826", "20201030"],
|
|
50
|
+
"f_ann_date": ["20200425", "20200826", "20201030"],
|
|
51
|
+
"end_date": ["20200331", "20200630", "20200930"],
|
|
52
|
+
"report_type": ["1", "1", "1"],
|
|
53
|
+
"comp_type": ["1", "1", "1"],
|
|
54
|
+
"end_type": ["12", "12", "12"],
|
|
55
|
+
"update_flag": ["0", "0", "0"],
|
|
56
|
+
"basic_eps": [0.5, 1.2, 1.8],
|
|
57
|
+
"diluted_eps": [0.5, 1.2, 1.8],
|
|
58
|
+
"total_revenue": [100000000.0, 250000000.0, 380000000.0],
|
|
59
|
+
"revenue": [98000000.0, 245000000.0, 372000000.0],
|
|
60
|
+
"n_income": [12000000.0, 32000000.0, 48000000.0],
|
|
61
|
+
"n_income_attr_p": [11500000.0, 31000000.0, 46000000.0],
|
|
62
|
+
"total_profit": [15000000.0, 39000000.0, 58000000.0],
|
|
63
|
+
"income_tax": [3000000.0, 7000000.0, 10000000.0],
|
|
64
|
+
}
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@pytest.fixture
|
|
69
|
+
def expected_transformed_data():
|
|
70
|
+
"""Create expected transformed data"""
|
|
71
|
+
return pd.DataFrame(
|
|
72
|
+
{
|
|
73
|
+
"code": ["000001.SZ", "000001.SZ", "000001.SZ"],
|
|
74
|
+
"date": ["2020-03-31", "2020-06-30", "2020-09-30"],
|
|
75
|
+
"datecode": ["20200331", "20200630", "20200930"],
|
|
76
|
+
"ann_date": ["2020-04-25", "2020-08-26", "2020-10-30"],
|
|
77
|
+
"f_ann_date": ["2020-04-25", "2020-08-26", "2020-10-30"],
|
|
78
|
+
"end_date": ["2020-03-31", "2020-06-30", "2020-09-30"],
|
|
79
|
+
"report_type": ["1", "1", "1"],
|
|
80
|
+
"comp_type": ["1", "1", "1"],
|
|
81
|
+
"end_type": ["12", "12", "12"],
|
|
82
|
+
"update_flag": ["0", "0", "0"],
|
|
83
|
+
"basic_eps": [0.5, 1.2, 1.8],
|
|
84
|
+
"diluted_eps": [0.5, 1.2, 1.8],
|
|
85
|
+
"total_revenue": [100000000.0, 250000000.0, 380000000.0],
|
|
86
|
+
"revenue": [98000000.0, 245000000.0, 372000000.0],
|
|
87
|
+
"n_income": [12000000.0, 32000000.0, 48000000.0],
|
|
88
|
+
"n_income_attr_p": [11500000.0, 31000000.0, 46000000.0],
|
|
89
|
+
"total_profit": [15000000.0, 39000000.0, 58000000.0],
|
|
90
|
+
"income_tax": [3000000.0, 7000000.0, 10000000.0],
|
|
91
|
+
}
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
# ============================================================================
|
|
96
|
+
# Initialization Tests
|
|
97
|
+
# ============================================================================
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def test_companyprofit_init_basic(mock_session):
|
|
101
|
+
"""Test basic initialization with required session"""
|
|
102
|
+
profit = CompanyProfit(session=mock_session)
|
|
103
|
+
assert profit.name == NAME
|
|
104
|
+
assert profit.key == KEY
|
|
105
|
+
assert profit.source == SOURCE
|
|
106
|
+
assert profit.target == TARGET
|
|
107
|
+
assert profit.paginate == PAGINATE
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def test_companyprofit_init_with_params_dict(mock_session):
|
|
111
|
+
"""Test initialization with params as dict"""
|
|
112
|
+
params = {"ts_code": "000001.SZ", "start_date": "20200101", "end_date": "20201231"}
|
|
113
|
+
profit = CompanyProfit(session=mock_session, params=params)
|
|
114
|
+
assert profit.params.get("ts_code") == "000001.SZ"
|
|
115
|
+
assert profit.params.get("start_date") == "20200101"
|
|
116
|
+
assert profit.params.get("end_date") == "20201231"
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def test_companyprofit_init_with_params_object(mock_session):
|
|
120
|
+
"""Test initialization with params as Params object"""
|
|
121
|
+
params = Params(ts_code="000001.SZ", start_date="20200101")
|
|
122
|
+
profit = CompanyProfit(session=mock_session, params=params)
|
|
123
|
+
assert profit.params.get("ts_code") == "000001.SZ"
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def test_companyprofit_init_with_year_param(mock_session):
|
|
127
|
+
"""Test initialization with year parameter"""
|
|
128
|
+
params = {"ts_code": "000001.SZ", "year": "2020"}
|
|
129
|
+
profit = CompanyProfit(session=mock_session, params=params)
|
|
130
|
+
assert profit.params.get("year") == "2020"
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def test_companyprofit_init_with_cache_bool_true(mock_session):
|
|
134
|
+
"""Test initialization with cache as boolean True"""
|
|
135
|
+
profit = CompanyProfit(session=mock_session, cache=True)
|
|
136
|
+
assert profit.cache is not None
|
|
137
|
+
assert isinstance(profit.cache, Cache)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def test_companyprofit_init_with_cache_bool_false(mock_session):
|
|
141
|
+
"""Test initialization with cache as boolean False"""
|
|
142
|
+
profit = CompanyProfit(session=mock_session, cache=False)
|
|
143
|
+
assert profit.cache is None
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def test_companyprofit_init_with_cache_dict(mock_session):
|
|
147
|
+
"""Test initialization with cache as dict"""
|
|
148
|
+
cache_config = {"dir": "/tmp/test_cache", "ttl": 3600}
|
|
149
|
+
profit = CompanyProfit(session=mock_session, cache=cache_config)
|
|
150
|
+
assert profit.cache is not None
|
|
151
|
+
assert isinstance(profit.cache, Cache)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def test_companyprofit_init_default_paginate_limit(mock_session):
|
|
155
|
+
"""Test that default paginate settings are correctly set"""
|
|
156
|
+
profit = CompanyProfit(session=mock_session)
|
|
157
|
+
assert profit.paginate.pagesize == 1000
|
|
158
|
+
assert profit.paginate.pagelimit == 1000
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def test_companyprofit_init_with_all_params(mock_session):
|
|
162
|
+
"""Test initialization with all parameters"""
|
|
163
|
+
params = {"ts_code": "000001.SZ", "start_date": "20200101", "end_date": "20201231"}
|
|
164
|
+
coolant = {"interval": 0.5}
|
|
165
|
+
retry = {"max_attempts": 3}
|
|
166
|
+
cache = True
|
|
167
|
+
|
|
168
|
+
profit = CompanyProfit(session=mock_session, params=params, coolant=coolant, retry=retry, cache=cache)
|
|
169
|
+
assert profit.params.get("ts_code") == "000001.SZ"
|
|
170
|
+
assert profit.cache is not None
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
# ============================================================================
|
|
174
|
+
# Transform Tests
|
|
175
|
+
# ============================================================================
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def test_companyprofit_transform_basic(mock_session, sample_source_data):
|
|
179
|
+
"""Test basic transformation of source data"""
|
|
180
|
+
profit = CompanyProfit(session=mock_session)
|
|
181
|
+
result = profit.transform(sample_source_data)
|
|
182
|
+
|
|
183
|
+
assert not result.empty
|
|
184
|
+
assert len(result) == 3
|
|
185
|
+
assert "code" in result.columns
|
|
186
|
+
assert "date" in result.columns
|
|
187
|
+
assert "datecode" in result.columns
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def test_companyprofit_transform_code_mapping(mock_session, sample_source_data):
|
|
191
|
+
"""Test that ts_code is correctly mapped to code"""
|
|
192
|
+
profit = CompanyProfit(session=mock_session)
|
|
193
|
+
result = profit.transform(sample_source_data)
|
|
194
|
+
|
|
195
|
+
assert all(result["code"] == ["000001.SZ", "000001.SZ", "000001.SZ"])
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def test_companyprofit_transform_date_format(mock_session, sample_source_data):
|
|
199
|
+
"""Test that dates are formatted correctly (YYYY-MM-DD)"""
|
|
200
|
+
profit = CompanyProfit(session=mock_session)
|
|
201
|
+
result = profit.transform(sample_source_data)
|
|
202
|
+
|
|
203
|
+
# Check date format
|
|
204
|
+
assert result["date"].iloc[0] == "2020-03-31"
|
|
205
|
+
assert result["ann_date"].iloc[0] == "2020-04-25"
|
|
206
|
+
assert result["f_ann_date"].iloc[0] == "2020-04-25"
|
|
207
|
+
assert result["end_date"].iloc[0] == "2020-03-31"
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def test_companyprofit_transform_datecode_preserved(mock_session, sample_source_data):
|
|
211
|
+
"""Test that datecode is preserved in YYYYMMDD format"""
|
|
212
|
+
profit = CompanyProfit(session=mock_session)
|
|
213
|
+
result = profit.transform(sample_source_data)
|
|
214
|
+
|
|
215
|
+
assert result["datecode"].iloc[0] == "20200331"
|
|
216
|
+
assert result["datecode"].iloc[1] == "20200630"
|
|
217
|
+
assert result["datecode"].iloc[2] == "20200930"
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def test_companyprofit_transform_numeric_conversions(mock_session, sample_source_data):
|
|
221
|
+
"""Test that numeric fields are properly converted"""
|
|
222
|
+
profit = CompanyProfit(session=mock_session)
|
|
223
|
+
result = profit.transform(sample_source_data)
|
|
224
|
+
|
|
225
|
+
# Check numeric fields
|
|
226
|
+
assert result["basic_eps"].dtype == "float64"
|
|
227
|
+
assert result["diluted_eps"].dtype == "float64"
|
|
228
|
+
assert result["total_revenue"].dtype == "float64"
|
|
229
|
+
assert result["n_income"].dtype == "float64"
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def test_companyprofit_transform_string_fields(mock_session, sample_source_data):
|
|
233
|
+
"""Test that string fields are properly converted"""
|
|
234
|
+
profit = CompanyProfit(session=mock_session)
|
|
235
|
+
result = profit.transform(sample_source_data)
|
|
236
|
+
|
|
237
|
+
assert result["report_type"].dtype == "object"
|
|
238
|
+
assert result["comp_type"].dtype == "object"
|
|
239
|
+
assert result["end_type"].dtype == "object"
|
|
240
|
+
assert result["update_flag"].dtype == "object"
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def test_companyprofit_transform_empty_dataframe(mock_session):
|
|
244
|
+
"""Test transform with empty DataFrame"""
|
|
245
|
+
profit = CompanyProfit(session=mock_session)
|
|
246
|
+
empty_df = pd.DataFrame()
|
|
247
|
+
result = profit.transform(empty_df)
|
|
248
|
+
|
|
249
|
+
assert result.empty
|
|
250
|
+
assert len(result.columns) == len(TARGET.list_column_names())
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def test_companyprofit_transform_none_input(mock_session):
|
|
254
|
+
"""Test transform with None input"""
|
|
255
|
+
profit = CompanyProfit(session=mock_session)
|
|
256
|
+
result = profit.transform(None)
|
|
257
|
+
|
|
258
|
+
assert result.empty
|
|
259
|
+
assert len(result.columns) == len(TARGET.list_column_names())
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def test_companyprofit_transform_handles_invalid_dates(mock_session):
|
|
263
|
+
"""Test transform handles invalid dates gracefully"""
|
|
264
|
+
profit = CompanyProfit(session=mock_session)
|
|
265
|
+
data = pd.DataFrame(
|
|
266
|
+
{
|
|
267
|
+
"ts_code": ["000001.SZ"],
|
|
268
|
+
"end_date": ["invalid"],
|
|
269
|
+
"ann_date": ["20200101"],
|
|
270
|
+
"f_ann_date": ["20200101"],
|
|
271
|
+
"report_type": ["1"],
|
|
272
|
+
"comp_type": ["1"],
|
|
273
|
+
"end_type": ["12"],
|
|
274
|
+
"update_flag": ["0"],
|
|
275
|
+
}
|
|
276
|
+
)
|
|
277
|
+
result = profit.transform(data)
|
|
278
|
+
|
|
279
|
+
assert not result.empty
|
|
280
|
+
assert pd.isna(result["date"].iloc[0]) or result["date"].iloc[0] == "NaT"
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
def test_companyprofit_transform_removes_duplicates(mock_session):
|
|
284
|
+
"""Test that duplicates are removed"""
|
|
285
|
+
profit = CompanyProfit(session=mock_session)
|
|
286
|
+
data = pd.DataFrame(
|
|
287
|
+
{
|
|
288
|
+
"ts_code": ["000001.SZ", "000001.SZ"],
|
|
289
|
+
"end_date": ["20200331", "20200331"],
|
|
290
|
+
"ann_date": ["20200425", "20200425"],
|
|
291
|
+
"f_ann_date": ["20200425", "20200425"],
|
|
292
|
+
"report_type": ["1", "1"],
|
|
293
|
+
"comp_type": ["1", "1"],
|
|
294
|
+
"end_type": ["12", "12"],
|
|
295
|
+
"update_flag": ["0", "0"],
|
|
296
|
+
"basic_eps": [0.5, 0.5],
|
|
297
|
+
}
|
|
298
|
+
)
|
|
299
|
+
result = profit.transform(data)
|
|
300
|
+
|
|
301
|
+
assert len(result) == 1
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
def test_companyprofit_transform_sorts_by_code_and_date(mock_session):
|
|
305
|
+
"""Test that output is sorted by code and date"""
|
|
306
|
+
profit = CompanyProfit(session=mock_session)
|
|
307
|
+
data = pd.DataFrame(
|
|
308
|
+
{
|
|
309
|
+
"ts_code": ["000002.SZ", "000001.SZ", "000001.SZ"],
|
|
310
|
+
"end_date": ["20200331", "20200630", "20200331"],
|
|
311
|
+
"ann_date": ["20200425", "20200826", "20200425"],
|
|
312
|
+
"f_ann_date": ["20200425", "20200826", "20200425"],
|
|
313
|
+
"report_type": ["1", "1", "1"],
|
|
314
|
+
"comp_type": ["1", "1", "1"],
|
|
315
|
+
"end_type": ["12", "12", "12"],
|
|
316
|
+
"update_flag": ["0", "0", "0"],
|
|
317
|
+
}
|
|
318
|
+
)
|
|
319
|
+
result = profit.transform(data)
|
|
320
|
+
|
|
321
|
+
assert result["code"].iloc[0] == "000001.SZ"
|
|
322
|
+
assert result["code"].iloc[1] == "000001.SZ"
|
|
323
|
+
assert result["code"].iloc[2] == "000002.SZ"
|
|
324
|
+
assert result["date"].iloc[0] == "2020-03-31"
|
|
325
|
+
assert result["date"].iloc[1] == "2020-06-30"
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
def test_companyprofit_transform_resets_index(mock_session, sample_source_data):
|
|
329
|
+
"""Test that index is reset after transformation"""
|
|
330
|
+
profit = CompanyProfit(session=mock_session)
|
|
331
|
+
result = profit.transform(sample_source_data)
|
|
332
|
+
|
|
333
|
+
assert list(result.index) == [0, 1, 2]
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
def test_companyprofit_transform_only_target_columns(mock_session, sample_source_data):
|
|
337
|
+
"""Test that only target columns are returned"""
|
|
338
|
+
profit = CompanyProfit(session=mock_session)
|
|
339
|
+
|
|
340
|
+
# Add extra column to source data
|
|
341
|
+
data_with_extra = sample_source_data.copy()
|
|
342
|
+
data_with_extra["extra_column"] = "test"
|
|
343
|
+
|
|
344
|
+
result = profit.transform(data_with_extra)
|
|
345
|
+
|
|
346
|
+
assert "extra_column" not in result.columns
|
|
347
|
+
assert "ts_code" not in result.columns # Should be renamed to "code"
|
|
348
|
+
assert "code" in result.columns
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
# ============================================================================
|
|
352
|
+
# Run Tests
|
|
353
|
+
# ============================================================================
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
def test_companyprofit_run_with_cache_hit(mock_session, sample_source_data):
|
|
357
|
+
"""Test run with cache hit (no API call)"""
|
|
358
|
+
profit = CompanyProfit(
|
|
359
|
+
session=mock_session,
|
|
360
|
+
params={"ts_code": "000001.SZ"},
|
|
361
|
+
cache=True,
|
|
362
|
+
)
|
|
363
|
+
|
|
364
|
+
# Mock cache load to return data
|
|
365
|
+
with patch.object(profit, "_load_cache", return_value=sample_source_data):
|
|
366
|
+
result = profit.run()
|
|
367
|
+
|
|
368
|
+
assert not result.empty
|
|
369
|
+
mock_session.connection.income_vip.assert_not_called()
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
def test_companyprofit_run_basic_date_range(mock_session, sample_source_data):
|
|
373
|
+
"""Test run with start_date and end_date"""
|
|
374
|
+
profit = CompanyProfit(
|
|
375
|
+
session=mock_session,
|
|
376
|
+
params={"ts_code": "000001.SZ", "start_date": "20200101", "end_date": "20201231"},
|
|
377
|
+
)
|
|
378
|
+
|
|
379
|
+
with patch.object(profit, "_fetchall", return_value=sample_source_data):
|
|
380
|
+
with patch.object(profit, "_save_cache"):
|
|
381
|
+
result = profit.run()
|
|
382
|
+
|
|
383
|
+
assert not result.empty
|
|
384
|
+
assert "code" in result.columns
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
def test_companyprofit_run_with_year_param(mock_session, sample_source_data):
|
|
388
|
+
"""Test that year parameter is converted to start_date and end_date"""
|
|
389
|
+
profit = CompanyProfit(session=mock_session, params={"ts_code": "000001.SZ", "year": "2020"})
|
|
390
|
+
|
|
391
|
+
with patch.object(profit, "_fetchall", return_value=sample_source_data) as mock_fetch:
|
|
392
|
+
with patch.object(profit, "_save_cache"):
|
|
393
|
+
profit.run()
|
|
394
|
+
|
|
395
|
+
# Verify that year was converted to date range
|
|
396
|
+
call_kwargs = mock_fetch.call_args[1]
|
|
397
|
+
assert call_kwargs["start_date"] == "20200101"
|
|
398
|
+
assert call_kwargs["end_date"] == "20201231"
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
def test_companyprofit_run_with_year_param_int(mock_session, sample_source_data):
|
|
402
|
+
"""Test year parameter as integer"""
|
|
403
|
+
profit = CompanyProfit(session=mock_session, params={"ts_code": "000001.SZ", "year": 2020})
|
|
404
|
+
|
|
405
|
+
with patch.object(profit, "_fetchall", return_value=sample_source_data) as mock_fetch:
|
|
406
|
+
with patch.object(profit, "_save_cache"):
|
|
407
|
+
profit.run()
|
|
408
|
+
|
|
409
|
+
call_kwargs = mock_fetch.call_args[1]
|
|
410
|
+
assert call_kwargs["start_date"] == "20200101"
|
|
411
|
+
assert call_kwargs["end_date"] == "20201231"
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
def test_companyprofit_run_with_datetime_params(mock_session, sample_source_data):
|
|
415
|
+
"""Test run with datetime objects as parameters"""
|
|
416
|
+
profit = CompanyProfit(
|
|
417
|
+
session=mock_session,
|
|
418
|
+
params={
|
|
419
|
+
"ts_code": "000001.SZ",
|
|
420
|
+
"start_date": datetime(2020, 1, 1),
|
|
421
|
+
"end_date": datetime(2020, 12, 31),
|
|
422
|
+
},
|
|
423
|
+
)
|
|
424
|
+
|
|
425
|
+
with patch.object(profit, "_fetchall", return_value=sample_source_data) as mock_fetch:
|
|
426
|
+
with patch.object(profit, "_save_cache"):
|
|
427
|
+
profit.run()
|
|
428
|
+
|
|
429
|
+
call_kwargs = mock_fetch.call_args[1]
|
|
430
|
+
assert call_kwargs["start_date"] == "20200101"
|
|
431
|
+
assert call_kwargs["end_date"] == "20201231"
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
def test_companyprofit_run_adds_fields_param(mock_session, sample_source_data):
|
|
435
|
+
"""Test that fields parameter is automatically added"""
|
|
436
|
+
profit = CompanyProfit(session=mock_session, params={"ts_code": "000001.SZ"})
|
|
437
|
+
|
|
438
|
+
with patch.object(profit, "_fetchall", return_value=sample_source_data) as mock_fetch:
|
|
439
|
+
with patch.object(profit, "_save_cache"):
|
|
440
|
+
profit.run()
|
|
441
|
+
|
|
442
|
+
call_kwargs = mock_fetch.call_args[1]
|
|
443
|
+
assert "fields" in call_kwargs
|
|
444
|
+
assert isinstance(call_kwargs["fields"], str)
|
|
445
|
+
assert "ts_code" in call_kwargs["fields"]
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
def test_companyprofit_run_sets_cache(mock_session, sample_source_data):
|
|
449
|
+
"""Test that result is saved to cache"""
|
|
450
|
+
profit = CompanyProfit(
|
|
451
|
+
session=mock_session,
|
|
452
|
+
params={"ts_code": "000001.SZ"},
|
|
453
|
+
cache=True,
|
|
454
|
+
)
|
|
455
|
+
|
|
456
|
+
with patch.object(profit, "_fetchall", return_value=sample_source_data):
|
|
457
|
+
with patch.object(profit, "_save_cache") as mock_save:
|
|
458
|
+
profit.run()
|
|
459
|
+
|
|
460
|
+
mock_save.assert_called_once()
|
|
461
|
+
|
|
462
|
+
|
|
463
|
+
def test_companyprofit_run_calls_transform(mock_session, sample_source_data):
|
|
464
|
+
"""Test that run calls transform method"""
|
|
465
|
+
profit = CompanyProfit(session=mock_session, params={"ts_code": "000001.SZ"})
|
|
466
|
+
|
|
467
|
+
with patch.object(profit, "_fetchall", return_value=sample_source_data):
|
|
468
|
+
with patch.object(profit, "transform", return_value=pd.DataFrame()) as mock_transform:
|
|
469
|
+
with patch.object(profit, "_save_cache"):
|
|
470
|
+
profit.run()
|
|
471
|
+
|
|
472
|
+
mock_transform.assert_called_once()
|
|
473
|
+
|
|
474
|
+
|
|
475
|
+
def test_companyprofit_run_uses_income_vip_api(mock_session, sample_source_data):
|
|
476
|
+
"""Test that run method uses the correct API endpoint"""
|
|
477
|
+
profit = CompanyProfit(session=mock_session, params={"ts_code": "000001.SZ"})
|
|
478
|
+
|
|
479
|
+
with patch.object(profit, "_fetchall", return_value=sample_source_data) as mock_fetch:
|
|
480
|
+
with patch.object(profit, "_save_cache"):
|
|
481
|
+
profit.run()
|
|
482
|
+
|
|
483
|
+
# Verify the correct API method is used
|
|
484
|
+
assert mock_fetch.call_args[1]["api"] == mock_session.connection.income_vip
|
|
485
|
+
|
|
486
|
+
|
|
487
|
+
# ============================================================================
|
|
488
|
+
# Integration Tests
|
|
489
|
+
# ============================================================================
|
|
490
|
+
|
|
491
|
+
|
|
492
|
+
def test_companyprofit_full_workflow(mock_session, sample_source_data):
|
|
493
|
+
"""Test complete workflow from initialization to result"""
|
|
494
|
+
profit = CompanyProfit(
|
|
495
|
+
session=mock_session,
|
|
496
|
+
params={"ts_code": "000001.SZ", "start_date": "20200101", "end_date": "20201231"},
|
|
497
|
+
cache=True,
|
|
498
|
+
)
|
|
499
|
+
|
|
500
|
+
with patch.object(profit, "_fetchall", return_value=sample_source_data):
|
|
501
|
+
with patch.object(profit, "_save_cache"):
|
|
502
|
+
result = profit.run()
|
|
503
|
+
|
|
504
|
+
assert not result.empty
|
|
505
|
+
assert "code" in result.columns
|
|
506
|
+
assert "date" in result.columns
|
|
507
|
+
assert "datecode" in result.columns
|
|
508
|
+
assert all(result["code"] == "000001.SZ")
|
|
509
|
+
|
|
510
|
+
|
|
511
|
+
def test_companyprofit_cache_persistence(mock_session, sample_source_data):
|
|
512
|
+
"""Test that cache persists across runs"""
|
|
513
|
+
profit = CompanyProfit(
|
|
514
|
+
session=mock_session,
|
|
515
|
+
params={"ts_code": "000001.SZ"},
|
|
516
|
+
cache=True,
|
|
517
|
+
)
|
|
518
|
+
|
|
519
|
+
# First run - should fetch data
|
|
520
|
+
with patch.object(profit, "_fetchall", return_value=sample_source_data) as mock_fetch:
|
|
521
|
+
with patch.object(profit, "_load_cache", return_value=None):
|
|
522
|
+
with patch.object(profit, "_save_cache"):
|
|
523
|
+
first_result = profit.run()
|
|
524
|
+
assert mock_fetch.called
|
|
525
|
+
|
|
526
|
+
# Second run - should use cache
|
|
527
|
+
with patch.object(profit, "_fetchall", return_value=sample_source_data) as mock_fetch:
|
|
528
|
+
with patch.object(profit, "_load_cache", return_value=first_result):
|
|
529
|
+
profit.run()
|
|
530
|
+
assert not mock_fetch.called
|
|
531
|
+
|
|
532
|
+
|
|
533
|
+
def test_companyprofit_params_identifier_uniqueness(mock_session):
|
|
534
|
+
"""Test that different params create different cache identifiers"""
|
|
535
|
+
profit1 = CompanyProfit(
|
|
536
|
+
session=mock_session,
|
|
537
|
+
params={"ts_code": "000001.SZ", "start_date": "20200101"},
|
|
538
|
+
cache=True,
|
|
539
|
+
)
|
|
540
|
+
profit2 = CompanyProfit(
|
|
541
|
+
session=mock_session,
|
|
542
|
+
params={"ts_code": "000001.SZ", "start_date": "20210101"},
|
|
543
|
+
cache=True,
|
|
544
|
+
)
|
|
545
|
+
|
|
546
|
+
# Test that different params are properly stored
|
|
547
|
+
assert profit1.params.get("start_date") == "20200101"
|
|
548
|
+
assert profit2.params.get("start_date") == "20210101"
|
|
549
|
+
assert profit1.params.get("start_date") != profit2.params.get("start_date")
|
|
550
|
+
|
|
551
|
+
|
|
552
|
+
def test_companyprofit_different_stocks(mock_session, sample_source_data):
|
|
553
|
+
"""Test that different stock codes are handled correctly"""
|
|
554
|
+
profit1 = CompanyProfit(session=mock_session, params={"ts_code": "000001.SZ"})
|
|
555
|
+
profit2 = CompanyProfit(session=mock_session, params={"ts_code": "000002.SZ"})
|
|
556
|
+
|
|
557
|
+
assert profit1.params.get("ts_code") != profit2.params.get("ts_code")
|
|
558
|
+
|
|
559
|
+
|
|
560
|
+
def test_companyprofit_empty_result_handling(mock_session):
|
|
561
|
+
"""Test handling of empty result from API"""
|
|
562
|
+
profit = CompanyProfit(session=mock_session, params={"ts_code": "000001.SZ"})
|
|
563
|
+
|
|
564
|
+
with patch.object(profit, "_fetchall", return_value=pd.DataFrame()):
|
|
565
|
+
with patch.object(profit, "_save_cache"):
|
|
566
|
+
result = profit.run()
|
|
567
|
+
|
|
568
|
+
assert result.empty
|
|
569
|
+
assert len(result.columns) == len(TARGET.list_column_names())
|
|
570
|
+
|
|
571
|
+
|
|
572
|
+
def test_companyprofit_large_dataset_handling(mock_session):
|
|
573
|
+
"""Test handling of large dataset"""
|
|
574
|
+
large_data = pd.DataFrame(
|
|
575
|
+
{
|
|
576
|
+
"ts_code": ["000001.SZ"] * 1000,
|
|
577
|
+
"end_date": ["20200331"] * 1000,
|
|
578
|
+
"ann_date": ["20200425"] * 1000,
|
|
579
|
+
"f_ann_date": ["20200425"] * 1000,
|
|
580
|
+
"report_type": ["1"] * 1000,
|
|
581
|
+
"comp_type": ["1"] * 1000,
|
|
582
|
+
"end_type": ["12"] * 1000,
|
|
583
|
+
"update_flag": ["0"] * 1000,
|
|
584
|
+
"basic_eps": [0.5] * 1000,
|
|
585
|
+
}
|
|
586
|
+
)
|
|
587
|
+
|
|
588
|
+
profit = CompanyProfit(session=mock_session, params={"ts_code": "000001.SZ"})
|
|
589
|
+
|
|
590
|
+
with patch.object(profit, "_fetchall", return_value=large_data):
|
|
591
|
+
with patch.object(profit, "_save_cache"):
|
|
592
|
+
result = profit.run()
|
|
593
|
+
|
|
594
|
+
# After deduplication, should have only 1 row
|
|
595
|
+
assert len(result) == 1
|
|
596
|
+
|
|
597
|
+
|
|
598
|
+
def test_companyprofit_without_cache(mock_session, sample_source_data):
|
|
599
|
+
"""Test that data fetching works without cache"""
|
|
600
|
+
profit = CompanyProfit(
|
|
601
|
+
session=mock_session,
|
|
602
|
+
params={"ts_code": "000001.SZ"},
|
|
603
|
+
cache=False,
|
|
604
|
+
)
|
|
605
|
+
|
|
606
|
+
with patch.object(profit, "_fetchall", return_value=sample_source_data):
|
|
607
|
+
result = profit.run()
|
|
608
|
+
|
|
609
|
+
assert not result.empty
|
|
610
|
+
assert profit.cache is None
|
|
611
|
+
|
|
612
|
+
|
|
613
|
+
def test_companyprofit_handles_missing_numeric_fields(mock_session):
|
|
614
|
+
"""Test handling of missing numeric values"""
|
|
615
|
+
data = pd.DataFrame(
|
|
616
|
+
{
|
|
617
|
+
"ts_code": ["000001.SZ"],
|
|
618
|
+
"end_date": ["20200331"],
|
|
619
|
+
"ann_date": ["20200425"],
|
|
620
|
+
"f_ann_date": ["20200425"],
|
|
621
|
+
"report_type": ["1"],
|
|
622
|
+
"comp_type": ["1"],
|
|
623
|
+
"end_type": ["12"],
|
|
624
|
+
"update_flag": ["0"],
|
|
625
|
+
"basic_eps": [None],
|
|
626
|
+
"n_income": [None],
|
|
627
|
+
}
|
|
628
|
+
)
|
|
629
|
+
|
|
630
|
+
profit = CompanyProfit(session=mock_session, params={"ts_code": "000001.SZ"})
|
|
631
|
+
result = profit.transform(data)
|
|
632
|
+
|
|
633
|
+
assert not result.empty
|
|
634
|
+
assert pd.isna(result["basic_eps"].iloc[0])
|
|
635
|
+
assert pd.isna(result["n_income"].iloc[0])
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from xfintech.data.source.tushare.stock.conceptcapflowdc.conceptcapflowdc import (
|
|
4
|
+
ConceptCapflowDC,
|
|
5
|
+
)
|
|
6
|
+
from xfintech.data.source.tushare.stock.conceptcapflowdc.constant import (
|
|
7
|
+
KEY,
|
|
8
|
+
NAME,
|
|
9
|
+
PAGINATE,
|
|
10
|
+
SOURCE,
|
|
11
|
+
TARGET,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"ConceptCapflowDC",
|
|
16
|
+
"KEY",
|
|
17
|
+
"NAME",
|
|
18
|
+
"PAGINATE",
|
|
19
|
+
"SOURCE",
|
|
20
|
+
"TARGET",
|
|
21
|
+
]
|