12 KiB
12 KiB
QA Analysis
Issue: WealthLedger/main#54
Verification Scope
In:
- все Requirements и Scenarios
portfolio/investment-performanceиinstruments/market-index-history; - контракт
investment-performance.openapi.yaml; - семантика ошибок System Analysis.
Out:
- TWR, периоды, валютная конвертация;
- корректность самой синхронизации T-Bank (покрыта её спецификациями);
- точность значений MCFTRR у источника.
Coverage Matrix
| Requirement / Scenario | Проверка на уровне системы |
|---|---|
| Результат счёта: рублёвый счёт с известным началом | API: ответ 200 со всеми полями; значения совпадают с эталоном (ниже) |
| Результат счёта: счёт недоступен через подключение | API: чужой accountId → 404, в теле нет финансовых полей |
| Внешние потоки: пополнения, вывод, дивиденды | Эталонный счёт A: netInvested = 130000, profit = 10000 |
Внешние потоки: кандидат на перевод между счетами в all |
Счета B и C с равными суммами в пределах 5 минут исключаются с UNCONFIRMED_TRANSFER, а не объявляются внутренней парой |
| Внешние потоки: перевод на счёт вне результата | Счёт B исключён (USD), его пополнение от C → вывод у C в all |
| Доходность: период больше года | Счёт D: 100 000 → 121 000 за 730 дн. → return.value = 0.1, basis = annual |
| Доходность: меньше года | Счёт E, 200 дн.: basis = period; в UI нет слова «годовых» ни в одном значении |
| Доходность: нет решения | Счёт F (текущая стоимость 0 после пополнений, без выводов): NO_SOLUTION, profit доступен, outperformance = null |
| Доходность: несколько решений | Набор потоков с двумя корнями XIRR: AMBIGUOUS_RETURN, прибыль доступна, outperformance = null |
| Индекс: пополнение и рост индекса | Подменённая история (1 000 → 1 100) или ручной расчёт по реальным закрытиям: benchmark.value = 110000, outperformance.money = 5000 |
| Индекс: поток в выходной | Пополнение в субботу → используется закрытие пятницы (проверяется по series в день потока) |
| Индекс: отставание | Счёт со стоимостью ниже теневой: отрицательные percentagePoints и money, в UI «Отставание от рынка» |
| Справочный индекс недоступен | IMOEX нет, MCFTRR есть: reference.unavailableReason задан, benchmark и outperformance есть, в UI нет пунктирной линии |
| Недоступно: неизвестная дата открытия | Счёт с historyBoundary = unknown: data: null, HISTORY_START_UNKNOWN; в UI «Доходность недоступна», нет нулей |
| USD-поток игнорируется | Счёт с пополнением в USD: результат считается по рублёвым потокам, excludedAccounts пуст |
Недоступно: даты оценок в all различаются |
Последние оценки A и B в разные московские дни → data: null, ACCOUNT_VALUATION_DATES_DIFFER; стоимость не складывается |
| Недоступно: индекс недоступен | MOEX заблокирован и истории нет: profit есть, benchmark.unavailableReason = INDEX_HISTORY_UNAVAILABLE, series = [] |
| Недоступно: остальные причины | Перевод бумаг без пары → UNVALUED_SECURITIES_TRANSFER; позиция без цены → PORTFOLIO_VALUATION_INCOMPLETE; незавершённая синхронизация → OPERATIONS_HISTORY_INCOMPLETE |
| Вкладка: обгоняю, меньше года, неполный, недоступный, методика | UI по соответствующим данным; формулировки и тоны как в UI/UX Package; «Как считали» содержит все пункты спецификации |
| Вкладка: загрузка, ошибка не ломает другие вкладки | Задержка ответа → skeleton без чисел; 500 на /performance → ошибка только во вкладке, остальные вкладки работают |
| График: наведение, отсутствие прошлой стоимости | Наведение и фокус показывают дату и 3 значения; есть пояснение под графиком; aria-label с итогами |
| История индексов: первое получение, выходной | После первого чтения есть закрытия торговых дней без дубликатов; повторное чтение не меняет число записей; субботних дат нет |
| История индексов: источник недоступен, история есть / нет | Блокировка iss.moex.com после первичной загрузки → результат полный; на чистой БД → INDEX_HISTORY_UNAVAILABLE, остальные вкладки работают |
Эталон. Для счетов A–F и реальных данных пользователя XIRR
пересчитывается независимо (LibreOffice XIRR по тем же потокам и
currentValue в дату to). Допуск — 0,1 п.п., по Success Criteria.
Cross-system Verification
- Контракт. Ответы обоих маршрутов валидируются по
investment-performance.openapi.yaml: required, nullable, enum, шаблонDecimal. Неизвестное значениеUnavailableReason, подставленное в ответ через прокси или мок, UI показывает общей недоступностью без падения. - Ошибки доступа. 401 без сессии; 503
TBANK_NOT_CONNECTEDбез подключения; 404 для счёта не из подключения.allбез подключения тоже 503. - Таймаут MOEX. Ответ ISS задерживается больше 10 с: результат приходит
с сохранённой историей или с
INDEX_HISTORY_UNAVAILABLE. Время ответа — не больше ~10 с плюс время расчёта, без бесконечного ожидания. - Параллельные чтения. Два одновременных GET на чистой БД дают одну серию запросов к ISS и одинаковые ответы.
- Частота догрузки. Повторное чтение в течение часа после сбоя не обращается к ISS (проверяется по логам).
- Порядок данных. Пополнение, исполненное после
fetchedAtPortfolio, в потоки не входит.
Risk-based Priorities
- Критично — деньги. Значения прибыли, XIRR и опережения совпадают с эталоном. Ни один недоступный случай не показывает число.
- Критично — доступ. Нет утечки чужих счетов в
allиmeta.unavailable. - Высокий риск — переводы в
all(эвристика парности) и счета с реальной историей пользователя. - Высокий риск — меньше года. Ни одного «годовых».
- Средний риск — устойчивость к MOEX, график и a11y.
Test Data and Environment Needs
-
Пользователь с подключением T-Bank и наборами счетов:
- A — пополнения, вывод, дивиденды;
- B — USD;
- C — перевод с B и C;
- D — 730 дн.;
- E — 200 дн.;
- F — нет решения;
- G —
historyBoundary = unknown; - H — перевод бумаг без пары;
- I — позиция без цены.
Если sandbox T-Bank такие данные не даёт, их записывают напрямую в одноразовую SQLite-копию.
-
Возможность блокировать
iss.moex.com(сеть или прокси) и задерживать его ответы. -
Чистая БД и БД с предзагруженной историей индексов.
-
Реальные данные Change Owner для финальной приёмки.
Manual / Exploratory Scope
- Реальные счета пользователя:
- правдоподобие результата;
- парность переводов между своими счетами;
- какие счета исключаются и почему.
- Вёрстка 390px и desktop; клавиатурная навигация (вкладка, hint, «Как считали», график); экранный диктор читает итоги графика.
- Переключение счёт ↔
allи обратно: нет устаревших данных другого счёта.
Automation Candidates
- Black-box API-проверки контракта и сценариев A–I: deferred until QA repo exists.
- E2E вкладки «Результат» (состояния ready, unavailable, incomplete, error): deferred until QA repo exists.
Observability Verification
integration.moex.index_history.succeeded/.failedпоявляются при догрузке и сбое; в них есть secid, диапазон, pages, durationMs и нет данных пользователя.- В логах расчёта нет сумм и идентификаторов операций.
Regression Impact
- Вкладки счёта: позиции, концентрация, деньги, операции, события, доход — переключение и загрузка не меняются.
- Запись истории оценки Portfolio после выноса признака полной оценки
(
portfolio-value-history): полные оценки по-прежнему создают точки, неполные — нет. - Существующие запросы к MOEX по облигациям.
openapi:checkобоих репозиториев.
Gaps / Blockers
- Подтверждение переводов отсутствует в текущем контракте T-Bank. Routing: System Analysis при появлении нового признака связи; до этого кандидаты исключаются с явной причиной, не выдавая сомнительный процент.
- Sandbox T-Bank может не воспроизвести все наборы. Routing: Test Data — наполнение одноразовой SQLite-копии; не блокирует.