""" Comprehensive unit tests for the Election Assistant application — India 2026 Covers pages, API, security, Google services, rate limiting, and edge cases. """ import unittest import json import sys import os sys.path.insert(0, os.path.abspath(os.path.dirname(__file__))) from app import create_app, db from app.models import ElectionProcess, ProcessStep, FAQ, TimelineEvent class TestPageRoutes(unittest.TestCase): """Test all page routes return 200 with correct content.""" def setUp(self): self.app = create_app('testing') self.client = self.app.test_client() self.ctx = self.app.app_context() self.ctx.push() db.create_all() from app.models import seed_election_data seed_election_data() def tearDown(self): db.session.remove() db.drop_all() self.ctx.pop() def test_home_page(self): r = self.client.get('/') self.assertEqual(r.status_code, 200) self.assertIn(b'Election', r.data) def test_process_page(self): r = self.client.get('/process') self.assertEqual(r.status_code, 200) self.assertIn(b'Election Process', r.data) def test_timeline_page(self): r = self.client.get('/timeline') self.assertEqual(r.status_code, 200) self.assertIn(b'Timeline', r.data) def test_faq_page(self): r = self.client.get('/faq') self.assertEqual(r.status_code, 200) self.assertIn(b'FAQ', r.data) def test_resources_page(self): r = self.client.get('/resources') self.assertEqual(r.status_code, 200) self.assertIn(b'Resources', r.data) def test_404_page(self): r = self.client.get('/nonexistent-page') self.assertEqual(r.status_code, 404) class TestCoreAPI(unittest.TestCase): """Test core REST API endpoints.""" def setUp(self): self.app = create_app('testing') self.client = self.app.test_client() self.ctx = self.app.app_context() self.ctx.push() db.create_all() from app.models import seed_election_data seed_election_data() def tearDown(self): db.session.remove() db.drop_all() self.ctx.pop() def test_election_process(self): r = self.client.get('/api/election-process') self.assertEqual(r.status_code, 200) data = r.get_json() self.assertIn('India 2026', data['name']) def test_steps(self): r = self.client.get('/api/steps') self.assertEqual(r.status_code, 200) data = r.get_json() self.assertIsInstance(data, list) self.assertEqual(len(data), 5) def test_timeline(self): r = self.client.get('/api/timeline') self.assertEqual(r.status_code, 200) data = r.get_json() self.assertIsInstance(data, list) self.assertGreaterEqual(len(data), 20) def test_faq(self): r = self.client.get('/api/faq') self.assertEqual(r.status_code, 200) data = r.get_json() self.assertIsInstance(data, list) self.assertGreaterEqual(len(data), 10) def test_faq_categories(self): r = self.client.get('/api/faq/categories') self.assertEqual(r.status_code, 200) cats = r.get_json() self.assertIsInstance(cats, list) self.assertGreater(len(cats), 0) def test_faq_filter_by_category(self): r = self.client.get('/api/faq?category=Voting') self.assertEqual(r.status_code, 200) data = r.get_json() self.assertIsInstance(data, list) def test_health(self): r = self.client.get('/api/health') self.assertEqual(r.status_code, 200) self.assertEqual(r.get_json()['status'], 'healthy') def test_live_stats(self): r = self.client.get('/api/live/stats') self.assertEqual(r.status_code, 200) data = r.get_json() self.assertIn('total_events', data) def test_upcoming(self): r = self.client.get('/api/timeline/upcoming') self.assertEqual(r.status_code, 200) def test_events_by_status(self): r = self.client.get('/api/events/by-status/pending') self.assertEqual(r.status_code, 200) def test_invalid_status_filter(self): r = self.client.get('/api/events/by-status/invalid') self.assertEqual(r.status_code, 400) class TestGoogleServices(unittest.TestCase): """Test Google service integrations.""" def setUp(self): self.app = create_app('testing') self.client = self.app.test_client() self.ctx = self.app.app_context() self.ctx.push() db.create_all() from app.models import seed_election_data seed_election_data() def tearDown(self): db.session.remove() db.drop_all() self.ctx.pop() def test_google_config_endpoint(self): r = self.client.get('/api/google/config') self.assertEqual(r.status_code, 200) data = r.get_json() self.assertIn('translate_enabled', data) self.assertIn('maps_available', data) self.assertIn('calendar_supported', data) self.assertTrue(data['calendar_supported']) def test_calendar_link_valid_event(self): event = TimelineEvent.query.first() self.assertIsNotNone(event) r = self.client.get(f'/api/calendar-link/{event.id}') self.assertEqual(r.status_code, 200) data = r.get_json() self.assertIn('calendar_url', data) self.assertIn('calendar.google.com', data['calendar_url']) self.assertIn('event_title', data) def test_calendar_link_nonexistent_event(self): r = self.client.get('/api/calendar-link/99999') self.assertEqual(r.status_code, 404) def test_calendar_url_format(self): """Verify generated Calendar URL contains required params.""" from app.google_services import build_calendar_url from datetime import datetime url = build_calendar_url( title='Test Event', start_date=datetime(2026, 6, 7, 7, 0, 0), description='Test description', ) self.assertIn('action=TEMPLATE', url) self.assertIn('text=Test+Event', url) self.assertIn('calendar.google.com', url) def test_translate_enabled_by_default(self): from app.google_services import is_translate_enabled self.assertTrue(is_translate_enabled()) class TestSecurity(unittest.TestCase): """Test security headers and input sanitization.""" def setUp(self): self.app = create_app('testing') self.client = self.app.test_client() self.ctx = self.app.app_context() self.ctx.push() db.create_all() from app.models import seed_election_data seed_election_data() def tearDown(self): db.session.remove() db.drop_all() self.ctx.pop() def test_csp_header_present(self): r = self.client.get('/') self.assertIn('Content-Security-Policy', r.headers) csp = r.headers['Content-Security-Policy'] self.assertIn("default-src 'self'", csp) self.assertIn('translate.google.com', csp) def test_x_frame_options(self): r = self.client.get('/') self.assertEqual(r.headers.get('X-Frame-Options'), 'SAMEORIGIN') def test_x_content_type_options(self): r = self.client.get('/') self.assertEqual(r.headers.get('X-Content-Type-Options'), 'nosniff') def test_referrer_policy(self): r = self.client.get('/') self.assertEqual(r.headers.get('Referrer-Policy'), 'strict-origin-when-cross-origin') def test_input_sanitization(self): from app.security import sanitize_string self.assertEqual(sanitize_string(''), '<script>alert(1)</script>') def test_sanitize_max_length(self): from app.security import sanitize_string result = sanitize_string('a' * 1000, max_length=100) self.assertEqual(len(result), 100) def test_sanitize_json_input(self): from app.security import sanitize_json_input raw = {'status': 'pending', 'evil': '