Skip to content

Add opt-in free-session budget gate for unauthenticated HTTP traffic #1956

Add opt-in free-session budget gate for unauthenticated HTTP traffic

Add opt-in free-session budget gate for unauthenticated HTTP traffic #1956

Workflow file for this run

# .github/workflows/ci.yml
name: CI - Run Unit Tests
# This workflow runs on every push to any branch, and on any pull requests.
on:
push:
branches:
- '**'
pull_request:
jobs:
test:
name: Run Unit Tests (${{ matrix.ambient }})
# The job will run on the latest version of Ubuntu
runs-on: ubuntu-latest
# Backstop: if pytest-timeout fails to kill a hung test, don't let the
# job run for the full GitHub Actions default (6 hours).
timeout-minutes: 5
# This creates a build matrix to test against multiple Python versions,
# ensuring broad compatibility. The `ambient` dimension (issue #436) runs
# the suite once from a clean checkout and once under a deliberately
# hostile ambient configuration, so the unit suite's determinism is
# continuously enforced.
strategy:
matrix:
python-version: ["3.12"]
ambient: ["clean", "adverse"]
steps:
# Step 1: Check out the repository code so the workflow can access it.
- name: Check out repository
uses: actions/checkout@v4
with:
submodules: recursive
persist-credentials: false
# Step 2: Set up the specified Python version from the matrix.
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
# Step 3: Cache dependencies to speed up subsequent runs.
# The cache is invalidated if the Python version or pyproject.toml changes.
- name: Cache pip dependencies
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ matrix.python-version }}-${{ hashFiles('pyproject.toml') }}
restore-keys: |
${{ runner.os }}-pip-${{ matrix.python-version }}-
# Step 4: Install the project dependencies, including the [test] extras.
- name: Bake bundled skill metadata
run: python scripts/bake_skill_metadata.py
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -e ".[test]"
# Deliberately hostile ambient configuration (issue #436): the unit suite
# must give the same verdict regardless of local .env contents. Every
# ServerConfig field is set to a valid non-default value so that any test
# asserting on any operator-configurable setting would catch a leak;
# extend this list when adding new fields to ServerConfig.
- name: Write adverse .env
if: matrix.ambient == 'adverse'
run: |
cat > .env <<'EOF'
BLOCKSCOUT_BS_TIMEOUT=0.123
BLOCKSCOUT_BS_LIGHT_TIMEOUT=0.456
BLOCKSCOUT_BS_REQUEST_MAX_RETRIES=9
BLOCKSCOUT_BENS_URL=https://adverse-bens.example.com
BLOCKSCOUT_BENS_TIMEOUT=1.5
BLOCKSCOUT_CHAINSCOUT_URL=https://adverse-chains.example.com
BLOCKSCOUT_CHAINSCOUT_TIMEOUT=1.5
BLOCKSCOUT_PRO_API_BASE_URL=https://adverse-pro.example.com/
BLOCKSCOUT_PRO_API_CONFIG_TIMEOUT=1.5
BLOCKSCOUT_PRO_API_CONFIG_TTL_SECONDS=11
BLOCKSCOUT_PRO_API_CONFIG_REFRESH_RETRY_SECONDS=2
BLOCKSCOUT_PRO_API_KEY=adverse-pro-api-key
BLOCKSCOUT_PRO_API_KEY_HEADER=X-Adverse-Pro-Key
BLOCKSCOUT_PRO_API_KEY_REQUIRED_NOTICE=CI adverse notice
BLOCKSCOUT_PRO_API_LOW_CREDITS_THRESHOLD=1
BLOCKSCOUT_METADATA_TIMEOUT=1.5
BLOCKSCOUT_CHAINS_LIST_TTL_SECONDS=11
BLOCKSCOUT_PROGRESS_INTERVAL_SECONDS=0.5
BLOCKSCOUT_CONTRACTS_CACHE_MAX_NUMBER=1
BLOCKSCOUT_CONTRACTS_CACHE_TTL_SECONDS=11
BLOCKSCOUT_NFT_PAGE_SIZE=3
BLOCKSCOUT_LOGS_PAGE_SIZE=3
BLOCKSCOUT_ADVANCED_FILTERS_PAGE_SIZE=3
BLOCKSCOUT_DIRECT_API_RESPONSE_SIZE_LIMIT=1234
BLOCKSCOUT_RPC_REQUEST_TIMEOUT=1.5
BLOCKSCOUT_RPC_POOL_PER_HOST=1
BLOCKSCOUT_MCP_USER_AGENT=Adverse Agent
BLOCKSCOUT_MCP_ALLOWED_HOSTS=adverse.example.com
BLOCKSCOUT_MCP_ALLOWED_ORIGINS=https://adverse.example.com
BLOCKSCOUT_MIXPANEL_TOKEN=adverse-token
BLOCKSCOUT_MIXPANEL_API_HOST=api.mixpanel.com
BLOCKSCOUT_DISABLE_COMMUNITY_TELEMETRY=true
BLOCKSCOUT_MCP_TRANSPORT=http
BLOCKSCOUT_DEV_JSON_RESPONSE=true
BLOCKSCOUT_INTERMEDIARY_HEADER=X-Adverse-Intermediary
BLOCKSCOUT_INTERMEDIARY_ALLOWLIST=AdverseClient
PORT=4242
EOF
# Step 5: Run the unit tests with code coverage.
# Pytest will automatically run all tests that are NOT marked with @pytest.mark.integration.
# This is the core of our "fast feedback" strategy.
- name: Run unit tests with coverage (clean)
if: matrix.ambient == 'clean'
run: |
pytest --cov=blockscout_mcp_server --cov-report=xml
- name: Run unit tests with coverage (adverse)
if: matrix.ambient == 'adverse'
env:
BLOCKSCOUT_PRO_API_KEY_REQUIRED_NOTICE: "CI adverse notice"
# Lowercase on purpose: pydantic-settings matches environment variables
# case-insensitively, so the pristine_config fixture must neutralize
# non-uppercase spellings too.
blockscout_bs_timeout: "7.5"
# The `port` field reads the unprefixed PORT variable via its alias;
# exercise that channel through the exported environment as well.
PORT: "4242"
run: |
pytest --cov=blockscout_mcp_server --cov-report=xml