Skip to main content
Glama
rrudy9
by rrudy9

test-trust

Сообщает ИИ-агентам для программирования (Claude Code, Codex, OpenHands, Cursor или локальной модели — агностичен по отношению к агенту, через MCP, Model Context Protocol) не только то, какие тесты покрывают изменение, но и можно ли этим тестам на самом деле доверять в том, что они поймают регрессию в этом месте.

«CI зелёный» и «это изменение безопасно» сегодня каждый агент считает одним и тем же. Но это не так. Тест, покрывающий функцию, — не то же самое, что тест, который действительно упал бы, если бы эта функция сломалась: тесты с избыточным количеством моков, снэпшот-тесты, которые просто перезаписывают любой новый вывод, и тесты без утверждений — все показывают зелёный цвет, ничего при этом не выявляя.

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

  1. Мутационное тестирование (обёртка над зрелыми существующими движками, а не собственная реализация — mutmut для Python, StrykerJS для JS/TS, gremlins для Go, cargo-mutants для Rust): намеренно вносит небольшие баги в изменённый код и проверяет, действительно ли покрывающие тесты падают. Выбор тестов для каждого мутанта берётся из собственных данных о покрытии каждого движка, а не реализуется здесь заново — mutmut и Stryker (где есть поддерживаемый раннер: jest/mocha/vitest) выбирают только те конкретные тесты, которые покрывают каждый мутант; gremlins и cargo-mutants пропускают мутантов с нулевым покрытием, но перезапускают полный релевантный набор тестов для каждого мутанта, у которого покрытие есть, — механизм более грубый (и более медленный на больших репозиториях), но по-прежнему корректный.

  2. Скоупинг по диффу (наш): сопоставляет изменённые строки git-диффа с охватывающей их функцией через собственный парсинг каждого языка, так что и мутационный прогон, и оценка доверия ограничены тем, что действительно изменилось, а не целым файлом или репозиторием.

  3. Слияние (новая часть, нигде больше не реализованная, — проверено): объединяет вышеперечисленное в единую оценку доверия для каждой изменённой функции, представленную как MCP-инструмент, который любой агент может вызвать, прежде чем считать зелёный прогон тестов доказательством безопасности.

См. examples/weak-test-fixture/ (Python), examples/weak-test-fixture-js/ (JS), examples/weak-test-fixture-go/ (Go), examples/weak-test-fixture-rust/ (Rust), examples/plug-and-play-fixture/ (вообще без конфигурации) и examples/multi-file-fixture-js/ (функция, которая импортирует соседний модуль, что доказывает: мутационный скоупинг не ломает кросс-файловые импорты) — это рабочие, живые демонстрации: функция, покрытая тестом, который сегодня проходит, но не поймал бы реальный баг.

Related MCP server: sumo-qa

Проверено на реальных репозиториях, а не только на фикстурах

Каждая фикстура выше — игрушка. Прежде чем довериться концепции, инструмент также запускался без конфигурации на реальных, внешних, неизменённых репозиториях на всех четырёх языках: psf/requests (Python, оценены 37 реальных функций, например resolve_proxies корректно помечена с доверием 0.0 — на неё действительно не ссылается ни один тест), kind-of (JS, ~50M еженедельных загрузок, isArray и isRegexp помечены, несмотря на прохождение всех 36 тестов), dustin/go-humanize (Go) и chronotope/humantime (Rust, настоящий многомодульный крейт — подтверждает, что мутационный скоупинг не ломается на реальном кросс-модульном коде, а не только на нашей собственной фикстуре). Этот процесс выявил и исправил несколько реальных багов в трёх из четырёх — включая один, где неудачный мутационный прогон молча сообщался как ложная оценка доверия в 100%, и баг масштабирования, где запрос, ограниченный одним файлом, молча мутировал вместо этого целый реальный пакет. Проверка реального репозитория Rust не выявила нового бага, что согласуется с тем, что cargo-mutants -f — самый чистый и наиболее прямо поддерживаемый механизм скоупинга из четырёх. Полное описание см. в docs/architecture.md.

Внешние инструменты, которые он оборачивает (установите один раз, для каждого используемого языка)

  • Python: mutmut — устанавливается автоматически как зависимость этого проекта.

  • JS/TS: @stryker-mutator/core — загружается автоматически через npx при первом использовании.

  • Go: gremlins — go install github.com/go-gremlins/gremlins/cmd/gremlins@latest

  • Rust: cargo-mutants — cargo install cargo-mutants

Попробуйте

