Files
pic/tests/test_email_endpoints.py
T
roof 76bbc2b67a
Unit Tests / test (push) Successful in 7m27s
fix: EmailManager route calls get_email_users not get_users
The method is named get_email_users in EmailManager; the route was
calling the non-existent get_users, causing an AttributeError on every
GET /api/email/users request.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 10:12:24 -04:00

240 lines
8.1 KiB
Python

#!/usr/bin/env python3
"""
Unit tests for email Flask endpoints in api/app.py.
Covers:
GET /api/email/users
POST /api/email/users
DELETE /api/email/users/<username>
GET /api/email/status
GET /api/email/connectivity
"""
import sys
import json
import unittest
from pathlib import Path
from unittest.mock import patch, MagicMock
api_dir = Path(__file__).parent.parent / 'api'
sys.path.insert(0, str(api_dir))
from app import app
# Sentinel value that make service_registry.get(...) return non-None (service installed)
_INSTALLED = {'id': 'email', 'installed': True}
class TestGetEmailUsers(unittest.TestCase):
def setUp(self):
app.config['TESTING'] = True
self.client = app.test_client()
self._sr_patcher = patch('app.service_registry')
mock_sr = self._sr_patcher.start()
mock_sr.get.return_value = _INSTALLED
def tearDown(self):
self._sr_patcher.stop()
@patch('app.email_manager')
def test_get_users_returns_200_with_list(self, mock_em):
mock_em.get_email_users.return_value = [
{'username': 'alice@cell', 'domain': 'cell'},
{'username': 'bob@cell', 'domain': 'cell'},
]
r = self.client.get('/api/email/users')
self.assertEqual(r.status_code, 200)
data = json.loads(r.data)
self.assertIsInstance(data, list)
self.assertEqual(len(data), 2)
@patch('app.email_manager')
def test_get_users_returns_empty_list_when_no_users(self, mock_em):
mock_em.get_email_users.return_value = []
r = self.client.get('/api/email/users')
self.assertEqual(r.status_code, 200)
self.assertEqual(json.loads(r.data), [])
@patch('app.email_manager')
def test_get_users_returns_500_on_exception(self, mock_em):
mock_em.get_email_users.side_effect = Exception('mailbox unreachable')
r = self.client.get('/api/email/users')
self.assertEqual(r.status_code, 500)
self.assertIn('error', json.loads(r.data))
class TestCreateEmailUser(unittest.TestCase):
def setUp(self):
app.config['TESTING'] = True
self.client = app.test_client()
self._sr_patcher = patch('app.service_registry')
mock_sr = self._sr_patcher.start()
mock_sr.get.return_value = _INSTALLED
def tearDown(self):
self._sr_patcher.stop()
@patch('app.email_manager')
def test_create_user_returns_200_on_success(self, mock_em):
mock_em.create_email_user.return_value = True
r = self.client.post(
'/api/email/users',
data=json.dumps({'username': 'alice', 'password': 'secret123'}),
content_type='application/json',
)
self.assertEqual(r.status_code, 200)
data = json.loads(r.data)
self.assertIn('created', data)
@patch('app.email_manager')
def test_create_user_calls_manager_with_username_and_password(self, mock_em):
mock_em.create_email_user.return_value = True
self.client.post(
'/api/email/users',
data=json.dumps({'username': 'alice', 'password': 'secret123'}),
content_type='application/json',
)
mock_em.create_email_user.assert_called_once()
args = mock_em.create_email_user.call_args[0]
self.assertEqual(args[0], 'alice')
self.assertEqual(args[2], 'secret123')
@patch('app.email_manager')
def test_create_user_returns_400_when_username_missing(self, mock_em):
r = self.client.post(
'/api/email/users',
data=json.dumps({'password': 'secret123'}),
content_type='application/json',
)
self.assertEqual(r.status_code, 400)
self.assertIn('error', json.loads(r.data))
mock_em.create_email_user.assert_not_called()
@patch('app.email_manager')
def test_create_user_returns_400_when_password_missing(self, mock_em):
r = self.client.post(
'/api/email/users',
data=json.dumps({'username': 'alice'}),
content_type='application/json',
)
self.assertEqual(r.status_code, 400)
self.assertIn('error', json.loads(r.data))
mock_em.create_email_user.assert_not_called()
@patch('app.email_manager')
def test_create_user_returns_400_when_no_body(self, mock_em):
r = self.client.post('/api/email/users')
self.assertEqual(r.status_code, 400)
self.assertIn('error', json.loads(r.data))
@patch('app.email_manager')
def test_create_user_returns_500_on_exception(self, mock_em):
mock_em.create_email_user.side_effect = Exception('SMTP config error')
r = self.client.post(
'/api/email/users',
data=json.dumps({'username': 'alice', 'password': 'secret123'}),
content_type='application/json',
)
self.assertEqual(r.status_code, 500)
self.assertIn('error', json.loads(r.data))
class TestDeleteEmailUser(unittest.TestCase):
def setUp(self):
app.config['TESTING'] = True
self.client = app.test_client()
self._sr_patcher = patch('app.service_registry')
mock_sr = self._sr_patcher.start()
mock_sr.get.return_value = _INSTALLED
def tearDown(self):
self._sr_patcher.stop()
@patch('app.email_manager')
def test_delete_user_returns_200_on_success(self, mock_em):
mock_em.delete_email_user.return_value = True
r = self.client.delete('/api/email/users/alice')
self.assertEqual(r.status_code, 200)
data = json.loads(r.data)
self.assertIn('deleted', data)
@patch('app.email_manager')
def test_delete_user_calls_manager_with_username(self, mock_em):
mock_em.delete_email_user.return_value = True
self.client.delete('/api/email/users/bob')
mock_em.delete_email_user.assert_called_once()
args = mock_em.delete_email_user.call_args[0]
self.assertEqual(args[0], 'bob')
@patch('app.email_manager')
def test_delete_user_returns_500_on_exception(self, mock_em):
mock_em.delete_email_user.side_effect = Exception('LDAP error')
r = self.client.delete('/api/email/users/alice')
self.assertEqual(r.status_code, 500)
self.assertIn('error', json.loads(r.data))
class TestGetEmailStatus(unittest.TestCase):
def setUp(self):
app.config['TESTING'] = True
self.client = app.test_client()
@patch('app.email_manager')
def test_get_status_returns_200_with_status_dict(self, mock_em):
mock_em.get_status.return_value = {
'running': True,
'smtp_port': 25,
'imap_port': 993,
}
r = self.client.get('/api/email/status')
self.assertEqual(r.status_code, 200)
data = json.loads(r.data)
self.assertIn('running', data)
@patch('app.email_manager')
def test_get_status_returns_500_on_exception(self, mock_em):
mock_em.get_status.side_effect = Exception('container not found')
r = self.client.get('/api/email/status')
self.assertEqual(r.status_code, 500)
self.assertIn('error', json.loads(r.data))
class TestEmailConnectivity(unittest.TestCase):
def setUp(self):
app.config['TESTING'] = True
self.client = app.test_client()
self._sr_patcher = patch('app.service_registry')
mock_sr = self._sr_patcher.start()
mock_sr.get.return_value = _INSTALLED
def tearDown(self):
self._sr_patcher.stop()
@patch('app.email_manager')
def test_connectivity_returns_200_with_result(self, mock_em):
mock_em.test_connectivity.return_value = {
'smtp': True,
'imap': True,
'latency_ms': 12,
}
r = self.client.get('/api/email/connectivity')
self.assertEqual(r.status_code, 200)
data = json.loads(r.data)
self.assertIn('smtp', data)
@patch('app.email_manager')
def test_connectivity_returns_500_on_exception(self, mock_em):
mock_em.test_connectivity.side_effect = Exception('timeout')
r = self.client.get('/api/email/connectivity')
self.assertEqual(r.status_code, 500)
self.assertIn('error', json.loads(r.data))
if __name__ == '__main__':
unittest.main()