feat: declare governed external provider state
This commit is contained in:
@@ -0,0 +1,174 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import UTC, datetime
|
||||
import unittest
|
||||
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
|
||||
from govoplan_access.backend.db import models as access_models # noqa: F401
|
||||
from govoplan_calendar.backend.db.models import CalendarOutboxOperation, CalendarSyncSource
|
||||
from govoplan_calendar.backend.manifest import (
|
||||
CALDAV_PROVIDER_ID,
|
||||
EWS_PROVIDER_ID,
|
||||
GRAPH_PROVIDER_ID,
|
||||
ICS_PROVIDER_ID,
|
||||
manifest,
|
||||
)
|
||||
from govoplan_calendar.backend.provider_state import (
|
||||
caldav_provider_states,
|
||||
ews_provider_states,
|
||||
graph_provider_states,
|
||||
ics_provider_states,
|
||||
)
|
||||
from govoplan_calendar.backend.schemas import (
|
||||
CalendarCalDavSourceCreateRequest,
|
||||
CalendarCollectionCreateRequest,
|
||||
)
|
||||
from govoplan_calendar.backend.service import create_caldav_source, create_calendar
|
||||
from govoplan_core.core.provider_governance import ExternalProviderStateContext
|
||||
from govoplan_core.db.base import Base
|
||||
from govoplan_core.tenancy.scope import create_scope_tables
|
||||
from govoplan_tenancy.backend.db.models import Tenant
|
||||
|
||||
|
||||
class CalendarProviderStateTests(unittest.TestCase):
|
||||
def setUp(self) -> None:
|
||||
self.engine = create_engine("sqlite+pysqlite:///:memory:", future=True)
|
||||
create_scope_tables(self.engine)
|
||||
Base.metadata.create_all(self.engine)
|
||||
self.Session = sessionmaker(bind=self.engine, expire_on_commit=False)
|
||||
self.session = self.Session()
|
||||
self.session.add(Tenant(id="tenant-1", slug="tenant-1", name="Tenant"))
|
||||
self.calendar = create_calendar(
|
||||
self.session,
|
||||
tenant_id="tenant-1",
|
||||
user_id=None,
|
||||
payload=CalendarCollectionCreateRequest(name="Remote"),
|
||||
)
|
||||
self.source = create_caldav_source(
|
||||
self.session,
|
||||
tenant_id="tenant-1",
|
||||
user_id=None,
|
||||
payload=CalendarCalDavSourceCreateRequest(
|
||||
calendar_id=self.calendar.id,
|
||||
collection_url="https://dav.example.test/cal",
|
||||
),
|
||||
)
|
||||
self.session.commit()
|
||||
|
||||
def tearDown(self) -> None:
|
||||
self.session.close()
|
||||
self.engine.dispose()
|
||||
|
||||
def test_state_projects_health_freshness_and_unresolved_outcomes(self) -> None:
|
||||
self.source.last_status = "ok"
|
||||
self.source.last_synced_at = datetime.now(UTC)
|
||||
self.session.flush()
|
||||
|
||||
healthy = caldav_provider_states(
|
||||
ExternalProviderStateContext(
|
||||
session=self.session,
|
||||
tenant_id="tenant-1",
|
||||
)
|
||||
)[0]
|
||||
|
||||
self.assertEqual("healthy", healthy.health)
|
||||
self.assertEqual("current", healthy.freshness)
|
||||
self.assertEqual("clear", healthy.conflict)
|
||||
self.assertEqual("ready", healthy.recovery)
|
||||
self.assertEqual(
|
||||
f"calendar:sync-source:{self.source.id}",
|
||||
healthy.binding_ref,
|
||||
)
|
||||
self.assertNotIn("dav.example.test", str(healthy.to_dict()))
|
||||
|
||||
self.session.add(
|
||||
CalendarOutboxOperation(
|
||||
tenant_id="tenant-1",
|
||||
source_id=self.source.id,
|
||||
operation_kind="put",
|
||||
resource_href="event.ics",
|
||||
idempotency_key="a" * 64,
|
||||
status="conflict",
|
||||
)
|
||||
)
|
||||
self.session.flush()
|
||||
|
||||
conflicted = caldav_provider_states(
|
||||
ExternalProviderStateContext(
|
||||
session=self.session,
|
||||
tenant_id="tenant-1",
|
||||
)
|
||||
)[0]
|
||||
|
||||
self.assertEqual("warning", conflicted.health)
|
||||
self.assertEqual("pending", conflicted.conflict)
|
||||
self.assertEqual("attention", conflicted.recovery)
|
||||
|
||||
def test_state_is_tenant_bounded_and_manifest_registered(self) -> None:
|
||||
self.assertEqual(
|
||||
(),
|
||||
caldav_provider_states(
|
||||
ExternalProviderStateContext(
|
||||
session=self.session,
|
||||
tenant_id="tenant-2",
|
||||
)
|
||||
),
|
||||
)
|
||||
self.assertEqual(
|
||||
{CALDAV_PROVIDER_ID, ICS_PROVIDER_ID, GRAPH_PROVIDER_ID, EWS_PROVIDER_ID},
|
||||
{item.id for item in manifest.external_providers},
|
||||
)
|
||||
self.assertEqual(
|
||||
{CALDAV_PROVIDER_ID, ICS_PROVIDER_ID, GRAPH_PROVIDER_ID, EWS_PROVIDER_ID},
|
||||
{
|
||||
item.provider_id
|
||||
for item in manifest.external_provider_state_providers
|
||||
},
|
||||
)
|
||||
|
||||
def test_read_only_sources_have_distinct_secret_free_provider_state(self) -> None:
|
||||
now = datetime.now(UTC)
|
||||
sources = (
|
||||
("ics", "https://feeds.example.test/team.ics", "none"),
|
||||
("graph", "https://graph.microsoft.com/v1.0/me/calendar/events", "bearer"),
|
||||
("ews", "https://exchange.example.test/EWS/Exchange.asmx", "basic"),
|
||||
)
|
||||
for source_kind, collection_url, auth_type in sources:
|
||||
self.session.add(
|
||||
CalendarSyncSource(
|
||||
tenant_id="tenant-1",
|
||||
calendar_id=self.calendar.id,
|
||||
source_kind=source_kind,
|
||||
collection_url=collection_url,
|
||||
auth_type=auth_type,
|
||||
sync_enabled=True,
|
||||
sync_direction="read_only",
|
||||
last_status="ok",
|
||||
last_synced_at=now,
|
||||
)
|
||||
)
|
||||
self.session.flush()
|
||||
|
||||
states = (
|
||||
ics_provider_states(
|
||||
ExternalProviderStateContext(session=self.session, tenant_id="tenant-1")
|
||||
)[0],
|
||||
graph_provider_states(
|
||||
ExternalProviderStateContext(session=self.session, tenant_id="tenant-1")
|
||||
)[0],
|
||||
ews_provider_states(
|
||||
ExternalProviderStateContext(session=self.session, tenant_id="tenant-1")
|
||||
)[0],
|
||||
)
|
||||
|
||||
self.assertTrue(all(item.authority_mode == "external_mirror" for item in states))
|
||||
self.assertTrue(all(item.health == "healthy" for item in states))
|
||||
rendered = str([item.to_dict() for item in states])
|
||||
self.assertNotIn("feeds.example.test", rendered)
|
||||
self.assertNotIn("exchange.example.test", rendered)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user