diff --git a/app/model/schema/base/__init__.py b/app/model/schema/base/__init__.py index ff9ffe29..54ddc486 100644 --- a/app/model/schema/base/__init__.py +++ b/app/model/schema/base/__init__.py @@ -17,6 +17,7 @@ SPDX-License-Identifier: Apache-2.0 """ from .base import ( + CURRENCY_str, EMPTY_str, IbetShareContractVersion, IbetStraightBondContractVersion, diff --git a/app/model/schema/base/base.py b/app/model/schema/base/base.py index 4e77870b..bb9b0b34 100644 --- a/app/model/schema/base/base.py +++ b/app/model/schema/base/base.py @@ -44,6 +44,7 @@ class IbetShareContractVersion(StrEnum): pattern="^(19[0-9]{2}|20[0-9]{2})(0[1-9]|1[0-2])(0[1-9]|[12][0-9]|3[01])$" ), ] +CURRENCY_str = Annotated[str, StringConstraints(min_length=3, max_length=3)] EMPTY_str = Literal[""] diff --git a/app/model/schema/token.py b/app/model/schema/token.py index 0169550a..a8f5bc23 100644 --- a/app/model/schema/token.py +++ b/app/model/schema/token.py @@ -28,6 +28,7 @@ from web3 import Web3 from app.model.schema.base import ( + CURRENCY_str, EMPTY_str, IbetShareContractVersion, IbetStraightBondContractVersion, @@ -36,9 +37,7 @@ SortOrder, YYYYMMDD_constr, ) - -from . import LockEventCategory -from .position import LockEvent +from app.model.schema.position import LockEvent, LockEventCategory ############################ @@ -134,13 +133,9 @@ class IbetStraightBondUpdate(BaseModel): face_value_currency: Optional[str] = Field(default=None, min_length=3, max_length=3) interest_rate: Optional[float] = Field(None, ge=0.0000, le=100.0000) interest_payment_date: Optional[list[MMDD_constr]] = None - interest_payment_currency: Optional[str] = Field( - default=None, min_length=3, max_length=3 - ) + interest_payment_currency: Optional[CURRENCY_str | EMPTY_str] = Field(default=None) redemption_value: Optional[int] = Field(None, ge=0, le=5_000_000_000) - redemption_value_currency: Optional[str] = Field( - default=None, min_length=3, max_length=3 - ) + redemption_value_currency: Optional[CURRENCY_str | EMPTY_str] = Field(default=None) base_fx_rate: Optional[float] = Field(default=None, ge=0.000000) transferable: Optional[bool] = None status: Optional[bool] = None diff --git a/tests/test_app_routers_bond_tokens_{token_address}_POST.py b/tests/test_app_routers_bond_tokens_{token_address}_POST.py index 9a0c0013..61cc7113 100644 --- a/tests/test_app_routers_bond_tokens_{token_address}_POST.py +++ b/tests/test_app_routers_bond_tokens_{token_address}_POST.py @@ -78,7 +78,7 @@ class TestAppRoutersBondTokensTokenAddressPOST: # Normal Case ########################################################################### - # + # def test_normal_1(self, client, db): test_account = config_eth_account("user1") _issuer_address = test_account["address"] @@ -210,6 +210,139 @@ def test_normal_1(self, client, db): } assert operation_log.operation_category == "Update" + # + # Empty str set to currency code + def test_normal_1_2(self, client, db): + test_account = config_eth_account("user1") + _issuer_address = test_account["address"] + issuer_private_key = decode_keyfile_json( + raw_keyfile_json=test_account["keyfile_json"], + password="password".encode("utf-8"), + ) + _keyfile = test_account["keyfile_json"] + + # Prepare data : Token + token_contract = deploy_bond_token_contract(_issuer_address, issuer_private_key) + _token_address = token_contract.address + + # prepare data + account = Account() + account.issuer_address = _issuer_address + account.keyfile = _keyfile + account.eoa_password = E2EEUtils.encrypt("password") + db.add(account) + + token = Token() + token.type = TokenType.IBET_STRAIGHT_BOND.value + token.tx_hash = "" + token.issuer_address = _issuer_address + token.token_address = _token_address + token.abi = "" + token.version = TokenVersion.V_23_12 + db.add(token) + + db.commit() + + # request target API + req_param = { + "face_value": 10000, + "face_value_currency": "USD", + "interest_rate": 3.0, + "interest_payment_date": ["1201"], + "interest_payment_currency": "JPY", + "redemption_value": 0, + "redemption_value_currency": "", + "base_fx_rate": 123.456789, + "transferable": False, + "status": False, + "is_offering": False, + "is_redeemed": True, + "tradable_exchange_contract_address": "0xe883A6f441Ad5682d37DF31d34fc012bcB07A740", + "personal_info_contract_address": "0xa4CEe3b909751204AA151860ebBE8E7A851c2A1a", + "contact_information": "問い合わせ先test", + "privacy_policy": "プライバシーポリシーtest", + "transfer_approval_required": True, + "memo": "m" * 10000, + } + resp = client.post( + self.base_url.format(_token_address), + json=req_param, + headers={ + "issuer-address": _issuer_address, + "eoa-password": E2EEUtils.encrypt("password"), + }, + ) + + # assertion + assert resp.status_code == 200, resp.json() + assert resp.json() is None + + token_attr_update = db.scalars( + select(TokenAttrUpdate).where( + TokenAttrUpdate.token_address == _token_address + ) + ).all() + assert len(token_attr_update) == 1 + + update_token = db.scalars(select(UpdateToken).limit(1)).first() + assert update_token is None + + operation_log = db.scalars(select(TokenUpdateOperationLog).limit(1)).first() + assert operation_log.token_address == _token_address + assert operation_log.issuer_address == _issuer_address + assert operation_log.type == TokenType.IBET_STRAIGHT_BOND.value + assert operation_log.original_contents == { + "contract_name": "IbetStraightBond", + "token_address": _token_address, + "issuer_address": _issuer_address, + "name": "token.name", + "symbol": "token.symbol", + "total_supply": 100, + "tradable_exchange_contract_address": "0x0000000000000000000000000000000000000000", + "contact_information": "", + "privacy_policy": "", + "status": True, + "personal_info_contract_address": "0x0000000000000000000000000000000000000000", + "transferable": False, + "is_offering": False, + "transfer_approval_required": False, + "face_value": 20, + "face_value_currency": "JPY", + "interest_rate": 0, + "interest_payment_currency": "", + "redemption_date": "token.redemption_date", + "redemption_value": 30, + "redemption_value_currency": "JPY", + "return_date": "token.return_date", + "return_amount": "token.return_amount", + "base_fx_rate": 0.0, + "purpose": "token.purpose", + "memo": "", + "is_redeemed": False, + "interest_payment_date": ["", "", "", "", "", "", "", "", "", "", "", ""], + } + assert operation_log.arguments == { + "face_value": 10000, + "face_value_currency": "USD", + "interest_rate": 3.0, + "interest_payment_date": ["1201"], + "interest_payment_currency": "JPY", + "redemption_value": 0, + "redemption_value_currency": "", + "base_fx_rate": 123.456789, + "transferable": False, + "status": False, + "is_offering": False, + "is_redeemed": True, + "tradable_exchange_contract_address": "0xe883A6f441Ad5682d37DF31d34fc012bcB07A740", + "personal_info_contract_address": "0xa4CEe3b909751204AA151860ebBE8E7A851c2A1a", + "contact_information": "問い合わせ先test", + "privacy_policy": "プライバシーポリシーtest", + "transfer_approval_required": True, + "memo": "m" * 10000, + } + assert operation_log.operation_category == "Update" + # # No request parameters def test_normal_2(self, client, db): @@ -685,16 +818,23 @@ def test_error_1_7_1(self, client, db): assert resp.status_code == 422 assert resp.json() == { - "meta": {"code": 1, "title": "RequestValidationError"}, "detail": [ { - "type": "string_too_long", - "loc": ["body", "interest_payment_currency"], + "ctx": {"max_length": 3}, + "input": "JPYY", + "loc": ["body", "interest_payment_currency", "constrained-str"], "msg": "String should have at most 3 characters", + "type": "string_too_long", + }, + { + "ctx": {"expected": "''"}, "input": "JPYY", - "ctx": {"max_length": 3}, - } + "loc": ["body", "interest_payment_currency", "literal['']"], + "msg": "Input should be ''", + "type": "literal_error", + }, ], + "meta": {"code": 1, "title": "RequestValidationError"}, } # @@ -713,16 +853,23 @@ def test_error_1_7_2(self, client, db): assert resp.status_code == 422 assert resp.json() == { - "meta": {"code": 1, "title": "RequestValidationError"}, "detail": [ { - "type": "string_too_short", - "loc": ["body", "interest_payment_currency"], + "ctx": {"min_length": 3}, + "input": "JP", + "loc": ["body", "interest_payment_currency", "constrained-str"], "msg": "String should have at least 3 characters", + "type": "string_too_short", + }, + { + "ctx": {"expected": "''"}, "input": "JP", - "ctx": {"min_length": 3}, - } + "loc": ["body", "interest_payment_currency", "literal['']"], + "msg": "Input should be ''", + "type": "literal_error", + }, ], + "meta": {"code": 1, "title": "RequestValidationError"}, } # @@ -741,16 +888,23 @@ def test_error_1_8_1(self, client, db): assert resp.status_code == 422 assert resp.json() == { - "meta": {"code": 1, "title": "RequestValidationError"}, "detail": [ { - "type": "string_too_long", - "loc": ["body", "redemption_value_currency"], + "ctx": {"max_length": 3}, + "input": "JPYY", + "loc": ["body", "redemption_value_currency", "constrained-str"], "msg": "String should have at most 3 characters", + "type": "string_too_long", + }, + { + "ctx": {"expected": "''"}, "input": "JPYY", - "ctx": {"max_length": 3}, - } + "loc": ["body", "redemption_value_currency", "literal['']"], + "msg": "Input should be ''", + "type": "literal_error", + }, ], + "meta": {"code": 1, "title": "RequestValidationError"}, } # @@ -769,16 +923,23 @@ def test_error_1_8_2(self, client, db): assert resp.status_code == 422 assert resp.json() == { - "meta": {"code": 1, "title": "RequestValidationError"}, "detail": [ { - "type": "string_too_short", - "loc": ["body", "redemption_value_currency"], + "ctx": {"min_length": 3}, + "input": "JP", + "loc": ["body", "redemption_value_currency", "constrained-str"], "msg": "String should have at least 3 characters", + "type": "string_too_short", + }, + { + "ctx": {"expected": "''"}, "input": "JP", - "ctx": {"min_length": 3}, - } + "loc": ["body", "redemption_value_currency", "literal['']"], + "msg": "Input should be ''", + "type": "literal_error", + }, ], + "meta": {"code": 1, "title": "RequestValidationError"}, } #