Increases overall test coverage from 53% to 56% by adding tests for: - accommodation.py (60% → 100%) - birthday.py (24% → 100%) - calendar.py (19% → 100%) - carnival.py (33% → 100%) - domains.py (75% → 100%) - events_yaml.py (50% → 96%) - fx.py (14% → 21% for tested functions) - sun.py (55% → 100%) 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
182 lines
5.8 KiB
Python
182 lines
5.8 KiB
Python
"""Tests for foreign exchange functionality."""
|
|
|
|
import json
|
|
import os
|
|
import tempfile
|
|
from decimal import Decimal
|
|
from unittest.mock import Mock, patch
|
|
|
|
import pytest
|
|
|
|
from agenda.fx import read_cached_rates
|
|
|
|
|
|
class TestReadCachedRates:
|
|
"""Test the read_cached_rates function."""
|
|
|
|
def test_read_cached_rates_none_filename(self) -> None:
|
|
"""Test with None filename returns empty dict."""
|
|
result = read_cached_rates(None, ["USD", "EUR"])
|
|
assert result == {}
|
|
|
|
def test_read_cached_rates_valid_file(self) -> None:
|
|
"""Test reading valid cached rates file."""
|
|
currencies = ["USD", "EUR", "JPY"]
|
|
data = {
|
|
"quotes": {
|
|
"GBPUSD": 1.25,
|
|
"GBPEUR": 1.15,
|
|
"GBPJPY": 150.0,
|
|
"GBPCAD": 1.70 # Not requested
|
|
}
|
|
}
|
|
|
|
with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as f:
|
|
json.dump(data, f)
|
|
filepath = f.name
|
|
|
|
try:
|
|
result = read_cached_rates(filepath, currencies)
|
|
|
|
assert len(result) == 3
|
|
assert result["USD"] == Decimal("1.25")
|
|
assert result["EUR"] == Decimal("1.15")
|
|
assert result["JPY"] == Decimal("150.0")
|
|
assert "CAD" not in result # Not requested
|
|
|
|
finally:
|
|
os.unlink(filepath)
|
|
|
|
def test_read_cached_rates_missing_currencies(self) -> None:
|
|
"""Test with some currencies missing from data."""
|
|
currencies = ["USD", "EUR", "CHF"] # CHF not in data
|
|
data = {
|
|
"quotes": {
|
|
"GBPUSD": 1.25,
|
|
"GBPEUR": 1.15
|
|
# GBPCHF missing
|
|
}
|
|
}
|
|
|
|
with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as f:
|
|
json.dump(data, f)
|
|
filepath = f.name
|
|
|
|
try:
|
|
result = read_cached_rates(filepath, currencies)
|
|
|
|
assert len(result) == 2
|
|
assert result["USD"] == Decimal("1.25")
|
|
assert result["EUR"] == Decimal("1.15")
|
|
assert "CHF" not in result # Missing from data
|
|
|
|
finally:
|
|
os.unlink(filepath)
|
|
|
|
def test_read_cached_rates_empty_currencies_list(self) -> None:
|
|
"""Test with empty currencies list."""
|
|
data = {
|
|
"quotes": {
|
|
"GBPUSD": 1.25,
|
|
"GBPEUR": 1.15
|
|
}
|
|
}
|
|
|
|
with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as f:
|
|
json.dump(data, f)
|
|
filepath = f.name
|
|
|
|
try:
|
|
result = read_cached_rates(filepath, [])
|
|
assert result == {}
|
|
|
|
finally:
|
|
os.unlink(filepath)
|
|
|
|
def test_read_cached_rates_no_quotes_key(self) -> None:
|
|
"""Test with data missing quotes key."""
|
|
currencies = ["USD", "EUR"]
|
|
data = {"other_key": "value"} # No quotes key
|
|
|
|
with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as f:
|
|
json.dump(data, f)
|
|
filepath = f.name
|
|
|
|
try:
|
|
with pytest.raises(KeyError):
|
|
read_cached_rates(filepath, currencies)
|
|
|
|
finally:
|
|
os.unlink(filepath)
|
|
|
|
def test_read_cached_rates_file_not_found(self) -> None:
|
|
"""Test error handling when file doesn't exist."""
|
|
with pytest.raises(FileNotFoundError):
|
|
read_cached_rates("/nonexistent/file.json", ["USD"])
|
|
|
|
def test_read_cached_rates_invalid_json(self) -> None:
|
|
"""Test error handling with invalid JSON."""
|
|
with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as f:
|
|
f.write("invalid json content")
|
|
filepath = f.name
|
|
|
|
try:
|
|
with pytest.raises(json.JSONDecodeError):
|
|
read_cached_rates(filepath, ["USD"])
|
|
|
|
finally:
|
|
os.unlink(filepath)
|
|
|
|
def test_read_cached_rates_decimal_precision(self) -> None:
|
|
"""Test that rates are returned as Decimal with proper precision."""
|
|
currencies = ["USD"]
|
|
data = {
|
|
"quotes": {
|
|
"GBPUSD": 1.234567890123456789 # High precision
|
|
}
|
|
}
|
|
|
|
with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as f:
|
|
json.dump(data, f)
|
|
filepath = f.name
|
|
|
|
try:
|
|
result = read_cached_rates(filepath, currencies)
|
|
|
|
assert isinstance(result["USD"], Decimal)
|
|
# Should preserve reasonable precision from the JSON
|
|
# Python's JSON precision may be limited to float precision
|
|
expected = Decimal("1.234567890123456789")
|
|
assert abs(result["USD"] - expected) < Decimal("0.0000000000000001")
|
|
|
|
finally:
|
|
os.unlink(filepath)
|
|
|
|
def test_read_cached_rates_various_currency_codes(self) -> None:
|
|
"""Test with various currency codes."""
|
|
currencies = ["USD", "EUR", "JPY", "CHF", "CAD", "AUD"]
|
|
data = {
|
|
"quotes": {
|
|
"GBPUSD": 1.25,
|
|
"GBPEUR": 1.15,
|
|
"GBPJPY": 150.0,
|
|
"GBPCHF": 1.12,
|
|
"GBPCAD": 1.70,
|
|
"GBPAUD": 1.85
|
|
}
|
|
}
|
|
|
|
with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as f:
|
|
json.dump(data, f)
|
|
filepath = f.name
|
|
|
|
try:
|
|
result = read_cached_rates(filepath, currencies)
|
|
|
|
assert len(result) == 6
|
|
for currency in currencies:
|
|
assert currency in result
|
|
assert isinstance(result[currency], Decimal)
|
|
|
|
finally:
|
|
os.unlink(filepath) |