"""Hand corrections to an instrument over HTTP.""" from factories import make_instrument def url(instrument_id: int) -> str: return f"/api/v1/instruments/{instrument_id}" async def test_patch_lot_board_name_class_and_sector(client, auth_headers): iid = await make_instrument(ticker="MSNG", board="TQBR") r = await client.patch( url(iid), json={ "lot": 1000, "name": " Мосэнерго ", "asset_class": "bond", "sector": "Энергетика", "board": "TQTF", }, headers=auth_headers, ) assert r.status_code == 200, r.text row = r.json() assert (row["lot"], row["name"], row["asset_class"]) == (1000, "Мосэнерго", "bond") assert (row["sector"], row["board"]) == ("Энергетика", "TQTF") detail = (await client.get(url(iid), headers=auth_headers)).json()["instrument"] assert detail["lot"] == 1000 async def test_unset_fields_are_left_alone_and_null_clears_board(client, auth_headers): iid = await make_instrument(ticker="AFLT", board="TQBR", name="Аэрофлот") r = await client.patch(url(iid), json={"lot": 10}, headers=auth_headers) assert (r.json()["name"], r.json()["board"], r.json()["lot"]) == ("Аэрофлот", "TQBR", 10) r = await client.patch(url(iid), json={"board": None}, headers=auth_headers) assert r.status_code == 200 assert r.json()["board"] is None assert r.json()["lot"] == 10 async def test_invalid_values_are_refused(client, auth_headers): iid = await make_instrument(ticker="PHOR") for body in ({"lot": 0}, {"lot": -5}, {"name": ""}, {"unknown_field": 1}): r = await client.patch(url(iid), json=body, headers=auth_headers) assert r.status_code == 422, body for body in ({"lot": None}, {"name": None}, {"asset_class": None}, {"name": " "}): r = await client.patch(url(iid), json=body, headers=auth_headers) assert r.status_code == 400, body r = await client.patch(url(iid), json={"asset_class": "stock"}, headers=auth_headers) assert r.status_code == 422 assert "share" in r.json()["detail"] async def test_moving_to_a_taken_ticker_and_board_is_a_conflict(client, auth_headers): await make_instrument(ticker="SBER", board="TQBR") other = await make_instrument(ticker="SBER", board="SMAL") r = await client.patch(url(other), json={"board": "TQBR"}, headers=auth_headers) assert r.status_code == 409 assert r.headers["content-type"].startswith("application/problem+json") r = await client.patch(url(other), json={"board": "TQBR", "lot": 10}, headers=auth_headers) assert r.status_code == 409 # nothing was half-applied assert (await client.get(url(other), headers=auth_headers)).json()["instrument"]["lot"] == 1 async def test_missing_instrument_is_404(client, auth_headers): r = await client.patch(url(999), json={"lot": 2}, headers=auth_headers) assert r.status_code == 404 async def test_instrument_and_holding_expose_a_public_logo_url(client, auth_headers): from datetime import date from factories import make_account, make_event, refresh from fintracker.db import get_sessionmaker from fintracker.models import AccountKind, AccountRole, EventKind, Instrument iid = await make_instrument(ticker="SBER") async with get_sessionmaker()() as session: row = await session.get(Instrument, iid) assert row is not None row.logo_name, row.logo_color = "sber.png", "#21A038" await session.commit() account = await make_account( name="Т", kind=AccountKind.broker, role=AccountRole.investment, balance=None ) await make_event( date(2026, 3, 1), account_id=account, kind=EventKind.buy, instrument_id=iid, quantity=1, price=100, amount=-100, ) # fmt: skip await refresh() card = (await client.get(url(iid), headers=auth_headers)).json() assert card["instrument"]["logo_url"] == "https://invest-brands.cdn-tinkoff.ru/sberx160.png" assert card["instrument"]["logo_color"] == "#21A038" assert card["holding"]["logo_url"] == card["instrument"]["logo_url"] plain = await make_instrument(ticker="NOLOGO") assert (await client.get(url(plain), headers=auth_headers)).json()["instrument"][ "logo_url" ] is None