Expand serializer to more types and test

This commit is contained in:
Luís Murta 2024-01-22 21:27:15 +00:00
parent 6057ec59b4
commit 60c7028f0b
Signed by: satprog
GPG Key ID: 169EF1BBD7049F94
7 changed files with 166 additions and 8 deletions

View File

@ -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)

View File

@ -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
View File

@ -0,0 +1,4 @@
from pfbudget.db.model import AccountType, Bank
checking = Bank("bank", "BANK", AccountType.checking)

View File

@ -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")

View 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
View 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

View File

@ -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"