From df0db6c28f45c6071fdfa774fb6ac732cbb8631e Mon Sep 17 00:00:00 2001 From: kdmukai Date: Wed, 9 Aug 2023 08:30:15 -0500 Subject: [PATCH] flow test reorg; add network mismatch flow test --- src/seedsigner/views/view.py | 4 +- tests/base.py | 9 +++ tests/test_flows_seed.py | 129 +++++++++++++++++++++++------------ 3 files changed, 98 insertions(+), 44 deletions(-) diff --git a/src/seedsigner/views/view.py b/src/seedsigner/views/view.py index 2a0ff822..dfd59eb3 100644 --- a/src/seedsigner/views/view.py +++ b/src/seedsigner/views/view.py @@ -150,13 +150,13 @@ class Destination: def _run_view(self): + if self.view.has_redirect: + return self.view.get_redirect() return self.view.run() def run(self): self._instantiate_view() - if self.view.has_redirect: - return self.view.get_redirect() return self._run_view() diff --git a/tests/base.py b/tests/base.py index 708d64f7..0628fb1a 100644 --- a/tests/base.py +++ b/tests/base.py @@ -151,6 +151,15 @@ class FlowTest(BaseTest): # View.run() call below. sequence.pop(0) + if destination.view.has_redirect: + # TODO: Migrate all View redirects to use `View.set_redirect()` + # in their `__init__()` rather than `run()` and then refactor + # here to explicitly require `has_redirect` to be True. + # For now: Support the newer `set_redirect()` routing while + # still letting redirects get returned by `View.run()` further + # below. + return destination.view.get_redirect() + # Some Views reach into their Screen's variables directly (e.g. # Screen.buttons to preserve the scroll position), so we need to mock out the # Screen instance that is created by the View. diff --git a/tests/test_flows_seed.py b/tests/test_flows_seed.py index aaeff5e7..fde32231 100644 --- a/tests/test_flows_seed.py +++ b/tests/test_flows_seed.py @@ -1,13 +1,14 @@ +import pytest + # Must import test base before the Controller from base import BaseTest, FlowTest, FlowStep from base import FlowTestRunScreenNotExecutedException, FlowTestInvalidButtonDataSelectionException -import pytest from seedsigner.gui.screens.screen import RET_CODE__BACK_BUTTON from seedsigner.models.settings import SettingsConstants from seedsigner.models.seed import Seed -from seedsigner.views.view import MainMenuView, View -from seedsigner.views import seed_views, scan_views +from seedsigner.views.view import MainMenuView, View, NetworkMismatchErrorView +from seedsigner.views import seed_views, scan_views, settings_views @@ -291,52 +292,69 @@ class TestSeedFlows(FlowTest): ) + +class TestMessageSigningFlows(FlowTest): + MAINNET_DERIVATION_PATH = "m/84h/0h/0h/0/0" + TESTNET_DERIVATION_PATH = "m/84h/1h/0h/0/0" + SHORT_MESSAGE = "I attest that I control this bitcoin address blah blah blah" + MULTIPAGE_MESSAGE = """Chancellor on brink of second bailout for banks + + Billions may be needed as lending squeeze tightens + + Alistair Darling has been forced to consider a second bailout for banks as the lending drought worsens. + + The Chancellor will decide within weeks whether to pump billions more into the economy as evidence mounts that the £37 billion part-nationalisation last year has failed to keep credit flowing. Options include cash injections, offering banks cheaper state guarantees to raise money privately or buying up “toxic assets”, The Times has learnt.""" + + + def load_seed_into_decoder(self, view: scan_views.ScanView): + view.decoder.add_data("0000" * 11 + "0003") + + + def load_signmessage_into_decoder(self, view:View, derivation_path: str, message: str): + view.decoder.add_data(f"signmessage {derivation_path} ascii:{message}") + + + def load_short_message_into_decoder(self, view: View): + self.load_signmessage_into_decoder(view, self.MAINNET_DERIVATION_PATH, self.SHORT_MESSAGE) + + + def load_testnet_message_into_decoder(self, view: View): + self.load_signmessage_into_decoder(view, self.TESTNET_DERIVATION_PATH, self.SHORT_MESSAGE) + + + def load_multipage_message_into_decoder(self, view: View): + self.load_signmessage_into_decoder(view, self.MAINNET_DERIVATION_PATH, self.MULTIPAGE_MESSAGE) + + + def inject_mesage_as_paged_message(self, view: View): + # Because the Screen won't actually run, we have to do the Screen's work here + from seedsigner.gui.components import reflow_text_into_pages, GUIConstants + paged = reflow_text_into_pages( + text=self.controller.sign_message_data["message"], + width=240 - 2*GUIConstants.EDGE_PADDING, + height=240 - GUIConstants.TOP_NAV_HEIGHT - 3*GUIConstants.EDGE_PADDING - GUIConstants.BUTTON_HEIGHT, + ) + self.controller.sign_message_data["paged_message"] = paged + + def test_sign_message_flow(self): """ + Should scan a `signmessage` QR and complete the message review, address review, + and signing flow. """ # Ensure message signing is enabled self.settings.set_value(SettingsConstants.SETTING__MESSAGE_SIGNING, SettingsConstants.OPTION__ENABLED) - def load_seed_into_decoder(view: scan_views.ScanView): - view.decoder.add_data("0000" * 11 + "0003") - - derivation_path = "m/84h/0h/0h/0/0" - message = "I attest that I control this bitcoin address blah blah blah" - multipage_message = """Chancellor on brink of second bailout for banks - - Billions may be needed as lending squeeze tightens - - Alistair Darling has been forced to consider a second bailout for banks as the lending drought worsens. - - The Chancellor will decide within weeks whether to pump billions more into the economy as evidence mounts that the £37 billion part-nationalisation last year has failed to keep credit flowing. Options include cash injections, offering banks cheaper state guarantees to raise money privately or buying up “toxic assets”, The Times has learnt.""" - - - def load_standard_message_into_decoder(view: View): - view.decoder.add_data(f"signmessage {derivation_path} ascii:{message}") - - def load_multipage_message_into_decoder(view: View): - view.decoder.add_data(f"signmessage {derivation_path} ascii:{multipage_message}") - - def inject_mesage_as_paged_message(view: View): - # Because the Screen won't actually run, we have to do the Screen's work here - from seedsigner.gui.components import reflow_text_into_pages, GUIConstants - paged = reflow_text_into_pages( - text=self.controller.sign_message_data["message"], - width=240 - 2*GUIConstants.EDGE_PADDING, - height=240 - GUIConstants.TOP_NAV_HEIGHT - 3*GUIConstants.EDGE_PADDING - GUIConstants.BUTTON_HEIGHT, - ) - self.controller.sign_message_data["paged_message"] = paged - # Scenario 1: Load the mesage first, then the seed self.run_sequence([ FlowStep(MainMenuView, button_data_selection=MainMenuView.SCAN), - FlowStep(scan_views.ScanView, before_run=load_standard_message_into_decoder), # simulate read message QR; ret val is ignored + FlowStep(scan_views.ScanView, before_run=self.load_short_message_into_decoder), # simulate read message QR; ret val is ignored FlowStep(seed_views.SeedSignMessageStartView, is_redirect=True), FlowStep(seed_views.SeedSelectSeedView, button_data_selection=seed_views.SeedSelectSeedView.SCAN_SEED), - FlowStep(scan_views.ScanView, before_run=load_seed_into_decoder), # simulate read SeedQR; ret val is ignored + FlowStep(scan_views.ScanView, before_run=self.load_seed_into_decoder), # simulate read SeedQR; ret val is ignored FlowStep(seed_views.SeedFinalizeView, button_data_selection=seed_views.SeedFinalizeView.FINALIZE), FlowStep(seed_views.SeedOptionsView, is_redirect=True), - FlowStep(seed_views.SeedSignMessageConfirmMessageView, before_run=inject_mesage_as_paged_message, screen_return_value=0), + FlowStep(seed_views.SeedSignMessageConfirmMessageView, before_run=self.inject_mesage_as_paged_message, screen_return_value=0), FlowStep(seed_views.SeedSignMessageConfirmAddressView, screen_return_value=0), FlowStep(seed_views.SeedSignMessageSignedMessageQRView, screen_return_value=0), FlowStep(MainMenuView), @@ -346,12 +364,12 @@ class TestSeedFlows(FlowTest): self.controller.discard_seed(0) self.run_sequence([ FlowStep(MainMenuView, button_data_selection=MainMenuView.SCAN), - FlowStep(scan_views.ScanView, before_run=load_seed_into_decoder), # simulate read SeedQR; ret val is ignored + FlowStep(scan_views.ScanView, before_run=self.load_seed_into_decoder), # simulate read SeedQR; ret val is ignored FlowStep(seed_views.SeedFinalizeView, button_data_selection=seed_views.SeedFinalizeView.FINALIZE), FlowStep(seed_views.SeedOptionsView, button_data_selection=seed_views.SeedOptionsView.SIGN_MESSAGE), - FlowStep(scan_views.ScanView, before_run=load_standard_message_into_decoder), # simulate read message QR; ret val is ignored + FlowStep(scan_views.ScanView, before_run=self.load_short_message_into_decoder), # simulate read message QR; ret val is ignored FlowStep(seed_views.SeedSignMessageStartView, is_redirect=True), - FlowStep(seed_views.SeedSignMessageConfirmMessageView, before_run=inject_mesage_as_paged_message, screen_return_value=0), + FlowStep(seed_views.SeedSignMessageConfirmMessageView, before_run=self.inject_mesage_as_paged_message, screen_return_value=0), FlowStep(seed_views.SeedSignMessageConfirmAddressView, screen_return_value=0), FlowStep(seed_views.SeedSignMessageSignedMessageQRView, screen_return_value=0), FlowStep(MainMenuView), @@ -360,13 +378,13 @@ class TestSeedFlows(FlowTest): # Scenario 3: Load a long, multipage message self.run_sequence([ FlowStep(MainMenuView, button_data_selection=MainMenuView.SCAN), - FlowStep(scan_views.ScanView, before_run=load_multipage_message_into_decoder), # simulate read message QR; ret val is ignored + FlowStep(scan_views.ScanView, before_run=self.load_multipage_message_into_decoder), # simulate read message QR; ret val is ignored FlowStep(seed_views.SeedSignMessageStartView, is_redirect=True), FlowStep(seed_views.SeedSelectSeedView, button_data_selection=seed_views.SeedSelectSeedView.SCAN_SEED), - FlowStep(scan_views.ScanView, before_run=load_seed_into_decoder), # simulate read SeedQR; ret val is ignored + FlowStep(scan_views.ScanView, before_run=self.load_seed_into_decoder), # simulate read SeedQR; ret val is ignored FlowStep(seed_views.SeedFinalizeView, button_data_selection=seed_views.SeedFinalizeView.FINALIZE), FlowStep(seed_views.SeedOptionsView, is_redirect=True), - FlowStep(seed_views.SeedSignMessageConfirmMessageView, before_run=inject_mesage_as_paged_message, screen_return_value=0), # page 1/5 + FlowStep(seed_views.SeedSignMessageConfirmMessageView, before_run=self.inject_mesage_as_paged_message, screen_return_value=0), # page 1/5 FlowStep(seed_views.SeedSignMessageConfirmMessageView, screen_return_value=0), # page 2/5 FlowStep(seed_views.SeedSignMessageConfirmMessageView, screen_return_value=0), # page 3/5 FlowStep(seed_views.SeedSignMessageConfirmMessageView, screen_return_value=0), # page 4/5 @@ -389,3 +407,30 @@ class TestSeedFlows(FlowTest): FlowStep(seed_views.SeedSignMessageSignedMessageQRView, screen_return_value=0), FlowStep(MainMenuView), ]) + + + def test_sign_message_network_mismatch_flow(self): + """ + Should redirect to NetworkMismatchErrorView if a message's derivation path network doesn't match the current network. + + The error view should then forward to the Network Settings update View. + """ + # Ensure message signing is enabled + self.settings.set_value(SettingsConstants.SETTING__MESSAGE_SIGNING, SettingsConstants.OPTION__ENABLED) + + # Ensure we're configured for mainnet + self.settings.set_value(SettingsConstants.SETTING__NETWORK, SettingsConstants.MAINNET) + + self.run_sequence([ + FlowStep(MainMenuView, button_data_selection=MainMenuView.SCAN), + FlowStep(scan_views.ScanView, before_run=self.load_testnet_message_into_decoder), # simulate read message QR; ret val is ignored + FlowStep(seed_views.SeedSignMessageStartView, is_redirect=True), + FlowStep(seed_views.SeedSelectSeedView, button_data_selection=seed_views.SeedSelectSeedView.SCAN_SEED), + FlowStep(scan_views.ScanView, before_run=self.load_seed_into_decoder), # simulate read SeedQR; ret val is ignored + FlowStep(seed_views.SeedFinalizeView, button_data_selection=seed_views.SeedFinalizeView.FINALIZE), + FlowStep(seed_views.SeedOptionsView, is_redirect=True), + FlowStep(seed_views.SeedSignMessageConfirmMessageView, before_run=self.inject_mesage_as_paged_message, screen_return_value=0), + FlowStep(seed_views.SeedSignMessageConfirmAddressView, is_redirect=True), + FlowStep(NetworkMismatchErrorView), + FlowStep(settings_views.SettingsEntryUpdateSelectionView), + ])