# QA Analysis Ревизия спеков: G2 approved (comment 3082). Источник ожидаемого поведения — только Delta Specs `portfolio-concentration` (28 ADDED, ниже PC) и `portfolio-view` (2 MODIFIED + 2 ADDED, ниже PVΔ) этого change. Design пишется параллельно; на его внутреннее устройство не опираемся. Case пишется только там, где нужны данные, шаги или oracle сверх Scenario; остальные строки Coverage Matrix ссылаются на Scenarios. Написанные cases — в `analysis/test-cases/{conc,meta,reg}.md`. ## Risk Assessment | Риск | Затронутые требования | Вероятность | Ущерб | Глубина проверки | | --- | --- | --- | --- | --- | | Неверная классификация исхода получения (NOT_FOUND vs иная ошибка) ломает 24h-отсчёт и last-known | «Результат получения сектора и эмитента», F02–F05 | Высокая (клиент AS-IS отображает NOT_FOUND как Unavailable — failure-model:75) | Средний: группы «не указан» вместо значений | Интеграционные проверки всех 4 строк таблицы исходов + exploratory Q-F3 | | Доли/суммы считаются от неверной базы (Cash Balance, нули, другие валюты, отрицательные) | PC:129, PC:144, PC:289 | Средняя | Высокий: недоверие владельца к цифрам | Компонентные проверки с числовыми оракулами из сценариев | | Потеря/порча финансового Snapshot из-за обогащения (token, публикация, границы подключения) | «Свежесть и fallback полного Portfolio Snapshot», F08–F11 | Низкая | Критический: потеря данных портфеля | Интеграционные: token-отказ, гонка публикации, смена подключения; регрессия обхода | | Ошибка округления/формата долей (`,0`, `< 0,1 %`, половина вверх) | PC:304, PC:324 | Средняя | Низкий | Компонентные с точными строками-эталонами | | Ухудшение существующей вкладки «Позиций» / структуры портфеля | PC:9, регрессия | Средняя | Высокий | Регрессионный прогон (TC-REG) | | Доступность диаграммы только цветом / недоступна скринридеру | PC:443 | Средняя | Средний | Ручная проверка + текстовый эквивалент в component | | Квоты T-Bank при суточном обходе (до `U + A` вызовов) | F07, Q-F3 | Неизвестная | Средний: деградация синхронизации | Exploratory Q-F3 на тестовом токене; norm от квот не зависит | ## Coverage Matrix Уровни: unit / integration (backend, configured HTTP tests) / component (frontend) / e2e / manual. Ссылки на сценарии — `PC:<строка>` / `PVΔ:<строка>` файлов спек. | Requirement | Сценарии / ветви | Проверка / ожидаемое наблюдение | Уровень | Test cases | | --- | --- | --- | --- | --- | | PC:9 Вкладка «Концентрация» страницы счёта | 2 сценария (есть вкладка; «Все счета» — нет) + инвариант «прочие вкладки не меняются» | Вкладка рядом с «Позициями»; в «Всех счетах» отсутствует; дефолтная вкладка прежняя | e2e | Scenario «Вкладка на странице одного счёта», «Режим “Все счета”»; TC-REG-001 | | PC:20 Вкладка в состояниях страницы | загрузка/ошибка/пусто/только Cash Balance | Вкладки нет при загрузке, ошибке и пустом Portfolio; есть при любом Cash Balance | component + e2e | TC-CONC-002 + Scenario «Загрузка и ошибка» | | PC:37 Переключение разрезов | 4 разреза, один одновременно, порядок | Показан ровно один разрез, переключатели в заданном порядке | component | Scenario «Открытие вкладки», «Переключение разреза» | | PC:49 Начальное состояние вкладки | новый заход/reload → «Акции»+первая валюта; вкладки страницы → сохранение | Reset при новом заходе и смене счёта, persist при переключении вкладок | e2e | TC-CONC-005, TC-CONC-006 | | PC:69 Состав разрезов | 3 сценария (сектор объединяет; эмитент stock+bond без фонда; фонд вне акций/облигаций) | Агрегация и исключение типов по таблице | component | TC-CONC-007 | | PC:96 Названия элементов | без name → ticker; `issuer.name`; разные `issuer.id` не сливаются | Название по цепочке name→ticker→FIGI; раздельные элементы при равных именах | component | TC-CONC-008 | | PC:114 Название эмитента при разных названиях бренда | наибольшее `currentValue`; равные → меньший по алфавиту | Элемент назван по большей позиции; при равенстве — «Сбер» < «Сбербанк» | component | Scenario «Название по наибольшей позиции», «Равные наибольшие значения» | | PC:129 Позиции, учитываемые в разрезе | положительные; 0 — не учитывается и не неучтённая; null/отрицательные исключены | Сумма разреза только из положительных позиций выбранной валюты | component | Scenario «Отрицательная и неоценённая позиции», «Позиция с нулевой стоимостью» | | PC:144 Сноска о не учтённых позициях | N: null в любой валюте + отрицательные в выбранной; 0 → нет сноски | Точный N по правилу; сценарии 149–167 покрывают все ветви | component | Scenario «Сноска с числом позиций», «Неоценённая позиция при смене валюты», «Отрицательная позиция другой валюты», «Нет неучтённых позиций» | | PC:169 Одна валюта за раз | смена валюты пересчитывает; сохраняется между разрезами; нет конвертации | Значения только в выбранной валюте, без смешанного итога | component | Scenario «Смена валюты», «Валюта сохраняется при смене разреза» | | PC:184 Доступные валюты вкладки | STOCK/BOND/FUND ненулевые, включая отрицательные; 0 исключён; Select скрыт при ≤1; сортировка ISO | Список и сортировка точны; скрытие Select; пустое состояние без выбора | component | Scenario «Начальная валюта», «Единственная валюта», «Валюта только у позиции с нулевой стоимостью», «Валюта только у отрицательной позиции», «Нет оценённых позиций» | | PC:214 Независимый выбор валюты вкладки | независимость от «Структуры портфеля»; пропавшая валюта → первая списка | Валюта вкладки не меняется от внешнего Select; fallback при исчезновении | component + e2e | TC-CONC-015 | | PC:229 Топ-5 и строка «Прочее» | ≤6 строк без «Прочее» (5, 6, <5); ≥7 → топ-5 + «Прочее» с числом элементов | Границы 5/6/7 и формулировка числа элементов («2 элемента», «5 элементов») | component | TC-CONC-016 | | PC:254 Раскрытие строки «Прочее» | раскрытие/сворачивание (мышь и клавиатура); не меняет топ-5 и диаграмму; свёрнуто при показе/смене валюты | Список остальных в порядке ранжирования; инвариант неизменности топ-5 | component + e2e | Scenario «Раскрытие “Прочее”», «Повторное нажатие», «Смена валюты сворачивает “Прочее”»; клавиатура — TC-CONC-027 | | PC:274 Порядок элементов с равной суммой | цифры < латиница < кириллица, без регистра; граница 5-й строки | Порядок «VK», «X5», «Сбер»; «Альфа» в топ-5 при равенстве | component | Scenario «Равные суммы на границе пятой строки», «Цифры, латиница и кириллица при равных суммах» | | PC:289 Расчёт долей | точные доли суммируются в 100 %; доля «Прочее» = сумма долей элементов; Cash Balance вне | Числовые оракулы сценариев 294–302 | component | Scenario «Доля без денежных остатков», «Доля “Прочее”»; числовой прогон — TC-CONC-007 | | PC:304 Показ долей | округление до десятых, половина вверх, без «,0», «< 0,1 %», сумма ≠ 100 % допустима | Строки-эталоны «93,8 %», «6,3 %», «< 0,1 %», 100,1 % в сумме | component | TC-CONC-020 (все три ветви: середина вверх, «< 0,1 %», без «,0», сумма ≠ 100 %) | | PC:324 Суммарная доля топ-5 | показ при ≥6, скрыта при ≤5 (7/6/5/1 элементы) | «Топ-5 · N %» только при ≥6; значения 95 %, 96 % | component | Ветви покрыты TC-CONC-016 (наборы 1–4) | | PC:349 Группы «Сектор/Эмитент не указан» | группа ранжируется наравне, попадает в «Прочее», имя видно | Сценарии 354–362 | component | Scenario «Позиции без эмитента в топе», «Группа без сектора в “Прочее”» | | PC:364 Подписи секторов | известные — русская подпись, неизвестные — как получено; нет своей классификации | Неизвестный код показан буквально | component | Scenario «Неизвестное значение сектора» | | PC:374 Русские подписи известных секторов | 11 значений таблицы, включая `government` | Каждое значение → своя подпись (таблица — эталон) | component | TC-CONC-024 | | PC:402 Диаграмма распределения разреза | 6 сегментов при ≤6; 5+«Прочее» при ≥7; одинаково при раскрытии; нет при пустом разрезе | Число сегментов и доли совпадают со списком | component + manual | Scenario «Семь элементов», «Ровно шесть элементов» (обе диаграммные нормы); визуальный блок — TC-CONC-027 | | PC:415 Центр диаграммы | сумма точная; «Топ-5 · N %» при метрике; текстовый эквивалент | Центр: «95 %» + «1 000 ₽» / только сумма при ≤5 | component + manual | Scenario «Семь элементов», «Ровно шесть элементов» (обе диаграммные нормы); визуальный блок — TC-CONC-027 | | PC:430 Строки без переходов | клик по строке — нет навигации; эмитенты без логотипов | URL не меняется; нет `` логотипов | e2e | Scenario «Нажатие на строку акции», «Строка эмитента» | | PC:443 Доступность диаграммы без цвета | текстовый эквивалент, сопоставимость сегментов без цвета | Скринридер читает название/сумму/долю каждого сегмента | manual + component | TC-CONC-027 | | PC:451 Пустое состояние разреза | пустой разрез: заголовок + валюта, без списка/диаграммы; сноска и выбор сохраняются | Сценарий «Валюта только у отрицательной позиции» | component | Scenario «Валюта только у отрицательной позиции» (пустое состояние) | | PC:464 Предупреждение о неполных данных | неполнота `meta` → одно «Данные неполные» на вкладке, без дублирования на странице; неоценённые позиции не вызывают | Одно сообщение; доли по доступным данным | component + e2e | Scenario «Неполный Portfolio», «Предупреждение не дублируется» | | PC:484 Состояния Portfolio на вкладке | соответствие «Состояниям и ошибкам Portfolio на клиенте» | isStale/загрузка/ошибка отображаются как на «Позициях» | component | Scenario «Состояния Portfolio на вкладке “Концентрация”»; сверка с canonical — существующий regression-прогон frontend | | «Нормализованный состав Portfolio» (MODIFIED) | sector/issuer nullable; обрезка пробелов; только пробелы = отсутствие; sector только у STOCK/BOND/FUND; issuer.id+name; запрет эмитента по названию | Поля по правилам; сценарии 30–60 delta; GET-контракт не сломан | integration (configured HTTP tests) | PVΔ Scenario «Сектор инструмента доступен/отсутствует», «Эмитент позиции доступен/отсутствует», «Эмитент не определяется по названию», «Недоступность сектора и эмитента не нарушает Snapshot»; обратная совместимость GET — существующий contract-прогон | | «Свежесть и fallback полного Portfolio Snapshot» (MODIFIED) | исключение: token-отказ при sector/brand = 400 INVALID_TBANK_TOKEN; сектор/бренд — last-known, обновление по «Обновление сектора и эмитента»; GET не ждёт T-Bank | Fallback не маскирует token-отказ; хранимые sector/brand читаются локально | integration | TC-META-002; существующий backend contract-прогон | | «Обновление сектора и эмитента» (ADDED) | первый обход ≥24 ч после успеха; интервал из hourly-investment-sync; «не найдено» тоже успех | Сценарии 149–152: значение обновляется в первый обход после 24 ч | integration | TC-META-003 | | «Результат получения сектора и эмитента» (ADDED) | 4 строки таблицы: значение / нет значения / другой инструмент / иная ошибка; каждая — поле + отсчёт 24 ч | Каждая строка: точное поле и отсчёт (сценарии 170–202, включая перезапуск отсчёта от «не найдено») | integration | TC-META-004 | ## Negative and Boundary Focus - Границы элементов: 1 / 5 / 6 / 7 (строки и метрика топ-5) — TC-CONC-016. - Границы стоимости: 0 (исключена и не в N), отрицательная (в N только своей валюты), `null` (в N в любой валюте), крайне малая доля «< 0,1 %» — Scenario «Отрицательная и неоценённая»/«Нулевая стоимость»/«Сноска о не учтённых позициях», TC-CONC-020. - Округление: точная половина вверх (6,25 → «6,3»), сумма показанных ≠ 100 % — TC-CONC-020. - Валютные границы: единственная валюта (Select скрыт), валюта только отрицательной позиции (есть в выборе, разрез пуст), валюта пропала после обновления — Scenario «Доступные валюты вкладки», TC-CONC-015. - Тайбрейки: равные суммы (цифры/латиница/кириллица), граница 5-й строки, равные `currentValue` у одного `issuer.id`, одинаковые `issuer.name` при разных `id` — Scenario «Порядок элементов с равной суммой»/«Название эмитента при разных названиях», TC-CONC-008. - 24h-отсчёт: успех → сброс; «не найдено» → сброс; иной инструмент / иная ошибка → без сброса, повтор каждый обход; систематический сбой дольше 24 ч — TC-META-003/004. - Запрещённые эффекты: GET не запускает T-Bank, sector/brand не освежают Snapshot, enrichment не отменяет финансовый успех, «Прочее»-раскрытие не меняет топ-5 и диаграмму, строка не ведёт на другую страницу — TC-META-002, Scenario «Раскрытие “Прочее”», «Нажатие на строку акции». ## Cross-system Verification T-Bank → sync → хранилище → GET → frontend. Каждый F-сценарий failure-model: | ID | Проверка | Test cases | | --- | --- | --- | | F01 | Финансовый отказ не замещается успехом sector/brand; прежний Snapshot | TC-META-002, TC-REG-004 | | F02 | Иная ошибка: прежнее значение/`null`, отсчёт не меняется | TC-META-004 | | F03 | Сбой любого звена цепочки инструмента → бренда — как F02; поля независимы | TC-META-004 | | F04 | «Не найдено»/пусто/пробелы → `null` + сброс отсчёта | TC-META-004 + PVΔ Scenario «Ответ без значения заменяет известное значение» | | F05 | Чужой UID/asset_uid → прежнее значение, повтор | TC-META-004 | | F06 | Частичное обогащение: Snapshot публикуется целиком, группы наравне | PVΔ Scenario «Недоступность сектора и эмитента не нарушает Snapshot», Scenario «Позиции без эмитента в топе» | | F07 | RESOURCE_EXHAUSTED → как F02 после повторов клиента; (квоты — Q-F3) | TC-META-004 | | F08 | Token-отказ при sector/brand → 400 INVALID_TBANK_TOKEN, без fallback | TC-META-002, TC-REG-004 | | F09 | Отказ публикации: нет ложного `lastSuccessfulAt` | TC-META-002 | | F10 | Отказ сохранения sector/brand: поле и отсчёт прежние, финансовый успех цел | TC-META-003 | | F11 | Смена/удаление подключения: 404/409/400, sector/brand не переходят в новое | TC-META-005, TC-REG-004 | | F12 | Ошибка HTTP-чтения на клиенте: вкладка скрыта, данные не теряются | TC-CONC-002 | | F13 | Старый JSON без `sector`/`issuer` → `null`, группы «не указан» | PVΔ Scenario «Недоступность сектора и эмитента не нарушает Snapshot», Scenario «Позиции без эмитента в топе» | | F14 | Время sector/brand не освежает Snapshot; неполное `meta` — одно сообщение | TC-META-002; Scenario «Неполный Portfolio» | | F15 | Пустой разрез: нет NaN и фиктивных 100 % | Scenario «Валюта только у отрицательной позиции» | | F16 | Сбой дольше 24 ч: значение остаётся, повтор каждый обход | TC-META-003 | Инварианты data-mapping: сумма разреза = сумма учитываемых позиций; доли в сумме ровно 100 % (точные); отсутствие подстановки нуля вместо `null` — точные доли Scenario «Расчёт долей» и прогоны TC-CONC-007/016. ## Test Data and Environment Needs - **Портфель (мультивалютный, ≥10 позиций):** акции ≥7 (для «Прочее»), ровно 6 и 5 вариантов; облигации; ≥2 фонда; позиция `CURRENCY`; `FUTURE`/`OTHER` для исключения. Валюты: RUB + USD (два Select-варианта), одиночный RUB-вариант отдельным набором. - **Стоимости:** положительные, 0, отрицательная, `null` (в т.ч. в невыполненной валюте); 1 RUB против 100 000 000 RUB («< 0,1 %»); пары с равными суммами (граница топ-5). - **Бренды:** один `issuer.id` с разными `issuer.name` и разными суммами; равные суммы; два `issuer.id` с одинаковым `issuer.name`; акции + облигации одного бренда; позиции без бренда (группа «Эмитент не указан»). - **Секторы:** каждый из 11 кодов таблицы PC:374 (минимум `it`, `government`, `energy`), неизвестный код (`other`), пустой/пробельный, отсутствующий; один сектор у разных типов (STOCK+BOND+FUND); ≥6 секторов для «Прочее» в «Секторах». - **Upstream-моки (integration):** ответы T-Bank для всех 4 исходов таблицы «Результат получения сектора и эмитента» (значение / «не найдено» NOT_FOUND и пустой объект / чужой UID / UNAVAILABLE-timeout / PERMISSION_DENIED-token), частичный ответ, отказ публикации; `meta` неполноты. - Окружение: локальное backend + frontend с тестовым T-Bank-подключением; для Q-D1/Q-D2/Q-F3 — read-only доступ к реальному токену владельца. ## Manual / Exploratory Scope - Соответствие макетам ux-design v2 (`visuals/`): кольцевая диаграмма, центр, бары строк, «Прочее», предупреждение — по всем четырём разрезам. - Контраст тёмной/светлой темы: сегменты, подписи, «< 0,1 %», сноска. - Клавиатура: Tab по переключателям разрезов, строкам и «Прочее»; Enter/Space раскрытие (PC:254), видимый focus, отсутствие «клик-ловушек». - Скринридер (VoiceOver/NVDA): текстовый эквивалент диаграммы — название, сумма, доля каждого сегмента в порядке списка; центр; сопоставление сегмент↔строка без цвета (PC:443). - Русская локаль чисел: неразрывный пробел, `₽`/`$`, запятая в долях. - Exploratory: поведение при переключении счёта, глубоких ссылок на вкладку (если есть), медленной сети при раскрытии «Прочее». ## Automation Candidates - Компонентный вычислитель разреза (состав, ранжирование, тайбрейки, доли, границы 5/6/7, формат долей) — чистая функция от Portfolio: детерминированные оракулы из сценариев, максимум ценности на единицу поддержки. - Интеграционные проверки таблицы исходов «Результат получения сектора и эмитента» и 24h-логики (управляемое время) — регрессионно-критичны, зависят от классификации ошибок клиента. - e2e smoke: вкладка есть/нет, reset/persist состояния, отсутствие навигации по строкам, одно «Данные неполные». ## Regression Impact | Область | Риск | Проверка | | --- | --- | --- | | Вкладка «Позиции» существующего счёта | Вкладки/состояния страницы изменены | TC-REG-001: прежний дефолт, состав и порядок вкладок | | PortfolioStructure («Структура портфеля») | Общий выбор валюты «протекает» на вкладку | TC-CONC-015; существующий regression-прогон PortfolioStructure | | GET-контракт Portfolio | Новые поля sector/issuer ломают клиентов; старый JSON | Существующий backend contract-прогон (OpenAPI); обратная совместимость ответа | | Часовой обход hourly-investment-sync | Новые вызовы сектора/бренда меняют нагрузку и успех обхода | TC-REG-004: успех/причины обхода без sector/brand-сбоев; F07/F11 | | Инвалидация token | Новое ветвление token-отказа | TC-META-002/005: 400 INVALID_TBANK_TOKEN во всех точках, замена/удаление подключения — 404/409 | ## Entry / Exit Criteria Вход: G2 approved (есть); design готов или проверка не зависит от него (component- вычислитель и integration по спекам можно начинать); тестовые данные раздела выше доступны. Выход: все строки Coverage Matrix зелёные либо явные `not verified` ниже; все F01–F16 покрыты; регрессия TC-REG-001/004 и существующие contract/regression-прогоны пройдены; ручной блок (макеты, контраст, клавиатура, скринридер) зафиксирован; findings по Q-D1/Q-D2/Q-F3 записаны. ## Gaps / Blockers - not verified: соответствие квотам T-Bank при суточном обходе — риск деградации синхронизации; закрывается Q-F3 (измерение на реальном токене), норма исходов от результата не зависит. - not verified (до реализации): фактический вид «не найдено» у `EtfBy`/`BondBy`/`GetAssetBy` (Q-F3) — до подтверждения классификация NOT_FOUND в проверках задаётся моком по proto; расхождение с реальностью выявит Q-F3-проверка. - Открытый data-fact: Q-D1 (покрытие brand-пары) и Q-D2 (реальные `sector` фондов и госбумаг) — одноразовые read-only проверки на реальном Portfolio владельца; влияют на ожидаемый вид разрезов, не на нормы. Результат фиксируется в QA-отчёте change. - Пробелов в Specs, блокирующих проверку, нет (после G2 approved).