Ещё не опубликован на PyPI. Все команды ниже предполагают локальный клон этого репозитория и запуск изнутри него (uv run --directory <path> ... — снаружи, как в примере встраивания в агента ниже). После публикации всё это сводится к обычному pip install test-trust / uvx test-trust, запускаемому откуда угодно, — без клона, без запоминания пути, без uv run --directory. Это единственное изменение — последний шаг между «работает, но с настройкой» и «по-настоящему легко внедрить»; в поведении инструмента не меняется ничего, меняется только способ его получения.

Требуются Python 3.11+ и uv. Мутационные движки для отдельных языков (см. выше) нужны только для языков, которые вы действительно используете, — примеру на Python ниже не требуется ничего, кроме uv sync.

uv sync
uv run test-trust check examples/plug-and-play-fixture inventory.py

Никакого файла настройки, никакого ручного запуска мутационного тестирования: эта одна команда сама определяет язык, сама записывает конфигурацию, сама запускает ограниченное этим файлом мутационное тестирование, используя реальную команду тестирования самого репозитория, и выводит оценку доверия.

Чтобы запустить для другого файла или репозитория:

uv run test-trust check <path-to-repo> <source-file> [--changed-file F] [--base-ref REF] [--threshold T]

Изменение порога (по умолчанию 0.5) — что считается достаточно протестированным. Повысьте его (например, 0.8), чтобы помечать всё, что не дотягивает до почти полного мутационного покрытия; понизьте, чтобы помечать лишь самые серьёзные пробелы. Тот же параметр, три интерфейса:

  • CLI: --threshold 0.8, как показано выше.

  • MCP-инструмент: threshold — аргумент get_test_trust, например, get_test_trust(repo_path=..., source_file=..., threshold=0.8). Вы не вызываете его сами — это делает агент, — поэтому задать его означает указать агенту (в вашем промпте или как постоянную инструкцию в CLAUDE.md: «вызови get_test_trust с threshold=0.8»). Если параметр не задан, используется 0.5.

  • GitHub Action: входной параметр low-trust-threshold в вашем файле рабочего процесса:

    - uses: ./.github/actions/test-trust-pr
      with:
        github-token: ${{ secrets.GITHUB_TOKEN }}
        low-trust-threshold: "0.8"

Встраивание в агента

Одноразовая настройка, регистрируемая глобально, а не для каждого проекта: поскольку repo_path является параметром каждого вызова, а не фиксируется при запуске, одной регистрации хватает для всех проектов, которые вы откроете впоследствии, а не только для того, в котором она была выполнена. Для Claude Code добавьте в ~/.claude.json (на уровне пользователя, а не в .mcp.json проекта):

{
  "mcpServers": {
    "test-trust": {
      "command": "uv",
      "args": ["run", "--directory", "/absolute/path/to/test-trust", "test-trust", "mcp"]
    }
  }
}

Та же форма command/args работает для Codex, Cursor и любого другого MCP-клиента — по этим клиентам и по тому, что изменится после публикации на PyPI, см. docs/embedding.md (uvx test-trust mcp — локальный путь вообще не нужен).

После этого инструмент незаметен в повседневной работе: агент сам вызывает get_test_trust по ходу задачи, так же, как уже вызывает свои инструменты чтения файлов или bash, — вы не вызываете его напрямую.

Или запускайте его на каждом PR — агент не нужен

.github/actions/test-trust-pr оценивает каждый изменённый поддерживаемый файл в PR и публикует (или обновляет при последующих пушах) один комментарий — помечает любую функцию, существующие тесты которой на самом деле не поймали бы регрессию в этом месте. Это полезно любой команде, занимающейся ревью кода, независимо от того, использует ли кто-то ИИ-агента. .github/workflows/test-trust.yml — рабочий пример; проверен от начала до конца (обнаружение диффа, оценка, оба варианта отображения комментария — «всё чисто» и «с флагами») на реальных коммитах в реальном репозитории — не проверен только сам живой вызов GitHub API, поскольку для проверки нужен настоящий PR.

Разработка

uv sync
uv run pytest tests/            # this project's own unit test suite
uv run test-trust check <repo> <file>   # exercise it against real code

tests/ покрывает детерминированную логику (скоупинг по диффу, агрегацию оценки доверия, автоопределение языка, адаптер отчётов каждого мутационного движка) специально подготовленными фикстурами — быстро, без внешних инструментов мутационного тестирования, и именно этот набор реально выполняется в CI (.github/workflows/ci.yml). Он не заменяет описанную выше проверку на реальных репозиториях — это было ручное разовое упражнение, выполненное в ходе разработки (реальные внешние клоны, установленные реальные движки), а не то, что CI перезапускает при каждом пуше. Автоматизировать это стоит позже; пока не сделано.

Лицензия MIT.

Related MCP Connectors

Related MCP Servers