## MODIFIED Requirements ### Requirement: Неизвестный путь вне корней открывает приложение Сервис SHALL отдавать разметку приложения на всяком пути, который не принадлежит ни одному корню сервиса и не совпадает с отдельным адресом наблюдения. Корни перечислены поимённо — `/api` у хранилища, `/app` у приложения, `/_` у панели, — отдельными адресами стоят `/health` и `/metrics`. Корня `/auth` в перечне больше нет: собственного входа у сервиса не осталось, и адресов под этим корнем не существует. Прежние адреса входа поэтому отвечают тем же, чем отвечает всякий путь вне корней, — разметкой приложения. Резервировать имя за отказом сервис не берётся: имя, за которым ничего не стоит, ничем не отличается от любого другого свободного имени, а второй перечень «когда-то занятых корней» разошёлся бы с первым молча. Путь принадлежит корню, когда **совпадает с ним точно либо начинается им вместе с косой чертой**. Оба условия обязательны: по одному лишь префиксу корню `/app` достался бы посторонний `/apple`, а по одному лишь префиксу с косой чертой голый `/api` не достался бы никому и уехал бы разметкой. Путь, принадлежащий корню, MUST не проваливаться в приложение никогда: отказ контракта остаётся отказом контракта и уходит той формой, которой этот корень отвечает и сегодня. Иначе программа, ошибшаяся адресом под корнем приложения, получила бы разметку с кодом `200` вместо отказа с машиночитаемым кодом — и приняла бы её за ответ. Путь **под каталогом ресурсов** — тем, который наполняет сборщик, — разметкой не подменяется: не совпавший с файлом, он MUST отвечать `404`. Иначе разметка прежней сборки, назвавшая ресурс, которого в новой сборке уже нет, получает на него `200` и разметку вместо ресурса: браузер отвергнет её по типу содержимого, человек увидит пустой экран, а в кодах ответов сервиса не останется ничего. Открывающими страницу считаются `GET` и `HEAD`, и только они; прочие методы MUST отвечать `405`. #### Scenario: Обновление страницы посреди приложения открывает тот же экран - **GIVEN** приложение открыто на своём маршруте - **WHEN** браузер спрашивает этот путь заново - **THEN** ответ имеет код `200` - **AND** тело ответа — разметка приложения #### Scenario: Голый корень разметкой не подменяется - **WHEN** запрос приходит на путь, совпадающий с корнем сервиса точно и без косой черты - **THEN** тело ответа — не разметка приложения #### Scenario: Посторонний путь, начинающийся именем корня, открывает приложение - **WHEN** запрос приходит на путь, который начинается именем корня, но не отделён от него косой чертой - **THEN** ответ имеет код `200` - **AND** тело ответа — разметка приложения #### Scenario: Прежний адрес входа открывает приложение - **WHEN** запрос приходит на путь под прежним корнем входа - **THEN** ответ имеет код `200` - **AND** тело ответа — разметка приложения #### Scenario: Неизвестный путь под корнем приложения отвечает отказом - **GIVEN** запрос идёт с заголовком, поставленным прокси - **WHEN** он спрашивает неизвестный путь под корнем приложения - **THEN** ответ имеет код `404` - **AND** тело ответа — отказ приложения с машиночитаемым кодом, а не разметка #### Scenario: Неизвестный путь под корнем приложения неузнанному отвечает как все прочие его адреса - **WHEN** запрос приходит на неизвестный путь под корнем приложения без заголовка - **THEN** ответ имеет код `401` - **AND** тело ответа — не разметка приложения #### Scenario: Неизвестный путь под корнем хранилища отвечает отказом - **WHEN** запрос приходит на неизвестный путь под корнем хранилища - **THEN** тело ответа — не разметка приложения #### Scenario: Несуществующий ресурс отвечает отсутствием, а не разметкой - **WHEN** браузер спрашивает под каталогом ресурсов файл, которого в сборке нет - **THEN** ответ имеет код `404` - **AND** тело ответа — не разметка приложения ### Requirement: Открытое приложение показывает вошедшего Приложение SHALL спрашивать сервис, кто пришёл, и показывать его имя. Отказ `401` MUST показываться строкой о том, что сервис его не узнал, и MUST никуда не уводить: своего входа у сервиса нет, а вести человека некуда — заголовок ставит обратный прокси, и человек, которого прокси не назвал, до приложения дошёл бы только мимо него. Всякий **иной** отказ и сорванный запрос MUST показываться строкой о неудаче. Разделять их приложение обязано: «сервис вас не узнал» и «сервис не отвечает» — разные состояния, и человек по ним делает разное. Прежде отказ `401` уводил ко входу; уводить стало некуда, и различие сохраняется ради текста, а не ради перехода. Имя пришедшего берётся ответом сервиса, а не заголовком запроса: заголовок ставит прокси, и приложение его не видит вовсе. Имени в ответе может не быть — тогда приложение MUST показать, что человек узнан, и MUST не подставлять вместо имени адрес почты: его в ответе нет по норме `access`. #### Scenario: Узнанный виден - **GIVEN** запрос приложения идёт с заголовком, поставленным прокси - **WHEN** он открывает приложение - **THEN** приложение показывает его имя #### Scenario: Неузнанному показывают, что его не узнали - **GIVEN** сервис отвечает на вопрос о пришедшем кодом `401` - **WHEN** человек открывает приложение - **THEN** приложение показывает строку о том, что его не узнали - **AND** никуда его не уводит #### Scenario: Отказ сервиса от неузнавания отличается - **GIVEN** сервис отвечает на вопрос о пришедшем отказом, который не является неузнаванием - **WHEN** человек открывает приложение - **THEN** приложение показывает строку о неудаче - **AND** эта строка не та, которой оно сообщает о неузнавании