feat: OSCBR optional provider, re-baseline after inactivity, field audit, provider UI/feature matrix tests, benchmark chain; 1.3.1
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -17,7 +17,7 @@ def test_health_is_unavailable_when_database_is_down():
|
||||
|
||||
def test_version_endpoint_reports_release_and_fiscal_versions():
|
||||
body = TestClient(create_app(database_probe=lambda: True)).get("/version").json()
|
||||
assert body["version"] == "1.3.0" and body["api_compat"] == "1.0" and body["engine_version"] and body["decision_version"]
|
||||
assert body["version"] == "1.3.1" and body["api_compat"] == "1.0" and body["engine_version"] and body["decision_version"]
|
||||
|
||||
|
||||
def test_production_refuses_the_dev_token(monkeypatch):
|
||||
|
||||
@@ -158,3 +158,45 @@ def test_corrector_switch_off_stops_jobs_but_not_enrichment(monkeypatch):
|
||||
s.commit()
|
||||
assert client.get("/v1/jobs/next", headers=H).json() is None
|
||||
assert resolve(client)["state"] != "FEATURE_DISABLED"
|
||||
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.mark.parametrize("corrector,mode,configured,jobs,enrich", [
|
||||
(True, "DISABLED", True, True, False), # Corrector ON / Enrichment OFF
|
||||
(False, "DRY_RUN", True, False, True), # Corrector OFF / DRY_RUN
|
||||
(False, "ENABLED", True, False, True), # Corrector OFF / ENABLED
|
||||
(True, "ENABLED", True, True, True), # both ON
|
||||
(False, "DISABLED", False, False, False), # no features -> WAITING_FEATURE_CONFIGURATION
|
||||
])
|
||||
def test_feature_mode_matrix(monkeypatch, corrector, mode, configured, jobs, enrich):
|
||||
client, engine = setup(monkeypatch, mode)
|
||||
with Session(engine) as s:
|
||||
i = s.scalars(select(Installation)).one()
|
||||
i.corrector_enabled, i.features_configured = corrector, configured
|
||||
from central.app.jobs import create_job
|
||||
from central.app.models import Agent
|
||||
create_job(s, s.scalars(select(Agent)).one(), "SYNC_INCREMENTAL", {})
|
||||
s.commit()
|
||||
assert (client.get("/v1/jobs/next", headers=H).json() is not None) == jobs
|
||||
assert (resolve(client)["state"] != "FEATURE_DISABLED") == enrich
|
||||
feats = client.get("/v1/agents/me/authorizations", headers=H).json()["features"]
|
||||
assert feats["features_configured"] == configured
|
||||
|
||||
|
||||
def test_provider_panel_shows_every_state_and_column(monkeypatch):
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from central.app.models import ProviderState, ProviderUsageDaily
|
||||
client, agent_id, _ = _panel_client()
|
||||
now = datetime.now(timezone.utc)
|
||||
with client._session_factory() as s:
|
||||
s.add(ProviderState(provider="upcitemdb", configured=True, status="RATE_LIMITED", quota_kind="REMOTE_QUOTA", daily_limit=100, remaining=0, reset_at=now + timedelta(hours=3), last_success_at=now, last_error="RATE_LIMITED: HTTP 429", last_latency_ms=140))
|
||||
s.add(ProviderState(provider="openfoodfacts", configured=True, status="DEGRADED", last_latency_ms=655))
|
||||
s.add(ProviderState(provider="openproductsfacts", configured=True, status="AUTH_ERROR"))
|
||||
s.add(ProviderState(provider="openbeautyfacts", configured=True, status="OFFLINE"))
|
||||
s.add(ProviderUsageDaily(provider="upcitemdb", day=now.strftime("%Y-%m-%d"), requests=100, cache_hits=30, cache_misses=70))
|
||||
s.commit()
|
||||
page = client.get("/").text
|
||||
for label in ("Limite atingido", "Instável", "Credencial recusada", "Indisponível", "Não configurado", "100 / 100", "restam 0", "renova", "último sucesso", "último erro", "140 ms", "cache 30%"):
|
||||
assert label in page, label
|
||||
|
||||
@@ -10,7 +10,7 @@ from sqlalchemy.pool import StaticPool
|
||||
|
||||
from central.app.enrichment import kb
|
||||
from central.app.enrichment.gtin import check_digit_ok
|
||||
from central.app.enrichment.providers import Cosmos, OpenFoodFacts, Provider, ProviderError, ProviderProduct, UpcItemDb, build_providers
|
||||
from central.app.enrichment.providers import Oscbr, Cosmos, OpenFoodFacts, Provider, ProviderError, ProviderProduct, UpcItemDb, build_providers
|
||||
from central.app.enrichment.quota import ProviderGateway
|
||||
from central.app.enrichment.resolver import resolve
|
||||
from central.app.models import (Base, CestEntry, CestNcmEntry, Dataset, GtinLookupCache, KbKnowledge, NcmEntry, ProviderUsageDaily, TipiEntry)
|
||||
@@ -223,7 +223,7 @@ def test_cosmos_without_token_is_not_configured_and_costs_nothing(env):
|
||||
|
||||
def test_paid_provider_guard():
|
||||
class S: allow_paid_providers = False; cosmos_token = None
|
||||
assert [p.name for p in build_providers(S())] == ["cosmos", "upcitemdb", "openfoodfacts", "openproductsfacts", "openbeautyfacts"]
|
||||
assert [p.name for p in build_providers(S())] == ["cosmos", "oscbr", "upcitemdb", "openfoodfacts", "openproductsfacts", "openbeautyfacts"]
|
||||
|
||||
|
||||
# ---- provider contract tests against recorded fixtures (httpx.MockTransport, no network) ----------------------------------------------------------
|
||||
@@ -269,3 +269,26 @@ def test_cosmos_contract_and_errors():
|
||||
with pytest.raises(ProviderError) as e:
|
||||
Cosmos(_client(boom), "tok").lookup_gtin(G1)
|
||||
assert e.value.kind == "TIMEOUT"
|
||||
|
||||
|
||||
def test_oscbr_optional_and_contract():
|
||||
calls = []
|
||||
assert not Oscbr(_client(lambda r: pytest.fail("no call expected"))).configured
|
||||
with pytest.raises(ProviderError):
|
||||
Oscbr(_client(lambda r: pytest.fail("no call expected"))).lookup_gtin(G1)
|
||||
|
||||
def handler(request):
|
||||
calls.append(str(request.url))
|
||||
if request.url.path == "/oauth/token":
|
||||
assert request.headers["authorization"].startswith("Basic ")
|
||||
return httpx.Response(200, json={"token": "tkn"})
|
||||
assert request.headers["authorization"] == "Bearer tkn"
|
||||
return httpx.Response(200, json={"ean": G1, "nome": "COLA 350ML", "marca": "X", "ncm": "2202.10.00", "cest": "03.007.00"})
|
||||
p = Oscbr(_client(handler), None, "u", "p")
|
||||
first, second = p.lookup_gtin(G1), p.lookup_gtin(G1)
|
||||
assert first.ncm == "22021000" and first.cest == "0300700" and second.found
|
||||
assert sum(1 for c in calls if "oauth" in c) == 1 # token reused for ~55 min
|
||||
bad = Oscbr(_client(lambda r: httpx.Response(401)), None, "u", "p")
|
||||
with pytest.raises(ProviderError) as e:
|
||||
bad.lookup_gtin(G1)
|
||||
assert e.value.kind == "AUTH_ERROR"
|
||||
|
||||
Reference in New Issue
Block a user