def test_purge_ethereum_transaction_data(rotkehlchen_api_server): rotki = rotkehlchen_api_server.rest_api.rotkehlchen db = DBEthTx(rotki.data.db) db.add_ethereum_transactions([ EthereumTransaction( tx_hash=bytes(), timestamp=1, block_number=1, from_address=make_ethereum_address(), to_address=make_ethereum_address(), value=FVal(1), gas=FVal(1), gas_price=FVal(1), gas_used=FVal(1), input_data=bytes(), nonce=1, ) ], ) filter_ = ETHTransactionsFilterQuery.make() result, filter_count = db.get_ethereum_transactions(filter_) assert len(result) == 1 assert filter_count == 1 response = requests.delete( api_url_for( rotkehlchen_api_server, "ethereumtransactionsresource", ), ) assert_simple_ok_response(response) result, filter_count = db.get_ethereum_transactions(filter_) assert len(result) == 0 assert filter_count == 0
def test_user_set_premium_credentials(rotkehlchen_api_server, username): """Test that setting the premium credentials endpoint works. We mock the server accepting the premium credentials """ rotki = rotkehlchen_api_server.rest_api.rotkehlchen _, patched_premium_at_set, patched_get = create_patched_premium( PremiumCredentials(VALID_PREMIUM_KEY, VALID_PREMIUM_SECRET), patch_get=True, metadata_last_modify_ts=0, metadata_data_hash=b'', metadata_data_size=0, ) # Set premium credentials for current user data = { 'premium_api_key': VALID_PREMIUM_KEY, 'premium_api_secret': VALID_PREMIUM_SECRET } with patched_premium_at_set: response = requests.patch( api_url_for(rotkehlchen_api_server, "usersbynameresource", name=username), json=data, ) assert_simple_ok_response(response) assert rotki.premium is not None assert rotki.premium.credentials.serialize_key() == VALID_PREMIUM_KEY assert rotki.premium.credentials.serialize_secret() == VALID_PREMIUM_SECRET with patched_get: assert rotki.premium.is_active()
def test_qerying_settings(rotkehlchen_api_server, username): """Make sure that querying settings works for logged in user""" response = requests.get( api_url_for(rotkehlchen_api_server, "settingsresource")) assert_proper_response(response) json_data = response.json() result = json_data['result'] assert json_data['message'] == '' assert result['version'] == ROTKEHLCHEN_DB_VERSION for setting in DBSettings._fields: assert setting in result # Logout of the active user data = {'action': 'logout'} response = requests.patch( api_url_for(rotkehlchen_api_server, "usersbynameresource", name=username), json=data, ) assert_simple_ok_response(response) # and now with no logged in user it should fail response = requests.get( api_url_for(rotkehlchen_api_server, "settingsresource")) assert_error_response( response=response, contained_in_msg='No user is currently logged in', status_code=HTTPStatus.CONFLICT, )
def test_query_aave_history_with_borrowing(rotkehlchen_api_server, ethereum_accounts, aave_use_graph): # pylint: disable=unused-argument # noqa: E501 """Check querying the aave histoy endpoint works. Uses real data.""" rotki = rotkehlchen_api_server.rest_api.rotkehlchen setup = setup_balances( rotki, ethereum_accounts=ethereum_accounts, btc_accounts=None, original_queries=['zerion', 'logs', 'blocknobytime'], ) _query_borrowing_aave_history_test(setup, rotkehlchen_api_server) # Run it 2 times to make sure that data can be queried properly from the DB _query_borrowing_aave_history_test(setup, rotkehlchen_api_server) # Make sure events end up in the DB assert len(rotki.data.db.get_aave_events(AAVE_TEST_ACC_3)) != 0 # test aave data purging from the db works response = requests.delete( api_url_for( rotkehlchen_api_server, 'namedethereummoduledataresource', module_name='aave', )) assert_simple_ok_response(response) assert len(rotki.data.db.get_aave_events(AAVE_TEST_ACC_3)) == 0
def test_setup_exchange_does_not_stay_in_mapping_after_500_error( rotkehlchen_api_server): """Test that if 500 error is returned during setup of an exchange and it's stuck in the exchange mapping Rotki doesn't still think the exchange is registered. Regression test for the second part of https://github.com/rotki/rotki/issues/943 """ data = {'name': 'kraken', 'api_key': 'ddddd', 'api_secret': 'fffffff'} with API_KEYPAIR_KRAKEN_VALIDATION_FAIL_PATCH: response = requests.put( api_url_for(rotkehlchen_api_server, "exchangesresource"), json=data, ) assert_error_response( response=response, status_code=HTTPStatus.INTERNAL_SERVER_ERROR, ) # Now try to register the exchange again data = {'name': 'kraken', 'api_key': 'ddddd', 'api_secret': 'fffffff'} with API_KEYPAIR_VALIDATION_PATCH: response = requests.put( api_url_for(rotkehlchen_api_server, "exchangesresource"), json=data, ) assert_simple_ok_response(response) # and check that kraken is now registered response = requests.get( api_url_for(rotkehlchen_api_server, "exchangesresource")) assert_proper_response(response) json_data = response.json() assert json_data['message'] == '' assert json_data['result'] == ['kraken']
def test_remove_exchange(rotkehlchen_api_server): """Test that removing a setup exchange via the api works""" rotki = rotkehlchen_api_server.rest_api.rotkehlchen db = rotki.data.db # Setup coinbase exchange data = {'name': 'coinbase', 'api_key': 'ddddd', 'api_secret': 'fffffff'} with API_KEYPAIR_COINBASE_VALIDATION_PATCH: response = requests.put( api_url_for(rotkehlchen_api_server, "exchangesresource"), json=data, ) assert_simple_ok_response(response) # Add query ranges to see that they also get deleted when removing the exchange cursor = db.conn.cursor() cursor.executemany( 'INSERT OR REPLACE INTO used_query_ranges(name, start_ts, end_ts) VALUES (?, ?, ?)', [('coinbasepro_trades', 0, 1579564096), ('coinbasepro_margins', 0, 1579564096), ('coinbasepro_asset_movements', 0, 1579564096), ('coinbase_trades', 0, 1579564096), ('coinbase_margins', 0, 1579564096), ('coinbase_asset_movements', 0, 1579564096), ('binance_trades', 0, 1579564096), ('binance_margins', 0, 1579564096), ('binance_asset_movements', 0, 1579564096)], ) # Now remove the registered coinbase exchange data = {'name': 'coinbase'} response = requests.delete(api_url_for(rotkehlchen_api_server, "exchangesresource"), json=data) assert_simple_ok_response(response) # and check that it's not registered anymore response = requests.get( api_url_for(rotkehlchen_api_server, "exchangesresource")) assert_proper_response(response) json_data = response.json() assert json_data['message'] == '' assert json_data['result'] == [] # Also check that the coinbase query ranges have been deleted but not the other ones cursor = db.conn.cursor() result = cursor.execute('SELECT name from used_query_ranges') count = 0 for entry in result: count += 1 msg = 'only binance or coinbasepro query ranges should remain' assert 'binance' in entry[0] or 'coinbasepro' in entry[0], msg assert count == 6, 'only 6 query ranges should remain in the DB' # now try to remove a non-registered exchange data = {'name': 'binance'} response = requests.delete(api_url_for(rotkehlchen_api_server, "exchangesresource"), json=data) assert_error_response( response=response, contained_in_msg='Exchange binance is not registered', status_code=HTTPStatus.CONFLICT, )
def test_purge_ethereum_transaction_data(rotkehlchen_api_server): rotki = rotkehlchen_api_server.rest_api.rotkehlchen db = rotki.data.db db.add_ethereum_transactions( [EthereumTransaction( tx_hash=bytes(), timestamp=1, block_number=1, from_address=make_ethereum_address(), to_address=make_ethereum_address(), value=FVal(1), gas=FVal(1), gas_price=FVal(1), gas_used=FVal(1), input_data=bytes(), nonce=1, )], from_etherscan=True, ) assert len(db.get_ethereum_transactions()) == 1 response = requests.delete( api_url_for( rotkehlchen_api_server, "ethereumtransactionsresource", ), ) assert_simple_ok_response(response) assert len(db.get_ethereum_transactions()) == 0
def test_query_yearn_vault_history(rotkehlchen_api_server, ethereum_accounts): """Check querying the yearn vaults history endpoint works. Uses real data.""" async_query = random.choice([True, False]) rotki = rotkehlchen_api_server.rest_api.rotkehlchen setup = setup_balances( rotki, ethereum_accounts=ethereum_accounts, btc_accounts=None, original_queries=['zerion', 'logs', 'blocknobytime'], ) for _ in range(2): # Run 2 times to make sure that loading data from DB the 2nd time works fine with ExitStack() as stack: # patch ethereum/etherscan to not autodetect tokens (not needed with infura) setup.enter_ethereum_patches(stack) response = requests.get(api_url_for( rotkehlchen_api_server, "yearnvaultshistoryresource", ), json={'async_query': async_query}) if async_query: task_id = assert_ok_async_response(response) outcome = wait_for_async_task(rotkehlchen_api_server, task_id, timeout=600) assert outcome['message'] == '' result = outcome['result'] else: result = assert_proper_response_with_result(response) # Make sure some data was saved in the DB after first call events = rotki.data.db.get_yearn_vaults_events( TEST_ACC1, YEARN_VAULTS['yyDAI+yUSDC+yUSDT+yTUSD'], ) assert len(events) >= 11 result = result[TEST_ACC1] check_vault_history('YALINK Vault', EXPECTED_HISTORY, result) check_vault_history('YCRV Vault', EXPECTED_HISTORY, result) check_vault_history('YSRENCURVE Vault', EXPECTED_HISTORY, result) check_vault_history('YUSDC Vault', EXPECTED_HISTORY, result) check_vault_history('YUSDT Vault', EXPECTED_HISTORY, result) check_vault_history('YYFI Vault', EXPECTED_HISTORY, result) # Make sure events end up in the DB # test yearn vault data purging from the db works response = requests.delete( api_url_for( rotkehlchen_api_server, 'ethereummoduledataresource', module_name='yearn_vaults', )) assert_simple_ok_response(response) events = rotki.data.db.get_yearn_vaults_events( TEST_ACC1, YEARN_VAULTS['yyDAI+yUSDC+yUSDT+yTUSD'], ) assert len(events) == 0
def test_query_yearn_vault_v2_history(rotkehlchen_api_server, ethereum_accounts): """Check querying the yearn vaults v2 history endpoint works. Uses real data.""" async_query = random.choice([True, False]) rotki = rotkehlchen_api_server.rest_api.rotkehlchen setup = setup_balances( rotki, ethereum_accounts=ethereum_accounts, btc_accounts=None, token_balances={ '0x5f18C75AbDAe578b483E5F43f12a39cF75b973a9': ['70000000'], '0xB8C3B7A2A618C552C23B1E4701109a9E756Bab67': ['2550000000000000000000'], }, original_queries=['blocknobytime'], ) with ExitStack() as stack: # patch ethereum/etherscan to not autodetect tokens setup.enter_ethereum_patches(stack) response = requests.get(api_url_for( rotkehlchen_api_server, "yearnvaultsv2historyresource", ), json={'async_query': async_query}) if async_query: task_id = assert_ok_async_response(response) outcome = wait_for_async_task(rotkehlchen_api_server, task_id) assert outcome['message'] == '' result = outcome['result'] else: result = assert_proper_response_with_result(response) # Make sure some data was saved in the DB after first call events = rotki.data.db.get_yearn_vaults_v2_events(TEST_V2_ACC2, 0, 12770065) assert len(events) >= 11 result = result[TEST_V2_ACC2] check_vault_history( '_ceth_0x1C6a9783F812b3Af3aBbf7de64c3cD7CC7D1af44', EXPECTED_V2_HISTORY, result, ) # Make sure events end up in the DB # test yearn vault data purging from the db works response = requests.delete( api_url_for( rotkehlchen_api_server, 'ethereummoduledataresource', module_name='yearn_vaults_v2', )) assert_simple_ok_response(response) events = rotki.data.db.get_yearn_vaults_v2_events(TEST_V2_ACC2, 0, 12770065) assert len(events) == 0
def test_edit_exchange_credentials(rotkehlchen_api_server_with_exchanges): server = rotkehlchen_api_server_with_exchanges rotki = rotkehlchen_api_server_with_exchanges.rest_api.rotkehlchen # Test that valid api key/secret is edited properly new_key = 'new_key' new_secret = 'new_secret' for location in SUPPORTED_EXCHANGES: exchange = try_get_first_exchange(rotki.exchange_manager, location) # change both passphrase and name -- kucoin data = { 'name': exchange.name, 'location': str(location), 'new_name': f'my_{exchange.name}', 'api_key': new_key, 'api_secret': new_secret, } with mock_validate_api_key_success(location): response = requests.patch(api_url_for(server, 'exchangesresource'), json=data) assert_simple_ok_response(response) assert exchange.api_key == new_key assert exchange.secret == new_secret.encode() if location == Location.ICONOMI: continue # except for iconomi # all of the api keys end up in session headers. Check they are properly # updated there assert any(new_key in value for _, value in exchange.session.headers.items()) # Test that api key validation failure is handled correctly for location in SUPPORTED_EXCHANGES: exchange = try_get_first_exchange(rotki.exchange_manager, location) # change both passphrase and name -- kucoin data = { 'name': exchange.name, 'location': str(location), 'new_name': f'my_{exchange.name}', 'api_key': 'invalid', 'api_secret': 'invalid', } with mock_validate_api_key_failure(location): response = requests.patch(api_url_for(server, 'exchangesresource'), json=data) assert_error_response( response=response, contained_in_msg='BOOM ERROR', status_code=HTTPStatus.CONFLICT, ) # Test that the api key/secret DID NOT change assert exchange.api_key == new_key assert exchange.secret == new_secret.encode() if location == Location.ICONOMI: continue # except for iconomi # all of the api keys end up in session headers. Check they are properly # updated there assert any(new_key in value for _, value in exchange.session.headers.items())
def test_purge_single_exchange_data(rotkehlchen_api_server_with_exchanges, added_exchanges): rotki = rotkehlchen_api_server_with_exchanges.rest_api.rotkehlchen target_exchange = Location.POLONIEX mock_exchange_data_in_db(added_exchanges, rotki) response = requests.delete( api_url_for( rotkehlchen_api_server_with_exchanges, "named_exchanges_data_resource", location=target_exchange, ), ) assert_simple_ok_response(response) check_saved_events_for_exchange(target_exchange, rotki.data.db, should_exist=False) check_saved_events_for_exchange(Location.BINANCE, rotki.data.db, should_exist=True)
def test_purge_all_exchange_data(rotkehlchen_api_server_with_exchanges, added_exchanges): rotki = rotkehlchen_api_server_with_exchanges.rest_api.rotkehlchen mock_exchange_data_in_db(added_exchanges, rotki) for exchange_location in added_exchanges: check_saved_events_for_exchange(exchange_location, rotki.data.db, should_exist=True) response = requests.delete( api_url_for( rotkehlchen_api_server_with_exchanges, "exchangesdataresource", ), ) assert_simple_ok_response(response) for exchange_location in added_exchanges: check_saved_events_for_exchange(exchange_location, rotki.data.db, should_exist=False)
def test_setup_exchange_does_not_stay_in_mapping_after_500_error( rotkehlchen_api_server): """Test that if 500 error is returned during setup of an exchange and it's stuck in the exchange mapping rotki doesn't still think the exchange is registered. Regression test for the second part of https://github.com/rotki/rotki/issues/943 """ data = { 'location': 'kraken', 'name': 'my_kraken', 'api_key': 'ddddd', 'api_secret': 'fffffff' } with API_KEYPAIR_KRAKEN_VALIDATION_FAIL_PATCH: response = requests.put( api_url_for(rotkehlchen_api_server, 'exchangesresource'), json=data, ) assert_error_response( response=response, status_code=HTTPStatus.CONFLICT, ) # Now try to register the exchange again data = { 'location': 'kraken', 'name': 'my_kraken', 'api_key': 'ddddd', 'api_secret': 'fffffff' } with mock_validate_api_key_success(Location.KRAKEN): response = requests.put( api_url_for(rotkehlchen_api_server, 'exchangesresource'), json=data, ) assert_simple_ok_response(response) # and check that kraken is now registered response = requests.get( api_url_for(rotkehlchen_api_server, 'exchangesresource')) result = assert_proper_response_with_result(response) assert result == [{ 'location': 'kraken', 'name': 'my_kraken', 'kraken_account_type': 'starter' }] # noqa: E501
def test_refresh_icon(rotkehlchen_api_server): """Test that checks refreshing the icon of an asset works.""" # add icon for an asset icon_manager = rotkehlchen_api_server.rest_api.rotkehlchen.icon_manager root_path = Path(__file__).resolve().parent.parent.parent.parent sample_filepath = root_path / 'frontend' / 'app' / 'src' / 'assets' / 'images' / 'exchanges' / 'kraken.svg' # noqa: E501 icon_filepath = icon_manager.icons_dir / 'DOGE_small.png' shutil.copyfile(sample_filepath, icon_filepath) now = ts_now() response = requests.patch( api_url_for( rotkehlchen_api_server, 'asseticonsresource', asset=A_DOGE.identifier, ), ) assert_simple_ok_response(response) assert icon_filepath.stat().st_ctime > now
def test_purge_single_exchange_data(rotkehlchen_api_server_with_exchanges, added_exchanges): rotki = rotkehlchen_api_server_with_exchanges.rest_api.rotkehlchen target_exchange = 'poloniex' mock_exchange_data_in_db(added_exchanges, rotki) response = requests.delete( api_url_for( rotkehlchen_api_server_with_exchanges, "named_exchanges_data_resource", name=target_exchange, ), ) assert_simple_ok_response(response) check_saved_events_for_exchange(target_exchange, rotki.data.db, should_exist=False) check_saved_events_for_exchange('binance', rotki.data.db, should_exist=True)
def test_purge_ethereum_transaction_data(rotkehlchen_api_server): rotki = rotkehlchen_api_server.rest_api.rotkehlchen addr1 = make_ethereum_address() rotki.data.db.add_blockchain_accounts( blockchain=SupportedBlockchain.ETHEREUM, account_data=[ BlockchainAccountData(address=addr1), ], ) db = DBEthTx(rotki.data.db) db.add_ethereum_transactions( [ EthereumTransaction( tx_hash=make_evm_tx_hash(bytes()), timestamp=1, block_number=1, from_address=addr1, to_address=make_ethereum_address(), value=FVal(1), gas=FVal(1), gas_price=FVal(1), gas_used=FVal(1), input_data=bytes(), nonce=1, ) ], relevant_address=addr1, ) filter_ = ETHTransactionsFilterQuery.make() result, filter_count = db.get_ethereum_transactions_and_limit_info( filter_, True) assert len(result) == 1 assert filter_count == 1 response = requests.delete( api_url_for( rotkehlchen_api_server, 'ethereumtransactionsresource', ), ) assert_simple_ok_response(response) result, filter_count = db.get_ethereum_transactions_and_limit_info( filter_, True) assert len(result) == 0 assert filter_count == 0
def test_user_logout(rotkehlchen_api_server, username): """Test that user logout works succesfully and that common errors are handled""" rotki = rotkehlchen_api_server.rest_api.rotkehlchen # Logout of a non-existing/different user data = {'action': 'logout'} response = requests.patch( api_url_for(rotkehlchen_api_server, "usersbynameresource", name='nobody'), json=data, ) assert_error_response( response=response, contained_in_msg='Provided user nobody is not the logged in user', status_code=HTTPStatus.CONFLICT, ) assert rotki.user_is_logged_in is True # Logout of the active user data = {'action': 'logout'} response = requests.patch( api_url_for(rotkehlchen_api_server, "usersbynameresource", name=username), json=data, ) assert_simple_ok_response(response) assert rotki.user_is_logged_in is False # Now try to log out of the same user again response = requests.patch( api_url_for(rotkehlchen_api_server, "usersbynameresource", name=username), json=data, ) assert_error_response( response=response, contained_in_msg='No user is currently logged in', status_code=HTTPStatus.CONFLICT, ) assert rotki.user_is_logged_in is False
def test_importing_custom_assets_list(rotkehlchen_api_server, method, file_type): """Test that the endpoint for importing custom assets works correctly""" dir_path = Path(__file__).resolve().parent.parent if file_type == 'zip': filepath = dir_path / 'data' / 'exported_assets.zip' else: filepath = dir_path / 'data' / 'exported_assets.json' if method == 'put': response = requests.put( api_url_for( rotkehlchen_api_server, 'userassetsresource', ), json={ 'action': 'upload', 'file': str(filepath) }, ) else: response = requests.post( api_url_for( rotkehlchen_api_server, 'userassetsresource', ), json={'action': 'upload'}, files={'file': open(filepath, 'rb')}, ) assert_simple_ok_response(response) rotki = rotkehlchen_api_server.rest_api.rotkehlchen errors = rotki.msg_aggregator.consume_errors() warnings = rotki.msg_aggregator.consume_errors() assert len(errors) == 0 assert len(warnings) == 0 assert_proper_response_with_result(response) stinch = EthereumToken('0xA0446D8804611944F1B527eCD37d7dcbE442caba') assert stinch.symbol == 'st1INCH' assert len(stinch.underlying_tokens) == 1 assert stinch.decimals == 18
def test_create_download_delete_backup(rotkehlchen_api_server, data_dir, username): """Test that creating, downloading and deleting a backup works fine""" start_ts = ts_now() response = requests.put(api_url_for(rotkehlchen_api_server, 'databasebackupsresource')) filepath = Path(assert_proper_response_with_result(response)) assert filepath.exists() assert filepath.parent == Path(data_dir, username) response = requests.get(api_url_for(rotkehlchen_api_server, 'databaseinforesource')) result = assert_proper_response_with_result(response) backups = result['userdb']['backups'] assert len(backups) == 1 assert backups[0]['time'] >= start_ts assert backups[0]['version'] == ROTKEHLCHEN_DB_VERSION # now also try to download that backup and make sure it's the same file response = requests.get( api_url_for(rotkehlchen_api_server, 'databasebackupsresource'), json={'file': str(filepath)}, ) with tempfile.TemporaryDirectory() as tmpdirname: tempdbpath = Path(tmpdirname, 'temp.db') tempdbpath.write_bytes(response.content) assert filecmp.cmp(filepath, tempdbpath) # create an extra database to check that lists work correctly second_filepath = filepath.parent / 'back.db' second_filepath.touch() response = requests.delete( api_url_for( rotkehlchen_api_server, 'databasebackupsresource'), json={'files': [str(filepath), str(second_filepath)]}, ) assert_simple_ok_response(response) assert not filepath.exists() assert not second_filepath.exists() response = requests.get(api_url_for(rotkehlchen_api_server, 'databaseinforesource')) result = assert_proper_response_with_result(response) backups = result['userdb']['backups'] assert len(backups) == 0
def test_delete_snapshot(rotkehlchen_api_server): conn = rotkehlchen_api_server.rest_api.rotkehlchen.data.db.conn ts = Timestamp(ts_now()) _populate_db_with_balances(conn, ts) _populate_db_with_location_data(conn, ts) rotkehlchen_api_server.rest_api.rotkehlchen.data.db.set_settings( ModifiableDBSettings(main_currency=A_EUR)) # noqa: E501 response = requests.delete( api_url_for( rotkehlchen_api_server, 'dbsnapshotdeletingresource', ), json={'timestamp': ts}, ) assert_simple_ok_response(response) cursor = conn.cursor() assert len( cursor.execute('SELECT time FROM timed_balances WHERE time=?', (ts, )).fetchall()) == 0 # noqa: 501 assert len( cursor.execute('SELECT time FROM timed_location_data WHERE time=?', (ts, )).fetchall()) == 0 # noqa: 501 # check that an error is thrown for invalid timestamp response = requests.delete( api_url_for( rotkehlchen_api_server, 'dbsnapshotdeletingresource', ), json={'timestamp': 1000000}, ) assert_error_response( response, contained_in_msg='No snapshot found for the specified timestamp', status_code=HTTPStatus.CONFLICT, )
def assert_csv_export_response(response, csv_dir, main_currency: Asset, is_download=False): if is_download: assert response.status_code == HTTPStatus.OK else: assert_simple_ok_response(response) with open(os.path.join(csv_dir, BALANCES_FILENAME), newline='') as csvfile: reader = csv.DictReader(csvfile) count = 0 for row in reader: assert len(row) == 5 assert row['category'] in ( 'asset', 'liability', ) assert row['amount'] is not None assert row['asset'] is not None assert row['timestamp'] is not None assert row[f'{main_currency.symbol.lower()}_value'] is not None count += 1 assert count == 2 with open(os.path.join(csv_dir, BALANCES_FOR_IMPORT_FILENAME), newline='') as csvfile: reader = csv.DictReader(csvfile) count = 0 for row in reader: assert len(row) == 5 assert row['category'] in ( 'asset', 'liability', ) assert row['amount'] is not None assert row['asset_identifier'] is not None assert row['timestamp'] is not None assert row['usd_value'] is not None count += 1 assert count == 2 with open(os.path.join(csv_dir, LOCATION_DATA_FILENAME), newline='') as csvfile: reader = csv.DictReader(csvfile) count = 0 for row in reader: assert len(row) == 3 assert row['timestamp'] is not None assert Location.deserialize(row['location']) is not None assert row[f'{main_currency.symbol.lower()}_value'] is not None count += 1 assert count == 3 with open(os.path.join(csv_dir, LOCATION_DATA_IMPORT_FILENAME), newline='') as csvfile: reader = csv.DictReader(csvfile) count = 0 for row in reader: assert len(row) == 3 assert row['timestamp'] is not None assert Location.deserialize(row['location']) is not None assert row['usd_value'] is not None count += 1 assert count == 3
def test_setup_exchange(rotkehlchen_api_server): """Test that setting up an exchange via the api works""" # Check that no exchanges are registered response = requests.get( api_url_for(rotkehlchen_api_server, "exchangesresource")) assert_proper_response(response) json_data = response.json() assert json_data['message'] == '' assert json_data['result'] == [] # First test that if api key validation fails we get an error, for every exchange for location in SUPPORTED_EXCHANGES: data = { 'location': str(location), 'name': f'my_{str(location)}', 'api_key': 'ddddd', 'api_secret': 'fffffff' } # noqa: E501 if location in (Location.COINBASEPRO, Location.KUCOIN): data['passphrase'] = '123' response = requests.put( api_url_for(rotkehlchen_api_server, 'exchangesresource'), json=data, ) assert_error_response( response=response, contained_in_msg=[ 'Provided API Key or secret is invalid', 'Provided API Key is invalid', 'Provided API Key is in invalid Format', 'Provided API Secret is invalid', 'Provided Gemini API key needs to have "Auditor" permission activated', BITSTAMP_API_KEY_ERROR_CODE_ACTION['API0011'], BITFINEX_API_KEY_ERROR_MESSAGE, KUCOIN_API_KEY_ERROR_CODE[400003], 'Bad combination of API Keys', 'ApiKey has invalid value', ], status_code=HTTPStatus.CONFLICT, ) # Make sure that no exchange is registered after that rotki = rotkehlchen_api_server.rest_api.rotkehlchen assert len(rotki.exchange_manager.connected_exchanges) == 0 # Mock the api pair validation and make sure that the exchange is setup data = { 'location': 'kraken', 'name': 'my_kraken', 'api_key': 'ddddd', 'api_secret': 'fffffff' } # noqa: E501 with mock_validate_api_key_success(Location.KRAKEN): response = requests.put( api_url_for(rotkehlchen_api_server, 'exchangesresource'), json=data, ) assert_simple_ok_response(response) # and check that kraken is now registered response = requests.get( api_url_for(rotkehlchen_api_server, 'exchangesresource')) result = assert_proper_response_with_result(response) assert result == [{ 'location': 'kraken', 'name': 'my_kraken', 'kraken_account_type': 'starter' }] # noqa: E501 # Check that we get an error if we try to re-setup an already setup exchange data = { 'location': 'kraken', 'name': 'my_kraken', 'api_key': 'ddddd', 'api_secret': 'fffffff' } # noqa: E501 with mock_validate_api_key_success(Location.KRAKEN): response = requests.put( api_url_for(rotkehlchen_api_server, 'exchangesresource'), json=data, ) assert_error_response( response=response, contained_in_msg='kraken exchange my_kraken is already registered', status_code=HTTPStatus.CONFLICT, ) # But check that same location different name works data = { 'location': 'kraken', 'name': 'my_other_kraken', 'api_key': 'aadddddd', 'api_secret': 'fffffff' } # noqa: E501 with mock_validate_api_key_success(Location.KRAKEN): response = requests.put( api_url_for(rotkehlchen_api_server, 'exchangesresource'), json=data, ) assert_simple_ok_response(response) # and check that kraken is now registered response = requests.get( api_url_for(rotkehlchen_api_server, 'exchangesresource')) result = assert_proper_response_with_result(response) assert result == [ { 'location': 'kraken', 'name': 'my_kraken', 'kraken_account_type': 'starter' }, { 'location': 'kraken', 'name': 'my_other_kraken', 'kraken_account_type': 'starter' }, ] # Check that giving a passphrase is fine data = { 'location': 'coinbasepro', 'name': 'my_coinbasepro', 'api_key': 'ddddd', 'api_secret': 'fffff', 'passphrase': 'sdf' } # noqa: E501 with mock_validate_api_key_success(Location.COINBASEPRO): response = requests.put( api_url_for(rotkehlchen_api_server, 'exchangesresource'), json=data, ) assert_simple_ok_response(response) # and check that coinbasepro is now registered response = requests.get( api_url_for(rotkehlchen_api_server, 'exchangesresource')) result = assert_proper_response_with_result(response) assert result == [ { 'location': 'kraken', 'name': 'my_kraken', 'kraken_account_type': 'starter' }, { 'location': 'kraken', 'name': 'my_other_kraken', 'kraken_account_type': 'starter' }, { 'location': 'coinbasepro', 'name': 'my_coinbasepro' }, ]
def test_get_events(rotkehlchen_api_server, ethereum_accounts): # pylint: disable=unused-argument async_query = random.choice([False, True]) rotki = rotkehlchen_api_server.rest_api.rotkehlchen setup = setup_balances( rotki, ethereum_accounts=ethereum_accounts, btc_accounts=None, original_queries=['zerion', 'logs', 'blocknobytime'], ) with ExitStack() as stack: # patch ethereum/etherscan to not autodetect tokens setup.enter_ethereum_patches(stack) response = requests.get( api_url_for(rotkehlchen_api_server, 'adexhistoryresource'), json={ 'async_query': async_query, 'to_timestamp': 1611747322 }, ) if async_query: task_id = assert_ok_async_response(response) outcome = wait_for_async_task(rotkehlchen_api_server, task_id) assert outcome['message'] == '' result = outcome['result'] else: result = assert_proper_response_with_result(response) identity_address = '0x2a6c38D16BFdc7b4a20f1F982c058F07BDCe9204' tom_pool_id = '0x2ce0c96383fb229d9776f33846e983a956a7d95844fac57b180ed0071d93bb28' bond_id = '0x540cab9883923c01e657d5da4ca5674b6e4626b4a148224635495502d674c7c5' channel_id = '0x30d87bab0ef1e7f8b4c3b894ca2beed41bbd54c481f31e5791c1e855c9dbf4ba' result = result[ADEX_TEST_ADDR] expected_events = [ Bond( tx_hash= '0x9989f47c6c0a761f98f910ac24e2438d858be96c12124a13be4bb4b3150c55ea', address=ADEX_TEST_ADDR, identity_address=identity_address, timestamp=1604366004, bond_id=bond_id, pool_id=tom_pool_id, value=Balance(FVal(100000), FVal(200000)), nonce=0, slashed_at=0, ), ChannelWithdraw( tx_hash= '0xa9ee91af823c0173fc5ada908ff9fe3f4d7c84a2c9da795f0889b3f4ace75b13', address=ADEX_TEST_ADDR, identity_address=identity_address, timestamp=1607453764, channel_id=channel_id, pool_id=tom_pool_id, value=Balance(FVal('5056.894263641728544592'), FVal('10113.788527283457089184')), token=A_ADX, log_index=316, ), Unbond( tx_hash= '0xa9ee91af823c0173fc5ada908ff9fe3f4d7c84a2c9da795f0889b3f4ace75b13', address=ADEX_TEST_ADDR, identity_address=identity_address, timestamp=1607453764, bond_id=bond_id, pool_id=tom_pool_id, value=Balance(FVal(100000), FVal(200000)), ) ] assert len(result['events']) == 8 assert result['events'][:len(expected_events)] == [ x.serialize() for x in expected_events ] assert 'staking_details' in result # Make sure events end up in the DB assert len(rotki.data.db.get_adex_events()) != 0 # test adex data purging from the db works response = requests.delete( api_url_for( rotkehlchen_api_server, 'namedethereummoduledataresource', module_name='adex', )) assert_simple_ok_response(response) assert len(rotki.data.db.get_adex_events()) == 0
def test_query_transactions_over_limit( rotkehlchen_api_server, ethereum_accounts, start_with_valid_premium, ): start_ts = 0 end_ts = 1598453214 rotki = rotkehlchen_api_server.rest_api.rotkehlchen db = rotki.data.db all_transactions_num = FREE_ETH_TX_LIMIT + 50 transactions = [ EthereumTransaction( tx_hash=x.to_bytes(2, byteorder='little'), timestamp=x, block_number=x, from_address=ethereum_accounts[0], to_address=make_ethereum_address(), value=x, gas=x, gas_price=x, gas_used=x, input_data=x.to_bytes(2, byteorder='little'), nonce=x, ) for x in range(FREE_ETH_TX_LIMIT - 10) ] transactions.extend([ EthereumTransaction( tx_hash=(x + 500).to_bytes(2, byteorder='little'), timestamp=x, block_number=x, from_address=ethereum_accounts[1], to_address=make_ethereum_address(), value=x, gas=x, gas_price=x, gas_used=x, input_data=x.to_bytes(2, byteorder='little'), nonce=x, ) for x in range(60) ]) dbethtx = DBEthTx(db) dbethtx.add_ethereum_transactions(transactions) # Also make sure to update query ranges so as not to query etherscan at all for address in ethereum_accounts: DBQueryRanges(db).update_used_query_range( location_string=f'ethtxs_{address}', start_ts=start_ts, end_ts=end_ts, ranges_to_query=[], ) free_expected_entries_total = [FREE_ETH_TX_LIMIT - 10, 10] free_expected_entries_found = [FREE_ETH_TX_LIMIT - 10, 60] premium_expected_entries = [FREE_ETH_TX_LIMIT - 10, 60] # Check that we get all transactions correctly even if we query two times for _ in range(2): response = requests.post( api_url_for( rotkehlchen_api_server, 'limitscounterresetresource', location='ethereum_transactions', ), ) assert_simple_ok_response(response) for idx, address in enumerate(ethereum_accounts): response = requests.get( api_url_for( rotkehlchen_api_server, 'ethereumtransactionsresource', ), json={ 'from_timestamp': start_ts, 'to_timestamp': end_ts, 'address': address }, ) result = assert_proper_response_with_result(response) if start_with_valid_premium: assert len(result['entries']) == premium_expected_entries[idx] assert result['entries_total'] == all_transactions_num assert result['entries_found'] == premium_expected_entries[idx] assert result['entries_limit'] == -1 else: assert len( result['entries']) == free_expected_entries_total[idx] assert result['entries_total'] == all_transactions_num assert result['entries_found'] == free_expected_entries_found[ idx] assert result['entries_limit'] == FREE_ETH_TX_LIMIT
def test_setup_exchange(rotkehlchen_api_server): """Test that setting up an exchange via the api works""" # Check that no exchanges are registered response = requests.get( api_url_for(rotkehlchen_api_server, "exchangesresource")) assert_proper_response(response) json_data = response.json() assert json_data['message'] == '' assert json_data['result'] == [] # First test that if api key validation fails we get an error, for every exchange for name in SUPPORTED_EXCHANGES: data = {'name': name, 'api_key': 'ddddd', 'api_secret': 'fffffff'} if name in ('coinbasepro', 'kucoin'): data['passphrase'] = '123' response = requests.put( api_url_for(rotkehlchen_api_server, "exchangesresource"), json=data, ) assert_error_response( response=response, contained_in_msg=[ 'Provided API Key or secret is invalid', 'Provided API Key is invalid', 'Provided API Key is in invalid Format', 'Provided API Secret is invalid', 'Provided Gemini API key needs to have "Auditor" permission activated', BITSTAMP_API_KEY_ERROR_CODE_ACTION['API0011'], BITFINEX_API_KEY_ERROR_MESSAGE, KUCOIN_API_KEY_ERROR_CODE[400003], 'Bad combination of API Keys', ], status_code=HTTPStatus.CONFLICT, ) # Make sure that no exchange is registered after that rotki = rotkehlchen_api_server.rest_api.rotkehlchen assert len(rotki.exchange_manager.connected_exchanges) == 0 # Mock the api pair validation and make sure that the exchange is setup data = {'name': 'kraken', 'api_key': 'ddddd', 'api_secret': 'fffffff'} with API_KEYPAIR_VALIDATION_PATCH: response = requests.put( api_url_for(rotkehlchen_api_server, "exchangesresource"), json=data, ) assert_simple_ok_response(response) # and check that kraken is now registered response = requests.get( api_url_for(rotkehlchen_api_server, "exchangesresource")) assert_proper_response(response) json_data = response.json() assert json_data['message'] == '' assert json_data['result'] == ['kraken'] # Check that we get an error if we try to re-setup an already setup exchange data = {'name': 'kraken', 'api_key': 'ddddd', 'api_secret': 'fffffff'} with API_KEYPAIR_VALIDATION_PATCH: response = requests.put( api_url_for(rotkehlchen_api_server, "exchangesresource"), json=data, ) assert_error_response( response=response, contained_in_msg='Exchange kraken is already registered', status_code=HTTPStatus.CONFLICT, ) # Check that giving a passphrase is fine data = { 'name': 'coinbasepro', 'api_key': 'ddddd', 'api_secret': 'fffff', 'passphrase': 'sdf' } with API_KEYPAIR_COINBASEPRO_VALIDATION_PATCH: response = requests.put( api_url_for(rotkehlchen_api_server, "exchangesresource"), json=data, ) assert_simple_ok_response(response) # and check that coinbasepro is now registered response = requests.get( api_url_for(rotkehlchen_api_server, "exchangesresource")) assert_proper_response(response) json_data = response.json() assert json_data['message'] == '' assert json_data['result'] == ['kraken', 'coinbasepro']
def test_setup_exchange(rotkehlchen_api_server): """Test that setting up an exchange via the api works""" # Check that no exchanges are registered response = requests.get( api_url_for(rotkehlchen_api_server, "exchangesresource")) assert_proper_response(response) json_data = response.json() assert json_data['message'] == '' assert json_data['result'] == [] # First test that if api key validation fails we get an error data = {'name': 'kraken', 'api_key': 'ddddd', 'api_secret': 'fffffff'} response = requests.put(api_url_for(rotkehlchen_api_server, "exchangesresource"), json=data) assert_error_response( response=response, contained_in_msg='Provided API Key or secret is in invalid Format', status_code=HTTPStatus.CONFLICT, ) # Mock the api pair validation and make sure that the exchange is setup data = {'name': 'kraken', 'api_key': 'ddddd', 'api_secret': 'fffffff'} with API_KEYPAIR_VALIDATION_PATCH: response = requests.put( api_url_for(rotkehlchen_api_server, "exchangesresource"), json=data, ) assert_simple_ok_response(response) # and check that kraken is now registered response = requests.get( api_url_for(rotkehlchen_api_server, "exchangesresource")) assert_proper_response(response) json_data = response.json() assert json_data['message'] == '' assert json_data['result'] == ['kraken'] # Check that we get an error if we try to re-setup an already setup exchange data = {'name': 'kraken', 'api_key': 'ddddd', 'api_secret': 'fffffff'} with API_KEYPAIR_VALIDATION_PATCH: response = requests.put( api_url_for(rotkehlchen_api_server, "exchangesresource"), json=data, ) assert_error_response( response=response, contained_in_msg='Exchange kraken is already registered', status_code=HTTPStatus.CONFLICT, ) # Check that giving a passphrase is fine data = { 'name': 'coinbasepro', 'api_key': 'ddddd', 'api_secret': 'fffff', 'passphrase': 'sdf' } with API_KEYPAIR_COINBASEPRO_VALIDATION_PATCH: response = requests.put( api_url_for(rotkehlchen_api_server, "exchangesresource"), json=data, ) assert_simple_ok_response(response) # and check that coinbasepro is now registered response = requests.get( api_url_for(rotkehlchen_api_server, "exchangesresource")) assert_proper_response(response) json_data = response.json() assert json_data['message'] == '' assert json_data['result'] == ['kraken', 'coinbasepro']
def test_import_snapshot(rotkehlchen_api_server, tmpdir_factory): conn = rotkehlchen_api_server.rest_api.rotkehlchen.data.db.conn ts = Timestamp(ts_now()) _populate_db_with_balances(conn, ts) _populate_db_with_location_data(conn, ts) rotkehlchen_api_server.rest_api.rotkehlchen.data.db.set_settings( ModifiableDBSettings(main_currency=A_EUR)) # noqa: E501 # check that importing a valid snapshot passes using PUT csv_dir = str(tmpdir_factory.mktemp('test_csv_dir')) _create_snapshot_with_valid_data(csv_dir, Timestamp(1651071105)) response = requests.put( api_url_for( rotkehlchen_api_server, 'dbsnapshotimportingresource', ), json={ 'balances_snapshot_file': f'{csv_dir}/{BALANCES_FOR_IMPORT_FILENAME}', 'location_data_snapshot_file': f'{csv_dir}/{LOCATION_DATA_IMPORT_FILENAME}', }, ) assert_simple_ok_response(response) # check that POST with the file works. csv_dir2 = str(tmpdir_factory.mktemp('test_csv_dir_2')) _create_snapshot_with_valid_data_for_post(csv_dir2, Timestamp(1651075)) response = requests.post( api_url_for( rotkehlchen_api_server, 'dbsnapshotimportingresource', ), files={ 'balances_snapshot_file': open(f'{csv_dir2}/{BALANCES_FOR_IMPORT_FILENAME}'), 'location_data_snapshot_file': open(f'{csv_dir2}/{LOCATION_DATA_IMPORT_FILENAME}'), }, ) assert_simple_ok_response(response) # check that importing a snapshot that is present in the db fails. csv_dir3 = str(tmpdir_factory.mktemp('test_csv_dir3')) response = requests.post( api_url_for( rotkehlchen_api_server, 'dbsnapshotexportingresource', ), json={ 'timestamp': ts, 'path': csv_dir3, }, ) assert_csv_export_response(response, csv_dir3, main_currency=A_EUR, is_download=False) response = requests.put( api_url_for( rotkehlchen_api_server, 'dbsnapshotimportingresource', ), json={ 'balances_snapshot_file': f'{csv_dir3}/{BALANCES_FOR_IMPORT_FILENAME}', 'location_data_snapshot_file': f'{csv_dir3}/{LOCATION_DATA_IMPORT_FILENAME}', }, ) assert_error_response( response, contained_in_msg='Adding timed_balance failed', status_code=HTTPStatus.CONFLICT, ) # check that importing snapshot with different timestamps fails. csv_dir4 = str(tmpdir_factory.mktemp('test_csv_dir4')) _create_snapshot_different_timestamps(csv_dir4, ts) response = requests.put( api_url_for( rotkehlchen_api_server, 'dbsnapshotimportingresource', ), json={ 'balances_snapshot_file': f'{csv_dir4}/{BALANCES_FOR_IMPORT_FILENAME}', 'location_data_snapshot_file': f'{csv_dir4}/{LOCATION_DATA_IMPORT_FILENAME}', }, ) assert_error_response( response, contained_in_msg='csv file has different timestamps', status_code=HTTPStatus.CONFLICT, ) # check that importing snapshot with invalid header fails. csv_dir5 = str(tmpdir_factory.mktemp('test_csv_dir5')) _create_snapshot_with_invalid_headers(csv_dir5, ts) response = requests.put( api_url_for( rotkehlchen_api_server, 'dbsnapshotimportingresource', ), json={ 'balances_snapshot_file': f'{csv_dir5}/{BALANCES_FOR_IMPORT_FILENAME}', 'location_data_snapshot_file': f'{csv_dir5}/{LOCATION_DATA_IMPORT_FILENAME}', }, ) assert_error_response( response, contained_in_msg='csv file has invalid headers', status_code=HTTPStatus.CONFLICT, ) # check that importing snapshot with unknown asset_identifier fails. csv_dir6 = str(tmpdir_factory.mktemp('test_csv_dir6')) _create_snapshot_with_unknown_asset(csv_dir6, ts) response = requests.put( api_url_for( rotkehlchen_api_server, 'dbsnapshotimportingresource', ), json={ 'balances_snapshot_file': f'{csv_dir6}/{BALANCES_FOR_IMPORT_FILENAME}', 'location_data_snapshot_file': f'{csv_dir6}/{LOCATION_DATA_IMPORT_FILENAME}', }, ) assert_error_response( response, contained_in_msg='snapshot contains an unknown asset', status_code=HTTPStatus.CONFLICT, )
def test_users_by_name_endpoint_errors(rotkehlchen_api_server, username, db_password): """Test that user by name endpoint errors are handled (for login/logout and edit)""" rotki = rotkehlchen_api_server.rest_api.rotkehlchen # Now let's try to login while the user is already logged in data = { 'action': 'login', 'password': db_password, 'sync_approval': 'unknown' } response = requests.patch( api_url_for(rotkehlchen_api_server, "usersbynameresource", name=username), json=data, ) expected_msg = ( f'Can not login to user {username} because user {username} is ' f'already logged in. Log out of that user first') assert_error_response( response=response, contained_in_msg=expected_msg, status_code=HTTPStatus.CONFLICT, ) assert rotki.user_is_logged_in is True # Logout of the active user data = {'action': 'logout'} response = requests.patch( api_url_for(rotkehlchen_api_server, "usersbynameresource", name=username), json=data, ) assert_simple_ok_response(response) assert rotki.user_is_logged_in is False # Now let's try to login with an invalid password data = { 'action': 'login', 'password': '******', 'sync_approval': 'unknown' } response = requests.patch( api_url_for(rotkehlchen_api_server, "usersbynameresource", name=username), json=data, ) assert_error_response( response=response, contained_in_msg='Wrong password or invalid/corrupt database for user', status_code=HTTPStatus.UNAUTHORIZED, ) assert rotki.user_is_logged_in is False # Login action without a password data = {'action': 'login', 'sync_approval': 'unknown'} response = requests.patch( api_url_for(rotkehlchen_api_server, "usersbynameresource", name=username), json=data, ) assert_error_response( response=response, contained_in_msg='Missing password field for login', status_code=HTTPStatus.BAD_REQUEST, ) assert rotki.user_is_logged_in is False # No action and no premium credentials data = {'sync_approval': 'unknown'} response = requests.patch( api_url_for(rotkehlchen_api_server, "usersbynameresource", name=username), json=data, ) assert_error_response( response=response, contained_in_msg= 'Without an action premium api key and secret must be provided', status_code=HTTPStatus.BAD_REQUEST, ) assert rotki.user_is_logged_in is False # No action and only premium key data = {'sync_approval': 'unknown', 'premium_api_key': VALID_PREMIUM_KEY} response = requests.patch( api_url_for(rotkehlchen_api_server, "usersbynameresource", name=username), json=data, ) assert_error_response( response=response, contained_in_msg= 'Without an action premium api key and secret must be provided', status_code=HTTPStatus.BAD_REQUEST, ) assert rotki.user_is_logged_in is False # No action and only premium secret data = { 'sync_approval': 'unknown', 'premium_api_secret': VALID_PREMIUM_SECRET } response = requests.patch( api_url_for(rotkehlchen_api_server, "usersbynameresource", name=username), json=data, ) assert_error_response( response=response, contained_in_msg= 'Without an action premium api key and secret must be provided', status_code=HTTPStatus.BAD_REQUEST, ) assert rotki.user_is_logged_in is False # Invalid action type data = { 'action': 555.3, 'password': db_password, 'sync_approval': 'unknown' } response = requests.patch( api_url_for(rotkehlchen_api_server, "usersbynameresource", name=username), json=data, ) assert_error_response( response=response, contained_in_msg='Not a valid string', status_code=HTTPStatus.BAD_REQUEST, ) assert rotki.user_is_logged_in is False # Invalid action string data = { 'action': 'chopwood', 'password': db_password, 'sync_approval': 'unknown' } response = requests.patch( api_url_for(rotkehlchen_api_server, "usersbynameresource", name=username), json=data, ) assert_error_response( response=response, contained_in_msg='Must be one of: login, logout', status_code=HTTPStatus.BAD_REQUEST, ) assert rotki.user_is_logged_in is False # Invalid password type data = {'action': 'login', 'password': True, 'sync_approval': 'unknown'} response = requests.patch( api_url_for(rotkehlchen_api_server, "usersbynameresource", name=username), json=data, ) assert_error_response( response=response, contained_in_msg='Not a valid string', status_code=HTTPStatus.BAD_REQUEST, ) assert rotki.user_is_logged_in is False
def test_user_login(rotkehlchen_api_server, username, db_password, data_dir): """Test that user login works properly""" rotki = rotkehlchen_api_server.rest_api.rotkehlchen # Let's pretend there is another user, and try to create them again Path(data_dir / 'another_user').mkdir() Path(data_dir / 'another_user' / 'rotkehlchen.db').touch() # Check users status users_data = check_user_status(rotkehlchen_api_server) assert len(users_data) == 2 assert users_data[username] == 'loggedin' assert users_data['another_user'] == 'loggedout' # Logout of the active user data = {'action': 'logout'} response = requests.patch( api_url_for(rotkehlchen_api_server, "usersbynameresource", name=username), json=data, ) assert_simple_ok_response(response) assert rotki.user_is_logged_in is False users_data = check_user_status(rotkehlchen_api_server) assert len(users_data) == 2 assert users_data[username] == 'loggedout' assert users_data['another_user'] == 'loggedout' # Now let's try to login data = { 'action': 'login', "password": db_password, 'sync_approval': 'unknown' } response = requests.patch( api_url_for(rotkehlchen_api_server, "usersbynameresource", name=username), json=data, ) # And make sure it works assert_proper_response(response) check_proper_unlock_result(response.json()) assert rotki.user_is_logged_in is True users_data = check_user_status(rotkehlchen_api_server) assert len(users_data) == 2 assert users_data[username] == 'loggedin' assert users_data['another_user'] == 'loggedout' # Logout again data = {'action': 'logout'} response = requests.patch( api_url_for(rotkehlchen_api_server, "usersbynameresource", name=username), json=data, ) assert_simple_ok_response(response) assert rotki.user_is_logged_in is False users_data = check_user_status(rotkehlchen_api_server) assert len(users_data) == 2 assert users_data[username] == 'loggedout' assert users_data['another_user'] == 'loggedout' # Now try to login with a wrong password data = { 'action': 'login', "password": '******', 'sync_approval': 'unknown' } response = requests.patch( api_url_for(rotkehlchen_api_server, "usersbynameresource", name=username), json=data, ) # And make sure it fails assert_error_response( response=response, contained_in_msg='Wrong password or invalid/corrupt database for user', status_code=HTTPStatus.UNAUTHORIZED, ) users_data = check_user_status(rotkehlchen_api_server) assert len(users_data) == 2 assert users_data[username] == 'loggedout' assert users_data['another_user'] == 'loggedout' # Now let's manually add valid but not authenticable premium credentials in the DB data = { 'action': 'login', "password": db_password, 'sync_approval': 'unknown' } response = requests.patch( api_url_for(rotkehlchen_api_server, "usersbynameresource", name=username), json=data, ) credentials = PremiumCredentials(VALID_PREMIUM_KEY, VALID_PREMIUM_SECRET) rotki.data.db.set_rotkehlchen_premium(credentials) data = {'action': 'logout'} response = requests.patch( api_url_for(rotkehlchen_api_server, "usersbynameresource", name=username), json=data, ) assert_simple_ok_response(response) assert rotki.user_is_logged_in is False # And try to login while having these unauthenticable premium credentials in the DB data = { 'action': 'login', "password": db_password, 'sync_approval': 'unknown' } response = requests.patch( api_url_for(rotkehlchen_api_server, "usersbynameresource", name=username), json=data, ) # And make sure it works despite having unauthenticable premium credentials in the DB assert_proper_response(response) check_proper_unlock_result(response.json()) assert rotki.user_is_logged_in is True users_data = check_user_status(rotkehlchen_api_server) assert len(users_data) == 2 assert users_data[username] == 'loggedin' assert users_data['another_user'] == 'loggedout'
def test_user_password_change(rotkehlchen_api_server, username, db_password): """ Test that changing a logged-in user's users password works successfully and that common errors are handled. """ # wrong username data_wrong_user = { 'name': 'billybob', 'current_password': '******', 'new_password': '******', } response = requests.patch(api_url_for(rotkehlchen_api_server, "userpasswordchangeresource", name=username), json=data_wrong_user) msg = ( f'Provided user "{data_wrong_user["name"]}" is not the logged in user') assert_error_response( response=response, contained_in_msg=msg, status_code=HTTPStatus.BAD_REQUEST, ) # wrong password data_wrong_pass = { 'name': username, 'current_password': '******', 'new_password': '******', } response = requests.patch(api_url_for(rotkehlchen_api_server, "userpasswordchangeresource", name=username), json=data_wrong_pass) msg = ('Provided current password is not correct') assert_error_response( response=response, contained_in_msg=msg, status_code=HTTPStatus.UNAUTHORIZED, ) # success data_success = { 'name': username, 'current_password': db_password, 'new_password': '******', } response = requests.patch(api_url_for(rotkehlchen_api_server, "userpasswordchangeresource", name=username), json=data_success) assert_simple_ok_response(response) # revert password data_revert = { 'name': username, 'current_password': data_success['new_password'], 'new_password': db_password, } response = requests.patch(api_url_for(rotkehlchen_api_server, "userpasswordchangeresource", name=username), json=data_revert) assert_simple_ok_response(response)