Repository: rotki/rotki

Issue:
Add tests for all enum types which also have DB equivalents
## Abstract

All enum types like `Location` that also have DB equivalents need to have

1. unit tests that serialize and deserialize it for from/to DB
2. unit tests that read and write them from/to the DB

Reason is that if we forget one we may not figure it out until it ends up needing to be used and then it breaks in ugly way.

## Task

Do it


Discussion:

Resolving patch:
diff --git a/rotkehlchen/accounting/structures.py b/rotkehlchen/accounting/structures.py
--- a/rotkehlchen/accounting/structures.py
+++ b/rotkehlchen/accounting/structures.py
@@ -15,8 +15,8 @@
 
 
 class BalanceType(Enum):
-    ASSET = 0
-    LIABILITY = 1
+    ASSET = 1
+    LIABILITY = 2
 
     def __str__(self) -> str:
         if self == BalanceType.ASSET:
@@ -287,14 +287,14 @@ def _evaluate_balance_sheet_input(other: Any, operation: str) -> BalanceSheet:
 
 
 class LedgerActionType(Enum):
-    INCOME = 0
-    EXPENSE = 1
-    LOSS = 2
-    DIVIDENDS_INCOME = 3
-    DONATION_RECEIVED = 4
-    AIRDROP = 5
-    GIFT = 6
-    GRANT = 7
+    INCOME = 1
+    EXPENSE = 2
+    LOSS = 3
+    DIVIDENDS_INCOME = 4
+    DONATION_RECEIVED = 5
+    AIRDROP = 6
+    GIFT = 7
+    GRANT = 8
 
     def serialize(self) -> str:
         return str(self)
@@ -381,10 +381,10 @@ def is_profitable(self) -> bool:
 
 
 class ActionType(Enum):
-    TRADE = 0
-    ASSET_MOVEMENT = 1
-    ETHEREUM_TX = 2
-    LEDGER_ACTION = 3
+    TRADE = 1
+    ASSET_MOVEMENT = 2
+    ETHEREUM_TX = 3
+    LEDGER_ACTION = 4
 
     def __str__(self) -> str:
         if self == ActionType.TRADE:


Test patch:
diff --git a/rotkehlchen/tests/db/test_db.py b/rotkehlchen/tests/db/test_db.py
--- a/rotkehlchen/tests/db/test_db.py
+++ b/rotkehlchen/tests/db/test_db.py
@@ -7,7 +7,11 @@
 
 import pytest
 
-from rotkehlchen.accounting.structures import BalanceType
+from rotkehlchen.accounting.structures import (
+    BalanceType,
+    ActionType,
+    LedgerActionType,
+)
 from rotkehlchen.assets.asset import Asset
 from rotkehlchen.balances.manual import ManuallyTrackedBalance
 from rotkehlchen.constants import YEAR_IN_SECONDS
@@ -41,6 +45,18 @@
 from rotkehlchen.exchanges.data_structures import AssetMovement, MarginPosition, Trade
 from rotkehlchen.fval import FVal
 from rotkehlchen.premium.premium import PremiumCredentials
+from rotkehlchen.serialization.deserialize import (
+    deserialize_location_from_db,
+    deserialize_location,
+    deserialize_trade_type_from_db,
+    deserialize_trade_type,
+    deserialize_action_type_from_db,
+    deserialize_action_type,
+    deserialize_ledger_action_type_from_db,
+    deserialize_ledger_action_type,
+    deserialize_asset_movement_category_from_db,
+    deserialize_asset_movement_category,
+)
 from rotkehlchen.tests.utils.constants import (
     A_DAO,
     A_DOGE,
@@ -1274,3 +1290,71 @@ def test_int_overflow_at_tuple_insertion(database, caplog):
     assert len(errors) == 1
     assert 'Failed to add "asset_movement" to the DB with overflow error' in errors[0]
     assert 'Overflow error while trying to add "asset_movement" tuples to the DB. Tuples:' in caplog.text  # noqa: E501
+
+
+@pytest.mark.parametrize("enum_class, query, deserialize_from_db, deserialize", [
+    (Location, 'SELECT location, seq from location',
+        deserialize_location_from_db, deserialize_location),
+    (TradeType, 'SELECT type, seq from trade_type',
+        deserialize_trade_type_from_db, deserialize_trade_type),
+    (ActionType, 'SELECT type, seq from action_type',
+        deserialize_action_type_from_db, deserialize_action_type),
+    (LedgerActionType, 'SELECT type, seq from ledger_action_type',
+        deserialize_ledger_action_type_from_db, deserialize_ledger_action_type),
+    (AssetMovementCategory, 'SELECT category, seq from asset_movement_category',
+        deserialize_asset_movement_category_from_db, deserialize_asset_movement_category),
+])
+def test_enum_in_db(database, enum_class, query, deserialize_from_db, deserialize):
+    """
+    Test that all enum represented in DB deserialize to a valid matching Enum class
+    """
+    # Query for all objects in the db table
+    cursor = database.conn.cursor()
+    query_result = cursor.execute(query)
+
+    # We deserialize, then serialize and compare the result
+    for letter, seq in query_result:
+        deserialized = deserialize_from_db(letter)
+        assert deserialized.value == seq
+        assert enum_class(seq).serialize_for_db() == letter
+        name = deserialize(str(deserialized))
+        assert name == deserialized
+
+
+def test_all_balance_types_in_db(database):
+    """
+    Test that all balance_category in DB deserialize to a valid BalanceType
+    """
+    # Query for all balace_category rows
+    cursor = database.conn.cursor()
+    balance_types = cursor.execute('SELECT category, seq from balance_category')
+
+    # We deserialize, then serialize and compare the result
+    for category, seq in balance_types:
+        deserialized_balance_type = BalanceType.deserialize_from_db(category)
+        assert deserialized_balance_type.value == seq
+        balance_type_serialization = BalanceType(
+            deserialized_balance_type.value,
+        ).serialize_for_db()
+        assert category == balance_type_serialization
+
+
+@pytest.mark.parametrize('enum_class, table_name', [
+    (Location, 'location'),
+    (TradeType, 'trade_type'),
+    (ActionType, 'action_type'),
+    (LedgerActionType, 'ledger_action_type'),
+    (BalanceType, 'balance_category'),
+    (AssetMovementCategory, 'asset_movement_category'),
+])
+def test_values_are_present_in_db(database, enum_class, table_name):
+    """
+    Check that all enum classes have the same number of possible values
+    in the class definition as in the database
+    """
+    cursor = database.conn.cursor()
+    query = 'SELECT COUNT(*) FROM {} WHERE seq=?'.format(table_name)
+
+    for enum_class_entry in enum_class:
+        r = cursor.execute(query, (enum_class_entry.value,))
+        assert r.fetchone() == (1,)