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,891 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Test suite for Params class
|
|
3
|
+
Tests cover initialization, attribute access, serialization, and data conversion
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from datetime import datetime
|
|
7
|
+
|
|
8
|
+
import pytest
|
|
9
|
+
|
|
10
|
+
from xfintech.data.common.params import Params
|
|
11
|
+
|
|
12
|
+
# ============================================================================
|
|
13
|
+
# Initialization Tests
|
|
14
|
+
# ============================================================================
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def test_params_init_empty():
|
|
18
|
+
"""Test Params initialization with no arguments"""
|
|
19
|
+
params = Params()
|
|
20
|
+
assert params.to_dict() == {}
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def test_params_init_single_kwarg():
|
|
24
|
+
"""Test Params initialization with single keyword argument"""
|
|
25
|
+
params = Params(name="test")
|
|
26
|
+
assert params.name == "test"
|
|
27
|
+
assert params.to_dict() == {"name": "test"}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def test_params_init_multiple_kwargs():
|
|
31
|
+
"""Test Params initialization with multiple keyword arguments"""
|
|
32
|
+
params = Params(symbol="AAPL", price=150.5, quantity=100)
|
|
33
|
+
assert params.symbol == "AAPL"
|
|
34
|
+
assert params.price == 150.5
|
|
35
|
+
assert params.quantity == 100
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def test_params_init_with_various_types():
|
|
39
|
+
"""Test Params initialization with various data types"""
|
|
40
|
+
params = Params(
|
|
41
|
+
string="text",
|
|
42
|
+
integer=42,
|
|
43
|
+
floating=3.14,
|
|
44
|
+
boolean=True,
|
|
45
|
+
none_value=None,
|
|
46
|
+
list_value=[1, 2, 3],
|
|
47
|
+
dict_value={"key": "value"},
|
|
48
|
+
)
|
|
49
|
+
assert params.string == "text"
|
|
50
|
+
assert params.integer == 42
|
|
51
|
+
assert params.floating == 3.14
|
|
52
|
+
assert params.boolean is True
|
|
53
|
+
assert params.none_value is None
|
|
54
|
+
assert params.list_value == [1, 2, 3]
|
|
55
|
+
assert params.dict_value == {"key": "value"}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def test_params_init_with_datetime():
|
|
59
|
+
"""Test Params initialization with datetime object"""
|
|
60
|
+
dt = datetime(2024, 1, 15, 10, 30, 0)
|
|
61
|
+
params = Params(timestamp=dt)
|
|
62
|
+
assert params.timestamp == dt
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def test_params_init_with_nested_params():
|
|
66
|
+
"""Test Params initialization with nested Params object"""
|
|
67
|
+
nested = Params(inner="value")
|
|
68
|
+
params = Params(outer="test", nested=nested)
|
|
69
|
+
assert params.outer == "test"
|
|
70
|
+
assert isinstance(params.nested, Params)
|
|
71
|
+
assert params.nested.inner == "value"
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
# ============================================================================
|
|
75
|
+
# Attribute Access Tests
|
|
76
|
+
# ============================================================================
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def test_params_attribute_access():
|
|
80
|
+
"""Test direct attribute access"""
|
|
81
|
+
params = Params(key="value")
|
|
82
|
+
assert params.key == "value"
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def test_params_attribute_assignment():
|
|
86
|
+
"""Test direct attribute assignment"""
|
|
87
|
+
params = Params()
|
|
88
|
+
params.new_key = "new_value"
|
|
89
|
+
assert params.new_key == "new_value"
|
|
90
|
+
assert params.to_dict() == {"new_key": "new_value"}
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def test_params_attribute_modification():
|
|
94
|
+
"""Test modifying existing attribute"""
|
|
95
|
+
params = Params(value=10)
|
|
96
|
+
params.value = 20
|
|
97
|
+
assert params.value == 20
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def test_params_attribute_error():
|
|
101
|
+
"""Test accessing non-existent attribute raises AttributeError"""
|
|
102
|
+
params = Params()
|
|
103
|
+
with pytest.raises(AttributeError):
|
|
104
|
+
_ = params.nonexistent
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
# ============================================================================
|
|
108
|
+
# Contains Tests (__contains__)
|
|
109
|
+
# ============================================================================
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def test_params_contains_true():
|
|
113
|
+
"""Test __contains__ returns True for existing attribute"""
|
|
114
|
+
params = Params(key="value")
|
|
115
|
+
assert "key" in params
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def test_params_contains_false():
|
|
119
|
+
"""Test __contains__ returns False for non-existent attribute"""
|
|
120
|
+
params = Params(key="value")
|
|
121
|
+
assert "other" not in params
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def test_params_contains_after_set():
|
|
125
|
+
"""Test __contains__ after setting attribute"""
|
|
126
|
+
params = Params()
|
|
127
|
+
assert "new_attr" not in params
|
|
128
|
+
params.new_attr = "value"
|
|
129
|
+
assert "new_attr" in params
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def test_params_contains_private_attribute():
|
|
133
|
+
"""Test __contains__ with private attributes"""
|
|
134
|
+
params = Params()
|
|
135
|
+
params._private = "value"
|
|
136
|
+
assert "_private" in params
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
# ============================================================================
|
|
140
|
+
# String Representation Tests
|
|
141
|
+
# ============================================================================
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def test_params_str_empty():
|
|
145
|
+
"""Test __str__ with empty Params"""
|
|
146
|
+
params = Params()
|
|
147
|
+
assert str(params) == "{}"
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def test_params_str_with_data():
|
|
151
|
+
"""Test __str__ with data"""
|
|
152
|
+
params = Params(key="value", number=42)
|
|
153
|
+
result = str(params)
|
|
154
|
+
# Could be in different order
|
|
155
|
+
assert "key" in result
|
|
156
|
+
assert "value" in result
|
|
157
|
+
assert "number" in result
|
|
158
|
+
assert "42" in result
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def test_params_repr_empty():
|
|
162
|
+
"""Test __repr__ with empty Params"""
|
|
163
|
+
params = Params()
|
|
164
|
+
assert repr(params) == "Params({})"
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def test_params_repr_with_data():
|
|
168
|
+
"""Test __repr__ with data"""
|
|
169
|
+
params = Params(key="value")
|
|
170
|
+
result = repr(params)
|
|
171
|
+
assert result.startswith("Params(")
|
|
172
|
+
assert "key" in result
|
|
173
|
+
assert "value" in result
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
# ============================================================================
|
|
177
|
+
# Get Method Tests
|
|
178
|
+
# ============================================================================
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def test_params_get_existing_key():
|
|
182
|
+
"""Test get method with existing key"""
|
|
183
|
+
params = Params(key="value")
|
|
184
|
+
assert params.get("key") == "value"
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def test_params_get_nonexistent_key():
|
|
188
|
+
"""Test get method with non-existent key returns None"""
|
|
189
|
+
params = Params()
|
|
190
|
+
assert params.get("nonexistent") is None
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def test_params_get_with_default():
|
|
194
|
+
"""Test get method with default value"""
|
|
195
|
+
params = Params()
|
|
196
|
+
assert params.get("missing", "default") == "default"
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def test_params_get_existing_key_ignores_default():
|
|
200
|
+
"""Test get method with existing key ignores default"""
|
|
201
|
+
params = Params(key="value")
|
|
202
|
+
assert params.get("key", "default") == "value"
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def test_params_get_none_value():
|
|
206
|
+
"""Test get method with None as actual value"""
|
|
207
|
+
params = Params(key=None)
|
|
208
|
+
assert params.get("key") is None
|
|
209
|
+
assert params.get("key", "default") is None
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
# ============================================================================
|
|
213
|
+
# Set Method Tests
|
|
214
|
+
# ============================================================================
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def test_params_set_new_attribute():
|
|
218
|
+
"""Test set method creates new attribute"""
|
|
219
|
+
params = Params()
|
|
220
|
+
params.set("key", "value")
|
|
221
|
+
assert params.key == "value"
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def test_params_set_existing_attribute():
|
|
225
|
+
"""Test set method overwrites existing attribute"""
|
|
226
|
+
params = Params(key="old")
|
|
227
|
+
params.set("key", "new")
|
|
228
|
+
assert params.key == "new"
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def test_params_set_various_types():
|
|
232
|
+
"""Test set method with various types"""
|
|
233
|
+
params = Params()
|
|
234
|
+
params.set("string", "text")
|
|
235
|
+
params.set("integer", 42)
|
|
236
|
+
params.set("list", [1, 2, 3])
|
|
237
|
+
|
|
238
|
+
assert params.string == "text"
|
|
239
|
+
assert params.integer == 42
|
|
240
|
+
assert params.list == [1, 2, 3]
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def test_params_set_none():
|
|
244
|
+
"""Test set method with None value"""
|
|
245
|
+
params = Params()
|
|
246
|
+
params.set("key", None)
|
|
247
|
+
assert params.key is None
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
# ============================================================================
|
|
251
|
+
# From Dict Tests
|
|
252
|
+
# ============================================================================
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
def test_params_from_dict_empty():
|
|
256
|
+
"""Test from_dict with empty dictionary"""
|
|
257
|
+
params = Params.from_dict({})
|
|
258
|
+
assert params.to_dict() == {}
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def test_params_from_dict_single_item():
|
|
262
|
+
"""Test from_dict with single item"""
|
|
263
|
+
params = Params.from_dict({"key": "value"})
|
|
264
|
+
assert params.key == "value"
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def test_params_from_dict_multiple_items():
|
|
268
|
+
"""Test from_dict with multiple items"""
|
|
269
|
+
data = {"symbol": "AAPL", "price": 150.5, "quantity": 100}
|
|
270
|
+
params = Params.from_dict(data)
|
|
271
|
+
assert params.symbol == "AAPL"
|
|
272
|
+
assert params.price == 150.5
|
|
273
|
+
assert params.quantity == 100
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
def test_params_from_dict_with_params_instance():
|
|
277
|
+
"""Test from_dict with Params instance returns same instance"""
|
|
278
|
+
original = Params(key="value")
|
|
279
|
+
result = Params.from_dict(original)
|
|
280
|
+
assert result is original
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
def test_params_from_dict_with_nested_dict():
|
|
284
|
+
"""Test from_dict with nested dictionary"""
|
|
285
|
+
data = {"outer": "value", "inner": {"nested": "data"}}
|
|
286
|
+
params = Params.from_dict(data)
|
|
287
|
+
assert params.outer == "value"
|
|
288
|
+
assert params.inner == {"nested": "data"}
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
# ============================================================================
|
|
292
|
+
# Ensure Serialisable Tests
|
|
293
|
+
# ============================================================================
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
def test_params_ensure_serialisable_int():
|
|
297
|
+
"""Test ensure_serialisable with integer"""
|
|
298
|
+
assert Params.ensure_serialisable(42) == 42
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def test_params_ensure_serialisable_float():
|
|
302
|
+
"""Test ensure_serialisable with float"""
|
|
303
|
+
assert Params.ensure_serialisable(3.14) == 3.14
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
def test_params_ensure_serialisable_string():
|
|
307
|
+
"""Test ensure_serialisable with string"""
|
|
308
|
+
assert Params.ensure_serialisable("text") == "text"
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
def test_params_ensure_serialisable_bool():
|
|
312
|
+
"""Test ensure_serialisable with boolean"""
|
|
313
|
+
assert Params.ensure_serialisable(True) is True
|
|
314
|
+
assert Params.ensure_serialisable(False) is False
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
def test_params_ensure_serialisable_none():
|
|
318
|
+
"""Test ensure_serialisable with None"""
|
|
319
|
+
assert Params.ensure_serialisable(None) is None
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
def test_params_ensure_serialisable_datetime():
|
|
323
|
+
"""Test ensure_serialisable with datetime converts to string"""
|
|
324
|
+
dt = datetime(2024, 1, 15)
|
|
325
|
+
result = Params.ensure_serialisable(dt)
|
|
326
|
+
assert result == "2024-01-15"
|
|
327
|
+
assert isinstance(result, str)
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
def test_params_ensure_serialisable_params_instance():
|
|
331
|
+
"""Test ensure_serialisable with Params instance converts to dict"""
|
|
332
|
+
params = Params(key="value", number=42)
|
|
333
|
+
result = Params.ensure_serialisable(params)
|
|
334
|
+
assert isinstance(result, dict)
|
|
335
|
+
assert result["key"] == "value"
|
|
336
|
+
assert result["number"] == 42
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
def test_params_ensure_serialisable_dict():
|
|
340
|
+
"""Test ensure_serialisable with dictionary"""
|
|
341
|
+
data = {"key": "value", "number": 42}
|
|
342
|
+
result = Params.ensure_serialisable(data)
|
|
343
|
+
assert result == {"key": "value", "number": 42}
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
def test_params_ensure_serialisable_dict_with_datetime():
|
|
347
|
+
"""Test ensure_serialisable with dict containing datetime"""
|
|
348
|
+
dt = datetime(2024, 1, 15)
|
|
349
|
+
data = {"date": dt, "value": 100}
|
|
350
|
+
result = Params.ensure_serialisable(data)
|
|
351
|
+
assert result == {"date": "2024-01-15", "value": 100}
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
def test_params_ensure_serialisable_list():
|
|
355
|
+
"""Test ensure_serialisable with list"""
|
|
356
|
+
data = [1, 2, 3]
|
|
357
|
+
result = Params.ensure_serialisable(data)
|
|
358
|
+
assert result == [1, 2, 3]
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
def test_params_ensure_serialisable_list_with_datetime():
|
|
362
|
+
"""Test ensure_serialisable with list containing datetime"""
|
|
363
|
+
dt = datetime(2024, 1, 15)
|
|
364
|
+
data = [dt, "text", 42]
|
|
365
|
+
result = Params.ensure_serialisable(data)
|
|
366
|
+
assert result == ["2024-01-15", "text", 42]
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
def test_params_ensure_serialisable_nested_dict():
|
|
370
|
+
"""Test ensure_serialisable with nested dictionary"""
|
|
371
|
+
dt = datetime(2024, 1, 15)
|
|
372
|
+
data = {"outer": {"inner": {"date": dt, "value": 100}}}
|
|
373
|
+
result = Params.ensure_serialisable(data)
|
|
374
|
+
assert result["outer"]["inner"]["date"] == "2024-01-15"
|
|
375
|
+
assert result["outer"]["inner"]["value"] == 100
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
def test_params_ensure_serialisable_nested_list():
|
|
379
|
+
"""Test ensure_serialisable with nested list"""
|
|
380
|
+
dt = datetime(2024, 1, 15)
|
|
381
|
+
data = [[dt, 1], [2, "text"]]
|
|
382
|
+
result = Params.ensure_serialisable(data)
|
|
383
|
+
assert result == [["2024-01-15", 1], [2, "text"]]
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
def test_params_ensure_serialisable_custom_object():
|
|
387
|
+
"""Test ensure_serialisable with custom object converts to string"""
|
|
388
|
+
|
|
389
|
+
class CustomClass:
|
|
390
|
+
def __str__(self):
|
|
391
|
+
return "custom_object"
|
|
392
|
+
|
|
393
|
+
obj = CustomClass()
|
|
394
|
+
result = Params.ensure_serialisable(obj)
|
|
395
|
+
assert result == "custom_object"
|
|
396
|
+
assert isinstance(result, str)
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
def test_params_ensure_serialisable_complex_nested():
|
|
400
|
+
"""Test ensure_serialisable with complex nested structure"""
|
|
401
|
+
dt = datetime(2024, 1, 15)
|
|
402
|
+
nested_params = Params(inner_key="inner_value")
|
|
403
|
+
data = {"date": dt, "params": nested_params, "list": [dt, nested_params, 42], "nested": {"deep": {"date": dt}}}
|
|
404
|
+
result = Params.ensure_serialisable(data)
|
|
405
|
+
assert result["date"] == "2024-01-15"
|
|
406
|
+
assert result["params"]["inner_key"] == "inner_value"
|
|
407
|
+
assert result["list"][0] == "2024-01-15"
|
|
408
|
+
assert result["list"][1]["inner_key"] == "inner_value"
|
|
409
|
+
assert result["list"][2] == 42
|
|
410
|
+
assert result["nested"]["deep"]["date"] == "2024-01-15"
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
# ============================================================================
|
|
414
|
+
# Describe Method Tests
|
|
415
|
+
# ============================================================================
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
def test_params_describe_empty():
|
|
419
|
+
"""Test describe with empty Params"""
|
|
420
|
+
params = Params()
|
|
421
|
+
assert params.describe() == {}
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
def test_params_describe_basic_types():
|
|
425
|
+
"""Test describe with basic types"""
|
|
426
|
+
params = Params(string="text", number=42, boolean=True)
|
|
427
|
+
result = params.describe()
|
|
428
|
+
assert result == {"string": "text", "number": 42, "boolean": True}
|
|
429
|
+
|
|
430
|
+
|
|
431
|
+
def test_params_describe_with_datetime():
|
|
432
|
+
"""Test describe serializes datetime to string"""
|
|
433
|
+
dt = datetime(2024, 1, 15, 10, 30, 0)
|
|
434
|
+
params = Params(date=dt, value=100)
|
|
435
|
+
result = params.describe()
|
|
436
|
+
assert result["date"] == "2024-01-15"
|
|
437
|
+
assert result["value"] == 100
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
def test_params_describe_with_nested_params():
|
|
441
|
+
"""Test describe serializes nested Params"""
|
|
442
|
+
nested = Params(inner="value")
|
|
443
|
+
params = Params(outer="test", nested=nested)
|
|
444
|
+
result = params.describe()
|
|
445
|
+
assert result["outer"] == "test"
|
|
446
|
+
assert result["nested"] == {"inner": "value"}
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
def test_params_describe_excludes_private():
|
|
450
|
+
"""Test describe excludes private attributes (starting with _)"""
|
|
451
|
+
params = Params(public="visible")
|
|
452
|
+
params._private = "hidden"
|
|
453
|
+
result = params.describe()
|
|
454
|
+
assert "public" in result
|
|
455
|
+
assert "_private" not in result
|
|
456
|
+
|
|
457
|
+
|
|
458
|
+
def test_params_describe_with_list():
|
|
459
|
+
"""Test describe with list containing various types"""
|
|
460
|
+
dt = datetime(2024, 1, 15)
|
|
461
|
+
params = Params(items=[1, "text", dt])
|
|
462
|
+
result = params.describe()
|
|
463
|
+
assert result["items"] == [1, "text", "2024-01-15"]
|
|
464
|
+
|
|
465
|
+
|
|
466
|
+
def test_params_describe_with_dict():
|
|
467
|
+
"""Test describe with dict containing datetime"""
|
|
468
|
+
dt = datetime(2024, 1, 15)
|
|
469
|
+
params = Params(data={"date": dt, "value": 100})
|
|
470
|
+
result = params.describe()
|
|
471
|
+
assert result["data"]["date"] == "2024-01-15"
|
|
472
|
+
assert result["data"]["value"] == 100
|
|
473
|
+
|
|
474
|
+
|
|
475
|
+
# ============================================================================
|
|
476
|
+
# To Dict Method Tests
|
|
477
|
+
# ============================================================================
|
|
478
|
+
|
|
479
|
+
|
|
480
|
+
def test_params_to_dict_empty():
|
|
481
|
+
"""Test to_dict with empty Params"""
|
|
482
|
+
params = Params()
|
|
483
|
+
assert params.to_dict() == {}
|
|
484
|
+
|
|
485
|
+
|
|
486
|
+
def test_params_to_dict_basic():
|
|
487
|
+
"""Test to_dict with basic types"""
|
|
488
|
+
params = Params(key="value", number=42)
|
|
489
|
+
result = params.to_dict()
|
|
490
|
+
assert result == {"key": "value", "number": 42}
|
|
491
|
+
|
|
492
|
+
|
|
493
|
+
def test_params_to_dict_preserves_datetime():
|
|
494
|
+
"""Test to_dict preserves datetime objects (does not serialize)"""
|
|
495
|
+
dt = datetime(2024, 1, 15)
|
|
496
|
+
params = Params(date=dt)
|
|
497
|
+
result = params.to_dict()
|
|
498
|
+
assert result["date"] == dt
|
|
499
|
+
assert isinstance(result["date"], datetime)
|
|
500
|
+
|
|
501
|
+
|
|
502
|
+
def test_params_to_dict_preserves_params():
|
|
503
|
+
"""Test to_dict preserves Params objects (does not serialize)"""
|
|
504
|
+
nested = Params(inner="value")
|
|
505
|
+
params = Params(nested=nested)
|
|
506
|
+
result = params.to_dict()
|
|
507
|
+
assert isinstance(result["nested"], Params)
|
|
508
|
+
assert result["nested"].inner == "value"
|
|
509
|
+
|
|
510
|
+
|
|
511
|
+
def test_params_to_dict_excludes_private():
|
|
512
|
+
"""Test to_dict excludes private attributes"""
|
|
513
|
+
params = Params(public="visible")
|
|
514
|
+
params._private = "hidden"
|
|
515
|
+
result = params.to_dict()
|
|
516
|
+
assert "public" in result
|
|
517
|
+
assert "_private" not in result
|
|
518
|
+
|
|
519
|
+
|
|
520
|
+
def test_params_to_dict_vs_describe():
|
|
521
|
+
"""Test differences between to_dict and describe"""
|
|
522
|
+
dt = datetime(2024, 1, 15)
|
|
523
|
+
nested = Params(inner="value")
|
|
524
|
+
params = Params(date=dt, nested=nested, value=100)
|
|
525
|
+
|
|
526
|
+
to_dict_result = params.to_dict()
|
|
527
|
+
describe_result = params.describe()
|
|
528
|
+
|
|
529
|
+
# to_dict preserves datetime
|
|
530
|
+
assert isinstance(to_dict_result["date"], datetime)
|
|
531
|
+
# describe serializes datetime
|
|
532
|
+
assert describe_result["date"] == "2024-01-15"
|
|
533
|
+
|
|
534
|
+
# to_dict preserves Params
|
|
535
|
+
assert isinstance(to_dict_result["nested"], Params)
|
|
536
|
+
# describe serializes Params
|
|
537
|
+
assert describe_result["nested"] == {"inner": "value"}
|
|
538
|
+
|
|
539
|
+
|
|
540
|
+
# ============================================================================
|
|
541
|
+
# Integration Tests
|
|
542
|
+
# ============================================================================
|
|
543
|
+
|
|
544
|
+
|
|
545
|
+
def test_params_full_workflow():
|
|
546
|
+
"""Test complete workflow with Params"""
|
|
547
|
+
# Create from dict
|
|
548
|
+
data = {"symbol": "AAPL", "quantity": 100}
|
|
549
|
+
params = Params.from_dict(data)
|
|
550
|
+
|
|
551
|
+
# Add attributes
|
|
552
|
+
params.set("price", 150.5)
|
|
553
|
+
params.timestamp = datetime(2024, 1, 15)
|
|
554
|
+
|
|
555
|
+
# Check attributes
|
|
556
|
+
assert "symbol" in params
|
|
557
|
+
assert params.get("quantity") == 100
|
|
558
|
+
|
|
559
|
+
# Serialize
|
|
560
|
+
serialized = params.describe()
|
|
561
|
+
assert serialized["timestamp"] == "2024-01-15"
|
|
562
|
+
|
|
563
|
+
|
|
564
|
+
def test_params_dict_roundtrip():
|
|
565
|
+
"""Test converting to dict and back"""
|
|
566
|
+
original = Params(key="value", number=42, flag=True)
|
|
567
|
+
data = original.to_dict()
|
|
568
|
+
restored = Params.from_dict(data)
|
|
569
|
+
|
|
570
|
+
assert restored.key == original.key
|
|
571
|
+
assert restored.number == original.number
|
|
572
|
+
assert restored.flag == original.flag
|
|
573
|
+
|
|
574
|
+
|
|
575
|
+
def test_params_api_request_simulation():
|
|
576
|
+
"""Test simulating API request parameters"""
|
|
577
|
+
params = Params(symbol="AAPL", start_date=datetime(2024, 1, 1), end_date=datetime(2024, 1, 31), limit=100, offset=0)
|
|
578
|
+
|
|
579
|
+
# Serialize for API
|
|
580
|
+
api_params = params.describe()
|
|
581
|
+
assert api_params["symbol"] == "AAPL"
|
|
582
|
+
assert api_params["start_date"] == "2024-01-01"
|
|
583
|
+
assert api_params["end_date"] == "2024-01-31"
|
|
584
|
+
assert api_params["limit"] == 100
|
|
585
|
+
|
|
586
|
+
|
|
587
|
+
def test_params_nested_structure():
|
|
588
|
+
"""Test complex nested structure"""
|
|
589
|
+
params = Params(
|
|
590
|
+
user=Params(name="John", id=123), preferences=Params(theme="dark", notifications=True), data=[1, 2, 3]
|
|
591
|
+
)
|
|
592
|
+
|
|
593
|
+
assert params.user.name == "John"
|
|
594
|
+
assert params.preferences.theme == "dark"
|
|
595
|
+
|
|
596
|
+
# Serialize entire structure
|
|
597
|
+
serialized = params.describe()
|
|
598
|
+
assert serialized["user"]["name"] == "John"
|
|
599
|
+
assert serialized["preferences"]["notifications"] is True
|
|
600
|
+
|
|
601
|
+
|
|
602
|
+
def test_params_modification_tracking():
|
|
603
|
+
"""Test modifying params and tracking changes"""
|
|
604
|
+
params = Params(value=10)
|
|
605
|
+
original = params.to_dict()
|
|
606
|
+
|
|
607
|
+
params.set("value", 20)
|
|
608
|
+
params.set("new_field", "added")
|
|
609
|
+
|
|
610
|
+
modified = params.to_dict()
|
|
611
|
+
assert original != modified
|
|
612
|
+
assert modified["value"] == 20
|
|
613
|
+
assert "new_field" in modified
|
|
614
|
+
|
|
615
|
+
|
|
616
|
+
def test_params_multiple_instances_independence():
|
|
617
|
+
"""Test multiple Params instances are independent"""
|
|
618
|
+
p1 = Params(value=10)
|
|
619
|
+
p2 = Params(value=20)
|
|
620
|
+
|
|
621
|
+
p1.set("value", 15)
|
|
622
|
+
|
|
623
|
+
assert p1.value == 15
|
|
624
|
+
assert p2.value == 20
|
|
625
|
+
|
|
626
|
+
|
|
627
|
+
def test_params_dynamic_attributes():
|
|
628
|
+
"""Test dynamically adding and removing attributes"""
|
|
629
|
+
params = Params()
|
|
630
|
+
|
|
631
|
+
# Add attributes
|
|
632
|
+
for i in range(5):
|
|
633
|
+
params.set(f"key{i}", i * 10)
|
|
634
|
+
|
|
635
|
+
# Verify all added
|
|
636
|
+
for i in range(5):
|
|
637
|
+
assert f"key{i}" in params
|
|
638
|
+
assert params.get(f"key{i}") == i * 10
|
|
639
|
+
|
|
640
|
+
|
|
641
|
+
def test_params_empty_string_handling():
|
|
642
|
+
"""Test handling of empty strings"""
|
|
643
|
+
params = Params(empty="", value="text")
|
|
644
|
+
assert params.empty == ""
|
|
645
|
+
assert params.value == "text"
|
|
646
|
+
|
|
647
|
+
result = params.to_dict()
|
|
648
|
+
assert result["empty"] == ""
|
|
649
|
+
|
|
650
|
+
|
|
651
|
+
def test_params_zero_value_handling():
|
|
652
|
+
"""Test handling of zero values"""
|
|
653
|
+
params = Params(zero=0, positive=1, negative=-1)
|
|
654
|
+
assert params.zero == 0
|
|
655
|
+
|
|
656
|
+
result = params.describe()
|
|
657
|
+
assert result["zero"] == 0
|
|
658
|
+
|
|
659
|
+
|
|
660
|
+
def test_params_special_characters_in_keys():
|
|
661
|
+
"""Test keys with special characters"""
|
|
662
|
+
params = Params()
|
|
663
|
+
params.set("key-with-dash", "value1")
|
|
664
|
+
params.set("key_with_underscore", "value2")
|
|
665
|
+
params.set("key.with.dot", "value3")
|
|
666
|
+
|
|
667
|
+
assert params.get("key-with-dash") == "value1"
|
|
668
|
+
assert params.get("key_with_underscore") == "value2"
|
|
669
|
+
assert params.get("key.with.dot") == "value3"
|
|
670
|
+
|
|
671
|
+
|
|
672
|
+
# ============================================================================
|
|
673
|
+
# Identifier Property Tests
|
|
674
|
+
# ============================================================================
|
|
675
|
+
|
|
676
|
+
|
|
677
|
+
def test_params_identifier_basic():
|
|
678
|
+
"""Test identifier property returns SHA256 hash"""
|
|
679
|
+
params = Params(symbol="AAPL", quantity=100)
|
|
680
|
+
identifier = params.identifier
|
|
681
|
+
|
|
682
|
+
# SHA256 hash should be 64 characters (256 bits in hex)
|
|
683
|
+
assert isinstance(identifier, str)
|
|
684
|
+
assert len(identifier) == 64
|
|
685
|
+
assert all(c in "0123456789abcdef" for c in identifier)
|
|
686
|
+
|
|
687
|
+
|
|
688
|
+
def test_params_identifier_deterministic():
|
|
689
|
+
"""Test that same params produce same identifier"""
|
|
690
|
+
params1 = Params(symbol="AAPL", price=150.5, quantity=100)
|
|
691
|
+
params2 = Params(symbol="AAPL", price=150.5, quantity=100)
|
|
692
|
+
|
|
693
|
+
assert params1.identifier == params2.identifier
|
|
694
|
+
|
|
695
|
+
|
|
696
|
+
def test_params_identifier_order_independent():
|
|
697
|
+
"""Test that parameter order doesn't affect identifier"""
|
|
698
|
+
# Different order of initialization
|
|
699
|
+
params1 = Params(symbol="AAPL", price=150.5, quantity=100)
|
|
700
|
+
params2 = Params(quantity=100, symbol="AAPL", price=150.5)
|
|
701
|
+
|
|
702
|
+
# Should produce same identifier because JSON uses sorted keys
|
|
703
|
+
assert params1.identifier == params2.identifier
|
|
704
|
+
|
|
705
|
+
|
|
706
|
+
def test_params_identifier_different_values():
|
|
707
|
+
"""Test that different values produce different identifiers"""
|
|
708
|
+
params1 = Params(symbol="AAPL", quantity=100)
|
|
709
|
+
params2 = Params(symbol="AAPL", quantity=200)
|
|
710
|
+
|
|
711
|
+
assert params1.identifier != params2.identifier
|
|
712
|
+
|
|
713
|
+
|
|
714
|
+
def test_params_identifier_different_keys():
|
|
715
|
+
"""Test that different keys produce different identifiers"""
|
|
716
|
+
params1 = Params(symbol="AAPL", quantity=100)
|
|
717
|
+
params2 = Params(symbol="AAPL", amount=100)
|
|
718
|
+
|
|
719
|
+
assert params1.identifier != params2.identifier
|
|
720
|
+
|
|
721
|
+
|
|
722
|
+
def test_params_identifier_empty_params():
|
|
723
|
+
"""Test identifier for empty Params"""
|
|
724
|
+
params = Params()
|
|
725
|
+
identifier = params.identifier
|
|
726
|
+
|
|
727
|
+
assert isinstance(identifier, str)
|
|
728
|
+
assert len(identifier) == 64
|
|
729
|
+
|
|
730
|
+
|
|
731
|
+
def test_params_identifier_with_datetime():
|
|
732
|
+
"""Test identifier handles datetime serialization"""
|
|
733
|
+
dt = datetime(2024, 1, 15, 10, 30, 0)
|
|
734
|
+
params1 = Params(timestamp=dt)
|
|
735
|
+
params2 = Params(timestamp=dt)
|
|
736
|
+
|
|
737
|
+
# Same datetime should produce same identifier
|
|
738
|
+
assert params1.identifier == params2.identifier
|
|
739
|
+
|
|
740
|
+
# Different datetime should produce different identifier
|
|
741
|
+
params3 = Params(timestamp=datetime(2024, 1, 16, 10, 30, 0))
|
|
742
|
+
assert params1.identifier != params3.identifier
|
|
743
|
+
|
|
744
|
+
|
|
745
|
+
def test_params_identifier_with_nested_params():
|
|
746
|
+
"""Test identifier with nested Params objects"""
|
|
747
|
+
nested1 = Params(inner="value1")
|
|
748
|
+
nested2 = Params(inner="value1")
|
|
749
|
+
|
|
750
|
+
params1 = Params(outer="test", nested=nested1)
|
|
751
|
+
params2 = Params(outer="test", nested=nested2)
|
|
752
|
+
|
|
753
|
+
# Same nested structure should produce same identifier
|
|
754
|
+
assert params1.identifier == params2.identifier
|
|
755
|
+
|
|
756
|
+
|
|
757
|
+
def test_params_identifier_with_nested_dict():
|
|
758
|
+
"""Test identifier with nested dictionary"""
|
|
759
|
+
params1 = Params(data={"key1": "value1", "key2": "value2"})
|
|
760
|
+
params2 = Params(data={"key2": "value2", "key1": "value1"})
|
|
761
|
+
|
|
762
|
+
# Order in nested dict shouldn't matter
|
|
763
|
+
assert params1.identifier == params2.identifier
|
|
764
|
+
|
|
765
|
+
|
|
766
|
+
def test_params_identifier_with_list():
|
|
767
|
+
"""Test identifier with list values"""
|
|
768
|
+
params1 = Params(items=[1, 2, 3])
|
|
769
|
+
params2 = Params(items=[1, 2, 3])
|
|
770
|
+
params3 = Params(items=[3, 2, 1])
|
|
771
|
+
|
|
772
|
+
# Same list should produce same identifier
|
|
773
|
+
assert params1.identifier == params2.identifier
|
|
774
|
+
|
|
775
|
+
# Different order in list should produce different identifier
|
|
776
|
+
assert params1.identifier != params3.identifier
|
|
777
|
+
|
|
778
|
+
|
|
779
|
+
def test_params_identifier_with_various_types():
|
|
780
|
+
"""Test identifier with mixed data types"""
|
|
781
|
+
params = Params(
|
|
782
|
+
string="text",
|
|
783
|
+
integer=42,
|
|
784
|
+
floating=3.14,
|
|
785
|
+
boolean=True,
|
|
786
|
+
none_value=None,
|
|
787
|
+
list_value=[1, 2, 3],
|
|
788
|
+
dict_value={"key": "value"},
|
|
789
|
+
)
|
|
790
|
+
|
|
791
|
+
identifier = params.identifier
|
|
792
|
+
assert isinstance(identifier, str)
|
|
793
|
+
assert len(identifier) == 64
|
|
794
|
+
|
|
795
|
+
|
|
796
|
+
def test_params_identifier_with_unicode():
|
|
797
|
+
"""Test identifier handles Unicode characters"""
|
|
798
|
+
params1 = Params(name="中文测试", symbol="股票")
|
|
799
|
+
params2 = Params(name="中文测试", symbol="股票")
|
|
800
|
+
|
|
801
|
+
assert params1.identifier == params2.identifier
|
|
802
|
+
|
|
803
|
+
|
|
804
|
+
def test_params_identifier_with_special_characters():
|
|
805
|
+
"""Test identifier with special characters in values"""
|
|
806
|
+
params1 = Params(text="Hello, World! @#$%^&*()")
|
|
807
|
+
params2 = Params(text="Hello, World! @#$%^&*()")
|
|
808
|
+
|
|
809
|
+
assert params1.identifier == params2.identifier
|
|
810
|
+
|
|
811
|
+
|
|
812
|
+
def test_params_identifier_immutability():
|
|
813
|
+
"""Test that identifier changes when params are modified"""
|
|
814
|
+
params = Params(symbol="AAPL", quantity=100)
|
|
815
|
+
identifier1 = params.identifier
|
|
816
|
+
|
|
817
|
+
# Modify params
|
|
818
|
+
params.quantity = 200
|
|
819
|
+
identifier2 = params.identifier
|
|
820
|
+
|
|
821
|
+
# Identifier should change
|
|
822
|
+
assert identifier1 != identifier2
|
|
823
|
+
|
|
824
|
+
|
|
825
|
+
def test_params_identifier_with_complex_nested_structure():
|
|
826
|
+
"""Test identifier with deeply nested structures"""
|
|
827
|
+
params1 = Params(
|
|
828
|
+
level1={"level2": {"level3": {"value": "deep"}}},
|
|
829
|
+
list_of_dicts=[{"a": 1}, {"b": 2}],
|
|
830
|
+
)
|
|
831
|
+
params2 = Params(
|
|
832
|
+
level1={"level2": {"level3": {"value": "deep"}}},
|
|
833
|
+
list_of_dicts=[{"a": 1}, {"b": 2}],
|
|
834
|
+
)
|
|
835
|
+
|
|
836
|
+
assert params1.identifier == params2.identifier
|
|
837
|
+
|
|
838
|
+
|
|
839
|
+
def test_params_identifier_consistency_after_modification():
|
|
840
|
+
"""Test that adding new attributes changes identifier"""
|
|
841
|
+
params = Params(symbol="AAPL")
|
|
842
|
+
identifier1 = params.identifier
|
|
843
|
+
|
|
844
|
+
# Add new attribute
|
|
845
|
+
params.quantity = 100
|
|
846
|
+
identifier2 = params.identifier
|
|
847
|
+
|
|
848
|
+
assert identifier1 != identifier2
|
|
849
|
+
|
|
850
|
+
# Remove attribute
|
|
851
|
+
delattr(params, "quantity")
|
|
852
|
+
identifier3 = params.identifier
|
|
853
|
+
|
|
854
|
+
# Should be back to original identifier
|
|
855
|
+
assert identifier1 == identifier3
|
|
856
|
+
|
|
857
|
+
|
|
858
|
+
def test_params_identifier_with_datetime_serialization():
|
|
859
|
+
"""Test that datetime is properly serialized in identifier"""
|
|
860
|
+
dt = datetime(2024, 1, 15)
|
|
861
|
+
params1 = Params(date=dt)
|
|
862
|
+
# Manually create params with string date (what describe() converts to)
|
|
863
|
+
params2 = Params(date="2024-01-15")
|
|
864
|
+
|
|
865
|
+
# These should produce same identifier since datetime is serialized to string
|
|
866
|
+
assert params1.identifier == params2.identifier
|
|
867
|
+
|
|
868
|
+
|
|
869
|
+
def test_params_identifier_hash_distribution():
|
|
870
|
+
"""Test that similar params produce different hashes (no collision)"""
|
|
871
|
+
identifiers = set()
|
|
872
|
+
|
|
873
|
+
for i in range(100):
|
|
874
|
+
params = Params(index=i, value=f"test_{i}")
|
|
875
|
+
identifiers.add(params.identifier)
|
|
876
|
+
|
|
877
|
+
# All 100 should be unique
|
|
878
|
+
assert len(identifiers) == 100
|
|
879
|
+
|
|
880
|
+
|
|
881
|
+
def test_params_identifier_stability():
|
|
882
|
+
"""Test identifier remains stable across multiple reads"""
|
|
883
|
+
params = Params(symbol="AAPL", quantity=100, price=150.5)
|
|
884
|
+
|
|
885
|
+
# Read identifier multiple times
|
|
886
|
+
id1 = params.identifier
|
|
887
|
+
id2 = params.identifier
|
|
888
|
+
id3 = params.identifier
|
|
889
|
+
|
|
890
|
+
# All should be identical
|
|
891
|
+
assert id1 == id2 == id3
|