Files
DECNET/tests/api/test_schemathesis_ttp.py
anti 0fe9f895d0 feat(test): add test-schema target and SCHEMA_QUICK=1 mode for schemathesis
- Add dedicated test-schema Makefile target (xdist logical, 600s timeout,
  -m fuzz) so schemathesis runs separately from test-fuzz, which was
  spinning up competing uvicorn workers per xdist process
- Exclude all test_schemathesis*.py files from FUZZ_FLAGS via --ignore
- Add schema to _ALL_SUITES between api and fuzz
- Add SCHEMA_QUICK env var (default 0): caps every max_examples to 100
  across all four schemathesis files (4520 -> 600 total examples)
- Fix pre-push hook: use .311 venv and delegate to make test-all FAIL_FAST=0
  instead of hand-rolling five separate pytest invocations
2026-05-16 18:25:40 -04:00

91 lines
3.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Schemathesis contract tests scoped to the TTP Tagging API surface.
E.3.17 of ``development/TTP_TAGGING.md``. The full ``test_schemathesis``
suite fuzzes every endpoint with ``max_examples=3000`` — slow and
overkill when iterating on TTP-routes-only changes (E.3.13E.3.16).
This file filters by the OpenAPI ``tags=["TTP Tagging"]`` annotation
the eight TTP routes carry, runs against the same live uvicorn
subprocess the wider suite spins up, and applies the same check
battery so a 4xx-shape regression on a TTP route fails here without
waiting on the rest of the API.
Routes covered (all decorated ``tags=["TTP Tagging"]``):
* ``GET /api/v1/ttp/techniques``
* ``GET /api/v1/ttp/by-identity/{identity_uuid}``
* ``GET /api/v1/ttp/by-attacker/{attacker_uuid}``
* ``GET /api/v1/ttp/by-campaign/{campaign_uuid}``
* ``GET /api/v1/ttp/by-session/{session_id}``
* ``GET /api/v1/ttp/rules``
* ``POST /api/v1/ttp/rules/{rule_id}/state``
* ``DELETE /api/v1/ttp/rules/{rule_id}/state``
* ``GET /api/v1/ttp/export/navigator``
* ``GET /api/v1/ttp/export/navigator/identity/{identity_uuid}``
"""
from __future__ import annotations
import os
import pytest
_QUICK = os.getenv("SCHEMA_QUICK") == "1"
import schemathesis as st
from hypothesis import HealthCheck, Verbosity, settings
from tests.api.test_schemathesis import (
ALL_CHECKS,
AUTH_CHECKS,
LIVE_SERVER_URL,
before_call as _shared_before_call, # noqa: F401 (registers @st.hook)
)
# Reuse the schema fetched against the same uvicorn subprocess started
# by ``test_schemathesis``. Filtering by tag keeps the TTP suite a
# fast, focused contract gate without re-spinning the server.
TTP_SCHEMA = st.openapi.from_url(
f"{LIVE_SERVER_URL}/openapi.json",
).include(tag="TTP Tagging")
@pytest.mark.fuzz
@TTP_SCHEMA.parametrize()
@settings(
max_examples=100 if _QUICK else 400,
deadline=None,
verbosity=Verbosity.normal,
suppress_health_check=[
HealthCheck.filter_too_much,
HealthCheck.too_slow,
HealthCheck.data_too_large,
],
)
def test_ttp_schema_compliance(case):
"""Per-TTP-route schema compliance — valid + invalid inputs."""
case.call_and_validate(checks=ALL_CHECKS)
@pytest.mark.fuzz
@TTP_SCHEMA.parametrize()
@settings(
max_examples=100 if _QUICK else 120,
deadline=None,
verbosity=Verbosity.normal,
suppress_health_check=[
HealthCheck.filter_too_much,
HealthCheck.too_slow,
],
)
def test_ttp_auth_enforcement(case):
"""Every TTP route rejects requests without a Bearer token (401).
The mutation endpoints additionally require ``admin`` (server-side
``require_admin``); the authless probe doesn't distinguish 401 vs
403 here — the ``ignored_auth`` check just asserts that an absent
token never lands the request inside the handler with a usable
identity.
"""
case.headers = {
k: v for k, v in (case.headers or {}).items()
if k.lower() != "authorization"
}
case.call_and_validate(checks=AUTH_CHECKS)