infrasynth-backend-kit/tests/test_extensibility.py
jcv-dev 5df0be1f5c feat: uniform extensibility across every module
Close the remaining places where an extension point was hardcoded, and
expose a stable public import surface for every app.

- files: register_storage_backend(...) / STORAGE_BACKENDS[name]["CLASS"];
  unknown backends now fail loudly instead of silently using local
- files: PipelineStepRegistry + @pipeline_step; PIPELINE_EXECUTOR setting
- billing: INVOICE_PDF_BUILDER setting
- security: TWO_FACTOR_SERVICE / TWO_FACTOR_RECOVERY_SERVICE settings
- all app packages expose lazy public exports (PEP 562); infrasynth.shared
  re-exports its primitives eagerly
- README documents the per-module extension-point table
- tests/test_extensibility.py pins each hook plus the public surface
2026-09-24 11:08:08 -05:00

194 lines
6.3 KiB
Python

"""Every module must be extensible without editing the kit.
These tests pin the extension points that let a pip-installed consumer add
storage backends, pipeline steps, an invoice builder, or a 2FA method purely
through settings/registries, and confirm the public import surface.
"""
import io
import pytest
from django.core.exceptions import ImproperlyConfigured
pytestmark = pytest.mark.django_db
# --- fakes referenced by dotted path from settings --------------------------
class FakeStorage:
def __init__(self, config):
self.config = config
self.saved = {}
def save(self, name, content):
self.saved[name] = content.read()
return name
def open(self, name, mode="rb"):
return io.BytesIO(self.saved[name])
def exists(self, name):
return name in self.saved
def url(self, name):
return f"/fake/{name}"
def delete(self, name):
self.saved.pop(name, None)
def generate_signed_url(self, name, expiry_seconds=3600):
return f"/fake/{name}?signed=1"
class RecordingExecutor:
def execute(self, execution):
execution.status = "completed"
execution.save(update_fields=["status"])
return execution
def fake_invoice_builder(invoice):
return b"%PDF-1.4 custom-builder"
class FakeTwoFactorService:
def generate_secret(self):
return "FAKESECRET"
class TestPublicImportSurface:
def test_app_packages_expose_public_names(self):
from infrasynth.audit import ModelChangeLog
from infrasynth.billing import EntitlementService, Plan
from infrasynth.features import FeatureService
from infrasynth.files import FileService, get_storage_backend
from infrasynth.gates import GatePermission
from infrasynth.notifications import NotificationService
from infrasynth.scheduler import TaskService
from infrasynth.security import AuthorizationService, HybridPermission
from infrasynth.shared import Result
from infrasynth.tenancy import TenantManager
from infrasynth.webhooks import EventRegistry
from infrasynth.workflows import WorkflowEngine
assert all(
x is not None
for x in (
ModelChangeLog,
EntitlementService,
Plan,
FeatureService,
FileService,
get_storage_backend,
NotificationService,
TaskService,
AuthorizationService,
HybridPermission,
Result,
TenantManager,
EventRegistry,
WorkflowEngine,
GatePermission,
)
)
def test_unknown_attribute_raises(self):
import infrasynth.security as security
with pytest.raises(AttributeError):
security.ThisDoesNotExist
class TestStorageExtension:
def test_custom_backend_via_settings_class(self, settings):
settings.INFRASYNTH_FILES = {
**settings.INFRASYNTH_FILES,
"STORAGE_BACKENDS": {
**settings.INFRASYNTH_FILES.get("STORAGE_BACKENDS", {}),
"fake": {"CLASS": "tests.test_extensibility.FakeStorage", "REGION": "x"},
},
}
storage = get_backend("fake")
assert isinstance(storage, FakeStorage)
assert storage.config.get("REGION") == "x"
def test_custom_backend_via_registry(self):
from infrasynth.files.storage import register_storage_backend
register_storage_backend("factory-made", FakeStorage)
assert isinstance(get_backend("factory-made"), FakeStorage)
def test_unknown_backend_raises(self):
from infrasynth.files.storage import get_storage_backend
with pytest.raises(ImproperlyConfigured):
get_storage_backend("does-not-exist")
class TestPipelineExtension:
def test_registered_step_is_used(self, settings, user, media_root):
from django.core.files.uploadedfile import SimpleUploadedFile
from infrasynth.files.models import PipelineExecution, ProcessingPipeline
from infrasynth.files.processing import PipelineExecutor, pipeline_step
from infrasynth.files.services import FileService
calls = []
@pipeline_step("test.tag")
def tag_step(data, mime_type, params):
calls.append(params)
return data, mime_type
uploaded = FileService().upload(
SimpleUploadedFile("a.txt", b"hello", content_type="text/plain"),
filename="a.txt",
user=user,
)
pipeline = ProcessingPipeline.objects.create(
name="Tag", slug="tag", steps=[{"type": "test.tag", "params": {"k": "v"}}]
)
execution = PipelineExecution.objects.create(file=uploaded, pipeline=pipeline)
PipelineExecutor().execute(execution)
execution.refresh_from_db()
assert execution.status == PipelineExecution.Status.COMPLETED
assert calls == [{"k": "v"}]
def test_executor_is_swappable(self, settings):
from infrasynth.files.processing import get_pipeline_executor
settings.INFRASYNTH_FILES = {
**settings.INFRASYNTH_FILES,
"PIPELINE_EXECUTOR": "tests.test_extensibility.RecordingExecutor",
}
assert isinstance(get_pipeline_executor(), RecordingExecutor)
class TestInvoiceBuilderExtension:
def test_builder_is_swappable(self, settings):
from infrasynth.billing.invoice_generator import get_invoice_pdf_builder
settings.INFRASYNTH_BILLING = {
**settings.INFRASYNTH_BILLING,
"INVOICE_PDF_BUILDER": "tests.test_extensibility.fake_invoice_builder",
}
assert get_invoice_pdf_builder()(object()) == b"%PDF-1.4 custom-builder"
class TestTwoFactorExtension:
def test_service_is_swappable(self, settings):
from infrasynth.security.two_factor.services import get_two_factor_service
settings.INFRASYNTH_SECURITY = {
**settings.INFRASYNTH_SECURITY,
"TWO_FACTOR_SERVICE": "tests.test_extensibility.FakeTwoFactorService",
}
assert get_two_factor_service().generate_secret() == "FAKESECRET"
def get_backend(name):
from infrasynth.files.storage import get_storage_backend
return get_storage_backend(name)