from __future__ import annotations import unittest from govoplan_core.core.datasources import ( CAPABILITY_DATASOURCE_CATALOGUE, CAPABILITY_DATASOURCE_LIFECYCLE, CAPABILITY_DATASOURCE_ORIGINS, CAPABILITY_DATASOURCE_PUBLICATION, DatasourceCatalogueProvider, DatasourceDescriptor, DatasourceField, DatasourceLifecycleProvider, DatasourceMaterialization, DatasourceOrigin, DatasourceOriginProvider, DatasourceOriginReadResult, DatasourcePublicationProvider, DatasourcePublicationResult, DatasourceReadResult, DatasourceStage, datasource_catalogue, datasource_lifecycle, datasource_origins, datasource_publication, ) from govoplan_core.core.modules import ModuleContext, ModuleManifest from govoplan_core.core.registry import PlatformRegistry class _Provider: descriptor = DatasourceDescriptor( ref="datasource:source-1", source_name="monthly_cases", name="Monthly cases", kind="upload", mode="static", shape="tabular", schema=(DatasourceField(name="case_id", data_type="string", nullable=False),), fingerprint="abc123", ) def list_datasources(self, session, principal, *, query="", limit=100): del session, principal, query return (self.descriptor,)[:limit] def get_datasource(self, session, principal, *, datasource_ref): del session, principal return self.descriptor if datasource_ref == self.descriptor.ref else None def read_datasource(self, session, principal, *, request): del session, principal, request return DatasourceReadResult( datasource=self.descriptor, rows=({"case_id": "0012"},), total_rows=1, truncated=False, ) def list_materializations(self, session, principal, *, datasource_ref): del session, principal, datasource_ref return () def list_stages(self, session, principal, *, limit=100): del session, principal, limit return () def create_stage(self, session, principal, *, stage): del session, principal, stage return DatasourceStage( ref="stage:1", name="Stage", source_name="stage", kind="upload", mode="static", shape="tabular", state="ready", ) def promote_stage(self, session, principal, *, stage_ref, freeze=False, frozen_label=None): del session, principal, stage_ref, freeze, frozen_label return self.descriptor, DatasourceMaterialization( ref="materialization:1", datasource_ref=self.descriptor.ref, revision=1, state="published", fingerprint=self.descriptor.fingerprint, ) def register_origin(self, session, principal, **kwargs): del session, principal, kwargs return self.descriptor def refresh_datasource(self, session, principal, *, datasource_ref): del session, principal, datasource_ref return self.promote_stage(object(), object(), stage_ref="stage:1") def freeze_datasource(self, session, principal, *, datasource_ref, label=None): del session, principal, datasource_ref, label return self.promote_stage(object(), object(), stage_ref="stage:1")[1] def retire_datasource(self, session, principal, *, datasource_ref): del session, principal, datasource_ref return self.descriptor def publish_rows(self, session, principal, *, request): del session, principal, request return DatasourcePublicationResult( ref="publication:1", status="published", datasource=self.descriptor, materialization=DatasourceMaterialization( ref="materialization:1", datasource_ref=self.descriptor.ref, revision=1, state="published", fingerprint=self.descriptor.fingerprint, ), ) def list_origins(self, session, principal, *, query="", limit=100): del session, principal, query return ( DatasourceOrigin( ref="origin:1", source_name="origin", name="Origin", kind="database", shape="tabular", supported_modes=("live", "cached"), provider="test", ), )[:limit] def get_origin(self, session, principal, *, origin_ref): return self.list_origins(session, principal)[0] if origin_ref == "origin:1" else None def read_origin(self, session, principal, *, request): origin = self.get_origin(session, principal, origin_ref=request.origin_ref) return DatasourceOriginReadResult( origin=origin, rows=({"id": 1},), total_rows=1, truncated=False, ) class DatasourceContractTests(unittest.TestCase): def test_capabilities_are_runtime_checkable_and_resolved_without_modules(self) -> None: provider = _Provider() self.assertIsInstance(provider, DatasourceCatalogueProvider) self.assertIsInstance(provider, DatasourceLifecycleProvider) self.assertIsInstance(provider, DatasourcePublicationProvider) self.assertIsInstance(provider, DatasourceOriginProvider) registry = PlatformRegistry() registry.register( ModuleManifest( id="datasource_contract_test", name="Datasource contract test", version="test", capability_factories={ CAPABILITY_DATASOURCE_CATALOGUE: lambda context: provider, CAPABILITY_DATASOURCE_LIFECYCLE: lambda context: provider, CAPABILITY_DATASOURCE_PUBLICATION: lambda context: provider, CAPABILITY_DATASOURCE_ORIGINS: lambda context: provider, }, ) ) registry.configure_capability_context(ModuleContext(registry=registry, settings=object())) self.assertIs(provider, datasource_catalogue(registry)) self.assertIs(provider, datasource_lifecycle(registry)) self.assertIs(provider, datasource_publication(registry)) self.assertIs(provider, datasource_origins(registry)) self.assertIsNone(datasource_catalogue(PlatformRegistry())) def test_descriptor_distinguishes_mode_kind_shape_and_materialization(self) -> None: descriptor = _Provider.descriptor self.assertEqual("static", descriptor.mode) self.assertEqual("upload", descriptor.kind) self.assertEqual("tabular", descriptor.shape) if __name__ == "__main__": unittest.main()