Let users email personal reports on demand
Magent CI/CD / verify (push) Successful in 11m6s
Magent CI/CD / deploy-prod (push) Skipped
Magent CI/CD / deploy-beta (push) Successful in 1m40s

This commit is contained in:
2026-09-10 16:15:39 +12:00
parent 6e473fd0a7
commit b286ca3c42
13 changed files with 261 additions and 36 deletions
+68
View File
@@ -491,3 +491,71 @@ class RecapEmailTests(unittest.TestCase):
if __name__ == '__main__':
unittest.main()
class OnDemandReportTests(RecapFixture, unittest.IsolatedAsyncioTestCase):
async def test_new_confirmation_defaults_to_manual_without_changing_schedule(self):
with patch.object(mail, 'send_email'):
result = await recaps.subscribe(self.user)
self.assertFalse(result['automatic_monthly'])
self.assertFalse(store.settings()['enabled'])
self.assertEqual(result['state'], 'pending')
with self.assertRaises(recaps.RecapError):
recaps.queue_personal(self.user, None, 'pending')
async def test_manual_current_month_delivers_with_monthly_schedule_off(self):
sub, _ = self.subscribe()
store.set_automatic(self.user['id'], False)
month = datetime.now(timezone.utc).strftime('%Y-%m')
queued = recaps.queue_personal(self.user, month, 'manual-1')
self.assertEqual(recaps.queue_personal(self.user, month, 'manual-1')['id'], queued['id'])
report = {**self.report, **month_periods(month, datetime.now(timezone.utc))}
def send(recipient, rendered, message_id, before_data):
before_data()
self.assertEqual(recipient, self.user['email'])
self.assertIn('so far', rendered['subject'])
self.assertNotIn('[Test]', rendered['subject'])
with patch.object(recaps, 'get_monthly_report', new_callable=AsyncMock, return_value=report), patch.object(mail, 'send_email', side_effect=send):
await recaps.process_delivery(store.claim_delivery(time.time()))
self.assertEqual(self.delivery(queued['id'])['state'], 'sent')
self.assertFalse(store.settings()['enabled'])
self.assertFalse(store.subscription(self.user['id'])['automatic_monthly'])
with self.assertRaises(recaps.RecapError) as error:
recaps.queue_personal(self.user, month, 'manual-2')
self.assertEqual(error.exception.status, 429)
async def test_automatic_opt_out_cancels_scheduled_but_keeps_manual(self):
sub, _ = self.subscribe()
with store.transaction() as conn:
scheduled = store._enqueue(conn, sub, self.report['month'], 'scheduled', 'scheduled-fixture', self.config['public_url'], time.time())
manual = recaps.queue_personal(self.user, None, 'manual')
store.set_automatic(self.user['id'], False)
self.assertEqual(self.delivery(scheduled)['state'], 'cancelled')
self.assertEqual(self.delivery(manual['id'])['state'], 'queued')
self.assertEqual(store.subscription(self.user['id'])['state'], 'enabled')
now = datetime.now(timezone.utc)
store.save_settings({**self.config, 'enabled': True}, now)
self.assertEqual(store.enqueue_due(now + timedelta(days=40)), 0)
async def test_changed_identity_cancels_manual_delivery(self):
self.subscribe()
queued = recaps.queue_personal(self.user, None, 'manual')
delivery = store.claim_delivery(time.time())
db.set_user_email('viewer', 'changed@example.test')
with patch.object(mail, 'send_email') as send:
await recaps.process_delivery(delivery)
send.assert_not_called()
self.assertEqual(self.delivery(queued['id'])['state'], 'cancelled')
async def test_regular_user_can_only_send_to_self(self):
self.subscribe()
app = FastAPI(); app.include_router(router.router)
app.dependency_overrides[get_current_user] = lambda: {'username': 'viewer', 'role': 'user'}
client = TestClient(app)
body = {'month': self.report['month'], 'request_id': '11111111-1111-4111-8111-111111111111'}
for extra in [{'email': 'other@example.test'}, {'user_id': 42}, {'kind': 'scheduled'}]:
self.assertEqual(client.post('/profile/email-recaps/send', json={**body, **extra}).status_code, 422)
self.assertEqual(client.post('/profile/email-recaps/send', json=body).status_code, 202)
response = client.get('/profile/email-recaps')
self.assertEqual(response.headers['cache-control'], 'no-store')
self.assertEqual(len(response.json()['deliveries']), 1)