201 lines
7.2 KiB
Python
201 lines
7.2 KiB
Python
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,
|
|
OPEN_XCHANGE_PROVIDER_ID,
|
|
manifest,
|
|
)
|
|
from govoplan_calendar.backend.provider_state import (
|
|
caldav_provider_states,
|
|
ews_provider_states,
|
|
graph_provider_states,
|
|
ics_provider_states,
|
|
open_xchange_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, OPEN_XCHANGE_PROVIDER_ID},
|
|
{item.id for item in manifest.external_providers},
|
|
)
|
|
self.assertEqual(
|
|
{CALDAV_PROVIDER_ID, ICS_PROVIDER_ID, GRAPH_PROVIDER_ID, EWS_PROVIDER_ID, OPEN_XCHANGE_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)
|
|
|
|
def test_open_xchange_profile_has_distinct_provider_state(self) -> None:
|
|
self.source.metadata_ = {
|
|
"integration_profile": "open_xchange",
|
|
"connector_profile_ref": "connector:secret-detail",
|
|
"identity_mapping_ref": "idm:secret-detail",
|
|
}
|
|
self.source.last_status = "ok"
|
|
self.source.last_synced_at = datetime.now(UTC)
|
|
self.session.flush()
|
|
|
|
self.assertEqual(
|
|
(),
|
|
caldav_provider_states(
|
|
ExternalProviderStateContext(session=self.session, tenant_id="tenant-1")
|
|
),
|
|
)
|
|
state = open_xchange_provider_states(
|
|
ExternalProviderStateContext(session=self.session, tenant_id="tenant-1")
|
|
)[0]
|
|
|
|
self.assertEqual(OPEN_XCHANGE_PROVIDER_ID, state.provider_id)
|
|
self.assertEqual("healthy", state.health)
|
|
self.assertNotIn("secret-detail", str(state.to_dict()))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|