Files
mcphub/tests/test_capability_probe_read_tier.py
airano-ir f203ca88de
Some checks failed
Release / Test before release (push) Has been cancelled
Release / Publish to PyPI (push) Has been cancelled
Release / Publish to Docker Hub (push) Has been cancelled
Release / Create GitHub Release (push) Has been cancelled
feat(v3.12.0): media pipeline, AI image generation, capability probe, companion v2.9.0
Three-month batch sync from internal repo (~80 commits) covering Tracks F.5a, F.7e, F.8, F.17, F.18, F.X.

WordPress media pipeline
- Pillow-based optimization, AI image generation (OpenAI / Stability / Replicate / Google Nano Banana / OpenRouter), chunked + resumable uploads, bulk delete/reassign, idempotent retries.

Capability discovery (F.7e)
- Per-site credential probe + adapters for WordPress / WooCommerce / Gitea, tier-fit unions granted ∪ roles, capability badge UI with HTMX partial re-check, install hint in every companion-unreachable error.

Companion plugin overhaul
- Renamed wordpress-plugin/airano-mcp-seo-bridge → wordpress-plugin/airano-mcp-bridge.
- Eight new endpoints: /capabilities, /bulk-meta, /export, /cache-purge, /transient-flush, /site-health, /audit-hook, /upload-and-attach.
- wp.org Plugin Check pass: i18n, WP_Filesystem, scheme allowlist on audit-hook URL.

Other
- Gitea ergonomics (F.17): batch files, tree, search, compare, releases, fork.
- Opportunistic bcrypt upgrade for legacy SHA-256 admin keys (F.8).
- n8n refactor: structured errors, capability probe, missing tools backfilled.
- Idempotency-Key dedup for AI media upload retries; WP client fast-fails on unreachable sites.

Docs
- README + CLAUDE.md drop the fixed "633 tools" claim. The total grows with each release; per-plugin approximations + dashboard-surfaced counts replace it.
- Tools/Tests badges removed in favour of "Plugins: 10".

Deployment
- PyPI mirror chain, optional BUILD_HTTP_PROXY, Alpine→Yandex apk mirror, Debian-slim Plan-B Dockerfile, mirror.gcr.io variant.

CI
- Black + Ruff clean on Python 3.12; pytest tests/ green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 16:25:58 +02:00

124 lines
4.1 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.
"""F.X.fix #5 — tier-fit for the WP ``read`` tier recognises role names.
Regression: every admin probe showed ``status=warning, missing=['read']``
because the companion capability payload places WP role strings under
``roles`` (e.g. ``administrator``) while the bare ``read`` capability
is only implied by the role and never written out as a standalone cap.
The ``_cap_matches`` alias resolver understood the mapping but was
only fed ``granted`` — not ``granted roles``.
"""
from __future__ import annotations
import pytest
from core.capability_probe import evaluate_tier_fit
# Realistic admin probe payload captured from Phase A of F.X.test on
# blog.example.com. Only the fields that ``evaluate_tier_fit``
# actually reads are kept.
ADMIN_PROBE_PAYLOAD = {
"probe_available": True,
"granted": [
"delete_others_pages",
"delete_others_posts",
"delete_pages",
"delete_posts",
"delete_private_pages",
"delete_private_posts",
"delete_published_pages",
"delete_published_posts",
"edit_others_pages",
"edit_others_posts",
"edit_pages",
"edit_posts",
"manage_options",
"moderate_comments",
"upload_files",
],
"roles": ["administrator"],
"plugin_version": "2.9.0",
}
@pytest.mark.unit
class TestReadTierForAdminRoles:
def test_read_tier_ok_when_only_role_is_administrator(self):
# Admin user: ``read`` is implied by the role, not present in
# granted. Before the fix this returned status=warning.
fit = evaluate_tier_fit(
plugin_type="wordpress",
tier="read",
probe_payload=ADMIN_PROBE_PAYLOAD,
)
assert fit["status"] == "ok"
assert fit["missing"] == []
def test_read_tier_ok_for_subscriber_role_only(self):
# Subscriber: no caps in granted, but ``subscriber`` role
# implies ``read``. This was also broken before the fix.
payload = {
"probe_available": True,
"granted": [],
"roles": ["subscriber"],
}
fit = evaluate_tier_fit(
plugin_type="wordpress",
tier="read",
probe_payload=payload,
)
assert fit["status"] == "ok"
def test_write_tier_ok_when_editor_role_present(self):
# ``edit_posts`` aliases cover ``editor`` and ``administrator``
# roles — this was the alias resolver's job all along; with
# the fix it now also works when the cap only arrives via
# ``roles`` (edge case but possible for leaner probes).
payload = {
"probe_available": True,
"granted": ["upload_files"], # one required cap missing from granted
"roles": ["editor"],
}
fit = evaluate_tier_fit(
plugin_type="wordpress",
tier="write",
probe_payload=payload,
)
assert fit["status"] == "ok"
assert fit["missing"] == []
@pytest.mark.unit
class TestWarningStillFiresForUnderPrivileged:
def test_read_tier_warning_when_no_role_and_no_cap(self):
# Sanity: the union doesn't silence ALL missing-cap warnings,
# only the "role implies cap" bucket.
payload = {
"probe_available": True,
"granted": [],
"roles": [],
}
fit = evaluate_tier_fit(
plugin_type="wordpress",
tier="read",
probe_payload=payload,
)
assert fit["status"] == "warning"
assert fit["missing"] == ["read"]
def test_admin_tier_still_requires_manage_options(self):
# An editor cannot satisfy the admin tier — roles are in the
# union but manage_options still isn't granted.
payload = {
"probe_available": True,
"granted": ["edit_posts", "upload_files"],
"roles": ["editor"],
}
fit = evaluate_tier_fit(
plugin_type="wordpress",
tier="admin",
probe_payload=payload,
)
assert fit["status"] == "warning"
assert fit["missing"] == ["manage_options"]