Ship fleet, availability, pricing, dispatch, routing, tracking, and settlement on delivery-service 0.10.8.0 with migrations and phase tests green. Co-authored-by: Cursor <cursoragent@cursor.com>
219 lines
7.6 KiB
Python
219 lines
7.6 KiB
Python
"""Architecture tests — Delivery module boundary enforcement."""
|
|
from __future__ import annotations
|
|
|
|
import ast
|
|
import re
|
|
from pathlib import Path
|
|
|
|
from app.models import foundation as models
|
|
|
|
FORBIDDEN_IMPORT_PREFIXES = (
|
|
"backend.services.accounting",
|
|
"backend.services.crm",
|
|
"backend.services.loyalty",
|
|
"backend.services.communication",
|
|
"backend.services.sports_center",
|
|
"backend.services.automation",
|
|
"backend.services.notification",
|
|
"backend.services.file_storage",
|
|
"backend.services.identity_access",
|
|
"backend.core_service",
|
|
)
|
|
|
|
FOUNDATION_MODELS = [
|
|
models.DeliveryOrganization,
|
|
models.DeliveryHub,
|
|
models.DeliveryRole,
|
|
models.DeliveryPermission,
|
|
models.ExternalProviderConfig,
|
|
models.RoutingEngineRegistration,
|
|
models.DeliveryConfiguration,
|
|
models.DeliverySetting,
|
|
models.DeliveryAuditLog,
|
|
]
|
|
|
|
PHASE_TABLES = {
|
|
"drivers",
|
|
"driver_credentials",
|
|
"driver_documents",
|
|
"driver_lifecycle_events",
|
|
"fleets",
|
|
"vehicle_types",
|
|
"vehicles",
|
|
"vehicle_assignments",
|
|
"driver_availabilities",
|
|
"shifts",
|
|
"shift_assignments",
|
|
"working_zones",
|
|
"pricing_rules",
|
|
"capability_definitions",
|
|
"capability_bundles",
|
|
"dispatch_jobs",
|
|
"job_assignments",
|
|
"route_plans",
|
|
"route_stops",
|
|
"optimization_runs",
|
|
"tracking_sessions",
|
|
"tracking_points",
|
|
"proof_of_delivery",
|
|
"customer_tracking_tokens",
|
|
"settlement_intents",
|
|
"settlement_lines",
|
|
"outbox_events",
|
|
}
|
|
|
|
|
|
def test_all_models_have_tenant_id():
|
|
from app.core.database import Base
|
|
import app.models # noqa: F401
|
|
|
|
skip = {"alembic_version"}
|
|
for table in Base.metadata.tables.values():
|
|
if table.name in skip:
|
|
continue
|
|
assert "tenant_id" in table.columns, f"{table.name} missing tenant_id"
|
|
|
|
|
|
def test_foundation_aggregates_are_independent():
|
|
assert len(FOUNDATION_MODELS) == 9
|
|
for model in FOUNDATION_MODELS:
|
|
assert hasattr(model, "tenant_id")
|
|
assert hasattr(model, "id")
|
|
assert not model.__mapper__.relationships
|
|
|
|
|
|
def test_phase_tables_registered():
|
|
from app.core.database import Base
|
|
import app.models # noqa: F401
|
|
|
|
names = set(Base.metadata.tables.keys())
|
|
assert PHASE_TABLES.issubset(names)
|
|
assert "journal_entries" not in names
|
|
|
|
|
|
def test_permissions_defined():
|
|
from app.permissions.definitions import ALL_PERMISSIONS, PERMISSION_PREFIXES
|
|
|
|
assert "delivery.view" in ALL_PERMISSIONS
|
|
assert "delivery.drivers.create" in ALL_PERMISSIONS
|
|
assert "delivery.fleets.create" in ALL_PERMISSIONS
|
|
assert "delivery.vehicle_types.view" in ALL_PERMISSIONS
|
|
assert "delivery.vehicles.assignments.manage" in ALL_PERMISSIONS
|
|
assert "delivery.availability.manage" in ALL_PERMISSIONS
|
|
assert "delivery.shifts.create" in ALL_PERMISSIONS
|
|
assert "delivery.working_zones.view" in ALL_PERMISSIONS
|
|
assert "delivery.pricing_rules.create" in ALL_PERMISSIONS
|
|
assert "delivery.capabilities.view" in ALL_PERMISSIONS
|
|
assert "delivery.bundles.manage" in ALL_PERMISSIONS
|
|
assert "delivery.dispatch.jobs.status" in ALL_PERMISSIONS
|
|
assert "delivery.routes.stops.manage" in ALL_PERMISSIONS
|
|
assert "delivery.optimization.runs.create" in ALL_PERMISSIONS
|
|
assert "delivery.tracking.sessions.view" in ALL_PERMISSIONS
|
|
assert "delivery.proof_of_delivery.create" in ALL_PERMISSIONS
|
|
assert "delivery.customer_tracking.revoke" in ALL_PERMISSIONS
|
|
assert "delivery.settlements.status" in ALL_PERMISSIONS
|
|
assert len(ALL_PERMISSIONS) >= 100
|
|
for prefix in PERMISSION_PREFIXES:
|
|
assert any(
|
|
p.startswith(prefix.rstrip(".")) or p.startswith(prefix)
|
|
for p in ALL_PERMISSIONS
|
|
)
|
|
|
|
|
|
def test_events_defined():
|
|
from app.events.types import DeliveryEventType
|
|
|
|
assert DeliveryEventType.DRIVER_CREATED.value == "delivery.driver.created"
|
|
assert DeliveryEventType.FLEET_CREATED.value == "delivery.fleet.created"
|
|
assert DeliveryEventType.SHIFT_CREATED.value == "delivery.shift.created"
|
|
assert DeliveryEventType.PRICING_RULE_CREATED.value == "delivery.pricing_rule.created"
|
|
assert DeliveryEventType.DISPATCH_JOB_CREATED.value == "delivery.dispatch_job.created"
|
|
assert DeliveryEventType.ROUTE_PLAN_CREATED.value == "delivery.route_plan.created"
|
|
assert DeliveryEventType.TRACKING_SESSION_STARTED.value == "delivery.tracking_session.started"
|
|
assert DeliveryEventType.SETTLEMENT_INTENT_CREATED.value == "delivery.settlement_intent.created"
|
|
|
|
|
|
def test_platform_provider_contracts_exist():
|
|
from app.providers import (
|
|
AIProvider,
|
|
AccountingProvider,
|
|
CRMProvider,
|
|
CommunicationProvider,
|
|
FleetProvider,
|
|
IdentityProvider,
|
|
LoyaltyProvider,
|
|
NotificationProvider,
|
|
RoutingEngineProvider,
|
|
StorageProvider,
|
|
)
|
|
|
|
assert AccountingProvider is not None
|
|
assert RoutingEngineProvider is not None
|
|
assert FleetProvider is not None
|
|
assert StorageProvider is not None
|
|
assert IdentityProvider is not None
|
|
assert CRMProvider is not None
|
|
assert CommunicationProvider is not None
|
|
assert NotificationProvider is not None
|
|
assert LoyaltyProvider is not None
|
|
assert AIProvider is not None
|
|
|
|
|
|
def test_no_forbidden_service_imports():
|
|
root = Path(__file__).resolve().parents[1]
|
|
violations: list[str] = []
|
|
for path in root.rglob("*.py"):
|
|
if "tests" in path.parts:
|
|
continue
|
|
tree = ast.parse(path.read_text(encoding="utf-8"), filename=str(path))
|
|
for node in ast.walk(tree):
|
|
if isinstance(node, ast.Import):
|
|
for alias in node.names:
|
|
for forbidden in FORBIDDEN_IMPORT_PREFIXES:
|
|
if alias.name.startswith(forbidden) or forbidden in alias.name:
|
|
violations.append(f"{path}: import {alias.name}")
|
|
elif isinstance(node, ast.ImportFrom) and node.module:
|
|
for forbidden in FORBIDDEN_IMPORT_PREFIXES:
|
|
if node.module.startswith(forbidden) or forbidden in node.module:
|
|
violations.append(f"{path}: from {node.module}")
|
|
assert violations == []
|
|
|
|
|
|
def test_api_ownership_is_delivery_only():
|
|
from app.api.v1 import api_router
|
|
|
|
prefixes = {getattr(r, "path", "") for r in api_router.routes}
|
|
joined = " ".join(sorted(prefixes))
|
|
assert "/organizations" in joined or any("/organizations" in p for p in prefixes)
|
|
assert "/drivers" in joined or any("/drivers" in p for p in prefixes)
|
|
assert "/fleets" in joined or any("/fleets" in p for p in prefixes)
|
|
assert "/dispatch/jobs" in joined or any("/dispatch/jobs" in p for p in prefixes)
|
|
assert "/settlements" in joined or any("/settlements" in p for p in prefixes)
|
|
forbidden = ("accounting", "crm", "loyalty", "notification", "automation", "sports")
|
|
for name in forbidden:
|
|
assert not re.search(rf"(^|/){re.escape(name)}(/|$|\s)", joined), name
|
|
|
|
|
|
def test_folder_structure():
|
|
root = Path(__file__).resolve().parents[2]
|
|
required = [
|
|
"app/models",
|
|
"app/repositories",
|
|
"app/services",
|
|
"app/validators",
|
|
"app/schemas",
|
|
"app/events",
|
|
"app/permissions",
|
|
"app/providers",
|
|
"app/policies",
|
|
"app/specifications",
|
|
"app/commands",
|
|
"app/queries",
|
|
"app/api/v1",
|
|
"app/tests",
|
|
"alembic/versions",
|
|
"README.md",
|
|
]
|
|
for rel in required:
|
|
assert (root / rel).exists(), f"missing {rel}"
|