PromptWarElection / tests.py
Mr-TD's picture
feat: initialize 2026 Election Assistant web application with real-time tracking and comprehensive event documentation
3c4d71f
"""
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>'),
'&lt;script&gt;alert(1)&lt;/script&gt;')
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': '<script>'}
clean = sanitize_json_input(raw, allowed_keys={'status'})
self.assertIn('status', clean)
self.assertNotIn('evil', clean)
def test_cache_control_on_api(self):
r = self.client.get('/api/steps')
self.assertIn('Cache-Control', r.headers)
self.assertIn('max-age', r.headers['Cache-Control'])
class TestEdgeCases(unittest.TestCase):
"""Test edge cases and error handling."""
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_update_status_missing_body(self):
event = TimelineEvent.query.first()
r = self.client.put(
f'/api/timeline/{event.id}/status',
content_type='application/json',
data=json.dumps({}),
)
self.assertEqual(r.status_code, 400)
def test_update_status_invalid_json(self):
event = TimelineEvent.query.first()
r = self.client.put(
f'/api/timeline/{event.id}/status',
content_type='application/json',
data='not-json',
)
self.assertIn(r.status_code, [400, 500])
def test_update_status_valid(self):
event = TimelineEvent.query.first()
r = self.client.put(
f'/api/timeline/{event.id}/status',
content_type='application/json',
data=json.dumps({'status': 'completed'}),
)
self.assertEqual(r.status_code, 200)
self.assertEqual(r.get_json()['new_status'], 'completed')
def test_update_nonexistent_event(self):
r = self.client.put(
'/api/timeline/99999/status',
content_type='application/json',
data=json.dumps({'status': 'completed'}),
)
self.assertEqual(r.status_code, 404)
def test_config_classes_exist(self):
from app.config import DevelopmentConfig, TestingConfig, ProductionConfig
self.assertTrue(TestingConfig.TESTING)
self.assertFalse(ProductionConfig.DEBUG)
self.assertTrue(DevelopmentConfig.DEBUG)
if __name__ == '__main__':
unittest.main()