Сценарий 07: Покрытие тестами

Обнаружение непротестированного кода на основе CPG, приоритизация тестирования, рекомендации по генерации тестов, импорт покрытия и гибридный анализ.

Содержание

Быстрый старт

/select 07

Как это работает

Архитектура

Модуль покрытия тестами (src/workflow/scenarios/coverage_handlers/, 13 файлов) состоит из 6 компонентов:

Запрос пользователя
    |
    v
CoverageIntentDetector (5 типов намерений, двуязычный)
    |
    v
HandlerRegistry (диспетчеризация по приоритету)
    |
    +---> TestGenerationHandler (приоритет 5)
    |         генерация рекомендаций по тестам для конкретных функций
    |
    +---> UntestedCodeHandler (приоритет 10)
    |         поиск непротестированного кода (эвристика или гибрид)
    |
    +---> TestPriorityHandler (приоритет 20)
    |         ранжирование непротестированных функций по критичности
    |
    v
CoverageReportFormatter (двуязычный вывод в Markdown)
    |
    v
CallGraphAnalyzer ──> ImpactAnalysis (impact_score, вызывающие, вызываемые)
Компонент Модуль Назначение
CoverageIntentDetector intent_detector.py Определение намерения запроса среди 5 типов с морфологическим сопоставлением
TestGenerationHandler handlers/test_generator.py Генерация рекомендаций по тестам для именованных функций
UntestedCodeHandler handlers/untested.py Поиск непротестированного кода (эвристика + гибрид)
TestPriorityHandler handlers/priority.py Ранжирование непротестированных функций по 4 факторам
CoverageReportFormatter formatters/coverage_report.py Форматирование отчётов с FormatterLocalization (EN/RU)
TestCoverageScanner handlers/coverage_scanner.py Сканирование интерфейсного покрытия (двойное: edges_call + эвристика по путям)

Определение намерения

CoverageIntentDetector классифицирует запросы в 5 типов намерений с морфологическим сопоставлением и границами кириллических слов:

Тип намерения Приоритет Ключевые слова EN Ключевые слова RU
test_generation 5 generate test, create test, write test, test suite, mutation test, stress test, property-based, chaos engineering сгенерировать тест, создать тест, написать тест, модульный тест, стресс-тесты, мутационное тестирование
untested_code_scan 10 untested, no test, missing test, not covered, uncovered непротестированный, без теста, нет тестов, непокрытый
test_priority 20 test priority, should test, critical test, high priority test приоритет теста, протестировать, критический тест, важный тест
coverage_gap 30 coverage gap, low coverage, coverage report, test coverage пробел покрытия, низкое покрытие, отчет покрытия, покрытие тестами
coverage_improvement 40 improve coverage, increase coverage, better coverage улучшить покрытие, увеличить покрытие, повысить покрытие

Дополнительное извлечение: - _extract_criticality(query) — возвращает "critical", "high", "medium" или "all" - _extract_scope(query) — возвращает "method", "class", "module" или "all"

Реестр обработчиков

Обработчики регистрируются через HandlerRegistry("coverage") с диспетчеризацией по приоритету. Меньший приоритет = более высокий приоритет обработки:

@coverage_registry.register(priority=5)
class TestGenerationHandlerRegistered(TestGenerationHandler): ...

@coverage_registry.register(priority=10)
class UntestedCodeHandlerRegistered(UntestedCodeHandler): ...

@coverage_registry.register(priority=20)
class TestPriorityHandlerRegistered(TestPriorityHandler): ...

Каждый обработчик реализует can_handle(query_info) -> bool и handle(query_info) -> HandlerResult. Реестр перебирает обработчики по приоритету и использует первый подходящий.

Режимы обнаружения

Эвристическое обнаружение

Режим по умолчанию, когда данные покрытия не импортированы. Методы без вызывающих test_* в edges_call помечаются как непротестированные:

