Skip to content

Mock AIOTIC server

Python SDK

A local stand-in for a tenant, for development and CI. It implements the public API with realistic behaviour and really calls your ERP receive endpoint on POST /erp/send.

bash
aiotic mock                      # http://localhost:8080
python -m aiotic.mock            # same, without the CLI extra
docker compose -f mock/docker-compose.yml up     # mock + demo integration service
SettingEnv / flagDefault
Keysfixedmock-integration-key (all endpoints), mock-sync-key (master data only)
ERP receive URL / keyMOCK_ERP_URL, MOCK_ERP_KEY or --erp-url/--erp-key or POST /_mock/confignone → /erp/send answers 503 like a tenant without ERP config
Processing webhookMOCK_WEBHOOK_URL, MOCK_WEBHOOK_KEYoff
Processing timeMOCK_PROCESSING_SECONDS / --processing-speed3 s

Behaviour

  • Uploads move QUEUEDPROCESSINGPROCESSED with a realistic extracted order (result), PO number taken from the file name (PO-4711.pdfPO-4711).
  • File name contains attentionATTENTION with one unresolved line (article_number: null, customer_item_number: "LT-ART-999").
  • File name contains failFAILED with last_error; POST /order/retry creates a new order that succeeds.
  • Raw .eml containing "quotation"/"offerte" → 400 not_a_purchase_order + entry in /rejected/list; containing two Subject: lines or SPLIT → split into two child orders with an email_group_id.
  • Master data: seeded with customer 58931, products PROD-001, PROD-002, 620206_01 (nl) and one mapping; a mapping needs an existing customer and product (404 otherwise); the sync key is rejected on non-master-data paths (401).
  • /erp/send: status gate (409), SENDING lock, real POST to the ERP URL with X-API-KEY, success interpretation, 422/500 and rollback, erp_ref on success.

Mock-only helpers

EndpointPurpose
POST /_mock/config {erp_url, erp_key, webhook_url, webhook_key}set targets at runtime
POST /_mock/orders/{id}/status {"status": "MODIFIED"}force a status the API cannot set (e.g. MODIFIED, CANCELED) to test your handling

In tests

A pytest fixture that starts the mock in a background thread and hands you a client:

python
import asyncio, threading, pytest, uvicorn
from aiotic import AioticClient
from aiotic.mock import create_mock_app

@pytest.fixture(scope="session")
def client():
    server = uvicorn.Server(uvicorn.Config(create_mock_app(processing_seconds=0.3), host="127.0.0.1", port=0, log_level="warning"))
    server.install_signal_handlers = lambda: None
    threading.Thread(target=server.run, daemon=True).start()
    while not server.started:
        asyncio.run(asyncio.sleep(0.05))
    port = server.servers[0].sockets[0].getsockname()[1]
    with AioticClient(base_url=f"http://127.0.0.1:{port}", api_key="mock-integration-key", rate_limit=0) as c:
        yield c
    server.should_exit = True

For pure unit tests of the receive endpoint you do not need the mock at all — call ErpReceiver.handle() directly.

Differences from a real tenant

No AI: the extraction is canned. No mailbox. No management endpoints. No operator app. Timing is instant-ish. Everything about the contracts — paths, headers, status codes, JSON shapes, the ERP call — matches the public API.

Documentation revision 3 · Published 8 September 2026 · commit 6862d5e. Verified against AIOTIC API v1.0.0. AIOTIC is a product of DevOps Company.