Expand serializer to more types and test
This commit is contained in:
parent
6057ec59b4
commit
60c7028f0b
@ -30,7 +30,7 @@ class ExportCommand(Command):
|
||||
match self.format:
|
||||
case ExportFormat.JSON:
|
||||
with open(self.fn, "w", newline="") as f:
|
||||
json.dump([serialize(e) for e in values], f, indent=4, default=str)
|
||||
json.dump([serialize(e) for e in values], f, indent=4)
|
||||
case ExportFormat.pickle:
|
||||
with open(self.fn, "wb") as f:
|
||||
pickle.dump([serialize(e) for e in values], f)
|
||||
pickle.dump(values, f)
|
||||
|
||||
@ -3,19 +3,52 @@ from dataclasses import fields
|
||||
from functools import singledispatch
|
||||
from typing import Any
|
||||
|
||||
from pfbudget.db.model import Transaction, TransactionCategory
|
||||
from pfbudget.db.model import Transaction, TransactionCategory, TransactionTag, Note
|
||||
|
||||
|
||||
class NotSerializableError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
@singledispatch
|
||||
def serialize(obj: Any) -> Mapping[str, Any]:
|
||||
raise NotImplementedError
|
||||
return {field.name: getattr(obj, field.name) for field in fields(obj)}
|
||||
|
||||
|
||||
@serialize.register
|
||||
def _(obj: Transaction) -> Mapping[str, Any]:
|
||||
return dict((field.name, getattr(obj, field.name)) for field in fields(obj))
|
||||
category = None
|
||||
if obj.category:
|
||||
category = {
|
||||
"name": obj.category.name,
|
||||
"selector": str(obj.category.selector.selector)
|
||||
if obj.category.selector
|
||||
else None,
|
||||
}
|
||||
|
||||
return dict(
|
||||
id=obj.id,
|
||||
date=obj.date.isoformat(),
|
||||
description=obj.description,
|
||||
amount=str(obj.amount),
|
||||
split=obj.split,
|
||||
category=category if category else None,
|
||||
tags=[{"tag": tag.tag} for tag in obj.tags],
|
||||
note=obj.note,
|
||||
type=obj.type,
|
||||
)
|
||||
|
||||
|
||||
@serialize.register
|
||||
def _(obj: TransactionCategory) -> Mapping[str, Any]:
|
||||
return dict((field.name, getattr(obj, field.name)) for field in fields(obj))
|
||||
def _(_: TransactionCategory) -> Mapping[str, Any]:
|
||||
raise NotSerializableError("TransactionCategory")
|
||||
|
||||
|
||||
@serialize.register
|
||||
def _(_: TransactionTag) -> Mapping[str, Any]:
|
||||
raise NotSerializableError("TransactionTag")
|
||||
|
||||
|
||||
@serialize.register
|
||||
def _(_: Note) -> Mapping[str, Any]:
|
||||
raise NotSerializableError("Note")
|
||||
|
||||
4
tests/mocks/banks.py
Normal file
4
tests/mocks/banks.py
Normal file
@ -0,0 +1,4 @@
|
||||
from pfbudget.db.model import AccountType, Bank
|
||||
|
||||
|
||||
checking = Bank("bank", "BANK", AccountType.checking)
|
||||
@ -1,3 +1,6 @@
|
||||
from pfbudget.extract.nordigen import NordigenCredentials
|
||||
|
||||
|
||||
id = "3fa85f64-5717-4562-b3fc-2c963f66afa6"
|
||||
|
||||
accounts_id = {
|
||||
@ -80,3 +83,5 @@ requisitions_id = {
|
||||
"account_selection": False,
|
||||
"redirect_immediate": False,
|
||||
}
|
||||
|
||||
credentials = NordigenCredentials("ID", "KEY", "TOKEN")
|
||||
|
||||
33
tests/mocks/transactions.py
Normal file
33
tests/mocks/transactions.py
Normal file
@ -0,0 +1,33 @@
|
||||
from datetime import date
|
||||
from decimal import Decimal
|
||||
|
||||
from pfbudget.db.model import (
|
||||
CategorySelector,
|
||||
Selector_T,
|
||||
Transaction,
|
||||
TransactionCategory,
|
||||
)
|
||||
|
||||
simple = [
|
||||
Transaction(date(2023, 1, 1), "", Decimal("-10")),
|
||||
Transaction(date(2023, 1, 2), "", Decimal("-50")),
|
||||
]
|
||||
|
||||
simple_transformed = [
|
||||
Transaction(
|
||||
date(2023, 1, 1),
|
||||
"",
|
||||
Decimal("-10"),
|
||||
category=TransactionCategory(
|
||||
"category#1", CategorySelector(Selector_T.algorithm)
|
||||
),
|
||||
),
|
||||
Transaction(
|
||||
date(2023, 1, 2),
|
||||
"",
|
||||
Decimal("-50"),
|
||||
category=TransactionCategory(
|
||||
"category#2", CategorySelector(Selector_T.algorithm)
|
||||
),
|
||||
),
|
||||
]
|
||||
78
tests/test_command.py
Normal file
78
tests/test_command.py
Normal file
@ -0,0 +1,78 @@
|
||||
from collections.abc import Sequence
|
||||
import json
|
||||
from pathlib import Path
|
||||
import pickle
|
||||
from typing import Any
|
||||
|
||||
import mocks.transactions
|
||||
|
||||
from pfbudget.common.types import ExportFormat
|
||||
from pfbudget.core.command import ExportCommand
|
||||
from pfbudget.db.client import Client
|
||||
from pfbudget.db.model import Transaction
|
||||
from pfbudget.utils.serializer import serialize
|
||||
|
||||
|
||||
class FakeClient(Client):
|
||||
def __init__(self):
|
||||
self._transactions = mocks.transactions.simple
|
||||
|
||||
def select(self, what: Any, *_) -> Sequence[Any]:
|
||||
if what == Transaction:
|
||||
return self.transactions
|
||||
return []
|
||||
|
||||
def insert(self, *_):
|
||||
pass
|
||||
|
||||
def update(self, *_):
|
||||
pass
|
||||
|
||||
def delete(self, *_):
|
||||
pass
|
||||
|
||||
@property
|
||||
def transactions(self):
|
||||
return self._transactions
|
||||
|
||||
@transactions.setter
|
||||
def transactions(self, value: Sequence[Transaction]):
|
||||
self._transactions = value
|
||||
|
||||
|
||||
class TestCommand:
|
||||
def test_export_json(self, tmp_path: Path):
|
||||
client = FakeClient()
|
||||
file = tmp_path / "test.json"
|
||||
command = ExportCommand(client, Transaction, file, ExportFormat.JSON)
|
||||
command.execute()
|
||||
|
||||
with open(file, newline="") as f:
|
||||
result = json.load(f)
|
||||
assert result == [serialize(t) for t in mocks.transactions.simple]
|
||||
|
||||
client.transactions = mocks.transactions.simple_transformed
|
||||
command.execute()
|
||||
|
||||
with open(file, newline="") as f:
|
||||
result = json.load(f)
|
||||
assert result == [
|
||||
serialize(t) for t in mocks.transactions.simple_transformed
|
||||
]
|
||||
|
||||
def test_export_pickle(self, tmp_path: Path):
|
||||
client = FakeClient()
|
||||
file = tmp_path / "test.pickle"
|
||||
command = ExportCommand(client, Transaction, file, ExportFormat.pickle)
|
||||
command.execute()
|
||||
|
||||
with open(file, "rb") as f:
|
||||
result = pickle.load(f)
|
||||
assert result == mocks.transactions.simple
|
||||
|
||||
client.transactions = mocks.transactions.simple_transformed
|
||||
command.execute()
|
||||
|
||||
with open(file, "rb") as f:
|
||||
result = pickle.load(f)
|
||||
assert result == mocks.transactions.simple_transformed
|
||||
@ -88,7 +88,12 @@ class TestExtractPSD2:
|
||||
with pytest.raises(requests.Timeout):
|
||||
extractor.extract(bank)
|
||||
|
||||
def test_extract(self, extractor: Extractor, bank: Bank):
|
||||
def test_extract(
|
||||
self, monkeypatch: pytest.MonkeyPatch, extractor: Extractor, bank: Bank
|
||||
):
|
||||
monkeypatch.setattr(
|
||||
"pfbudget.extract.nordigen.NordigenClient.dump", lambda *args: None
|
||||
)
|
||||
assert extractor.extract(bank) == [
|
||||
BankTransaction(
|
||||
dt.date(2023, 1, 14), "string", Decimal("328.18"), bank="Bank#1"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user