-- Методы без вызовов из тестов
SELECT m.id, m.name, m.full_name
FROM nodes_method m
WHERE NOT EXISTS (
    SELECT 1 FROM edges_call ec
    JOIN nodes_method caller ON ec.source_id = caller.id
    WHERE ec.target_id = m.id
    AND caller.name LIKE 'test_%'
)

Гибридное обнаружение

Автоматически активируется при наличии колонки coverage_percent в nodes_method (заполняется через coverage import):

  • Методы с coverage_percent < 1.0 → помечаются по данным выполнения
  • Методы с NULL coverage_percent → откат на эвристический анализ
  • Каждый кандидат получает метку detection_method: "runtime" или "heuristic"
  • Оценка покрытия использует AVG(coverage_percent) из данных выполнения

Проверка _has_coverage_data() гарантирует отсутствие изменений при отсутствии импортированных данных.

Обработчики

UntestedCodeHandler

Обрабатывает намерение untested_code_scan. Ключевые методы:

Метод Описание
can_handle(query_info) Возвращает True при type == "untested_code_scan"
handle(query_info) Поиск непротестированного кода, классификация по критичности, генерация рекомендаций
_find_untested_functions() Эвристика + необязательное обнаружение по данным покрытия
_classify_by_criticality(candidates) Группировка по уровню риска (critical/high/medium/low)
_estimate_coverage(candidates) Расчёт процента покрытия
_has_coverage_data() Проверка наличия колонки coverage_percent

Обогащает топ-20 кандидатов рекомендациями на основе CPG (покрытие ветвей, граничные значения параметров, пути ошибок).

TestGenerationHandler

Обрабатывает намерение test_generation. Ключевые методы:

Метод Описание
can_handle(query_info) Возвращает True при type == "test_generation" и извлекаемых именах функций
handle(query_info) Генерация рекомендаций по тестам для конкретных функций
_extract_function_names(query) Извлечение имён функций через приоритетные шаблоны
_search_functions_by_keywords(query) Концептуальный поиск функций (например, «тесты для выполнения запросов»)
_get_function_info(func_name) Регистронезависимый поиск функции
_get_function_callees(func_name) Зависимости для мокирования
_get_function_callers(func_name) Тестовые сценарии из вызывающих
_suggest_test_approach(...) Генерация стратегии (модульные/интеграционные/граничные тесты)

TestPriorityHandler

Обрабатывает намерение test_priority. Ранжирует непротестированные функции по 4 факторам:

Фактор Баллы Условие
Критичность модуля +3.0 Модули api, interface, core
Критичность модуля +2.0 Модули main, engine, system
Сложность +1.5 Длина сигнатуры > 200 символов
Сложность +1.0 Длина сигнатуры > 100 символов
Публичный API +1.0 Имя без префикса подчёркивания
Количество вызывающих +2.0 Выше порога высокой конкуренции
Количество вызывающих +1.0 Выше среднего порога

Оценка преобразуется в уровень приоритета через _score_to_rating(): high (≥ порога), medium, low.

Анализ влияния

