email-connect/tests/test_harness_users.py
tegwick 442a574cf9
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s
EMAIL-WP-0005-T03: add test-user addressing and reset helper
Adds the tests/harness helper package: address(), available(), reset(),
users(), smtp_provider() and mailbox_config().

Tests address mailboxes as harness.address("<test name>"), which slugs the name
under the RFC 2606 reserved harness.email-connect.test domain, so distinct test
names cannot collide and a stray send cannot leave the host. No provisioning
call is needed -- the auth-disabled harness creates the mailbox on first login.

reset() drops every user and message via GreenMail's service reset; the next
login recreates the mailbox empty. It is documented to run at test start so a
crashed test cannot leak state forward, and it is global, so resetting tests
cannot run in parallel against one harness.

Suite: 64 passed with the harness up, 61 passed + 3 skipped with it down.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 01:46:09 +02:00

124 lines
3.6 KiB
Python

"""Test-user addressing and reset contract for the local mail harness.
The slug and config cases run offline. The cases that talk to the harness are
skipped unless it is up, so the default suite stays container-free.
"""
from __future__ import annotations
import imaplib
import tempfile
from pathlib import Path
import pytest
import harness
requires_harness = pytest.mark.skipif(
not harness.available(),
reason="mail harness not running: docker compose -f tests/harness/docker-compose.yml up -d",
)
def mailbox_count(user_address: str) -> int:
connection = imaplib.IMAP4(harness.HOST, harness.IMAP_PORT)
try:
connection.login(user_address, harness.PASSWORD)
_status, data = connection.select("INBOX", readonly=True)
return int(data[0])
finally:
connection.logout()
@pytest.mark.parametrize(
("name", "expected"),
[
("alpha", "alpha"),
("invitation resend", "invitation-resend"),
("Invitation Resend", "invitation-resend"),
("test_user_01", "test-user-01"),
(" spaced ", "spaced"),
("weird!!name??", "weird-name"),
],
)
def test_address_is_a_deterministic_slug(name, expected):
assert harness.address(name) == f"{expected}@{harness.DOMAIN}"
assert harness.address(name) == harness.address(name)
def test_address_rejects_names_with_no_slug():
with pytest.raises(ValueError):
harness.address("!!!")
def test_addresses_use_the_reserved_test_tld():
assert harness.DOMAIN.endswith(".test")
def test_mailbox_config_sets_credential_environment(monkeypatch):
monkeypatch.delenv(harness.IMAP_USER_ENV, raising=False)
monkeypatch.delenv(harness.IMAP_PASSWORD_ENV, raising=False)
user_address = harness.address("config probe")
config = harness.mailbox_config(user_address, storage_path="state.sqlite", reports_dir="reports")
import os
assert os.environ[harness.IMAP_USER_ENV] == user_address
assert os.environ[harness.IMAP_PASSWORD_ENV] == harness.PASSWORD
assert config.mailbox.protocol == "imap"
assert config.mailbox.host == harness.HOST
assert config.mailbox.port == harness.IMAP_PORT
assert config.mailbox.tls is False
assert config.scan.mark_seen is False
@requires_harness
def test_reset_clears_users_and_mail():
harness.reset()
recipient = harness.address("reset probe")
harness.smtp_provider().send(recipient, "Probe", "body")
assert mailbox_count(recipient) == 1
assert any(user["email"] == recipient for user in harness.users())
harness.reset()
assert harness.users() == []
assert mailbox_count(recipient) == 0
@requires_harness
def test_named_test_users_get_isolated_mailboxes():
harness.reset()
first = harness.address("isolation one")
second = harness.address("isolation two")
provider = harness.smtp_provider()
provider.send(first, "For first", "body")
provider.send(first, "For first again", "body")
provider.send(second, "For second", "body")
assert mailbox_count(first) == 2
assert mailbox_count(second) == 1
@requires_harness
def test_scanner_reads_a_harness_mailbox():
harness.reset()
recipient = harness.address("scanner probe")
harness.smtp_provider().send(recipient, "Scanner probe", "body text")
from email_connect.scanner import scan_mailbox
with tempfile.TemporaryDirectory() as tmp:
root = Path(tmp)
config = harness.mailbox_config(
recipient,
storage_path=str(root / "state.sqlite"),
reports_dir=str(root / "reports"),
)
result = scan_mailbox(config)
assert result.scan.messages_seen == 1
assert result.scan.messages_new == 1