fix: clarify unavailable legacy blocker resolution
This commit is contained in:
@@ -3,7 +3,7 @@ import pytest
|
||||
from sqlalchemy import select
|
||||
|
||||
from central.app.blocked_products import upsert_blocker
|
||||
from central.app.blocker_resolution import resolve_blocker_with_context, validate_resolution_input
|
||||
from central.app.blocker_resolution import BlockerResolutionUnavailable, resolve_blocker_with_context, validate_resolution_input
|
||||
from central.app.main import create_app
|
||||
from central.app.models import AdminUser, Agent, AuditEvent, BlockedProduct, FiscalContextEntry, Job, Installation, Tenant
|
||||
from central.tests.test_control_plane import seed, setup_db
|
||||
@@ -117,7 +117,7 @@ def test_legacy_blocker_without_installation_fails_closed_without_job_or_audit()
|
||||
session.commit()
|
||||
admin = session.get(AdminUser, admin_id)
|
||||
|
||||
with pytest.raises(LookupError, match="installation"):
|
||||
with pytest.raises(BlockerResolutionUnavailable, match="Blocker legado"):
|
||||
resolve_blocker_with_context(session, blocker_id, admin, {"scope": "PRODUCT", "scope_key": "P-001", "key": "product_role", "value": "RESALE", "reason": "reviewed"})
|
||||
|
||||
assert session.query(FiscalContextEntry).count() == 0
|
||||
@@ -125,6 +125,29 @@ def test_legacy_blocker_without_installation_fails_closed_without_job_or_audit()
|
||||
assert session.query(AuditEvent).count() == 0
|
||||
|
||||
|
||||
def test_resolution_rejects_company_key_over_database_limit_before_mutation():
|
||||
session_factory = setup_db()
|
||||
blocker_id, admin_id, installation_id = _seed_blocker(session_factory)
|
||||
|
||||
with session_factory() as session:
|
||||
installation = session.get(Installation, installation_id)
|
||||
installation.company_code = "C" * 65
|
||||
session.commit()
|
||||
admin = session.get(AdminUser, admin_id)
|
||||
|
||||
with pytest.raises(ValueError, match="company key.*64"):
|
||||
resolve_blocker_with_context(
|
||||
session,
|
||||
blocker_id,
|
||||
admin,
|
||||
{"scope": "PRODUCT", "scope_key": "P-001", "key": "product_role", "value": "RESALE", "reason": "reviewed"},
|
||||
)
|
||||
|
||||
assert session.query(FiscalContextEntry).count() == 0
|
||||
assert session.query(Job).count() == 0
|
||||
assert session.query(AuditEvent).count() == 0
|
||||
|
||||
|
||||
def test_cross_tenant_installation_pointer_fails_before_mutation():
|
||||
session_factory = setup_db()
|
||||
blocker_id, admin_id, _ = _seed_blocker(session_factory)
|
||||
@@ -242,3 +265,47 @@ def test_human_and_json_resolution_routes_use_the_same_validated_workflow():
|
||||
json={"scope": "PRODUCT", "scope_key": "P-001", "key": "entry_scope", "value": "INTER", "reason": "reviewed"},
|
||||
)
|
||||
assert unauthenticated.status_code == 401
|
||||
|
||||
|
||||
def test_unbound_blocker_resolution_routes_return_conflict_but_missing_stays_not_found():
|
||||
session_factory = setup_db()
|
||||
blocker_id, _, _ = _seed_blocker(session_factory)
|
||||
with session_factory() as session:
|
||||
session.get(BlockedProduct, blocker_id).installation_id = None
|
||||
session.commit()
|
||||
|
||||
client = TestClient(create_app(database_probe=lambda: True, session_factory=session_factory, bootstrap_token=None))
|
||||
client.post("/login", data={"username": "operator", "password": "a-secure-password"}, follow_redirects=False)
|
||||
payload = {"csrf_token": client.cookies.get("gti_csrf"), "scope": "PRODUCT", "scope_key": "P-001", "key": "product_role", "value": "RESALE", "reason": "reviewed"}
|
||||
|
||||
for route in (f"/blockers/{blocker_id}/resolve-context", f"/v1/blockers/{blocker_id}/resolve-human"):
|
||||
response = client.post(route, data=payload) if route.startswith("/blockers/") else client.post(route, json=payload)
|
||||
assert response.status_code == 409
|
||||
assert "sem vínculo comprovado" in response.text.lower()
|
||||
|
||||
missing = client.post("/v1/blockers/999999/resolve-human", json=payload)
|
||||
assert missing.status_code == 404
|
||||
|
||||
|
||||
def test_invalid_resolution_rerenders_submitted_values_and_persisted_context():
|
||||
session_factory = setup_db()
|
||||
blocker_id, _, _ = _seed_blocker(session_factory)
|
||||
with session_factory() as session:
|
||||
blocker = session.get(BlockedProduct, blocker_id)
|
||||
session.add(FiscalContextEntry(tenant_id=blocker.tenant_id, company_key="ACME-COMPANY", scope="PRODUCT", scope_key="P-001", key="entry_scope", value="INTER", source="OPERATOR", reason="already saved"))
|
||||
session.commit()
|
||||
|
||||
client = TestClient(create_app(database_probe=lambda: True, session_factory=session_factory, bootstrap_token=None))
|
||||
client.post("/login", data={"username": "operator", "password": "a-secure-password"}, follow_redirects=False)
|
||||
response = client.post(
|
||||
f"/blockers/{blocker_id}/resolve-context",
|
||||
data={"csrf_token": client.cookies.get("gti_csrf"), "key": "product_role", "value": "NOT_VALID", "scope": "COMPANY", "scope_key": "ACME", "reason": "submitted reason"},
|
||||
)
|
||||
|
||||
assert response.status_code == 422
|
||||
assert 'option value="product_role" selected' in response.text
|
||||
assert 'name="value" required maxlength="512" value="NOT_VALID"' in response.text
|
||||
assert 'option value="COMPANY" selected' in response.text
|
||||
assert 'name="scope_key" value="ACME"' in response.text
|
||||
assert "submitted reason" in response.text
|
||||
assert "entry_scope" in response.text and "INTER" in response.text
|
||||
|
||||
Reference in New Issue
Block a user