Module Package Release / publish-packages (push) Successful in 13s
Release v0.1.46. Coordinated integrity review: GovOPlaN/govoplan-core#298.
207 lines
7.9 KiB
Python
207 lines
7.9 KiB
Python
from __future__ import annotations
|
|
|
|
import unittest
|
|
|
|
from govoplan_core.core.datasources import (
|
|
DatasourceDescriptor,
|
|
DatasourceReadRequest,
|
|
DatasourceReadResult,
|
|
)
|
|
from govoplan_core.core.modules import ModuleContext, ModuleManifest
|
|
from govoplan_core.core.registry import PlatformRegistry
|
|
from govoplan_core.core.tabular_sources import (
|
|
CAPABILITY_CONNECTORS_TABULAR_SNAPSHOT_WRITER,
|
|
CAPABILITY_CONNECTORS_TABULAR_SOURCES,
|
|
TabularColumn,
|
|
TabularPreviewDiagnostic,
|
|
TabularPushdown,
|
|
TabularReadRequest,
|
|
TabularReadResult,
|
|
TabularSnapshotInput,
|
|
TabularSnapshotWriter,
|
|
TabularSource,
|
|
TabularSourceHealth,
|
|
TabularSourceProvider,
|
|
TabularSourceValidationError,
|
|
parse_tabular_csv,
|
|
tabular_snapshot_writer,
|
|
tabular_source_provider,
|
|
)
|
|
|
|
|
|
class _TabularProvider:
|
|
source = TabularSource(
|
|
ref="snapshot:source-1",
|
|
provider="snapshot",
|
|
source_name="monthly_cases",
|
|
name="Monthly cases",
|
|
schema=(TabularColumn(name="case_id", data_type="string", nullable=False),),
|
|
fingerprint="abc123",
|
|
row_count=1,
|
|
source_mode="cached",
|
|
pushdown=TabularPushdown(projections=True, pagination=True),
|
|
health=TabularSourceHealth(
|
|
status="healthy",
|
|
code="snapshot.ready",
|
|
summary="Immutable snapshot is ready.",
|
|
),
|
|
)
|
|
|
|
def list_sources(self, session, principal, *, query="", limit=100):
|
|
del session, principal
|
|
if query and query.casefold() not in self.source.name.casefold():
|
|
return ()
|
|
return (self.source,)[:limit]
|
|
|
|
def get_source(self, session, principal, *, source_ref):
|
|
del session, principal
|
|
return self.source if source_ref == self.source.ref else None
|
|
|
|
def read_source(self, session, principal, *, request):
|
|
del session, principal
|
|
rows = ({"case_id": "A-1"},)
|
|
selected = rows[request.offset : request.offset + request.limit]
|
|
return TabularReadResult(
|
|
source=self.source,
|
|
rows=selected,
|
|
total_rows=len(rows),
|
|
truncated=len(selected) < len(rows),
|
|
returned_bytes=18,
|
|
elapsed_ms=1,
|
|
effective_row_limit=request.limit,
|
|
effective_byte_limit=request.max_bytes,
|
|
effective_timeout_ms=request.timeout_ms,
|
|
diagnostics=(
|
|
TabularPreviewDiagnostic(
|
|
severity="info",
|
|
code="preview.bounded",
|
|
message="The preview used explicit budgets.",
|
|
),
|
|
),
|
|
)
|
|
|
|
def create_snapshot(self, session, principal, *, snapshot):
|
|
del session, principal, snapshot
|
|
return self.source
|
|
|
|
|
|
class TabularSourceContractTests(unittest.TestCase):
|
|
def test_provider_and_snapshot_writer_are_runtime_checkable(self) -> None:
|
|
provider = _TabularProvider()
|
|
|
|
self.assertIsInstance(provider, TabularSourceProvider)
|
|
self.assertIsInstance(provider, TabularSnapshotWriter)
|
|
|
|
def test_capabilities_resolve_without_importing_connectors(self) -> None:
|
|
provider = _TabularProvider()
|
|
registry = PlatformRegistry()
|
|
registry.register(
|
|
ModuleManifest(
|
|
id="tabular_contract_test",
|
|
name="Tabular contract test",
|
|
version="test",
|
|
capability_factories={
|
|
CAPABILITY_CONNECTORS_TABULAR_SOURCES: lambda context: provider,
|
|
CAPABILITY_CONNECTORS_TABULAR_SNAPSHOT_WRITER: lambda context: provider,
|
|
},
|
|
)
|
|
)
|
|
registry.configure_capability_context(ModuleContext(registry=registry, settings=object()))
|
|
|
|
self.assertIs(provider, tabular_source_provider(registry))
|
|
self.assertIs(provider, tabular_snapshot_writer(registry))
|
|
self.assertIsNone(tabular_source_provider(PlatformRegistry()))
|
|
|
|
def test_read_and_snapshot_dtos_are_immutable_and_bounded_by_callers(self) -> None:
|
|
provider = _TabularProvider()
|
|
request = TabularReadRequest(source_ref=provider.source.ref, limit=1)
|
|
result = provider.read_source(object(), object(), request=request)
|
|
snapshot = TabularSnapshotInput(
|
|
name="Monthly cases",
|
|
source_name="monthly_cases",
|
|
rows=({"case_id": "A-1"},),
|
|
)
|
|
|
|
self.assertEqual(({"case_id": "A-1"},), result.rows)
|
|
self.assertEqual("cached", result.source.source_mode)
|
|
self.assertTrue(result.source.pushdown.projections)
|
|
self.assertEqual("healthy", result.source.health.status)
|
|
self.assertEqual("preview.bounded", result.diagnostics[0].code)
|
|
self.assertEqual(1_000_000, request.max_bytes)
|
|
self.assertEqual(2_000, request.timeout_ms)
|
|
self.assertEqual(provider.source, provider.create_snapshot(object(), object(), snapshot=snapshot))
|
|
|
|
def test_datasource_read_contract_preserves_live_preview_evidence(self) -> None:
|
|
request = DatasourceReadRequest(datasource_ref="datasource:monthly-cases")
|
|
result = DatasourceReadResult(
|
|
datasource=DatasourceDescriptor(
|
|
ref=request.datasource_ref,
|
|
source_name="monthly_cases",
|
|
name="Monthly cases",
|
|
kind="database",
|
|
mode="live",
|
|
shape="tabular",
|
|
),
|
|
rows=(),
|
|
total_rows=0,
|
|
truncated=False,
|
|
returned_bytes=2,
|
|
elapsed_ms=3,
|
|
effective_row_limit=request.limit,
|
|
effective_byte_limit=request.max_bytes,
|
|
effective_timeout_ms=request.timeout_ms,
|
|
diagnostics=(
|
|
TabularPreviewDiagnostic(
|
|
severity="info",
|
|
code="preview.complete",
|
|
message="The bounded preview completed.",
|
|
),
|
|
),
|
|
)
|
|
|
|
self.assertEqual(1_000_000, request.max_bytes)
|
|
self.assertEqual(2_000, request.timeout_ms)
|
|
self.assertEqual(2, result.returned_bytes)
|
|
self.assertEqual("preview.complete", result.diagnostics[0].code)
|
|
|
|
def test_shared_csv_parser_preserves_identifier_zeroes_and_rejects_extra_values(self) -> None:
|
|
rows = parse_tabular_csv(
|
|
"case_id;amount;active\n0012;7.5;true\n\n",
|
|
delimiter=";",
|
|
max_rows=2,
|
|
)
|
|
|
|
self.assertEqual(
|
|
({"case_id": "0012", "amount": 7.5, "active": True},),
|
|
rows,
|
|
)
|
|
with self.assertRaises(TabularSourceValidationError):
|
|
parse_tabular_csv("id,name\n1,Ada,extra\n")
|
|
|
|
def test_text_csv_mode_preserves_lexical_values_and_explicit_empty_records(self) -> None:
|
|
source = 'id,value\r\n9007199254740993," keep me "\r\ntrue,0.123456789012345678901234567890\r\n" ",""\r\n'
|
|
self.assertEqual(
|
|
(
|
|
{"id": "9007199254740993", "value": " keep me "},
|
|
{"id": "true", "value": "0.123456789012345678901234567890"},
|
|
{"id": " ", "value": ""},
|
|
),
|
|
parse_tabular_csv(source, value_mode="text"),
|
|
)
|
|
self.assertEqual(({"value": " "},), parse_tabular_csv('value\n" "\n', value_mode="text"))
|
|
|
|
def test_text_csv_mode_rejects_shape_loss_and_applies_input_and_row_bounds(self) -> None:
|
|
for source in ('id,name\n1\n', 'id,name\n1,Ada,\n'):
|
|
with self.subTest(source=source), self.assertRaises(TabularSourceValidationError):
|
|
parse_tabular_csv(source, value_mode="text")
|
|
with self.assertRaises(TabularSourceValidationError):
|
|
parse_tabular_csv('value\n""\n""\n', value_mode="text", max_rows=1)
|
|
with self.assertRaises(TabularSourceValidationError):
|
|
parse_tabular_csv('value\nä\n', value_mode="text", max_bytes=8)
|
|
with self.assertRaises(TabularSourceValidationError):
|
|
parse_tabular_csv('value\nx\n', value_mode="unknown")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|