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,564 @@
|
|
|
1
|
+
from unittest.mock import MagicMock, patch
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from xfintech.connect.artifact import Artifact
|
|
6
|
+
from xfintech.connect.common.connectref import ConnectRef
|
|
7
|
+
|
|
8
|
+
# ============================================================================
|
|
9
|
+
# Fixtures
|
|
10
|
+
# ============================================================================
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@pytest.fixture
|
|
14
|
+
def mock_connect_ref():
|
|
15
|
+
"""Create a mock ConnectRef"""
|
|
16
|
+
ref = MagicMock(spec=ConnectRef)
|
|
17
|
+
ref.location = "s3://test-bucket/test-key.json"
|
|
18
|
+
ref.describe = MagicMock(return_value={"location": ref.location})
|
|
19
|
+
ref.to_dict = MagicMock(return_value={"location": ref.location})
|
|
20
|
+
ref.__str__ = MagicMock(return_value=ref.location)
|
|
21
|
+
return ref
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@pytest.fixture
|
|
25
|
+
def mock_connect():
|
|
26
|
+
"""Create a mock Connect object"""
|
|
27
|
+
connect = MagicMock()
|
|
28
|
+
connect.get_object = MagicMock(return_value=b'{"key": "value"}')
|
|
29
|
+
connect.put_object = MagicMock(return_value=MagicMock(location="s3://test-bucket/test-key.json"))
|
|
30
|
+
return connect
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@pytest.fixture
|
|
34
|
+
def mock_serialiser():
|
|
35
|
+
"""Create a mock Serialiser"""
|
|
36
|
+
serialiser = MagicMock()
|
|
37
|
+
serialiser.serialise = MagicMock(return_value=b'{"serialised": true}')
|
|
38
|
+
return serialiser
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@pytest.fixture
|
|
42
|
+
def mock_deserialiser():
|
|
43
|
+
"""Create a mock Deserialiser"""
|
|
44
|
+
deserialiser = MagicMock()
|
|
45
|
+
deserialiser.deserialise = MagicMock(return_value={"deserialised": True})
|
|
46
|
+
return deserialiser
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
# ============================================================================
|
|
50
|
+
# Initialization Tests
|
|
51
|
+
# ============================================================================
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def test_artifact_init_with_ref_object(mock_connect_ref):
|
|
55
|
+
"""Test Artifact initialization with ConnectRef object"""
|
|
56
|
+
artifact = Artifact(ref=mock_connect_ref)
|
|
57
|
+
|
|
58
|
+
assert artifact.ref == mock_connect_ref
|
|
59
|
+
assert artifact.data is None
|
|
60
|
+
assert artifact.meta is None
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def test_artifact_init_with_ref_dict():
|
|
64
|
+
"""Test Artifact initialization with ref as dict"""
|
|
65
|
+
ref_dict = {"location": "s3://test-bucket/test-key.json"}
|
|
66
|
+
|
|
67
|
+
with patch("xfintech.connect.artifact.artifact.ConnectRef.from_dict") as mock_from_dict:
|
|
68
|
+
mock_ref = MagicMock()
|
|
69
|
+
mock_from_dict.return_value = mock_ref
|
|
70
|
+
|
|
71
|
+
artifact = Artifact(ref=ref_dict)
|
|
72
|
+
|
|
73
|
+
mock_from_dict.assert_called_once_with(ref_dict)
|
|
74
|
+
assert artifact.ref == mock_ref
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def test_artifact_init_with_data(mock_connect_ref):
|
|
78
|
+
"""Test Artifact initialization with data"""
|
|
79
|
+
test_data = {"key": "value", "number": 123}
|
|
80
|
+
artifact = Artifact(ref=mock_connect_ref, data=test_data)
|
|
81
|
+
|
|
82
|
+
assert artifact.ref == mock_connect_ref
|
|
83
|
+
assert artifact.data == test_data
|
|
84
|
+
assert artifact.meta is None
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def test_artifact_init_with_meta(mock_connect_ref):
|
|
88
|
+
"""Test Artifact initialization with metadata"""
|
|
89
|
+
test_meta = {"author": "test", "version": "1.0"}
|
|
90
|
+
artifact = Artifact(ref=mock_connect_ref, meta=test_meta)
|
|
91
|
+
|
|
92
|
+
assert artifact.ref == mock_connect_ref
|
|
93
|
+
assert artifact.data is None
|
|
94
|
+
assert artifact.meta == test_meta
|
|
95
|
+
assert artifact.meta is not test_meta # Should be a copy
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def test_artifact_init_with_all_params(mock_connect_ref):
|
|
99
|
+
"""Test Artifact initialization with all parameters"""
|
|
100
|
+
test_data = {"key": "value"}
|
|
101
|
+
test_meta = {"author": "test"}
|
|
102
|
+
|
|
103
|
+
artifact = Artifact(ref=mock_connect_ref, data=test_data, meta=test_meta)
|
|
104
|
+
|
|
105
|
+
assert artifact.ref == mock_connect_ref
|
|
106
|
+
assert artifact.data == test_data
|
|
107
|
+
assert artifact.meta == test_meta
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def test_artifact_init_with_invalid_ref_type():
|
|
111
|
+
"""Test Artifact initialization with invalid ref type"""
|
|
112
|
+
with pytest.raises(TypeError, match="Invalid type for ref"):
|
|
113
|
+
Artifact(ref="invalid_string")
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def test_artifact_init_with_none_ref():
|
|
117
|
+
"""Test Artifact initialization with None ref"""
|
|
118
|
+
with pytest.raises(TypeError):
|
|
119
|
+
Artifact(ref=None)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def test_artifact_meta_is_copied(mock_connect_ref):
|
|
123
|
+
"""Test that meta dict is copied, not referenced"""
|
|
124
|
+
original_meta = {"key": "value"}
|
|
125
|
+
artifact = Artifact(ref=mock_connect_ref, meta=original_meta)
|
|
126
|
+
|
|
127
|
+
# Modify original meta
|
|
128
|
+
original_meta["key"] = "changed"
|
|
129
|
+
|
|
130
|
+
# Artifact meta should be unchanged
|
|
131
|
+
assert artifact.meta["key"] == "value"
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
# ============================================================================
|
|
135
|
+
# from_dict Tests
|
|
136
|
+
# ============================================================================
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def test_artifact_from_dict_basic():
|
|
140
|
+
"""Test creating Artifact from dict with minimal data"""
|
|
141
|
+
ref_dict = {"location": "s3://test-bucket/test-key.json"}
|
|
142
|
+
artifact_dict = {"ref": ref_dict}
|
|
143
|
+
|
|
144
|
+
with patch("xfintech.connect.artifact.artifact.ConnectRef.from_dict") as mock_from_dict:
|
|
145
|
+
mock_ref = MagicMock()
|
|
146
|
+
mock_from_dict.return_value = mock_ref
|
|
147
|
+
|
|
148
|
+
artifact = Artifact.from_dict(artifact_dict)
|
|
149
|
+
|
|
150
|
+
assert artifact.ref == mock_ref
|
|
151
|
+
assert artifact.data is None
|
|
152
|
+
assert artifact.meta is None
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def test_artifact_from_dict_with_data():
|
|
156
|
+
"""Test creating Artifact from dict with data"""
|
|
157
|
+
ref_dict = {"location": "s3://test-bucket/test-key.json"}
|
|
158
|
+
test_data = {"key": "value"}
|
|
159
|
+
artifact_dict = {"ref": ref_dict, "data": test_data}
|
|
160
|
+
|
|
161
|
+
with patch("xfintech.connect.artifact.artifact.ConnectRef.from_dict") as mock_from_dict:
|
|
162
|
+
mock_ref = MagicMock()
|
|
163
|
+
mock_from_dict.return_value = mock_ref
|
|
164
|
+
|
|
165
|
+
artifact = Artifact.from_dict(artifact_dict)
|
|
166
|
+
|
|
167
|
+
assert artifact.data == test_data
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def test_artifact_from_dict_with_meta():
|
|
171
|
+
"""Test creating Artifact from dict with metadata"""
|
|
172
|
+
ref_dict = {"location": "s3://test-bucket/test-key.json"}
|
|
173
|
+
test_meta = {"author": "test"}
|
|
174
|
+
artifact_dict = {"ref": ref_dict, "meta": test_meta}
|
|
175
|
+
|
|
176
|
+
with patch("xfintech.connect.artifact.artifact.ConnectRef.from_dict") as mock_from_dict:
|
|
177
|
+
mock_ref = MagicMock()
|
|
178
|
+
mock_from_dict.return_value = mock_ref
|
|
179
|
+
|
|
180
|
+
artifact = Artifact.from_dict(artifact_dict)
|
|
181
|
+
|
|
182
|
+
assert artifact.meta == test_meta
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def test_artifact_from_dict_missing_ref():
|
|
186
|
+
"""Test from_dict raises error when ref is missing"""
|
|
187
|
+
artifact_dict = {"data": {"key": "value"}}
|
|
188
|
+
|
|
189
|
+
with pytest.raises(KeyError):
|
|
190
|
+
Artifact.from_dict(artifact_dict)
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def test_artifact_from_dict_with_all_fields():
|
|
194
|
+
"""Test creating Artifact from dict with all fields"""
|
|
195
|
+
ref_dict = {"location": "s3://test-bucket/test-key.json"}
|
|
196
|
+
test_data = {"key": "value"}
|
|
197
|
+
test_meta = {"author": "test", "version": "1.0"}
|
|
198
|
+
artifact_dict = {"ref": ref_dict, "data": test_data, "meta": test_meta}
|
|
199
|
+
|
|
200
|
+
with patch("xfintech.connect.artifact.artifact.ConnectRef.from_dict") as mock_from_dict:
|
|
201
|
+
mock_ref = MagicMock()
|
|
202
|
+
mock_from_dict.return_value = mock_ref
|
|
203
|
+
|
|
204
|
+
artifact = Artifact.from_dict(artifact_dict)
|
|
205
|
+
|
|
206
|
+
assert artifact.ref == mock_ref
|
|
207
|
+
assert artifact.data == test_data
|
|
208
|
+
assert artifact.meta == test_meta
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
# ============================================================================
|
|
212
|
+
# String Representation Tests
|
|
213
|
+
# ============================================================================
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def test_artifact_str(mock_connect_ref):
|
|
217
|
+
"""Test __str__ method"""
|
|
218
|
+
artifact = Artifact(ref=mock_connect_ref)
|
|
219
|
+
|
|
220
|
+
result = str(artifact)
|
|
221
|
+
|
|
222
|
+
assert result == str(mock_connect_ref)
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def test_artifact_repr(mock_connect_ref):
|
|
226
|
+
"""Test __repr__ method"""
|
|
227
|
+
artifact = Artifact(ref=mock_connect_ref)
|
|
228
|
+
|
|
229
|
+
result = repr(artifact)
|
|
230
|
+
|
|
231
|
+
assert "Artifact" in result
|
|
232
|
+
assert str(mock_connect_ref) in result
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
# ============================================================================
|
|
236
|
+
# write Method Tests
|
|
237
|
+
# ============================================================================
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def test_artifact_write_basic(mock_connect_ref, mock_connect):
|
|
241
|
+
"""Test basic write operation without serialisation"""
|
|
242
|
+
test_data = {"key": "value"}
|
|
243
|
+
artifact = Artifact(ref=mock_connect_ref, data=test_data)
|
|
244
|
+
|
|
245
|
+
artifact.write(connect=mock_connect)
|
|
246
|
+
|
|
247
|
+
mock_connect.put_object.assert_called_once_with(
|
|
248
|
+
data=test_data,
|
|
249
|
+
location=mock_connect_ref.location,
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def test_artifact_write_with_serialiser(mock_connect_ref, mock_connect, mock_serialiser):
|
|
254
|
+
"""Test write operation with serialiser"""
|
|
255
|
+
test_data = {"key": "value"}
|
|
256
|
+
artifact = Artifact(ref=mock_connect_ref, data=test_data)
|
|
257
|
+
|
|
258
|
+
artifact.write(connect=mock_connect, serialiser=mock_serialiser, format="json")
|
|
259
|
+
|
|
260
|
+
mock_serialiser.serialise.assert_called_once_with(
|
|
261
|
+
test_data,
|
|
262
|
+
format="json",
|
|
263
|
+
)
|
|
264
|
+
mock_connect.put_object.assert_called_once()
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def test_artifact_write_no_data(mock_connect_ref, mock_connect):
|
|
268
|
+
"""Test write raises error when no data"""
|
|
269
|
+
artifact = Artifact(ref=mock_connect_ref)
|
|
270
|
+
|
|
271
|
+
with pytest.raises(ValueError, match="No data to write"):
|
|
272
|
+
artifact.write(connect=mock_connect)
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def test_artifact_write_updates_ref(mock_connect_ref, mock_connect):
|
|
276
|
+
"""Test write updates the ref with returned value"""
|
|
277
|
+
test_data = {"key": "value"}
|
|
278
|
+
artifact = Artifact(ref=mock_connect_ref, data=test_data)
|
|
279
|
+
|
|
280
|
+
new_ref = MagicMock()
|
|
281
|
+
new_ref.location = "s3://test-bucket/new-key.json"
|
|
282
|
+
mock_connect.put_object.return_value = new_ref
|
|
283
|
+
|
|
284
|
+
artifact.write(connect=mock_connect)
|
|
285
|
+
|
|
286
|
+
assert artifact.ref == new_ref
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
def test_artifact_write_with_kwargs(mock_connect_ref, mock_connect):
|
|
290
|
+
"""Test write passes through kwargs"""
|
|
291
|
+
test_data = {"key": "value"}
|
|
292
|
+
artifact = Artifact(ref=mock_connect_ref, data=test_data)
|
|
293
|
+
|
|
294
|
+
artifact.write(connect=mock_connect, custom_param="value")
|
|
295
|
+
|
|
296
|
+
mock_connect.put_object.assert_called_once_with(
|
|
297
|
+
data=test_data,
|
|
298
|
+
location=mock_connect_ref.location,
|
|
299
|
+
custom_param="value",
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
def test_artifact_write_serialiser_error(mock_connect_ref, mock_connect, mock_serialiser):
|
|
304
|
+
"""Test write handles serialiser errors"""
|
|
305
|
+
test_data = {"key": "value"}
|
|
306
|
+
artifact = Artifact(ref=mock_connect_ref, data=test_data)
|
|
307
|
+
|
|
308
|
+
mock_serialiser.serialise.side_effect = Exception("Serialisation failed")
|
|
309
|
+
|
|
310
|
+
with pytest.raises(Exception, match="Failed to write artifact"):
|
|
311
|
+
artifact.write(connect=mock_connect, serialiser=mock_serialiser, format="json")
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
def test_artifact_write_connect_error(mock_connect_ref, mock_connect):
|
|
315
|
+
"""Test write handles connection errors"""
|
|
316
|
+
test_data = {"key": "value"}
|
|
317
|
+
artifact = Artifact(ref=mock_connect_ref, data=test_data)
|
|
318
|
+
|
|
319
|
+
mock_connect.put_object.side_effect = Exception("Connection failed")
|
|
320
|
+
|
|
321
|
+
with pytest.raises(Exception, match="Failed to write artifact"):
|
|
322
|
+
artifact.write(connect=mock_connect)
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
# ============================================================================
|
|
326
|
+
# read Method Tests
|
|
327
|
+
# ============================================================================
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
def test_artifact_read_basic(mock_connect_ref, mock_connect):
|
|
331
|
+
"""Test basic read operation without deserialisation"""
|
|
332
|
+
artifact = Artifact(ref=mock_connect_ref)
|
|
333
|
+
|
|
334
|
+
result = artifact.read(connect=mock_connect)
|
|
335
|
+
|
|
336
|
+
mock_connect.get_object.assert_called_once_with(
|
|
337
|
+
location=mock_connect_ref.location,
|
|
338
|
+
)
|
|
339
|
+
assert artifact.data == b'{"key": "value"}'
|
|
340
|
+
assert result is artifact # Should return self
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
def test_artifact_read_with_deserialiser(mock_connect_ref, mock_connect, mock_deserialiser):
|
|
344
|
+
"""Test read operation with deserialiser"""
|
|
345
|
+
artifact = Artifact(ref=mock_connect_ref)
|
|
346
|
+
|
|
347
|
+
artifact.read(connect=mock_connect, deserialiser=mock_deserialiser, format="json")
|
|
348
|
+
|
|
349
|
+
mock_connect.get_object.assert_called_once()
|
|
350
|
+
mock_deserialiser.deserialise.assert_called_once_with(
|
|
351
|
+
b'{"key": "value"}',
|
|
352
|
+
format="json",
|
|
353
|
+
)
|
|
354
|
+
assert artifact.data == {"deserialised": True}
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
def test_artifact_read_with_kwargs(mock_connect_ref, mock_connect):
|
|
358
|
+
"""Test read passes through kwargs"""
|
|
359
|
+
artifact = Artifact(ref=mock_connect_ref)
|
|
360
|
+
|
|
361
|
+
artifact.read(connect=mock_connect, custom_param="value")
|
|
362
|
+
|
|
363
|
+
mock_connect.get_object.assert_called_once_with(
|
|
364
|
+
location=mock_connect_ref.location,
|
|
365
|
+
custom_param="value",
|
|
366
|
+
)
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
def test_artifact_read_returns_self(mock_connect_ref, mock_connect):
|
|
370
|
+
"""Test read returns self for chaining"""
|
|
371
|
+
artifact = Artifact(ref=mock_connect_ref)
|
|
372
|
+
|
|
373
|
+
result = artifact.read(connect=mock_connect)
|
|
374
|
+
|
|
375
|
+
assert result is artifact
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
def test_artifact_read_connect_error(mock_connect_ref, mock_connect):
|
|
379
|
+
"""Test read handles connection errors"""
|
|
380
|
+
artifact = Artifact(ref=mock_connect_ref)
|
|
381
|
+
|
|
382
|
+
mock_connect.get_object.side_effect = Exception("Connection failed")
|
|
383
|
+
|
|
384
|
+
with pytest.raises(Exception, match="Failed to read artifact"):
|
|
385
|
+
artifact.read(connect=mock_connect)
|
|
386
|
+
|
|
387
|
+
|
|
388
|
+
def test_artifact_read_deserialiser_error(mock_connect_ref, mock_connect, mock_deserialiser):
|
|
389
|
+
"""Test read handles deserialiser errors"""
|
|
390
|
+
artifact = Artifact(ref=mock_connect_ref)
|
|
391
|
+
|
|
392
|
+
mock_deserialiser.deserialise.side_effect = Exception("Deserialisation failed")
|
|
393
|
+
|
|
394
|
+
with pytest.raises(Exception, match="Failed to read artifact"):
|
|
395
|
+
artifact.read(connect=mock_connect, deserialiser=mock_deserialiser, format="json")
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
# ============================================================================
|
|
399
|
+
# describe Method Tests
|
|
400
|
+
# ============================================================================
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
def test_artifact_describe_basic(mock_connect_ref):
|
|
404
|
+
"""Test describe method with minimal data"""
|
|
405
|
+
artifact = Artifact(ref=mock_connect_ref)
|
|
406
|
+
|
|
407
|
+
result = artifact.describe()
|
|
408
|
+
|
|
409
|
+
assert "ref" in result
|
|
410
|
+
mock_connect_ref.describe.assert_called_once()
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
def test_artifact_describe_with_meta(mock_connect_ref):
|
|
414
|
+
"""Test describe includes metadata"""
|
|
415
|
+
test_meta = {"author": "test", "version": "1.0"}
|
|
416
|
+
artifact = Artifact(ref=mock_connect_ref, meta=test_meta)
|
|
417
|
+
|
|
418
|
+
result = artifact.describe()
|
|
419
|
+
|
|
420
|
+
assert "ref" in result
|
|
421
|
+
assert "meta" in result
|
|
422
|
+
assert result["meta"] == test_meta
|
|
423
|
+
|
|
424
|
+
|
|
425
|
+
def test_artifact_describe_with_data(mock_connect_ref):
|
|
426
|
+
"""Test describe includes data type"""
|
|
427
|
+
test_data = {"key": "value"}
|
|
428
|
+
artifact = Artifact(ref=mock_connect_ref, data=test_data)
|
|
429
|
+
|
|
430
|
+
result = artifact.describe()
|
|
431
|
+
|
|
432
|
+
assert "ref" in result
|
|
433
|
+
assert "data_type" in result
|
|
434
|
+
assert result["data_type"] == "dict"
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
def test_artifact_describe_with_all_fields(mock_connect_ref):
|
|
438
|
+
"""Test describe with all fields present"""
|
|
439
|
+
test_data = {"key": "value"}
|
|
440
|
+
test_meta = {"author": "test"}
|
|
441
|
+
artifact = Artifact(ref=mock_connect_ref, data=test_data, meta=test_meta)
|
|
442
|
+
|
|
443
|
+
result = artifact.describe()
|
|
444
|
+
|
|
445
|
+
assert "ref" in result
|
|
446
|
+
assert "meta" in result
|
|
447
|
+
assert "data_type" in result
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
def test_artifact_describe_ref_without_method(mock_connect_ref):
|
|
451
|
+
"""Test describe handles ref without describe method"""
|
|
452
|
+
mock_connect_ref.describe = None
|
|
453
|
+
artifact = Artifact(ref=mock_connect_ref)
|
|
454
|
+
|
|
455
|
+
result = artifact.describe()
|
|
456
|
+
|
|
457
|
+
assert "ref" in result
|
|
458
|
+
assert isinstance(result["ref"], str)
|
|
459
|
+
|
|
460
|
+
|
|
461
|
+
# ============================================================================
|
|
462
|
+
# to_dict Method Tests
|
|
463
|
+
# ============================================================================
|
|
464
|
+
|
|
465
|
+
|
|
466
|
+
def test_artifact_to_dict_basic(mock_connect_ref):
|
|
467
|
+
"""Test to_dict method with minimal data"""
|
|
468
|
+
artifact = Artifact(ref=mock_connect_ref)
|
|
469
|
+
|
|
470
|
+
result = artifact.to_dict()
|
|
471
|
+
|
|
472
|
+
assert "ref" in result
|
|
473
|
+
mock_connect_ref.to_dict.assert_called_once()
|
|
474
|
+
|
|
475
|
+
|
|
476
|
+
def test_artifact_to_dict_with_meta(mock_connect_ref):
|
|
477
|
+
"""Test to_dict includes metadata"""
|
|
478
|
+
test_meta = {"author": "test"}
|
|
479
|
+
artifact = Artifact(ref=mock_connect_ref, meta=test_meta)
|
|
480
|
+
|
|
481
|
+
result = artifact.to_dict()
|
|
482
|
+
|
|
483
|
+
assert "ref" in result
|
|
484
|
+
assert "meta" in result
|
|
485
|
+
assert result["meta"] == test_meta
|
|
486
|
+
|
|
487
|
+
|
|
488
|
+
def test_artifact_to_dict_with_data(mock_connect_ref):
|
|
489
|
+
"""Test to_dict includes data"""
|
|
490
|
+
test_data = {"key": "value"}
|
|
491
|
+
artifact = Artifact(ref=mock_connect_ref, data=test_data)
|
|
492
|
+
|
|
493
|
+
result = artifact.to_dict()
|
|
494
|
+
|
|
495
|
+
assert "ref" in result
|
|
496
|
+
assert "data" in result
|
|
497
|
+
assert result["data"] == test_data
|
|
498
|
+
|
|
499
|
+
|
|
500
|
+
def test_artifact_to_dict_round_trip(mock_connect_ref):
|
|
501
|
+
"""Test from_dict and to_dict round trip"""
|
|
502
|
+
test_data = {"key": "value"}
|
|
503
|
+
test_meta = {"author": "test"}
|
|
504
|
+
artifact1 = Artifact(ref=mock_connect_ref, data=test_data, meta=test_meta)
|
|
505
|
+
|
|
506
|
+
# Mock to_dict to return a dict
|
|
507
|
+
mock_connect_ref.to_dict.return_value = {"location": "s3://test-bucket/test-key.json"}
|
|
508
|
+
|
|
509
|
+
artifact_dict = artifact1.to_dict()
|
|
510
|
+
|
|
511
|
+
with patch("xfintech.connect.artifact.artifact.ConnectRef.from_dict") as mock_from_dict:
|
|
512
|
+
mock_from_dict.return_value = mock_connect_ref
|
|
513
|
+
|
|
514
|
+
artifact2 = Artifact.from_dict(artifact_dict)
|
|
515
|
+
|
|
516
|
+
assert artifact2.data == test_data
|
|
517
|
+
assert artifact2.meta == test_meta
|
|
518
|
+
|
|
519
|
+
|
|
520
|
+
def test_artifact_to_dict_ref_without_method(mock_connect_ref):
|
|
521
|
+
"""Test to_dict handles ref without to_dict method"""
|
|
522
|
+
mock_connect_ref.to_dict = None
|
|
523
|
+
artifact = Artifact(ref=mock_connect_ref)
|
|
524
|
+
|
|
525
|
+
result = artifact.to_dict()
|
|
526
|
+
|
|
527
|
+
assert "ref" in result
|
|
528
|
+
assert isinstance(result["ref"], str)
|
|
529
|
+
|
|
530
|
+
|
|
531
|
+
# ============================================================================
|
|
532
|
+
# Integration Tests
|
|
533
|
+
# ============================================================================
|
|
534
|
+
|
|
535
|
+
|
|
536
|
+
def test_artifact_write_read_workflow(mock_connect_ref, mock_connect, mock_serialiser, mock_deserialiser):
|
|
537
|
+
"""Test complete write and read workflow"""
|
|
538
|
+
test_data = {"key": "value", "number": 123}
|
|
539
|
+
|
|
540
|
+
# Write
|
|
541
|
+
artifact1 = Artifact(ref=mock_connect_ref, data=test_data)
|
|
542
|
+
artifact1.write(connect=mock_connect, serialiser=mock_serialiser, format="json")
|
|
543
|
+
|
|
544
|
+
# Read
|
|
545
|
+
artifact2 = Artifact(ref=mock_connect_ref)
|
|
546
|
+
artifact2.read(connect=mock_connect, deserialiser=mock_deserialiser, format="json")
|
|
547
|
+
|
|
548
|
+
assert artifact2.data == {"deserialised": True}
|
|
549
|
+
|
|
550
|
+
|
|
551
|
+
def test_artifact_with_metadata_workflow(mock_connect_ref):
|
|
552
|
+
"""Test artifact with metadata through its lifecycle"""
|
|
553
|
+
test_data = {"key": "value"}
|
|
554
|
+
test_meta = {"author": "test", "version": "1.0", "created": "2026-01-08"}
|
|
555
|
+
|
|
556
|
+
artifact = Artifact(ref=mock_connect_ref, data=test_data, meta=test_meta)
|
|
557
|
+
|
|
558
|
+
# Verify metadata is preserved
|
|
559
|
+
description = artifact.describe()
|
|
560
|
+
assert description["meta"] == test_meta
|
|
561
|
+
|
|
562
|
+
# Verify metadata in dict representation
|
|
563
|
+
artifact_dict = artifact.to_dict()
|
|
564
|
+
assert artifact_dict["meta"] == test_meta
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from .connect import ConnectLike
|
|
2
|
+
from .connectref import ConnectRef, ConnectRefLike
|
|
3
|
+
from .error import ConnectFailedError, ConnectKeyError, ConnectRefKeyError
|
|
4
|
+
|
|
5
|
+
__all__ = [
|
|
6
|
+
"ConnectLike",
|
|
7
|
+
"ConnectRef",
|
|
8
|
+
"ConnectRefLike",
|
|
9
|
+
"ConnectRefKeyError",
|
|
10
|
+
"ConnectKeyError",
|
|
11
|
+
"ConnectFailedError",
|
|
12
|
+
]
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any, Protocol, runtime_checkable
|
|
4
|
+
|
|
5
|
+
from xfintech.connect.common.connectref import ConnectRef
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@runtime_checkable
|
|
9
|
+
class ConnectLike(Protocol):
|
|
10
|
+
"""
|
|
11
|
+
描述:
|
|
12
|
+
- 连接接口协议,定义存储连接的基本操作。
|
|
13
|
+
- 支持对象的读取和写入操作。
|
|
14
|
+
- 实现类可以是 S3、本地文件系统或其他存储后端。
|
|
15
|
+
- 使用 @runtime_checkable 装饰器支持运行时类型检查。
|
|
16
|
+
|
|
17
|
+
方法:
|
|
18
|
+
- get_object(location, **kwargs): 从指定位置获取对象数据。
|
|
19
|
+
- put_object(data, location, **kwargs): 将数据写入指定位置并返回引用。
|
|
20
|
+
|
|
21
|
+
例子:
|
|
22
|
+
```python
|
|
23
|
+
from xfintech.connect.common.connect import ConnectLike
|
|
24
|
+
|
|
25
|
+
class MyConnect:
|
|
26
|
+
def get_object(self, location: str, **kwargs) -> bytes:
|
|
27
|
+
return b"data"
|
|
28
|
+
|
|
29
|
+
def put_object(self, data: bytes, location: str, **kwargs) -> ConnectRef:
|
|
30
|
+
return ConnectRef(location=location)
|
|
31
|
+
|
|
32
|
+
# 运行时检查
|
|
33
|
+
connect = MyConnect()
|
|
34
|
+
assert isinstance(connect, ConnectLike)
|
|
35
|
+
```
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
def get_object(
|
|
39
|
+
self,
|
|
40
|
+
location: str,
|
|
41
|
+
**kwargs: Any,
|
|
42
|
+
) -> bytes: ...
|
|
43
|
+
|
|
44
|
+
def put_object(
|
|
45
|
+
self,
|
|
46
|
+
data: bytes,
|
|
47
|
+
location: str,
|
|
48
|
+
**kwargs: Any,
|
|
49
|
+
) -> ConnectRef: ...
|