Skip to main content

Diff-scoped test-gap analyzer: finds untested changed code, ranks it by risk, and generates tests it has actually executed and verified

Project description

Oneport Testgap

Diff-scoped test-gap analysis that doesn't stop at a number.

Codecov and diff-cover tell you "71% patch coverage" and leave. Qodo generates tests and hopes they work. Testgap does the whole job:

  1. Finds exactly which changed lines have zero test coverage (deterministic: git diffpytest --cov, never an LLM guess),
  2. Ranks each gap by risk — payment, auth, and data-write logic outrank logging — with a one-sentence explanation of why it's risky,
  3. Generates pytest tests for the riskiest gaps and — the part nobody else does — executes every generated test before showing it to you. Tests that fail, or that don't actually cover the gap lines they were written for, are discarded. What lands in tests/generated/ has already passed.
$ oneport-testgap analyze --staged --generate 3

Oneport Testgap — staged changes
1 file(s) analyzed · 6 changed line(s) · 4 with zero coverage

[CRITICAL] payment.py:4-7  (charge)
           Fee arithmetic and the negative-amount guard ship completely untested.

Generated tests (only VERIFIED tests are written to disk):
  VERIFIED  tests/generated/test_gap_charge.py — passed, covers 4/4 gap line(s)
            of payment.py [1 attempt(s)]

RESULT: critical-risk changed lines have zero coverage — exit 1 (CI gate).

Install

pip install oneport-testgap

Requires pytest + pytest-cov in the project you're analyzing (Testgap no-ops gracefully with a clear message if they're missing, or you can hand it an existing report with --coverage-file coverage.xml).

Set a model API key — either works, Gemini is free:

export GEMINI_API_KEY=AIza...        # free at https://aistudio.google.com/apikey
# or
export ANTHROPIC_API_KEY=sk-ant-...  # needs: pip install oneport-testgap[claude]

Usage

oneport-testgap analyze                    # staged changes (default)
oneport-testgap analyze --head             # the last commit
oneport-testgap analyze https://github.com/org/repo/pull/42   # a PR (run on a checkout of its branch)

oneport-testgap analyze --generate 3       # write verified tests for the top 3 gaps
oneport-testgap analyze --min-risk high    # only report high/critical gaps
oneport-testgap analyze --format json      # machine-readable, for pipelines
oneport-testgap analyze --coverage-file coverage.xml   # reuse an existing report
oneport-testgap analyze <PR_URL> --post    # comment the gap report on the PR

CI gate

Exit codes are the contract:

Code Meaning
0 No critical-risk gaps (or graceful no-op: no changes / no pytest)
1 Changed critical-risk lines have zero coverage — block the merge
2 Configuration / auth / usage error

--min-risk only filters the report; the exit-1 gate always fires on critical gaps. A ready-made GitHub Action lives in examples/workflows/testgap.yml.

How it works (and what the LLM is never allowed to do)

git diff ──► changed lines ─┐
                            ├─► gaps = changed ∩ uncovered   ← deterministic, always
pytest --cov ► coverage.xml ┘
                                    │
                                    ▼
                        LLM: rank by risk + explain why      ← judgment only
                                    │  --generate N
                                    ▼
                        LLM writes a pytest file
                                    │
                        temp file → real pytest run → real coverage check
                                    │
                       pass + gap lines covered?  ──no──► discarded (one repair
                                    │yes                   round, then gone)
                                    ▼
                        tests/generated/  (review it, commit it yourself)

The model never computes coverage facts. It sees gaps that are already proven uncovered and only decides how much they matter and how to test them. Its tests earn their place by executing — a generated test you see is a test that already ran green and demonstrably covered the lines it targets.

vs. the alternatives

Codecov / diff-cover Qodo Testgap
Diff-scoped coverage ✅ number only ✅ per-line, per-function
Risk ranking + why
Test generation
Generated tests executed & verified before you see them
Runs with zero servers ❌ (upload service)

Configuration

Testgap reads the suite-wide .oneportrc (same file as oneport-review). Top-level keys are shared; a testgap: section overrides them for this tool:

api_key: AIza...            # or use the env var
model: gemini-2.5-flash
ignore_paths:
  - migrations/**
  - "**/*_pb2.py"

testgap:
  output:
    min_risk: medium
  generate:
    repair_rounds: 1        # failed test gets its pytest output fed back once
    test_timeout: 120
    output_dir: tests/generated
  coverage:
    pytest_args: ["-m", "not slow"]
    timeout: 600

Team guidelines in .oneport/guidelines.md (the file oneport learn writes) are injected into both the ranking and generation prompts — testing rules like "all payment code needs edge-case tests" shift the ranking and shape the generated tests.

Privacy

No servers, no telemetry, no upload step. Your code goes to exactly one place: the model API (Google or Anthropic) on your own key. Coverage runs happen on your machine. See PRIVACY.md.

Part of the Oneport suite

Testgap is one gate in Oneport, the AI pre-ship check: oneport-review (AI code review) · oneport-testgap (this) · more coming.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

oneport_testgap-0.1.0.tar.gz (36.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

oneport_testgap-0.1.0-py3-none-any.whl (41.1 kB view details)

Uploaded Python 3

File details

Details for the file oneport_testgap-0.1.0.tar.gz.

File metadata

  • Download URL: oneport_testgap-0.1.0.tar.gz
  • Upload date:
  • Size: 36.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.0

File hashes

Hashes for oneport_testgap-0.1.0.tar.gz
Algorithm Hash digest
SHA256 01a122f67a7ce4f664deec1e8f565d6b2fa31b19edb3e97943f8ea6afa155a79
MD5 e6054966913840e901458a95b82332c0
BLAKE2b-256 f430343cdbe9713e6a6c8c260da6ed2c0974d3b7e6b6aca542f29c468251a2a3

See more details on using hashes here.

File details

Details for the file oneport_testgap-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for oneport_testgap-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c3b99d4d0ea7daf0a7d1156a04f35af54db479c5ea1e175386b278f2cfc53f11
MD5 b6383f089d4a119bf7f1a42e4a03b90f
BLAKE2b-256 9cb08d8165c43f636d484911dd1c314f70cba36c936667911f1e90099235ba96

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page