Metadata-Version: 2.4
Name: doqa-client
Version: 0.1.0
Summary: DoQA client core: config resolution, Direct Autotest API client and Allure-compatible file sink
Author-email: DoQA team <support@doqa.app>
License: Apache-2.0
Project-URL: Homepage, https://doqa.app
Project-URL: Repository, https://github.com/doqa-app/doqa-python
Project-URL: Issues, https://github.com/doqa-app/doqa-python/issues
Keywords: doqa,testing,reporting,autotests
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Testing
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: license-file

# doqa-client - клиентское ядро DoQA для Python

`doqa-client` - общее ядро, на котором работают адаптеры тестовых фреймворков DoQA (сейчас -
[`doqa-pytest`](../doqa-pytest/README.md)). Дистрибуция владеет top-level пакетом `doqa`
целиком: пользовательский фасад (`import doqa`), фреймворк-агностичное ядро репортинга
(`doqa.core`) и клиент DoQA Autotest API с файловым синком (`doqa.client`).

**Ноль рантайм-зависимостей**: HTTP через `http.client`, JSON через `json` - ничего постороннего
в окружение хоста не протекает, библиотеку безопасно ставить в любой тестовый проект.

## Требования

- Python 3.9 или новее.

## Установка

```bash
pip install doqa-client
```

Большинству проектов этот пакет напрямую не нужен - ставьте адаптер своего фреймворка
(`pip install doqa-pytest`), он принесёт ядро с собой и той же версией.

## Фасад `doqa`

Всё, чем размечают тесты, живёт здесь и одинаково во всех адаптерах DoQA - смена фреймворка
не потребует править импорты:

- декораторы разметки: `doqa.id`, `doqa.title`, `doqa.description`, `doqa.display_name`,
  `doqa.label`, `doqa.tag`, `doqa.link` (+ шорткаты `doqa.link.defect` и другие),
  `doqa.case_ids`, `doqa.namespace`, `doqa.class_name`, `doqa.create_manual_case`;
- шаги: `doqa.step` - контекст-менеджер и декоратор, с `{param}`-плейсхолдерами;
- runtime-вызовы: `doqa.add_*`, `doqa.attach`, `doqa.attach_file`,
  `doqa.capture_context` / `doqa.run_with` для своих потоков.

Вне активного теста всё это - безопасный no-op. Примеры и подробности - в
[README адаптера](../doqa-pytest/README.md).

## Конфигурация

Настройки собираются из трёх слоёв; приоритет: **CLI-слой адаптера (для pytest - опции
`--doqa-*` и ini-ключи) > переменные окружения (`DOQA_*`) > файл `doqa.properties`**. Файл
ищется в рабочей директории либо по пути из `DOQA_CONFIG` / CLI-ключа `doqa.config`. Ядро про
конкретный фреймворк не знает: CLI-слой - инъектируемый словарь ключей `doqa.<поле>`, который
адаптер наполняет из своих опций.

Канонические поля - `url`, `token`, `spaceId`, `configurationId`, `testRunId`, `testRunName`,
`adapterMode`, `importRealtime`, `certValidation`, `proxy`, `reporting`, `resultsDir`,
`environment`, `pipelineId`, `ciRunId`, `branch`, `batchSize`, `requestTimeoutMs`, `retries`,
`retryBackoffMs`, `maxTraceLength`, `maxMessageLength`, `maxParameterLength` - полная таблица
с дефолтами и семантикой в [README адаптера](../doqa-pytest/README.md#полная-конфигурация).
`pipelineId` и `branch` подхватываются из стандартных CI-переменных GitLab / GitHub Actions;
пустые env-значения игнорируются, а явно пустое CLI-значение очищает унаследованное поле.

### Семантика доставки

Ретраи уважают идемпотентность: GET-запросы, 429 и создание рана с идемпотентным ключом
переигрываются при 5xx/сетевых ошибках; остальные POST повторяются только если соединение
вообще не установилось - потерянный ответ не должен продублировать ран или результат. Circuit
breaker размыкается после нескольких подряд отказов и на время «остывания» отвечает мгновенной
ошибкой, чтобы мёртвый бэкенд не стоил `retries × timeout` на каждый вызов. Токен никогда не
попадает в сообщения ошибок.

## Использование

Высокоуровневые точки входа:

```python
from doqa.client.api import ApiClient
from doqa.client.config import DoqaConfig
from doqa.client.models import AutotestDef, AutotestResult, Step
from doqa.client.run_context import RunContext

config = DoqaConfig.resolve()
client = ApiClient(config)
run = RunContext.establish(client, config)

definition = AutotestDef("LOGIN-1", "Login works")
definition.title = "Login"
definition.steps.append(Step.of("open the login page"))
client.upsert_autotests([definition])

result = AutotestResult("LOGIN-1", "passed")
result.started_on, result.completed_on = start_ms, stop_ms
result.duration_ms = stop_ms - start_ms
client.upload_results(run.run_id, run.configuration_id, [result])
```

Вложения загружаются заранее и затем указываются в результатах через
`Attachment(media_file_id)`: `client.upload_attachment(path)` стримит файл прямо с диска (без
копии в памяти - безопасно для больших видео), `client.upload_attachment_bytes(filename, content,
content_type)` загружает контент из памяти; content-type multipart-части выводится из имени
файла.

Для файлового синка `AllureFileWriter` сериализует ту же модель `AutotestDef`/`AutotestResult`
в файлы `*-result.json` / `*-container.json`, которые принимает парсер DoQA, плюс
`environment.properties`, когда задан ключ `environment`.

`Transport` - инжектируемый HTTP-шов: тесты и адаптеры подставляют фейковую реализацию и
работают полностью офлайн.

## Как написать адаптер поверх ядра

Мост фреймворка отвечает ровно за четыре вещи:

1. **Идентичность** - `AdapterRuntime.configure("<framework>", "<label>")` в точке входа: id
   становится префиксом fallback-externalId, label попадает в файловый синк.
2. **Жизненный цикл** - на старте теста `DoqaContexts.open(unique_id)` и заполнить
   `RuntimeContext.test_ref` (собирайте `TestRef` в ОДНОЙ фабрике на адаптер, чтобы discovery,
   ordering и репортинг сходились в идентичностях); на завершении `DoqaContexts.remove(...)`,
   проекция через `result_builder.build(...)` и `session.report(built)`.
3. **Точки flush** - `session.flush_class(fqcn)` на границе контейнера (realtime),
   `session.flush()` в конце прогона. Ошибка репортинга никогда не должна вылетать в прогон
   пользователя.
4. **Опциональные discovery-фичи** - селективный запуск и порядок по плану, если фреймворк
   даёт хуки: `plan.allows(ref)` для деселекта, `plan.plan_index(ref)` как ключ сортировки,
   `plan.reset()` на границе прогона.

`doqa-pytest` - эталонная реализация этого рецепта.

## Сборка

Модуль живёт в монорепозитории `doqa-python`:

```bash
pip install -e ./doqa-client
python -m pytest doqa-client/tests
```

Тесты - контрактные фикстуры над мок-транспортом, сеть не нужна.

## Лицензия

[Apache License 2.0](../LICENSE)
