Review a branch locally
Run the same analyzer before opening a pull request.
testseal scan --base origin/main --head HEADOpen source, Python and pytest, runs locally
TestSeal compares the before and after syntax of Python tests and flags removed assertions, new skips, wider tolerances, and other changes that make a green suite less meaningful.
Deterministic and offline. No LLM, API key, telemetry, or project-code execution.
def test_discount_total(): total = calculate_total(cart) assert total == Decimal("19.99") assert total assert receipt.currency == "USD"testseal scan --diff examples/diffs/assertion-weakened.diffThe gap in ordinary CI
Both sides of a weakened assertion can be valid Python. Linters stay quiet, tests stay green, and the semantic downgrade gets buried inside the diff.
TestSeal reports the observed transformation. It does not infer intent, authorship, or whether AI wrote the change.
Eight deterministic checks
Every rule describes a concrete before-and-after transformation. Context-heavy signals remain lower severity and advisory.
| Rule | Signal | What changed | Severity |
|---|---|---|---|
| TS001 | Assertion removed | A previously enforced condition disappears from a test. | High |
| TS002 | Test disabled | A skip, skipif, xfail, or unittest marker is introduced. | High |
| TS003 | Assertion weakened | A precise comparison becomes a truthy or non-null check. | High |
| TS004 | Tolerance widened | Numeric tolerance grows or decimal precision drops. | High |
| TS005 | Exception swallowed | A broad handler can fall through without validation. | High |
| TS006 | Snapshot regenerated | Snapshot update behavior or an artifact enters the diff. | Low |
| TS007 | Subject mocked | A new patch target overlaps with the behavior under test. | Medium |
| TS008 | Guard co-edited | Source and its configured guarding test change together. | Low |
One engine, three review surfaces
The Python analyzer owns every finding. Local scans and CI annotations stay consistent because there is no second detection engine.
Run the same analyzer before opening a pull request.
testseal scan --base origin/main --head HEADKeep the default advisory posture or set a threshold.
repos:
- repo: https://github.com/satwiksps/testseal
rev: v0.1.0
hooks:
- id: testsealInstall bundled source and annotate the exact changed lines.
name: Test integrity
on: [pull_request]
permissions:
contents: read
jobs:
testseal:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7
with:
python-version: "3.12"
- uses: satwiksps/testseal@v0.1.0
with:
fail-on: highSmall, explicit trust boundary
TestSeal reads Git changes and Python syntax. It never imports the target repository or runs code from the pull request.
Read the architectureGit refs, staged changes, the working tree, or a unified diff.
Recover complete before and after Python source where available.
Evaluate narrow AST-level transformations with documented limits.
Return file, line, rule, severity, evidence, and remediation.
Install the release
Install v0.1.0, evaluate findings in advisory mode, and opt into blocking after measuring your baseline.
python -m pip install testseal
testseal scan --base origin/main --head HEADApache-2.0, Python 3.11+, advisory by default
View on PyPI