Files
scadalink-design/docs/test_infra/test_infra_restapi.md
Joseph Doherty d91aa83665 refactor(docs): move requirements and test infra docs into docs/ subdirectories
Organize documentation by moving requirements (HighLevelReqs, Component-*,
lmxproxy_protocol) to docs/requirements/ and test infrastructure docs to
docs/test_infra/. Updates all cross-references in README, CLAUDE.md,
infra/README, component docs, and 23 plan files.
2026-03-21 01:11:35 -04:00

6.8 KiB
Raw Permalink Blame History

Test Infrastructure: REST API Server (Flask)

Overview

The test REST API server is a lightweight Python/Flask application that provides HTTP endpoints matching the patterns used by ScadaLink's External System Gateway and Inbound API components. It supports simple parameter/response methods, complex nested object/list methods, authentication, and error simulation.

Image & Ports

  • Image: Custom build from infra/restapi/Dockerfile (Python 3.13 + Flask)
  • API port: 5200

Configuration

Setting Value Description
API_NO_AUTH 0 Set to 1 to disable API key authentication
PORT 5200 Server listen port

Authentication

The server validates requests using one of two methods:

  • API Key: X-API-Key: scadalink-test-key-1 header
  • Basic Auth: Any username/password (accepts all credentials)

The GET /api/Ping endpoint is always unauthenticated (health check).

Auth can be disabled entirely by setting API_NO_AUTH=1 in the Docker Compose environment or passing --no-auth when running directly.

For appsettings.Development.json (External System Gateway):

{
  "ExternalSystems": {
    "TestApi": {
      "BaseUrl": "http://localhost:5200",
      "AuthMode": "ApiKey",
      "ApiKey": "scadalink-test-key-1"
    }
  }
}

Endpoints

Simple Methods

Method Path HTTP Params Response Description
Ping /api/Ping GET {"pong": true} Health check (no auth)
Add /api/Add POST {"a": 5, "b": 3} {"result": 8} Add two numbers
Multiply /api/Multiply POST {"a": 4, "b": 7} {"result": 28} Multiply two numbers
Echo /api/Echo POST {"message": "hello"} {"message": "hello"} Echo back input
GetStatus /api/GetStatus POST {} {"status": "running", "uptime": 123.4} Server status

Complex Methods (nested objects + lists)

Method Path HTTP Description
GetProductionReport /api/GetProductionReport POST Production report with line details
GetRecipe /api/GetRecipe POST Recipe with ingredients list
SubmitBatch /api/SubmitBatch POST Submit batch with items (complex input)
GetEquipmentStatus /api/GetEquipmentStatus POST Equipment list with nested status objects

Error Simulation

Method Path HTTP Params Description
SimulateTimeout /api/SimulateTimeout POST {"seconds": 5} Delay response (max 60s)
SimulateError /api/SimulateError POST {"code": 500} Return specified HTTP error (400599)

Method Discovery

Method Path HTTP Description
methods /api/methods GET List all available methods with signatures

Response Examples

GetProductionReport ({"siteId": "SiteA", "startDate": "2026-03-01", "endDate": "2026-03-16"}):

{
  "siteName": "Site SiteA",
  "totalUnits": 14250,
  "lines": [
    { "lineName": "Line-1", "units": 8200, "efficiency": 92.5 },
    { "lineName": "Line-2", "units": 6050, "efficiency": 88.1 }
  ]
}

GetRecipe ({"recipeId": "R-100"}):

{
  "recipeId": "R-100",
  "name": "Standard Mix",
  "version": 3,
  "ingredients": [
    { "name": "Material-A", "quantity": 45.0, "unit": "kg" },
    { "name": "Material-B", "quantity": 12.5, "unit": "L" }
  ]
}

SubmitBatch (complex input with items list):

{
  "siteId": "SiteA",
  "recipeId": "R-100",
  "items": [
    { "materialId": "MAT-001", "quantity": 45.0, "lotNumber": "LOT-2026-001" },
    { "materialId": "MAT-002", "quantity": 12.5, "lotNumber": "LOT-2026-002" }
  ]
}

Response: {"batchId": "BATCH-A1B2C3D4", "accepted": true, "itemCount": 2}

GetEquipmentStatus ({"siteId": "SiteA"}):

{
  "siteId": "SiteA",
  "equipment": [
    {
      "equipmentId": "PUMP-001",
      "name": "Feed Pump A",
      "status": { "state": "running", "health": 98.5, "lastMaintenance": "2026-02-15" }
    },
    {
      "equipmentId": "TANK-001",
      "name": "Mix Tank 1",
      "status": { "state": "idle", "health": 100.0, "lastMaintenance": "2026-03-01" }
    },
    {
      "equipmentId": "CONV-001",
      "name": "Conveyor B",
      "status": { "state": "alarm", "health": 72.3, "lastMaintenance": "2026-01-20" }
    }
  ]
}

Verification

  1. Check the container is running:
docker ps --filter name=scadalink-restapi
  1. Test the health endpoint:
curl http://localhost:5200/api/Ping
  1. Test an authenticated call:
curl -X POST http://localhost:5200/api/Add \
  -H "X-API-Key: scadalink-test-key-1" \
  -H "Content-Type: application/json" \
  -d '{"a": 2, "b": 3}'

CLI Tool

The infra/tools/restapi_tool.py script provides a CLI for interacting with the REST API server. This tool requires the requests library (included in tools/requirements.txt).

Commands:

# Check API server connectivity and status
python infra/tools/restapi_tool.py check

# Call a simple method
python infra/tools/restapi_tool.py call --method Add --params '{"a": 2, "b": 3}'

# Call a complex method
python infra/tools/restapi_tool.py call --method GetProductionReport --params '{"siteId": "SiteA", "startDate": "2026-03-01", "endDate": "2026-03-16"}'

# Simulate an error
python infra/tools/restapi_tool.py call --method SimulateError --params '{"code": 503}'

# List all available methods
python infra/tools/restapi_tool.py methods

Use --url to override the base URL (default: http://localhost:5200), --api-key for the API key. Run with --help for full usage.

  • External System Gateway — test HTTP/REST calls (ExternalSystem.Call() and CachedCall()), API key authentication, error classification (5xx vs 4xx), and timeout handling.
  • Inbound API — test the POST /api/{methodName} pattern, flat JSON parameters, and extended type system (Object, List) with complex nested responses.
  • Store-and-Forward Engine — verify buffered retry by using SimulateError to return transient errors (503, 408, 429) and observing store-and-forward behavior.

Notes

  • The server is stateless — no data persistence between container restarts.
  • SimulateTimeout caps at 60 seconds to prevent accidental container hangs.
  • SimulateError accepts codes 400599; other values default to 500.
  • SubmitBatch generates a unique batchId per call (UUID-based).
  • The /api/methods endpoint provides machine-readable method discovery (useful for CLI tool and automated testing).
  • To simulate connection failures for store-and-forward testing, stop the container: docker compose stop restapi. Restart with docker compose start restapi.