Workflow использует CallGraphAnalyzer (Graph Method #2) из src/analysis/callgraph/analyzer.py для анализа влияния непротестированных методов.

Датакласс ImpactAnalysis:

Поле Тип Описание
method_name str Анализируемый метод
direct_callers list[str] Методы, вызывающие напрямую
transitive_callers list[str] Все транзитивные вызывающие
direct_callees list[str] Методы, вызываемые напрямую
transitive_callees list[str] Все транзитивные вызываемые
impact_score float Оценка влияния 0.0–1.0

3 категории инсайтов в state["metadata"]:

Инсайт Условие
high_impact_untested impact_score > thresholds.high_impact
untested_entry_points Много вызывающих + мало вызываемых
critical_untested callers > min_callers && impact_score > impact_score_medium

Рекомендации на основе CPG

Для каждого непротестированного метода (топ-20 по критичности) обработчик генерирует конкретные рекомендации:

Анализ покрытия ветвей

Подсчитывает управляющие структуры (IF, FOR, WHILE, SWITCH) из nodes_control_structure и оценивает число тестовых сценариев.

Анализ граничных значений параметров

Сопоставляет типы параметров из nodes_param с рекомендациями по граничным значениям:

Тип Граничные тесты
int, long, size_t, float zero, negative, max, min
char*, string, str empty, null, very long, special chars
Указатели (*, ptr) null pointer
bool true, false
Вариативные параметры zero args, one arg, many args

Анализ путей ошибок

Подсчитывает блоки TRY в nodes_control_structure и операторы RETURN в nodes_return. Множественные return указывают на пути обработки ошибок.

Импорт покрытия

Импорт данных покрытия из внешних инструментов для включения гибридного обнаружения.

Поддерживаемые форматы

Формат Инструмент Тип файла
pytest-cov pytest-cov (--cov-report=json) JSON
lcov gcov / lcov / geninfo Текст (.info, .lcov)
cobertura Cobertura, JaCoCo, coverage.py XML XML

Как это работает

  1. Парсер читает отчёт покрытия и извлекает построчные данные по каждому файлу
  2. Импортёр добавляет колонку coverage_percent в nodes_method (если отсутствует)
  3. Каждый метод сопоставляется с данными покрытия через суффиксное сопоставление имени файла и пересечение диапазона строк с покрытыми строками
  4. coverage_percent вычисляется как покрытые_строки / всего_строк * 100

Нормализация путей: Отчёты покрытия часто содержат абсолютные пути, а CPG хранит относительные. Импортёр нормализует пути (убирает ./, конвертирует обратные слэши) и использует суффиксное сопоставление. Параметр --source-root позволяет обрезать общий префикс.

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

Параметры покрытия из get_unified_config():

Пороговые значения

Параметр По умолчанию Описание
coverage_high 0.75 Порог высокого покрытия
coverage_low 0.25 Порог низкого покрытия
test_coverage_minimum 50 Минимально требуемое покрытие %
test_coverage_good 80 Хорошее покрытие %
high_impact Мин. оценка для высоковлиятельных методов
min_callers Мин. вызывающих для критической классификации
impact_score_medium Порог среднего влияния

Весовые коэффициенты

Параметр По умолчанию Описание
coverage_base_score 5.0 Базовая оценка приоритета
coverage_critical_module 3.0 Бонус за критический модуль
coverage_important_module 2.0 Бонус за важный модуль
coverage_long_signature 1.5 Бонус за сложную сигнатуру (>200 символов)
coverage_medium_signature 1.0 Бонус за среднюю сигнатуру (>100 символов)

Лимиты обработчиков

Параметр По умолчанию Описание
display_items 15 Элементов в отчёте
summary_items 5 Элементов в сводке
query_medium 30 Лимит среднего запроса (анализ влияния)
cpg_results 50 Лимит результатов CPG
retrieved_functions 25 Функций для оценки бенчмарка
priority_functions 10 Размер приоритетного списка

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

# Импорт JSON-отчёта pytest-cov
python -m src.cli.import_commands coverage import --file coverage.json --format pytest-cov --db data/projects/postgres.duckdb

# Импорт lcov trace файла
python -m src.cli.import_commands coverage import --file lcov.info --format lcov

# Импорт Cobertura XML (например, из Java/C# инструментов)
python -m src.cli.import_commands coverage import --file coverage.xml --format cobertura --source-root /project

# Просмотр импортированных данных
python -m src.cli.import_commands coverage show
python -m src.cli.import_commands coverage show --uncovered-only

REST API

Запросы покрытия тестами обрабатываются через сценарный роутер:

Метод Эндпоинт Описание
POST /api/v1/scenarios/test_coverage/query Выполнение анализа покрытия

Модель запроса (ScenarioQueryRequest):

Поле Тип Описание
query str Запрос на анализ (1–10000 символов)
session_id str? Идентификатор сессии
language str "en" или "ru" (по умолчанию: "en")

Модель ответа (ScenarioQueryResponse):

Поле Тип Описание
answer str Форматированный результат анализа
scenario_id str "test_coverage"
confidence float Уверенность определения намерения
evidence list[dict] Подтверждающие факты
processing_time_ms float Время обработки

Пример:

curl -X POST http://localhost:8000/api/v1/scenarios/test_coverage/query \
  -H "Content-Type: application/json" \
  -d '{"query": "Find untested functions", "language": "en"}'

Сценарии использования

Поиск непротестированного кода

> Какие функции не покрыты тестами?

## Пробелы в покрытии

**Режим обнаружения:** Эвристика
**Всего непротестированных:** 234 функции
**Оценка покрытия:** 78%

### Критические (executor)
- ExecParallelHashJoinNewBatch()
- ExecReScanGather()

### Высокий приоритет (storage)
- heap_lock_updated_tuple()
- heap_abort_speculative()

### Рекомендации для `heap_lock_updated_tuple`
1. Добавьте 5 тестовых сценариев для покрытия ветвей (IF: 3, SWITCH: 1)
2. Протестируйте граничные значения параметра `flags` (zero, negative, max)
3. Протестируйте обработку ошибок: 2 try/catch блока

Приоритизация тестирования

> Какие критические функции нужно протестировать в первую очередь?

## Рейтинг приоритетов тестирования

| Функция | Баллы | Приоритет | Причина |
|---------|-------|-----------|---------|
| heap_lock_updated_tuple | 8.5 | high | критический модуль, 23 вызывающих, публичный API |
| ExecParallelHashJoinNewBatch | 7.0 | high | модуль engine, сложная сигнатура |
| AtEOXact_RelationCache | 5.0 | medium | системный модуль, 4 вызывающих |

Генерация тестовых случаев

> Сгенерировать тестовые случаи для heap_insert

## Рекомендации по тестам: heap_insert()

**Файл:** src/backend/access/heap/heapam.c:2156

**Стратегия:**
- Модульные тесты: тестировать heap_insert изолированно, мокируя 5 зависимостей
- Интеграционные тесты: тестировать через 23 вызывающие функции
- Граничные случаи: тестировать граничные условия, null-ввод, обработку ошибок

**Зависимости для мокирования:**
- RelationGetBufferForTuple()
- heap_prepare_insert()
- XLogInsert()

**Тестовые сценарии из вызывающих функций:**
- simple_heap_insert() использует heap_insert для...
- toast_save_datum() использует heap_insert для...

Гибридное обнаружение (с данными покрытия)

> Найти непротестированный код

## Пробелы в покрытии (Гибрид)

**Режим обнаружения:** Данные выполнения + Эвристика
**Оценка покрытия:** 62.3% (по данным выполнения)

| Метод | Обнаружение | Покрытие | Причина |
|-------|-------------|----------|---------|
| parse_query() | Данные выполнения | 0.0% | Нет покрытых строк |
| exec_plan() | Данные выполнения | 12.5% | Частичное покрытие |
| helper_func() | Эвристика | --- | Нет вызовов из тестов |

Примеры вопросов

Обнаружение непротестированного кода: - «Какие функции не покрыты тестами?» - «Найти непротестированный код» - «Показать функции без тестов» - «Какой код не покрыт тестами?»

Приоритизация тестирования: - «Какие критические функции нужно протестировать в первую очередь?» - «Что нужно протестировать в первую очередь?» - «Приоритет тестирования»

Генерация тестов: - «Сгенерировать тестовые случаи для heap_insert» - «Создать тесты для функции palloc» - «Какие граничные случаи следует протестировать в ExecInitNode?» - «Написать мутационные тесты для парсера запросов»

Обзор покрытия: - «Показать пробелы в покрытии» - «Отчёт покрытия» - «Как улучшить тестовое покрытие?»