mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-07-18 08:39:40 +03:00
feat(web-test): управление пулом контекстов/лицензий в раннере
Раннер регресса теперь держит пул 1С-сеансов сам, вместо накопления контекстов
между тестами и ручного закрытия в хуках. Три необязательных поля webtest.config.mjs
(без них поведение прежнее):
- maxContexts — потолок одновременно живых сеансов (null = без лимита);
- contextPolicy — 'reuse' (держать открытыми в пределах лимита) | 'strict'
(закрывать non-pinned контексты теста сразу после него);
- pinnedContexts — не вытесняются LRU (default = [defaultContext]; [] делает
default вытесняемым на тесном стенде).
Перед setup каждого теста LRU-вытеснение освобождает слот под нужды теста;
уже открытые нужные контексты переиспользуются. Default больше не вечно-pinned.
Исчерпание пула даёт внятную ошибку вместо маскирующего «Browser not connected».
- new: cli/test-runner/context-pool.mjs — чистый планировщик planEviction + LRU.
- cli/commands/test.mjs (v1.4): парсинг/валидация полей, вытеснение с фолбэк-парковкой
на нужный контекст (нельзя закрыть единственный активный), strict-закрытие, LRU-трекинг.
- Доки: regression-spec (§7/§8/глоссарий), regression-guide (рецепт), regress.md.
- Регресс дай-фудит фичу: 14-multi-context-routing роутит в 3-й контекст c,
15-multi-context-handover проверяет вытеснение c на границе; конфиг maxContexts:2.
Юнит-краёв планировщика — в debug/ (gitignored). Live-регресс: 25/25 зелёных.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -104,7 +104,15 @@ export default async function({ кладовщик, менеджер, step, asse
|
||||
}
|
||||
```
|
||||
|
||||
Учтите ограничение по лицензиям 1С: каждый одновременно открытый пользователь — это занятая клиентская лицензия. Если в наборе много многопользовательских тестов, а на стенде лицензий впритык, прогоны начнут спотыкаться на «свободных лицензий не осталось». Модель освобождает сессии между тестами автоматически (закрывает контексты после процессного теста), но если стенд ограничен — закладывайте это в планирование набора: один-два многопользовательских сценария вместо десяти.
|
||||
Учтите ограничение по лицензиям 1С: каждый одновременно открытый пользователь — это занятая клиентская лицензия. Если в наборе много многопользовательских тестов, а на стенде лицензий впритык, прогоны начнут спотыкаться на нехватке лицензий.
|
||||
|
||||
Движок умеет держать пул сам — три поля в `webtest.config.mjs` (подробно в спецификации, раздел «Пул контекстов и лицензии»):
|
||||
|
||||
- `maxContexts` — потолок одновременно живых сеансов. При его достижении движок автоматически вытесняет наименее используемые контексты (LRU), освобождая лицензии под нужды следующего теста. Уже открытый нужный контекст переиспользуется — соседние тесты под тем же пользователем не переоткрывают сеанс.
|
||||
- `contextPolicy` — `'reuse'` (по умолчанию, держать открытыми ради скорости) или `'strict'` (закрывать контексты сразу после теста ради экономии лицензий).
|
||||
- `pinnedContexts` — что не вытеснять. По умолчанию защищён контекст по умолчанию; на тесном стенде поставьте `pinnedContexts: []`, чтобы и он мог освобождать слот.
|
||||
|
||||
Рецепт для стенда на 2 лицензии с процессными тестами на двух пользователей: `maxContexts: 2`, `pinnedContexts: []`. Если пула не хватает даже после вытеснения, движок валит тест внятной ошибкой `context pool exhausted` с подсказкой — вручную закрывать контексты в хуках больше не нужно.
|
||||
|
||||
### Воспроизведение ошибки тестом
|
||||
|
||||
|
||||
@@ -469,6 +469,12 @@ export default {
|
||||
// (config.contexts.<name>.isolation). См. §8.
|
||||
isolation: 'tab', // 'tab' | 'window'
|
||||
|
||||
// Управление пулом 1С-лицензий (см. §8 «Пул контекстов и лицензии»). Все три поля
|
||||
// необязательны; без них поведение прежнее (лимита нет, контексты копятся, default живёт весь прогон).
|
||||
maxContexts: 2, // потолок одновременно живых 1С-сеансов; опустить = без лимита
|
||||
contextPolicy: 'reuse', // 'reuse' (держать открытыми в пределах лимита) | 'strict' (закрывать сразу после теста)
|
||||
pinnedContexts: ['admin'], // никогда не вытесняются LRU; по умолчанию = [defaultContext], [] делает default вытесняемым
|
||||
|
||||
// Allure severity policy (опционально). Маппинг наоборот: уровень → [теги].
|
||||
// Резолв см. §9 «Авто-эмиссия label-ов».
|
||||
severity: {
|
||||
@@ -499,6 +505,8 @@ export default {
|
||||
|
||||
При нарушении любого правила движок выводит сообщение с указанием конфликта и завершается с ненулевым кодом до запуска тестов.
|
||||
|
||||
Пул контекстов валидируется при загрузке: `maxContexts` — положительное целое или опущено; `contextPolicy` — `'reuse'` либо `'strict'`; каждый элемент `pinnedContexts` должен присутствовать в `contexts`. Нарушение — явная ошибка до запуска.
|
||||
|
||||
Кириллица в ID контекстов работает, но смешанный регистр снижает читаемость кода (`testInfo.contexts.кладовщик.displayName` рядом с `testInfo.contexts.clerk.displayName`). Рекомендуем разделять технический ID и человекочитаемое имя.
|
||||
|
||||
Флаги CLI всегда переопределяют значения из файла конфигурации.
|
||||
@@ -537,11 +545,13 @@ export default async function({ clickElement, fillFields, … }) { }
|
||||
|
||||
Движок НЕ группирует тесты по контексту. Порядок выполнения — алфавитный по полному относительному пути файла (плюс порядок экспорта внутри файла). Для каждого теста:
|
||||
|
||||
1. Через `ensureContext(name)` создаются BrowserContext-ы, упомянутые в `t.context` / `t.contexts` (если ещё не созданы).
|
||||
2. `setActiveContext(primaryContext)` — активный контекст = первый объявленный (для single — `t.context || defaultContext`, для multi — `t.contexts[0]`).
|
||||
3. После теста встроенный сброс пробегает по всем использованным контекстам.
|
||||
1. **(при заданном `maxContexts`)** Освобождение пула: если `union(открытые, нужные)` превышает лимит, движок вытесняет LRU-старейшие контексты, не входящие в `pinned` и не нужные этому тесту (см. §8 «Пул контекстов и лицензии»).
|
||||
2. Через `ensureContext(name)` создаются BrowserContext-ы, упомянутые в `t.context` / `t.contexts` (уже открытые переиспользуются — повторного входа в 1С нет).
|
||||
3. `setActiveContext(primaryContext)` — активный контекст = первый объявленный (для single — `t.context || defaultContext`, для multi — `t.contexts[0]`).
|
||||
4. После теста встроенный сброс пробегает по всем использованным контекстам.
|
||||
5. **(при `contextPolicy: 'strict'`)** Non-pinned контексты теста закрываются сразу после него.
|
||||
|
||||
Контексты живут между тестами: переключение через `setActiveContext` — дешёвое, повторный вход в 1С не требуется. Закрываются явно (`closeContext`) или финальной очисткой движка перед закрытием браузера.
|
||||
Контексты живут между тестами (при `contextPolicy: 'reuse'` — по умолчанию): переключение через `setActiveContext` — дешёвое, повторный вход в 1С не требуется. Закрываются явно (`closeContext`), LRU-вытеснением под лимит, `strict`-политикой либо финальной очисткой движка перед закрытием браузера.
|
||||
|
||||
### Мульти-контекст (процессные тесты)
|
||||
|
||||
@@ -600,6 +610,24 @@ await step('Кладовщик проверяет статус', async () => {
|
||||
|
||||
`closeContext(name)` нельзя вызвать на активном контексте — будет исключение. В scoped API это естественно: вызывать `manager.closeContext('clerk')` (scoped-обёртка сначала переключает активный на `manager`, потом закрывает `clerk`). Если контекст лишний (роль больше не нужна в рамках теста / прогона) — закрывайте его сразу: освобождает лицензию платформы и снимает нагрузку со следующих тестов.
|
||||
|
||||
### Пул контекстов и лицензии
|
||||
|
||||
Каждый живой контекст — отдельный 1С-сеанс, то есть отдельная лицензия (в режиме `'tab'` каждая вкладка = свой `seanceId`). На стендах с малым пулом лицензий бесконтрольное накопление контекстов между тестами рано или поздно пробивает лимит, и тест падает в setup. Три поля конфигурации переносят управление пулом в движок:
|
||||
|
||||
| Поле | Значение | По умолчанию |
|
||||
|------|----------|--------------|
|
||||
| `maxContexts` | Потолок одновременно живых 1С-сеансов. `null`/опущено — без лимита, вытеснения нет (прежнее поведение). | `null` |
|
||||
| `contextPolicy` | `'reuse'` — держать контексты открытыми в пределах лимита ради скорости (соседние тесты под тем же контекстом не переоткрывают сеанс). `'strict'` — закрывать non-pinned контексты теста сразу после него ради экономии лицензий. | `'reuse'` |
|
||||
| `pinnedContexts` | Контексты, которые НЕ вытесняются LRU. По умолчанию `[defaultContext]` — сохраняет прежнее «default живёт весь прогон». `[]` делает default обычным вытесняемым контекстом. | `[defaultContext]` |
|
||||
|
||||
**Вытеснение (LRU).** При заданном `maxContexts` перед setup каждого теста движок проверяет `union(открытые, нужные_тесту)`. Если он превышает лимит — закрывает контексты в порядке давности использования (старейшие первыми), пропуская `pinned` и нужные текущему тесту, пока не влезет в лимит. Уже открытый нужный контекст переиспользуется. Вытесненный при следующей надобности переоткрывается штатным `ensureContext`.
|
||||
|
||||
**default вытесняем.** `defaultContext` (на нём исполняется `beforeAll` и хуки) больше не «вечный» — под лимитом он такой же кандидат на вытеснение, если не входит в `pinnedContexts`. Это осознанно: обычно default не держит состояния, нужного между тестами. Если ваши хуки требуют постоянного admin-сеанса — оставьте его в `pinnedContexts`.
|
||||
|
||||
**Исчерпание пула.** Если даже после вытеснения всего допустимого `pinned ∪ нужные` не влезает в `maxContexts`, движок валит тест с внятной ошибкой `context pool exhausted: …` (а не маскирующим «Browser not connected») и подсказкой — поднять `maxContexts`, сократить объявленные контексты или уменьшить `pinnedContexts`.
|
||||
|
||||
**Рецепт тесного пула.** Стенд на 2 лицензии, процессные тесты на 2 контекста: `maxContexts: 2`, `pinnedContexts: []` (чтобы default вытеснялся и освобождал слот). `contextPolicy: 'reuse'` даёт переиспользование соседних тестов; `'strict'` — максимально быстрое освобождение лицензий на шаренном стенде.
|
||||
|
||||
---
|
||||
|
||||
## 9. Отчёты
|
||||
@@ -982,7 +1010,8 @@ JSON-отчёт (`tests[]`, полная структура — §9) для ка
|
||||
| **Context (BrowserContext)** | Изолированная сессия Playwright. Куки/состояние/страница независимы. В рамках одного теста используется один или несколько контекстов. |
|
||||
| **Active context** | Контекст, на котором сейчас оперируют функции browser-API. Переключается `setActiveContext`. |
|
||||
| **Primary context** | Контекст, активный на входе в тест. Декларация (`mod.context` или `mod.contexts[0]`). Зафиксирован в `testInfo.primaryContext`. |
|
||||
| **Default context** | Контекст из `config.defaultContext` (или единственный URL в упрощённой конфигурации). Используется, если тест не указал `context` / `contexts`. |
|
||||
| **Default context** | Контекст из `config.defaultContext` (или единственный URL в упрощённой конфигурации). Используется, если тест не указал `context` / `contexts`. Под лимитом `maxContexts` — вытесняем, если не входит в `pinnedContexts` (см. §8 «Пул контекстов и лицензии»). |
|
||||
| **Pinned context** | Контекст из `config.pinnedContexts`, не подлежащий LRU-вытеснению. По умолчанию = `[defaultContext]`. |
|
||||
| **Scoped API** | Объект на `ctx.<name>` в мульти-контекстных тестах — обёртки browser-функций, авто-переключающие контекст перед каждым вызовом. |
|
||||
| **Action function (ACTION_FN)** | Browser-функция, обёрнутая авто-обнаружением 1С-ошибок. Список — в §3. |
|
||||
| **Step** | Логический блок внутри теста, обёрнутый `step(name, fn)`. Маппится на Allure-step, попадает в `report.tests[].steps[]`. |
|
||||
|
||||
Reference in New Issue
Block a user