test: address claude-review on Pact consumer contract (#883)
- pact.yml: pin tailscale/github-action@v3 to commit SHA (3 jobs) and pact-ruby-standalone install.sh to v2.6.1 (2 jobs) — supply-chain hardening - pact.yml: drop redundant `-o "addopts=..."` override (pyproject.toml already sets the same addopts; the override would silently mask future additions) - test_mcp_provider_verification.py: remove dead `pytestmark` shadowed by the list assignment; gate the module skip on PACT_USERNAME/PACT_PASSWORD too so a broker-set-but-creds-missing CI skips cleanly instead of raising KeyError - conftest.py: drop the unused `pact_dir` fixture Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
d33832aba9
commit
72592c3bca
@@ -40,12 +40,12 @@ jobs:
|
|||||||
uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0
|
uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0
|
||||||
|
|
||||||
- name: Generate consumer pacts
|
- name: Generate consumer pacts
|
||||||
run: uv run pytest -v -m contract -o "addopts=-p no:asyncio" tests/contract/
|
run: uv run pytest -v -m contract tests/contract/
|
||||||
|
|
||||||
# Only publish from non-fork builds that have the broker secrets.
|
# Only publish from non-fork builds that have the broker secrets.
|
||||||
- name: Join tailnet
|
- name: Join tailnet
|
||||||
if: ${{ env.PACT_BROKER != '' }}
|
if: ${{ env.PACT_BROKER != '' }}
|
||||||
uses: tailscale/github-action@v3
|
uses: tailscale/github-action@6cae46e2d796f265265cfcf628b72a32b4d7cade # v3
|
||||||
with:
|
with:
|
||||||
oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }}
|
oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }}
|
||||||
oauth-secret: ${{ secrets.TS_OAUTH_SECRET }}
|
oauth-secret: ${{ secrets.TS_OAUTH_SECRET }}
|
||||||
@@ -54,7 +54,7 @@ jobs:
|
|||||||
- name: Install Pact CLI
|
- name: Install Pact CLI
|
||||||
if: ${{ env.PACT_BROKER != '' }}
|
if: ${{ env.PACT_BROKER != '' }}
|
||||||
run: |
|
run: |
|
||||||
curl -fsSL https://raw.githubusercontent.com/pact-foundation/pact-ruby-standalone/master/install.sh | bash
|
curl -fsSL https://raw.githubusercontent.com/pact-foundation/pact-ruby-standalone/v2.6.1/install.sh | bash
|
||||||
echo "$PWD/pact/bin" >> "$GITHUB_PATH"
|
echo "$PWD/pact/bin" >> "$GITHUB_PATH"
|
||||||
|
|
||||||
- name: Publish pacts to broker
|
- name: Publish pacts to broker
|
||||||
@@ -110,7 +110,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Join tailnet
|
- name: Join tailnet
|
||||||
if: ${{ env.PACT_BROKER != '' }}
|
if: ${{ env.PACT_BROKER != '' }}
|
||||||
uses: tailscale/github-action@v3
|
uses: tailscale/github-action@6cae46e2d796f265265cfcf628b72a32b4d7cade # v3
|
||||||
with:
|
with:
|
||||||
oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }}
|
oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }}
|
||||||
oauth-secret: ${{ secrets.TS_OAUTH_SECRET }}
|
oauth-secret: ${{ secrets.TS_OAUTH_SECRET }}
|
||||||
@@ -124,7 +124,7 @@ jobs:
|
|||||||
PACT_PROVIDER_BRANCH: ${{ github.head_ref || github.ref_name }}
|
PACT_PROVIDER_BRANCH: ${{ github.head_ref || github.ref_name }}
|
||||||
# Publish results only from master so PR runs don't pollute the matrix.
|
# Publish results only from master so PR runs don't pollute the matrix.
|
||||||
PACT_PUBLISH_RESULTS: ${{ github.ref == 'refs/heads/master' }}
|
PACT_PUBLISH_RESULTS: ${{ github.ref == 'refs/heads/master' }}
|
||||||
run: uv run pytest -v -m contract -o "addopts=-p no:asyncio" tests/contract/test_mcp_provider_verification.py
|
run: uv run pytest -v -m contract tests/contract/test_mcp_provider_verification.py
|
||||||
|
|
||||||
can-i-deploy:
|
can-i-deploy:
|
||||||
name: can-i-deploy
|
name: can-i-deploy
|
||||||
@@ -133,7 +133,7 @@ jobs:
|
|||||||
if: ${{ github.ref == 'refs/heads/master' }}
|
if: ${{ github.ref == 'refs/heads/master' }}
|
||||||
steps:
|
steps:
|
||||||
- name: Join tailnet
|
- name: Join tailnet
|
||||||
uses: tailscale/github-action@v3
|
uses: tailscale/github-action@6cae46e2d796f265265cfcf628b72a32b4d7cade # v3
|
||||||
with:
|
with:
|
||||||
oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }}
|
oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }}
|
||||||
oauth-secret: ${{ secrets.TS_OAUTH_SECRET }}
|
oauth-secret: ${{ secrets.TS_OAUTH_SECRET }}
|
||||||
@@ -141,7 +141,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Install Pact CLI
|
- name: Install Pact CLI
|
||||||
run: |
|
run: |
|
||||||
curl -fsSL https://raw.githubusercontent.com/pact-foundation/pact-ruby-standalone/master/install.sh | bash
|
curl -fsSL https://raw.githubusercontent.com/pact-foundation/pact-ruby-standalone/v2.6.1/install.sh | bash
|
||||||
echo "$PWD/pact/bin" >> "$GITHUB_PATH"
|
echo "$PWD/pact/bin" >> "$GITHUB_PATH"
|
||||||
|
|
||||||
- name: Can I deploy nextcloud-mcp-server?
|
- name: Can I deploy nextcloud-mcp-server?
|
||||||
|
|||||||
@@ -30,12 +30,6 @@ def _clean_pact_dir():
|
|||||||
yield
|
yield
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def pact_dir() -> Path:
|
|
||||||
"""Directory the generated pact files are written to."""
|
|
||||||
return PACT_DIR
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def consumer_pact():
|
def consumer_pact():
|
||||||
"""A fresh Pact (consumer=nextcloud-mcp-server, provider=astrolabe).
|
"""A fresh Pact (consumer=nextcloud-mcp-server, provider=astrolabe).
|
||||||
|
|||||||
@@ -35,24 +35,30 @@ from collections.abc import Callable
|
|||||||
import pytest
|
import pytest
|
||||||
from pact import Verifier
|
from pact import Verifier
|
||||||
|
|
||||||
pytestmark = pytest.mark.contract
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
PROVIDER_NAME = "nextcloud-mcp-server"
|
PROVIDER_NAME = "nextcloud-mcp-server"
|
||||||
|
|
||||||
_PROVIDER_URL = os.environ.get("PACT_PROVIDER_URL")
|
_PROVIDER_URL = os.environ.get("PACT_PROVIDER_URL")
|
||||||
_BROKER_URL = os.environ.get("PACT_BROKER")
|
_BROKER_URL = os.environ.get("PACT_BROKER")
|
||||||
|
_BROKER_USERNAME = os.environ.get("PACT_USERNAME")
|
||||||
|
_BROKER_PASSWORD = os.environ.get("PACT_PASSWORD")
|
||||||
_LOCAL_PACT_DIR = os.environ.get("PACT_PROVIDER_PACT_DIR")
|
_LOCAL_PACT_DIR = os.environ.get("PACT_PROVIDER_PACT_DIR")
|
||||||
|
|
||||||
|
# A usable broker source needs the URL *and* its basic-auth credentials; gating
|
||||||
|
# on all three keeps a misconfigured CI (broker set, creds missing) a clean skip
|
||||||
|
# rather than a confusing KeyError at verify time.
|
||||||
|
_BROKER_READY = bool(_BROKER_URL and _BROKER_USERNAME and _BROKER_PASSWORD)
|
||||||
|
|
||||||
# Skip the whole module unless we have a provider to hit AND a pact source.
|
# Skip the whole module unless we have a provider to hit AND a pact source.
|
||||||
pytestmark = [
|
pytestmark = [
|
||||||
pytest.mark.contract,
|
pytest.mark.contract,
|
||||||
pytest.mark.skipif(
|
pytest.mark.skipif(
|
||||||
not _PROVIDER_URL or not (_BROKER_URL or _LOCAL_PACT_DIR),
|
not _PROVIDER_URL or not (_BROKER_READY or _LOCAL_PACT_DIR),
|
||||||
reason=(
|
reason=(
|
||||||
"Provider verification needs PACT_PROVIDER_URL and a pact source "
|
"Provider verification needs PACT_PROVIDER_URL and a pact source: "
|
||||||
"(PACT_BROKER or PACT_PROVIDER_PACT_DIR). Skipped outside CI."
|
"PACT_BROKER (+ PACT_USERNAME/PACT_PASSWORD) or "
|
||||||
|
"PACT_PROVIDER_PACT_DIR. Skipped outside CI."
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
@@ -90,11 +96,10 @@ def test_verify_astrolabe_consumer_pacts() -> None:
|
|||||||
verifier = Verifier(PROVIDER_NAME).add_transport(url=_PROVIDER_URL)
|
verifier = Verifier(PROVIDER_NAME).add_transport(url=_PROVIDER_URL)
|
||||||
verifier.state_handler(_dispatch_state, teardown=True)
|
verifier.state_handler(_dispatch_state, teardown=True)
|
||||||
|
|
||||||
if _BROKER_URL:
|
if _BROKER_URL and _BROKER_USERNAME and _BROKER_PASSWORD:
|
||||||
# Basic-auth creds accompany the broker URL (see module skipif).
|
verifier.broker_source(
|
||||||
username = os.environ["PACT_USERNAME"]
|
_BROKER_URL, username=_BROKER_USERNAME, password=_BROKER_PASSWORD
|
||||||
password = os.environ["PACT_PASSWORD"]
|
)
|
||||||
verifier.broker_source(_BROKER_URL, username=username, password=password)
|
|
||||||
else:
|
else:
|
||||||
assert _LOCAL_PACT_DIR is not None # guaranteed by module skipif
|
assert _LOCAL_PACT_DIR is not None # guaranteed by module skipif
|
||||||
verifier.add_source(_LOCAL_PACT_DIR)
|
verifier.add_source(_LOCAL_PACT_DIR)
|
||||||
|
|||||||
Reference in New Issue
Block a user