Agentic-Reliability-Framework-API / tests /test_outcome_service.py
petter2025's picture
Upload folder using huggingface_hub
afa4de7 verified
raw
history blame
4.17 kB
import pytest
import datetime
from unittest.mock import MagicMock
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from app.database.base import Base
from app.database.models_intents import IntentDB
from app.services.outcome_service import record_outcome, OutcomeConflictError
from agentic_reliability_framework.core.governance.intents import (
ProvisionResourceIntent,
ResourceType,
)
@pytest.fixture
def db_session():
engine = create_engine("sqlite:///:memory:", future=True)
TestingSessionLocal = sessionmaker(bind=engine, future=True)
Base.metadata.create_all(bind=engine)
sess = TestingSessionLocal()
yield sess
sess.close()
@pytest.fixture
def mock_risk_engine():
engine = MagicMock()
engine.update_outcome = MagicMock()
return engine
def test_record_outcome_creates_row_and_updates_engine(
db_session, mock_risk_engine):
oss_intent = ProvisionResourceIntent(
resource_type=ResourceType.VM,
region="eastus",
size="Standard",
environment="dev",
requester="test_user"
)
oss_payload = oss_intent.model_dump(mode='json')
intent = IntentDB(
deterministic_id="intent_abc",
intent_type="ProvisionResourceIntent",
payload={},
oss_payload=oss_payload,
created_at=datetime.datetime.utcnow()
)
db_session.add(intent)
db_session.commit()
db_session.refresh(intent)
outcome = record_outcome(
db=db_session,
deterministic_id="intent_abc",
success=True,
recorded_by="tester",
notes="works",
risk_engine=mock_risk_engine,
idempotency_key="key123"
)
assert outcome.success is True
assert outcome.recorded_by == "tester"
assert outcome.idempotency_key == "key123"
mock_risk_engine.update_outcome.assert_called_once()
# Idempotent call with same key should return existing outcome and not
# call engine again
outcome2 = record_outcome(
db=db_session,
deterministic_id="intent_abc",
success=True,
recorded_by="tester",
notes="again",
risk_engine=mock_risk_engine,
idempotency_key="key123"
)
assert outcome2.id == outcome.id
mock_risk_engine.update_outcome.assert_called_once() # still once
def test_conflict_different_result(db_session, mock_risk_engine):
intent = IntentDB(
deterministic_id="intent_def",
intent_type="ProvisionResourceIntent",
payload={},
created_at=datetime.datetime.utcnow()
)
db_session.add(intent)
db_session.commit()
record_outcome(
db_session,
"intent_def",
True,
None,
None,
mock_risk_engine)
with pytest.raises(OutcomeConflictError):
record_outcome(
db_session,
"intent_def",
False,
None,
None,
mock_risk_engine)
def test_nonexistent_intent(db_session, mock_risk_engine):
with pytest.raises(ValueError):
record_outcome(
db_session,
"missing",
True,
None,
None,
mock_risk_engine)
def test_record_outcome_reconstruction_failure_does_not_update_engine(
db_session, mock_risk_engine):
# Create an intent with invalid oss_payload (missing required fields)
intent = IntentDB(
deterministic_id="intent_bad",
intent_type="ProvisionResourceIntent",
payload={},
oss_payload={"intent_type": "provision_resource"}, # missing fields
created_at=datetime.datetime.utcnow()
)
db_session.add(intent)
db_session.commit()
# This should NOT call risk_engine.update_outcome (no dummy fallback)
outcome = record_outcome(
db=db_session,
deterministic_id="intent_bad",
success=True,
recorded_by="tester",
notes="fallback test",
risk_engine=mock_risk_engine
)
assert outcome.success is True
# The engine should NOT be updated because reconstruction failed
mock_risk_engine.update_outcome.assert_not_called()