test(ledger): cover amount-missing reject and real-postgres alias amount API path

Co-authored-by: genichin <4282227+genichin@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-09-09 14:05:19 +00:00
committed by GitHub
co-authored by genichin
parent 42297030ca
commit 8368e7ba16
3 changed files with 90 additions and 0 deletions
@@ -10,21 +10,26 @@ from __future__ import annotations
import os
import threading
from contextlib import ExitStack
from datetime import UTC, datetime
from decimal import Decimal
from urllib.parse import unquote, urlparse
from uuid import uuid4
from unittest.mock import patch
import psycopg
import pytest
from fastapi.testclient import TestClient
from psycopg import sql
from app.core.auth import AuthContext, Permission, require_read
from app.core.config import Settings
from app.db.migrations import (
bootstrap_or_upgrade_to_head,
get_alembic_config,
upgrade_to_head,
)
from app.main import create_app
from app.repositories.postgres import PortfolioDBConfig, PostgresPortfolioRepository
_LOCAL_DSN_ENV = "BAT025_LOCAL_POSTGRES_DSN"
@@ -159,6 +164,23 @@ def _seed_portfolio(repo: PostgresPortfolioRepository, suffix: str) -> str:
return portfolio_id
def _api_client(settings: Settings, repo: PostgresPortfolioRepository, *, user_id: str) -> tuple[TestClient, ExitStack]:
app = create_app(settings_factory=lambda: settings)
app.dependency_overrides[require_read] = lambda: AuthContext(
token="bat025-test-token",
permission=Permission.READ,
user_id=user_id,
token_type="session",
token_source="db",
)
stack = ExitStack()
stack.enter_context(patch("app.api.v1_portfolios.get_portfolio_repository_from_settings", return_value=repo))
stack.enter_context(patch("app.core.auth.get_portfolio_repository_from_settings", return_value=repo))
stack.enter_context(patch("app.api.v1_portfolios.get_settings", return_value=settings))
client = TestClient(app)
return client, stack
def test_upgrade_from_pre_0028_preserves_business_data_and_backfills_source(
isolated_settings: Settings,
) -> None:
@@ -463,6 +485,72 @@ def test_dividend_tax_alias_prone_decimals_round_trip_distinctly_on_real_postgre
repo.close()
def test_dividend_amount_alias_prone_decimals_are_not_misdetected_as_duplicate_in_api_on_real_postgres(
isolated_settings: Settings,
) -> None:
upgrade_to_head(isolated_settings)
repo = _repo(isolated_settings)
suffix = "amount-alias-api"
portfolio_id = _seed_portfolio(repo, suffix)
user_id = f"bat025-user-{suffix}"
settings = isolated_settings.model_copy(update={"credential_encryption_key": "bat025-test-key"})
client, stack = _api_client(settings, repo, user_id=user_id)
auth_header = {"Authorization": "Bea" + "rer bat025-test-token"}
try:
first = client.post(
f"/v1/portfolios/{portfolio_id}/ledger/manual",
json={
"action": "DIVIDEND",
"amount": "90000000.00000001",
"tax": "0.00000000",
"executed_at": "2026-09-01",
"note": "amount-alias",
},
headers=auth_header,
)
second = client.post(
f"/v1/portfolios/{portfolio_id}/ledger/manual",
json={
"action": "DIVIDEND",
"amount": "90000000.00000002",
"tax": "0.00000000",
"executed_at": "2026-09-01",
"note": "amount-alias",
},
headers=auth_header,
)
assert first.status_code == 201
assert second.status_code == 201
assert first.json()["amount"] == "90000000.00000001"
assert second.json()["amount"] == "90000000.00000002"
history = client.get(
f"/v1/portfolios/{portfolio_id}/ledger?source=manual",
headers=auth_header,
)
assert history.status_code == 200
amounts = [item["amount"] for item in history.json()["items"] if item["action"] == "DIVIDEND"]
assert "90000000.00000001" in amounts
assert "90000000.00000002" in amounts
finally:
client.close()
stack.close()
with repo._connect().cursor() as cur:
cur.execute(
"""
SELECT price::text
FROM portfolio_ledger
WHERE portfolio_id = %s AND side = 'dividend' AND note = 'amount-alias'
ORDER BY ledger_entry_id ASC
""",
(portfolio_id,),
)
assert [row[0] for row in cur.fetchall()] == ["90000000.00000001", "90000000.00000002"]
repo.close()
def test_cold_repositories_concurrent_dividend_requests_follow_duplicate_contract(
isolated_settings: Settings,
) -> None:
@@ -600,6 +600,7 @@ def test_dividend_rejects_invalid_amount_tax_and_unrelated_fields_without_state_
)
invalid_payloads = [
{"action": "DIVIDEND", "executed_at": "2026-09-01"},
{"action": "DIVIDEND", "amount": 1},
{"action": "DIVIDEND", "amount": 0, "executed_at": "2026-09-01"},
{"action": "DIVIDEND", "amount": -1, "executed_at": "2026-09-01"},