Files
dzentra_bot/docs/migrations/build_025.md

600 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Build 025 — Удаление legacy `ExchangeSymbol` compatibility layer
**Статус:** ✅ Завершён
**Дата:** 2026-07-12
**Подсистема:** Instrument Reference / Exchange Integration
**Этап:** Завершение миграции на каноническую модель `Instrument`
---
# Цель Build
Полностью удалить временный compatibility layer, использовавшийся во время поэтапного перехода от legacy-модели:
```text
ExchangeSymbol
```
к канонической модели:
```text
Instrument
```
После завершения Build все production-потребители работают через единый канонический контур:
```text
Instrument Acquisition
Instrument Store
get_instruments()
validate_symbol()
Instrument
```
---
# Предпосылки
На предыдущих этапах были выполнены:
- создание канонической модели `Instrument`;
- построение Instrument Acquisition pipeline;
- создание `InstrumentStoreProtocol`;
- реализация `InMemoryInstrumentStore`;
- перенос кэша справочника инструментов в Storage;
- добавление `ExchangeService.get_instruments()`;
- перевод `validate_symbol()` на канонический справочник;
- перевод Telegram UI и runtime-потребителей на `Instrument`;
- удаление неиспользуемого legacy Market Handler.
После этого legacy-контур сохранялся только как временная проекция:
```text
Instrument
compatibility.py
ExchangeSymbol
get_exchange_symbols()
```
Реальных production-потребителей этого контура больше не осталось.
---
# Предварительный аудит
Выполнен поиск production-зависимостей:
```bash
grep -RIn \
--exclude-dir="__pycache__" \
--exclude="*.pyc" \
-E "ExchangeSymbol|get_exchange_symbols|map_instrument_to_exchange_symbol|map_instruments_to_exchange_symbols|_exchange_symbols_projection_cache" \
src
```
Установлено, что все найденные элементы находились только внутри самого legacy-контура:
```text
src/market_data/acquisition/compatibility.py
src/integrations/exchange/models.py
src/integrations/exchange/service.py
```
Канонические production-потребители уже использовали:
```text
get_instruments()
validate_symbol()
Instrument
```
---
# Аудит legacy parser helpers
Проверены методы:
```text
_extract_exchange_symbols_raw()
_parse_exchange_symbol()
_parse_exchange_symbol_status()
_parse_market_modes()
_extract_filter_value()
```
Выполнено:
```bash
grep -RIn \
--exclude-dir="__pycache__" \
--exclude="*.pyc" \
-E "_extract_exchange_symbols_raw|_parse_exchange_symbol\(|_parse_exchange_symbol_status|_parse_market_modes|_extract_filter_value" \
src tests
```
Подтверждено, что эти методы использовались только устаревшим equivalence-тестом и больше не требовались production-коду.
---
# Подготовка тестового контура
Перед удалением production compatibility layer были обновлены канонические тесты.
## Обновлён файл
```text
tests/unit/integrations/exchange/test_service_instruments.py
```
Из него удалены тесты legacy-проекции:
```text
test_get_exchange_symbols_uses_get_instruments
test_get_exchange_symbols_maps_canonical_instruments
test_get_exchange_symbols_projection_cache_preserves_identity
```
Сохранены все тесты канонического поведения:
- отключённая биржа;
- Instrument Store hit;
- Instrument Store miss;
- загрузка через acquisition;
- сохранение результата в Store;
- повторное использование Store;
- поддержка пустого immutable-набора;
- общий Store между экземплярами `ExchangeService`;
- обработка acquisition errors;
- логирование ошибок;
- отсутствие заполнения Store при ошибке.
---
## Обновлён файл
```text
tests/unit/integrations/exchange/test_service_validate_symbol.py
```
Legacy-проверка через monkeypatch метода:
```text
get_exchange_symbols()
```
заменена проверкой прямого использования:
```text
get_instruments()
```
Дополнительно добавлен отрицательный архитектурный тест:
```python
def test_exchange_service_has_no_legacy_get_exchange_symbols() -> None:
assert not hasattr(
ExchangeService,
"get_exchange_symbols",
)
```
---
## Обновлён файл
```text
tests/unit/telegram/ui/test_currency_ui.py
```
Legacy-проверка отсутствия вызова `get_exchange_symbols()` заменена прямой проверкой использования канонического:
```text
get_instruments()
```
---
# Проверка подготовительного пакета
Выполнена компиляция:
```bash
python -m py_compile \
tests/unit/integrations/exchange/test_service_instruments.py \
tests/unit/integrations/exchange/test_service_validate_symbol.py \
tests/unit/telegram/ui/test_currency_ui.py
```
Результат:
```text
Ошибок нет.
```
Выполнены целевые тесты:
```bash
python -m pytest \
tests/unit/integrations/exchange/test_service_instruments.py \
tests/unit/integrations/exchange/test_service_validate_symbol.py \
tests/unit/telegram/ui/test_currency_ui.py \
-q
```
Результат:
```text
49 passed
```
---
# Удалённые migration-only файлы
Полностью удалены:
```text
src/market_data/acquisition/compatibility.py
tests/unit/market_data/acquisition/test_compatibility.py
tests/unit/market_data/acquisition/test_equivalence_comparator.py
tests/unit/integrations/exchange/test_service_exchange_symbols.py
tests/integration/market_data/acquisition/test_instrument_reference_equivalence.py
tests/support/instrument_reference_equivalence.py
```
Эти файлы обслуживали только временный compatibility/equivalence-контур и завершили свою миграционную задачу.
---
# Изменения в `models.py`
Из файла:
```text
src/integrations/exchange/models.py
```
полностью удалена legacy-модель:
```python
@dataclass(slots=True)
class ExchangeSymbol:
...
```
Модель:
```python
SymbolValidationResult
```
сохраняет канонический контракт:
```python
symbol_info: Instrument | None
```
Импорт `Instrument` выполняется через:
```python
TYPE_CHECKING
```
что исключает runtime-cycle и сохраняет корректную типизацию.
---
# Изменения в `service.py`
Из файла:
```text
src/integrations/exchange/service.py
```
удалены следующие элементы.
## Legacy imports
Удалены:
```python
ExchangeSymbol
```
и:
```python
from src.market_data.acquisition.compatibility import (
map_instruments_to_exchange_symbols,
)
```
---
## Legacy projection cache
Удалено поле:
```python
_exchange_symbols_projection_cache
```
Теперь `ExchangeService` хранит только канонический Store:
```python
_instrument_store: InstrumentStoreProtocol = InMemoryInstrumentStore()
```
---
## Legacy API
Полностью удалён метод:
```python
get_exchange_symbols()
```
Единственным публичным API справочника инструментов остаётся:
```python
get_instruments()
```
---
## Legacy parser helpers
Удалены методы:
```text
_extract_exchange_symbols_raw()
_parse_exchange_symbol()
_parse_exchange_symbol_status()
_parse_market_modes()
_extract_filter_value()
```
Их функции полностью заменены новой pipeline:
```text
Dzengi REST document
Dzengi parser
validation
mapper
Instrument
```
---
## Сохранённый helper
Метод:
```python
_safe_str()
```
сохранён, так как продолжает использоваться обработкой trading fee payload.
---
# Финальный production-контур
После удаления compatibility layer работа со справочником инструментов выполняется так:
```text
DzengiInstrumentDocumentSource
DzengiInstrumentDocumentHandler
InstrumentFeed
InstrumentFeedRegistry
InstrumentAcquisitionService
tuple[Instrument, ...]
Instrument Store
ExchangeService.get_instruments()
```
Проверка пользовательского символа выполняется через:
```text
validate_symbol()
SymbolValidationResult
symbol_info: Instrument | None
```
---
# Архитектурные отрицательные тесты
Добавлены проверки физического отсутствия legacy API.
## Отсутствие legacy-метода
```python
def test_exchange_service_has_no_legacy_get_exchange_symbols() -> None:
assert not hasattr(
ExchangeService,
"get_exchange_symbols",
)
```
## Отсутствие legacy projection cache
```python
def test_exchange_service_has_no_legacy_projection_cache() -> None:
assert not hasattr(
ExchangeService,
"_exchange_symbols_projection_cache",
)
```
---
# Финальный аудит
Выполнено:
```bash
grep -RIn \
--exclude-dir="__pycache__" \
--exclude="*.pyc" \
-E "ExchangeSymbol|get_exchange_symbols|map_instrument_to_exchange_symbol|map_instruments_to_exchange_symbols|_exchange_symbols_projection_cache|market_data\.acquisition\.compatibility" \
src tests
```
Остались только ожидаемые упоминания в отрицательных архитектурных тестах:
```text
test_exchange_service_has_no_legacy_get_exchange_symbols
test_exchange_service_has_no_legacy_projection_cache
```
Других production- или test-зависимостей не обнаружено.
---
# Проверка компиляции
Выполнено:
```bash
python -m py_compile \
src/integrations/exchange/models.py \
src/integrations/exchange/service.py \
tests/unit/integrations/exchange/test_service_instruments.py \
tests/unit/integrations/exchange/test_service_validate_symbol.py \
tests/unit/telegram/ui/test_currency_ui.py
```
Результат:
```text
Ошибок нет.
```
---
# Целевой Regression Suite
Выполнено:
```bash
python -m pytest \
tests/unit/integrations/exchange/test_service_instruments.py \
tests/unit/integrations/exchange/test_service_validate_symbol.py \
tests/unit/integrations/exchange/test_service_symbol_runtime_status.py \
tests/unit/integrations/exchange/test_market_stream.py \
tests/unit/integrations/exchange/test_market_data_runner.py \
tests/unit/telegram/ui/test_currency_ui.py \
-q
```
Результат:
```text
94 passed
```
---
# Полный Regression Suite
Выполнено:
```bash
python -m pytest -q
```
Результат:
```text
423 passed
```
Снижение общего числа тестов относительно предыдущего Build является ожидаемым, поскольку были удалены временные compatibility- и equivalence-тесты вместе с соответствующим legacy-кодом.
---
# Архитектурный итог
До Build 025:
```text
Instrument
├── canonical consumers
└── compatibility mapper
ExchangeSymbol
legacy projection cache
```
После Build 025:
```text
Instrument
Instrument Store
canonical consumers
```
В проекте больше не существует:
```text
ExchangeSymbol
compatibility.py
get_exchange_symbols()
_exchange_symbols_projection_cache
Instrument → ExchangeSymbol mapping
legacy exchangeInfo parser helpers
migration equivalence framework
```
---
# Итог Build
Build 025 полностью завершён.
Подтверждено:
- все production-потребители переведены на `Instrument`;
- удалена legacy-модель `ExchangeSymbol`;
- удалён временный compatibility mapper;
- удалён legacy-метод `get_exchange_symbols()`;
- удалён projection cache;
- удалены неиспользуемые parser helpers;
- удалены migration-only и equivalence-тесты;
- сохранены и усилены канонические тесты Instrument Store;
- добавлены архитектурные тесты отсутствия legacy API;
- компиляция проходит без ошибок;
- целевой Regression Suite успешно пройден — **94 passed**;
- полный Regression Suite успешно пройден — **423 passed**.
**Статус Build:** ✅ Завершён.