cteward-ng/cteward_ng/tests/test_mappings.py
2026-06-08 20:33:47 +02:00

230 lines
8 KiB
Python

"""Tests for data mapping functions (mappings.py)."""
from unittest.mock import Mock, patch
import pytest
from cteward_ng.mappings import (
none_mapper,
contract_mapper,
contractlist_mapper,
debit_mapper,
debitlist_mapper,
contributions_mapper,
member_mapper,
memo_mapper,
memberlist_mapper,
memberlist_to_ldapcsv_mapper,
withdrawal_mapper,
withdrawallist_mapper,
)
# ── helpers ───────────────────────────────────────────────────────
def _ctx(data=None, config_base='', path='/test/', flags=None):
req = Mock()
req.path = path
return {
'data': data or [],
'config': {'base': config_base},
'request': req,
'flags': flags or [],
}
# ── none_mapper ─────────────────────────────────────────────────
class TestNoneMapper:
def test_identity(self):
ctx = _ctx(data=[{'x': 1}])
result = none_mapper(ctx)
assert result['data'] == [{'x': 1}]
# ── contract mappers ─────────────────────────────────────────────
class TestContractMapper:
def test_single_contract(self):
ctx = _ctx(data=[{
'VertragNr': '42',
'ArtName': 'Ehrenmitglied',
'Sollstellung': 'monatlich',
'Betrag': 15.0,
'VertragBegin': '2020-01-01T00:00:00Z',
'VerwZw1': 'Mahnung',
'VerwZw2': '',
}], config_base='https://api.example.com/')
contract_mapper(ctx)
assert ctx['data']['Vertragsnummer'] == 42
assert ctx['data']['Vertragsart'] == 'Ehrenmitglied'
assert 'Mahnung' in ctx['data']['Verwendungszweck']
def test_empty_data(self):
ctx = _ctx(data=[])
contract_mapper(ctx)
assert ctx['data'] == {}
class TestContractlistMapper:
def test_list(self):
ctx = _ctx(data=[{
'VertragNr': '1', 'ArtName': 'A', 'Sollstellung': 'x',
'Betrag': 10, 'VertragBegin': '2020-01-01T00:00:00Z',
}], config_base='https://api.example.com/')
contractlist_mapper(ctx)
assert ctx['data']['count'] == 1
assert ctx['data']['next'] is None
# ── debit mappers ───────────────────────────────────────────────
class TestDebitMapper:
def test_single_debit(self):
ctx = _ctx(data=[{
'VertragNr': '1', 'ArtName': 'Soll', 'Jahr': 2023,
'Monat': 6, 'Datum': '2023-06-01T00:00:00Z',
'Betrag': 25, 'Bezahlt': 25, 'Offen': 0, 'GUID': 'abc',
}], config_base='https://api.example.com/', path='/debit/')
debit_mapper(ctx)
assert ctx['data']['Vertragsnummer'] == 1
assert ctx['data']['Betrag'] == 25
def test_zeroed_fields(self):
ctx = _ctx(data=[{
'VertragNr': '1', 'ArtName': 'Soll', 'Jahr': 2023,
'Monat': 6, 'Datum': '2023-06-01T00:00:00Z',
'Betrag': None, 'Bezahlt': None, 'Offen': None,
}])
debit_mapper(ctx)
assert ctx['data']['Betrag'] == 0
# ── contributions mapper ───────────────────────────────────────
class TestContributionsMapper:
def test_aggregation(self):
ctx = _ctx(data=[{
'VertragNr': '1', 'ArtName': 'Ehren', 'Jahr': 2023,
'Betrag': 100, 'Bezahlt': 80, 'Offen': 20,
}, {
'VertragNr': '1', 'ArtName': 'Ehren', 'Jahr': 2024,
'Betrag': 120, 'Bezahlt': 60, 'Offen': 60,
}], config_base='https://api.example.com/')
contributions_mapper(ctx)
assert ctx['data']['total']['billed'] == 220
assert ctx['data']['total']['paid'] == 140
assert len(ctx['data']['contracts']) == 1
assert '2023' in ctx['data']['years']
assert '2024' in ctx['data']['years']
# ── member mappers ─────────────────────────────────────────────
class TestMemberMapper:
def test_full_member(self):
ctx = _ctx(data=[{
'Eintritt': '2020-01-15T00:00:00Z',
'Kontaktwoher': 'Alice, Bob',
'Vorname': 'Max',
'Nachname': 'Mustermann',
'Kurzname': 'maxm',
'Kennung3': 'crew',
'AdrNr': 42,
'MITGLNR': '1001',
'Zahlungsart': 'L',
'Betreuung': 'MÄNNLICH',
}], config_base='https://api.example.com/',
path='/member/maxm/', flags=['_board_'])
member_mapper(ctx)
d = ctx['data']
assert d['Vorname'] == 'Max'
assert d['Email'] == 'maxm@c-base.org'
assert d['Zahlungsart'] == 'Lastschrift'
assert d['Geschlecht'] == 'M'
assert d['Mitgliedsnummer'] == 1001
assert 'memo' in d # board-only
def test_no_board_no_memo(self):
ctx = _ctx(data=[{
'Eintritt': '2020-01-01T00:00:00Z',
'Kontaktwoher': '', 'Kurzname': 'x',
'Kennung3': 'crew',
}], flags=[])
member_mapper(ctx)
assert 'memo' not in ctx['data']
class TestMemberlistMapper:
def test_list(self):
ctx = _ctx(data=[{
'Kurzname': 'alice', 'AdrNr': 1, 'Kennung3': 'crew',
'Eintritt': '2020-01-01T00:00:00Z', 'Kontaktwoher': '',
'Vorname': 'A', 'Nachname': 'B',
}])
memberlist_mapper(ctx)
assert ctx['data']['count'] == 1
assert ctx['data']['results'][0]['Crewname'] == 'alice'
def test_x_prefix_no_url(self):
ctx = _ctx(data=[{
'Kurzname': 'Xdisabled', 'AdrNr': 2, 'Kennung3': 'crew',
'Eintritt': '', 'Kontaktwoher': '',
}])
memberlist_mapper(ctx)
assert 'url' not in ctx['data']['results'][0]
class TestMemberListToLdapCsv:
def test_csv_shape(self):
ctx = _ctx(data=[{
'Kurzname': 'alice', 'Nachname': 'A', 'Vorname': 'B',
'Kennung3': 'crew', 'Eintritt': '2020-01-01T00:00:00Z',
'Telefon3': 'alice@mail.org', 'Kontaktwoher': 'Bob',
}])
memberlist_to_ldapcsv_mapper(ctx)
assert len(ctx['data']) == 1
row = ctx['data'][0]
assert 'externe E-Mail' in row
# ── memo mapper ────────────────────────────────────────────────
class TestMemoMapper:
def test_basic(self):
ctx = _ctx(data=[{
'Eintritt': '2020-01-01T00:00:00Z',
'Memotext': '{\\rtf plain text}',
'Benutzer': 'admin',
}])
memo_mapper(ctx)
assert ctx['data']['Editor'] == 'admin'
def test_no_data(self):
ctx = _ctx(data=[])
memo_mapper(ctx)
assert ctx['data'] == {}
# ── withdrawal mappers ───────────────────────────────────────
class TestWithdrawalMapper:
def test_single(self):
ctx = _ctx(data=[{
'VertragNr': '5', 'Adr_BLZ': '1234', 'Adr_Konto': '5678',
'IBAN': '', 'BIC': '', 'MandatsNr': 'MAND1',
'Betrag_EU': 50, 'Jahr': 2023, 'Zeitraum': 3,
'ErstDatum': '2023-03-15T00:00:00Z',
}], config_base='https://api.example.com/', path='/withdrawal/')
withdrawal_mapper(ctx)
assert ctx['data']['Vertragsnummer'] == 5
assert ctx['data']['BLZ'] == '1234'
def test_list(self):
ctx = _ctx(data=[{
'VertragNr': '1', 'Adr_BLZ': '', 'Adr_Konto': '',
'IBAN': '', 'BIC': '', 'MandatsNr': '',
'Betrag_EU': 0, 'Jahr': 2023, 'Zeitraum': 1,
'ErstDatum': '',
}])
withdrawallist_mapper(ctx)
assert ctx['data']['count'] == 1