Files
scadalink-design/docs/test_infra/test_infra_smtp.md
Joseph Doherty e58e038db9 docs(test-infra): correct SMTP example — Basic auth, TlsMode None, container hostname
The appsettings example used AuthMode 'None', which the delivery code
(MailKitSmtpClientWrapper) rejects — only Basic and OAuth2 are valid.
Switch to a working Basic config with Credentials and TlsMode None, and
document that Server must be the container name scadalink-smtp when the
Notification Service runs inside the docker cluster.
2026-05-21 02:13:19 -04:00

5.1 KiB

Test Infrastructure: SMTP Server (Mailpit)

Overview

The test SMTP server uses Mailpit, a lightweight email testing tool that captures all outgoing emails without delivering them. It provides both an SMTP server for sending and a web UI for inspecting captured messages.

Image & Ports

  • Image: axllent/mailpit:latest
  • SMTP port: 1025
  • Web UI / API: http://localhost:8025

Configuration

Setting Value Description
MP_SMTP_AUTH_ACCEPT_ANY 1 Accept any SMTP credentials (or none) — no real authentication
MP_SMTP_AUTH_ALLOW_INSECURE 1 Allow auth over plain SMTP (no TLS required) — dev only
MP_MAX_MESSAGES 500 Maximum stored messages before oldest are auto-deleted

Mailpit accepts all emails regardless of sender/recipient domain. No emails leave the server — they are captured and viewable in the web UI.

SMTP Connection Settings

For appsettings.Development.json (Notification Service):

{
  "Smtp": {
    "Server": "localhost",
    "Port": 1025,
    "AuthMode": "Basic",
    "Credentials": "test:test",
    "TlsMode": "None",
    "FromAddress": "scada-notifications@company.com",
    "ConnectionTimeout": 30
  }
}

Server host: use localhost only when the Notification Service runs directly on the host. When it runs inside the docker cluster, set Server to the container name scadalink-smtp — the cluster compose stack and the infra compose stack share the scadalink-net network, so the container is reachable by name.

The delivery service (MailKitSmtpClientWrapper) only accepts Basic or OAuth2 — there is no "no auth" mode — so the working config above uses Basic:

  • Basic Auth: MP_SMTP_AUTH_ACCEPT_ANY makes Mailpit accept any username:password, so use a throwaway value such as test:test. This exercises the real auth code path without a real server.
  • OAuth2: Not supported by Mailpit. For OAuth2 testing, use a real Microsoft 365 tenant.

TlsMode must be None: Mailpit on port 1025 is plain SMTP and does not offer STARTTLS. StartTLS or SSL would fail the connection.

Mailpit API

Mailpit exposes a REST API at http://localhost:8025/api for programmatic access:

Endpoint Method Description
/api/v1/info GET Server info (version, message count)
/api/v1/messages GET List messages (supports ?limit=N)
/api/v1/message/{id} GET Read a specific message
/api/v1/messages DELETE Delete all messages
/api/v1/search?query=... GET Search messages

Verification

  1. Check the container is running:
docker ps --filter name=scadalink-smtp
  1. Open the web UI at http://localhost:8025 to view captured emails.

  2. Send a test email using curl or any SMTP client:

# Using Python's smtplib (one-liner)
python3 -c "
import smtplib; from email.mime.text import MIMEText
msg = MIMEText('Test body'); msg['Subject'] = 'Test'; msg['From'] = 'test@example.com'; msg['To'] = 'user@example.com'
smtplib.SMTP('localhost', 1025).sendmail('test@example.com', ['user@example.com'], msg.as_string())
print('Sent')
"

CLI Tool

The infra/tools/smtp_tool.py script provides a convenient CLI for interacting with the SMTP server and Mailpit API. This tool uses only Python standard library modules — no additional dependencies required.

Commands:

# Check SMTP connectivity and Mailpit status
python infra/tools/smtp_tool.py check

# Send a test email
python infra/tools/smtp_tool.py send --to user@example.com --subject "Alarm: Tank High Level" --body "Tank level exceeded 95%"

# Send with BCC (matches ScadaLink notification delivery pattern)
python infra/tools/smtp_tool.py send --to scada-notifications@company.com --bcc "operator1@company.com,operator2@company.com" --subject "Shift Report"

# List captured messages
python infra/tools/smtp_tool.py list

# Read a specific message by ID
python infra/tools/smtp_tool.py read --id <message-id>

# Clear all messages
python infra/tools/smtp_tool.py clear

Use --host and --port to override SMTP defaults (localhost:1025), --api for the Mailpit API URL. Run with --help for full usage.

  • Notification Service — test SMTP delivery, BCC recipient handling, plain-text formatting, and store-and-forward retry behavior (Mailpit can be stopped/started to simulate transient failures).
  • Store-and-Forward Engine — verify buffered retry by stopping the SMTP container and observing queued notifications.

Notes

  • Mailpit does not support OAuth2 Client Credentials authentication. To test the OAuth2 code path, use a real Microsoft 365 tenant (see Q12 in docs/plans/questions.md).
  • To simulate SMTP failures for store-and-forward testing, stop the container: docker compose stop smtp. Restart with docker compose start smtp.
  • The web UI at http://localhost:8025 provides real-time message inspection, search, and message source viewing.
  • No data persistence — messages are stored in a temporary database inside the container and lost on container removal.