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>
124 lines
3.6 KiB
Python
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
|