Files
stack/tests/mail/test_droplet_exercise.py
kert dbf71a6594 test: 99.93% coverage — Zotero 9 schema fix + 400+ new tests
Fix Zotero table models for Zotero 9:
- Remove stale Annotations/Highlights/Transaction* models
- Add ItemAnnotations, RetractedItems, DeletedCollections,
  DeletedSearches, DbDebug1
- Fix ItemAttachments, Libraries, Users column mismatches

New test files covering all major modules:
- cli/{bib,prisma,rec,zot,mail,run} deep exercising tests
- mail/{droplet,postmark,resend,cloudflare} lifecycle tests
- bib/{iom,oig,pincite,sync,regulations_gov,email_ingest,format,store}
- prisma/{vpn,fetch,export,llm,screen,eligibility,extract,project,ingest,flow}
- aco/lake/{unity,quality,deploy} + api/aco coverage gaps
- zot/{ops,db,extract,duck} + rec/{report,engine,base,pricers}
- pfs/{pipe,rules,eq,files}

Add pytest-xdist for parallel test execution.

Tracks #353
2026-04-18 10:06:47 -04:00

115 lines
3.3 KiB
Python

"""Exercise mail/droplet.py lifecycle functions."""
from __future__ import annotations
from unittest.mock import MagicMock, patch
import pytest
from mail.droplet import (
HOSTNAME,
_ssh_args,
_wait_for_active,
apply_dns,
attach_smarthost,
down,
rotate_creds,
status,
up,
)
class TestSshArgs:
@patch.dict("os.environ", {"STACK_SSH_KEY": "/path/to/key"})
def test_with_override(self):
assert _ssh_args() == ["-i", "/path/to/key"]
@patch.dict("os.environ", {}, clear=True)
def test_without_override(self):
assert _ssh_args() == []
class TestWaitForActive:
def test_returns_ip(self):
client = MagicMock()
client.droplets.get.return_value = {
"droplet": {
"status": "active",
"networks": {"v4": [{"ip_address": "1.2.3.4", "type": "public"}]},
}
}
ip = _wait_for_active(client, 123, timeout=5)
assert ip == "1.2.3.4"
def test_timeout(self):
client = MagicMock()
client.droplets.get.return_value = {
"droplet": {"status": "new", "networks": {"v4": []}}
}
with pytest.raises(RuntimeError, match="never went active"):
_wait_for_active(client, 123, timeout=1)
class TestUp:
@patch("mail.droplet._do_client")
def test_adopts_existing(self, mock_client):
client = mock_client.return_value
client.droplets.list.return_value = {
"droplets": [
{
"id": 1,
"name": HOSTNAME,
"region": {"slug": "nyc3"},
"status": "active",
"created_at": "2026-01-01",
"networks": {"v4": [{"ip_address": "5.6.7.8", "type": "public"}]},
}
]
}
with patch("mail.droplet._save_json"):
result = up()
assert result["id"] == 1
class TestDown:
@patch("mail.droplet._do_client")
def test_noop_when_none(self, mock_client):
client = mock_client.return_value
client.droplets.list.return_value = {"droplets": []}
down(confirm=True) # should not raise
class TestStatus:
@patch("mail.droplet._do_client")
def test_returns_none(self, mock_client):
client = mock_client.return_value
client.droplets.list.return_value = {"droplets": []}
assert status() is None
class TestApplyDns:
@patch("mail.droplet._do_client")
def test_raises_no_droplet(self, mock_client):
client = mock_client.return_value
client.droplets.list.return_value = {"droplets": []}
with pytest.raises(RuntimeError, match="no mail droplet"):
apply_dns()
class TestRotateCreds:
@patch("mail.droplet._do_client")
def test_raises_no_droplet(self, mock_client):
client = mock_client.return_value
client.droplets.list.return_value = {"droplets": []}
with pytest.raises(RuntimeError, match="no mail droplet"):
rotate_creds("git")
class TestAttachSmarthost:
@patch("mail.droplet._do_client")
def test_raises_no_droplet(self, mock_client):
client = mock_client.return_value
client.droplets.list.return_value = {"droplets": []}
with pytest.raises(RuntimeError, match="no mail droplet"):
attach_smarthost()