Scripts & E2E Testing
scripts/hanel_cli.py is a command-line tool for manually testing the gateway against the mock server (or a real t-Server endpoint). It exercises the full stack: input validation, serialization, HTTP transport, response parsing, and Python models.
Prerequisites
-
Mock server running:
bash docker compose up -d -
.envfile configured (or use--endpointto override):dotenv HANEL_ENDPOINT_URL=http://localhost:8080/HanelService HANEL_TEST_MODE=false HANEL_LOT_MANAGEMENT_ENABLED=falseSetHANEL_LOT_MANAGEMENT_ENABLED=trueto use V02/V03/V04 SOAP operations withbatch_numbersupport. -
Dependencies installed:
bash uv sync
Usage
uv run python scripts/hanel_cli.py <operation> [--input FILE] [config overrides...]
| Flag | Description |
|---|---|
operation |
One of: register_article, send_movement_order, get_completed_movements, get_all_orders, get_inventory, cancel_order, ping |
--input FILE |
JSON file with operation parameters. If omitted, reads from stdin. |
--endpoint URL |
Overrides HANEL_ENDPOINT_URL from .env. |
--test-mode / --no-test-mode |
Overrides HANEL_TEST_MODE. When enabled, prepends the test prefix to order numbers and to article numbers — both at registration and inside order lines — so test operations are identifiable warehouse-side. |
--test-prefix STR |
Overrides HANEL_TEST_PREFIX from .env (the prefix applied in test mode). |
--lot-management / --no-lot-management |
Overrides HANEL_LOT_MANAGEMENT_ENABLED (uses V02/V03/V04 SOAP operations with batch_number support). |
--log-level {DEBUG,INFO,WARNING,ERROR} |
Overrides HANEL_LOG_LEVEL from .env. |
--log-soap-payloads / --no-log-soap-payloads |
Overrides HANEL_LOG_SOAP_PAYLOADS from .env (logs the full request/response XML at DEBUG level). |
--verbose, -v |
Shortcut for --log-level DEBUG --log-soap-payloads. Explicit --log-level/--log-soap-payloads flags take precedence over --verbose. |
Configuration: .env → CLI mapping
All configuration is read from .env (or the environment) via GatewayConfig.from_env().
Each CLI flag below, when provided, overrides the corresponding .env value — the
command-line argument always wins. Omit the flag to keep the .env/environment value.
.env variable |
CLI override |
|---|---|
HANEL_ENDPOINT_URL |
--endpoint URL |
HANEL_TEST_MODE |
--test-mode / --no-test-mode |
HANEL_TEST_PREFIX |
--test-prefix STR |
HANEL_LOT_MANAGEMENT_ENABLED |
--lot-management / --no-lot-management |
HANEL_LOG_LEVEL |
--log-level {DEBUG,INFO,WARNING,ERROR} |
HANEL_LOG_SOAP_PAYLOADS |
--log-soap-payloads / --no-log-soap-payloads |
Output is always JSON on stdout. On success:
{"ok": true, "result": ...}
On error, every diagnostic attribute of the exception is included (the exact keys depend on the error type), for example:
{
"ok": false,
"type": "HanelGatewaySoapFaultError",
"message": "SOAP fault in deleteJobReqV01: Warehouse busy or unavailable",
"operation": "deleteJobReqV01",
"detail": "Warehouse busy or unavailable | actor=... | detail=<detail>...</detail>",
"timestamp": "2026-06-13T10:00:00",
"fault_code": "env:Server",
"fault_string": "Warehouse busy or unavailable",
"fault_actor": "...",
"fault_detail": "<detail>...</detail>"
}
Other error types expose their own fields: http_status (HTTP errors),
return_value (application errors), field/value (validation errors).
Combine --verbose with these fields to pinpoint warehouse-side failures.
Exit code is 0 on success, 1 on error.
Operations
register_article
Registers or updates an article in the warehouse catalogue.
JSON input:
{
"article_number": "2001",
"article_name": "M6 stainless bolt",
"batch_number": "LOTTO-2026-A"
}
batch_number is optional. It is only sent to the warehouse when HANEL_LOT_MANAGEMENT_ENABLED=true.
Command:
uv run python scripts/hanel_cli.py register_article \
--input scripts/examples/register_article.json \
--endpoint http://localhost:8080/HanelService
Expected output:
{
"ok": true,
"result": true
}
send_movement_order
Sends a movement order to the warehouse.
operation: "+" = load (put into warehouse), "-" = pick (take from warehouse).
JSON input — single position:
{
"order_number": "ORD-CLI-001",
"positions": [
{"article_number": "1001", "operation": "+", "nominal_quantity": 5}
]
}
JSON input — multiple positions:
{
"order_number": "ORD-CLI-002",
"positions": [
{"article_number": "1001", "operation": "+", "nominal_quantity": 5},
{"article_number": "1002", "operation": "-", "nominal_quantity": 2}
]
}
JSON input — with lot/batch numbers (requires HANEL_LOT_MANAGEMENT_ENABLED=true):
{
"order_number": "ORD-LOT-001",
"positions": [
{"article_number": "1001", "operation": "+", "nominal_quantity": 5, "batch_number": "LOTTO-2026-A"}
]
}
batch_number per position is optional. It is included in the SOAP request only when HANEL_LOT_MANAGEMENT_ENABLED=true.
Command:
uv run python scripts/hanel_cli.py send_movement_order \
--input scripts/examples/send_movement_order_single.json \
--endpoint http://localhost:8080/HanelService
get_completed_movements
Retrieves all completed orders. No JSON input required.
Command:
uv run python scripts/hanel_cli.py get_completed_movements \
--endpoint http://localhost:8080/HanelService
Expected output:
{
"ok": true,
"result": [
{
"job_number": "ORD-001",
"job_priority": 1,
"job_status": 3,
"job_date": "300425",
"job_time": "1430",
"positions": [
{
"article_number": "1001",
"operation": "+",
"nominal_quantity": 5.0,
"actual_quantity": 5.0,
"container_size": 1,
"position_status": 1
}
]
}
]
}
!!! note
If actual_quantity < nominal_quantity in a position, stock was insufficient. Handling is the caller's responsibility.
get_all_orders
Retrieves all orders currently in the warehouse queue (any status, not only completed). No JSON input required.
Command:
uv run python scripts/hanel_cli.py get_all_orders
Expected output:
{
"ok": true,
"result": [
{
"job_number": "ORD-001",
"job_priority": 1,
"job_status": 0,
"job_date": "300425",
"job_time": "1430",
"positions": [
{
"article_number": "1001",
"operation": "+",
"nominal_quantity": 5.0,
"actual_quantity": 0.0,
"container_size": 1,
"position_status": 0
}
]
}
]
}
!!! note
job_status: 0=queued, 1=in progress, 2=partial, 3=completed.
get_inventory
Retrieves stock levels for all articles in the warehouse. No JSON input required.
This is the only mechanism to detect manual movements performed directly at the warehouse console.
Command:
uv run python scripts/hanel_cli.py get_inventory
Expected output:
{
"ok": true,
"result": [
{
"article_number": "1001",
"article_name": "M6 stainless bolt",
"lift_number": 1,
"shelf_number": 2,
"compartment_number": 3,
"compartment_depth_number": 1,
"container_size": 1,
"fifo": 0,
"inventory_at_storage_location": 42.0,
"minimum_inventory": 5.0,
"batch_number": null
}
]
}
!!! note An article may appear in multiple records if distributed across several storage locations.
cancel_order
Cancels a queued order. Only applicable to orders not yet processed (status 0).
JSON input:
{
"order_number": "ORD-001"
}
Command:
echo '{"order_number": "ORD-001"}' | \
uv run python scripts/hanel_cli.py cancel_order \
--endpoint http://localhost:8080/HanelService
Output on failure (order already completed):
{
"ok": false,
"type": "HanelGatewayApplicationError",
"message": "deleteJobReqV01 returned error code 1",
"operation": "deleteJobReqV01",
"detail": "returnValue=1 | response=...",
"return_value": 1
}
ping
Connectivity health-check. Sends the lightweight read-only readAllJobs
request (the Hanel t-Server has no dedicated echo/ping operation) and reports
whether the server is reachable. Any HTTP reply — including a SOAP fault or a
non-2xx status — counts as alive; only a network failure (connection refused or
timeout) is reported as unreachable. To stay fast, the probe uses a single
attempt and a capped timeout instead of the operational retry/timeout settings,
so an unreachable server is reported within a few seconds. Takes no JSON input
and never fails: it returns a boolean result.
Command:
uv run python scripts/hanel_cli.py ping \
--endpoint http://localhost:8080/HanelService
Output when the server is reachable:
{
"ok": true,
"result": true
}
Output when the server is unreachable:
{
"ok": true,
"result": false
}
End-to-end test sequence
Run these commands in order to exercise the full workflow:
ENDPOINT=http://localhost:8080/HanelService
# 1. Reset mock server state
curl -s -X POST http://localhost:8080/admin/reset
# 2. Register a new article
echo '{"article_number": "2001", "article_name": "M6 stainless bolt"}' | \
uv run python scripts/hanel_cli.py register_article --endpoint $ENDPOINT
# 3. Send a movement order
echo '{
"order_number": "ORD-CLI-001",
"positions": [{"article_number": "1001", "operation": "+", "nominal_quantity": 3}]
}' | uv run python scripts/hanel_cli.py send_movement_order --endpoint $ENDPOINT
# 4. Complete all pending orders (mock server admin endpoint)
curl -s -X POST http://localhost:8080/admin/complete-all
# 5. Verify completed movements
uv run python scripts/hanel_cli.py get_completed_movements --endpoint $ENDPOINT
# 6. Try to cancel the (now completed) order — expect HanelGatewayApplicationError
echo '{"order_number": "ORD-CLI-001"}' | \
uv run python scripts/hanel_cli.py cancel_order --endpoint $ENDPOINT
End-to-end test sequence — lot management
Run these commands to test the lot management workflow (requires HANEL_LOT_MANAGEMENT_ENABLED=true in .env):
ENDPOINT=http://localhost:8080/HanelService
# 1. Reset mock server state
curl -s -X POST http://localhost:8080/admin/reset
# 2. Register an article with a batch number (uses sendAPDReqV03)
echo '{"article_number": "2001", "article_name": "M6 stainless bolt", "batch_number": "LOTTO-2026-A"}' | \
uv run python scripts/hanel_cli.py register_article --endpoint $ENDPOINT
# 3. Send a movement order with batch number on each position (uses sendJobsV02)
echo '{
"order_number": "ORD-LOT-001",
"positions": [{"article_number": "1001", "operation": "+", "nominal_quantity": 3, "batch_number": "LOTTO-2026-A"}]
}' | uv run python scripts/hanel_cli.py send_movement_order --endpoint $ENDPOINT
# 4. Complete all pending orders
curl -s -X POST http://localhost:8080/admin/complete-all
# 5. Verify completed movements — response includes batch_number per position (uses readAllJobsV02)
uv run python scripts/hanel_cli.py get_completed_movements --endpoint $ENDPOINT
Example JSON files
Ready-to-use files are in scripts/examples/:
| File | Operation |
|---|---|
register_article.json |
register_article |
send_movement_order_single.json |
send_movement_order — single position |
send_movement_order_multi.json |
send_movement_order — multiple positions |
cancel_order.json |
cancel_order |