"""
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': '