bot_pozhitki/tests/test_core.py

138 lines
4.6 KiB
Python

from pathlib import Path
import pytest
from app.bot import (
parse_cancellation,
parse_category,
parse_transaction,
webapp_url_for_user,
)
from app.config import Settings
from app.db import Database
def make_database(tmp_path: Path) -> Database:
database = Database(tmp_path / "test.sqlite3")
database.initialize()
return database
def add_user(database: Database, user_id: int, name: str) -> None:
database.upsert_user(
user_id=user_id,
first_name=name,
last_name=None,
username=None,
)
def test_transaction_and_leaderboard(tmp_path: Path) -> None:
database = make_database(tmp_path)
add_user(database, 1, "Аня")
item = parse_transaction(
'{"v":1,"type":"transaction","kind":"income","amount":"1000.50","category":"Зарплата","occurred_at":"2026-01-01T00:00:00Z"}'
)
database.add_transaction(1, item)
expense = parse_transaction(
'{"v":1,"type":"transaction","kind":"expense","amount":"250.25","category":"Еда","occurred_at":"2026-01-02T00:00:00Z"}'
)
database.add_transaction(1, expense)
summary = database.summary(1)
assert summary is not None
assert summary["income_cents"] == 100_050
assert summary["expense_cents"] == 25_025
assert summary["saved_percent"] == 75.0
assert database.leaderboard()[0]["nickname"] == "Аня"
def test_friendship_is_symmetric_and_idempotent(tmp_path: Path) -> None:
database = make_database(tmp_path)
add_user(database, 3, "Три")
add_user(database, 7, "Семь")
assert database.add_friendship(7, 3)
assert not database.add_friendship(3, 7)
assert database.friend_ids(3) == [7]
assert database.friend_ids(7) == [3]
def test_filtered_leaderboard(tmp_path: Path) -> None:
database = make_database(tmp_path)
for user_id in (1, 2, 3):
add_user(database, user_id, str(user_id))
database.add_transaction(
1,
parse_transaction(
'{"v":1,"type":"transaction","kind":"income","amount":"100","category":"Работа"}'
),
)
database.add_transaction(
3,
parse_transaction(
'{"v":1,"type":"transaction","kind":"expense","amount":"40","category":"Еда"}'
),
)
result = database.leaderboard(user_ids=[1, 3])
assert [item["user_id"] for item in result] == [1, 3]
filtered = database.leaderboard(user_ids=[1, 3], categories=["Еда"])
assert filtered[0]["user_id"] == 1
assert filtered[0]["balance_cents"] == 0
assert filtered[1]["user_id"] == 3
assert filtered[1]["balance_cents"] == -4_000
@pytest.mark.parametrize(
"payload",
[
"not-json",
'{"v":1,"type":"transaction","kind":"other","amount":"10","category":"Еда"}',
'{"v":1,"type":"transaction","kind":"income","amount":"-1","category":"Зарплата"}',
'{"v":1,"type":"transaction","kind":"income","amount":"10","category":""}',
],
)
def test_rejects_invalid_webapp_payload(payload: str) -> None:
with pytest.raises(ValueError):
parse_transaction(payload)
def test_personalized_keyboard_webapp_url(tmp_path: Path) -> None:
settings = Settings(
telegram_token="token",
base_app_url="https://example.com/",
data_dir=tmp_path,
)
assert webapp_url_for_user(settings, 123456) == (
"https://example.com/app/?user_id=123456&mode=keyboard"
)
def test_custom_category_and_cancellation(tmp_path: Path) -> None:
database = make_database(tmp_path)
add_user(database, 10, "Владелец")
add_user(database, 11, "Другой")
kind, name = parse_category(
'{"v":1,"type":"category_create","kind":"expense","category":"Подписки"}'
)
assert database.add_category(10, kind, name)
assert not database.add_category(10, kind, name)
assert database.user_categories(10) == [{"kind": "expense", "name": "Подписки"}]
transaction_id = database.add_transaction(
10,
parse_transaction(
'{"v":1,"type":"transaction","kind":"expense","amount":"499","category":"Подписки"}'
),
)
assert parse_cancellation(
f'{{"v":1,"type":"transaction_cancel","transaction_id":{transaction_id}}}'
) == transaction_id
assert not database.cancel_transaction(11, transaction_id)
assert database.cancel_transaction(10, transaction_id)
assert not database.cancel_transaction(10, transaction_id)
assert database.transactions(10) == []
assert database.summary(10)["expense_cents"] == 0
assert database.user_categories(10) == [{"kind": "expense", "name": "Подписки"}]