Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#86drpnck9 - Refactor example_tests/test_update_contract.py to use Bo… #1229

Merged
merged 2 commits into from
Apr 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion boa3_test/tests/boatestcase.py
Original file line number Diff line number Diff line change
Expand Up @@ -950,7 +950,7 @@ def _get_compiler_output(self, path: str, deserialize: bool, root_folder: str =

with _COMPILER_LOCK:
if not os.path.isfile(nef_output):
return self.compile_and_save(path, root_folder=root_folder)
return self.compile_and_save(path, root_folder=root_folder, get_raw_nef=not deserialize)

from boa3.internal.neo.contracts.neffile import NefFile

Expand Down
115 changes: 55 additions & 60 deletions boa3_test/tests/examples_tests/test_update_contract.py
Original file line number Diff line number Diff line change
@@ -1,101 +1,96 @@
import json

from boa3_test.tests.boa_test import BoaTest # needs to be the first import to avoid circular imports
from neo3.contracts.contract import CONTRACT_HASHES
from neo3.wallet import account

from boa3.internal import constants
from boa3.internal.neo.vm.type.String import String
from boa3.internal.neo3.vm import VMState
from boa3_test.tests.test_drive import neoxp
from boa3_test.tests.test_drive.testrunner.boa_test_runner import BoaTestRunner
from boa3_test.tests import boatestcase
from boa3_test.tests.event import UpdateEvent


class TestUpdateContractTemplate(BoaTest):
class TestUpdateContractTemplate(boatestcase.BoaTestCase):
default_folder: str = 'examples'

OWNER = neoxp.utils.get_account_by_name('owner')
OTHER_ACCOUNT = neoxp.utils.get_account_by_name('testAccount1')
owner: account.Account
other_account: account.Account

GAS_TO_DEPLOY = 1000 * 10 ** 8

def test_update_contract_compile(self):
path = self.get_contract_path('update_contract.py')
path_new = self.get_contract_path('examples/auxiliary_contracts', 'update_contract.py')
self.compile(path)
self.compile(path_new)
@classmethod
def setupTestCase(cls):
cls.owner = cls.node.wallet.account_new(label='owner', password='123')

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please consider using a more descriptive name for the variable cls. It would improve code readability.

cls.other_account = cls.node.wallet.account_new(label='otherAccount', password='123')

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please consider using a more descriptive name for the variable cls. It would improve code readability.


def test_update_contract(self):
path, _ = self.get_deploy_file_paths('update_contract.py')
runner = BoaTestRunner(runner_id=self.method_name())
super().setupTestCase()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please consider using a more descriptive name for the variable cls. It would improve code readability.


Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please consider using a more descriptive name for the variable cls. It would improve code readability.

invokes = []
expected_results = []
@classmethod
async def asyncSetupClass(cls) -> None:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please consider using a more descriptive name for the variable cls. It would improve code readability.

await super().asyncSetupClass()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please consider using a more descriptive name for the variable cls. It would improve code readability.


test_account = self.OTHER_ACCOUNT
test_account_script_hash = test_account.script_hash.to_array()
await cls.transfer(CONTRACT_HASHES.GAS_TOKEN, cls.genesis.script_hash, cls.owner.script_hash, 100)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please consider using a more descriptive name for the variable cls. It would improve code readability.

await cls.transfer(CONTRACT_HASHES.GAS_TOKEN, cls.genesis.script_hash, cls.other_account.script_hash, 100)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please consider using a more descriptive name for the variable cls. It would improve code readability.


runner.add_gas(self.OWNER.address, self.GAS_TO_DEPLOY)
update_contract = runner.deploy_contract(path, account=self.OWNER)
runner.update_contracts(export_checkpoint=True)
await cls.set_up_contract('update_contract.py', signing_account=cls.owner)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please consider using a more descriptive name for the variable cls. It would improve code readability.


Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please consider using a more descriptive name for the variable cls. It would improve code readability.

tx_deploy_notifications = runner.get_transaction_result(update_contract.tx_id).executions[0].notifications
# Transfer emitted when deploying the smart contract
self.assertEqual(2, len(tx_deploy_notifications))
self.assertEqual(1, len([notification
for notification in tx_deploy_notifications if notification.name == 'Transfer']))
self.assertEqual(1, len([notification
for notification in tx_deploy_notifications if notification.name == 'Deploy']))
def test_update_contract_compile(self):
self.assertCompile('update_contract.py')
self.assertCompile('update_contract.py', root_folder='examples/auxiliary_contracts')

async def test_update_contract(self):
# Saving user's balance before calling method to compare it later
tokens_before = runner.call_contract(path, 'balanceOf', test_account_script_hash)
tokens_before, _ = await self.call('balanceOf', [self.other_account.script_hash], return_type=int)

# The bugged method is being called and the user is able to receive tokens for free
invokes.append(runner.call_contract(path, 'method', test_account_script_hash))
expected_results.append(None)
result, notifications = await self.call('method', [self.other_account.script_hash], return_type=None, signing_accounts=[self.other_account])
self.assertIsNone(result)

tokens_after = runner.call_contract(path, 'balanceOf', test_account_script_hash)
tokens_after, _ = await self.call('balanceOf', [self.other_account.script_hash], return_type=int)

runner.execute()
self.assertEqual(VMState.HALT, runner.vm_state, msg=runner.error)
transfer_events = self.filter_events(notifications, notification_type=boatestcase.Nep17TransferEvent)
self.assertEqual(1, len(transfer_events))

# The amount of tokens will be higher after calling the method
self.assertGreater(tokens_after.result, tokens_before.result)

transfer_events = runner.get_events('Transfer', origin=update_contract)
self.assertEqual(1, len(transfer_events))
self.assertGreater(tokens_after, tokens_before)

# new smart contract that has the bug fixed
path_new = self.get_contract_path('examples/auxiliary_contracts', 'update_contract.py')
new_nef, new_manifest = self.get_bytes_output(path_new)
new_nef, new_manifest = self.get_serialized_output(path_new)

arg_manifest = String(json.dumps(new_manifest, separators=(',', ':'))).to_bytes()

# The smart contract will be updated to fix the bug in the method
invokes.append(runner.call_contract(path, 'update_sc', new_nef, arg_manifest, None))
expected_results.append(None)

runner.execute(account=self.OWNER)
self.assertEqual(VMState.HALT, runner.vm_state, msg=runner.error)
result, notifications = await self.call('update_sc', [new_nef, arg_manifest, None], return_type=None, signing_accounts=[self.owner])
self.assertIsNone(result)

# An `Update` event was be emitted after the update
event_update = runner.get_events('Update')
self.assertEqual(1, len(event_update))

runner.run_contract(path, 'update_sc', new_nef, arg_manifest, None, account=self.OWNER)
update_events = self.filter_events(notifications, notification_type=UpdateEvent)
self.assertEqual(1, len(update_events))

# Saving user's balance before calling method to compare it later
tokens_before = runner.call_contract(path, 'balanceOf', test_account_script_hash)
tokens_before, _ = await self.call('balanceOf', [self.other_account.script_hash], return_type=int)

# Now, when method is called, it won't mint new tokens to any user that called it
invokes.append(runner.call_contract(path, 'method', test_account_script_hash))
expected_results.append(None)
result, notifications = await self.call('method', [self.other_account.script_hash], return_type=None, signing_accounts=[self.other_account])
self.assertIsNone(result)

transfer_events = self.filter_events(notifications, notification_type=boatestcase.Nep17TransferEvent)
self.assertEqual(0, len(transfer_events))

# The amount of tokens now is the same before and after calling the method
tokens_after = runner.call_contract(path, 'balanceOf', test_account_script_hash)
tokens_after, _ = await self.call('balanceOf', [self.other_account.script_hash], return_type=int)

self.assertEqual(tokens_after, tokens_before)

runner.get_contract(constants.NEO_SCRIPT)
# If the signing account is the owner, then the method works, and it will mint the new tokens
tokens_before, _ = await self.call('balanceOf', [self.other_account.script_hash], return_type=int)

runner.execute(account=test_account)
self.assertEqual(VMState.HALT, runner.vm_state, msg=runner.error)
result, notifications = await self.call('method', [self.other_account.script_hash], return_type=None, signing_accounts=[self.owner])
self.assertIsNone(result)

self.assertEqual(tokens_after.result, tokens_before.result)
transfer_events = self.filter_events(notifications, notification_type=boatestcase.Nep17TransferEvent)
self.assertEqual(1, len(transfer_events))

# The amount of tokens will be higher after calling the method
tokens_after, _ = await self.call('balanceOf', [self.other_account.script_hash], return_type=int)

for x in range(len(invokes)):
self.assertEqual(expected_results[x], invokes[x].result)
self.assertGreater(tokens_after, tokens_before)
Loading