#!/usr/bin/env python3 """ Integration tests for the Rantii proxy server """ import pytest import asyncio import aiohttp from aiohttp import web from aiohttp.test_utils import AioHTTPTestCase, unittest_run_loop from proxy import ( create_app, API_BASE, PORT ) class TestProxyIntegration(AioHTTPTestCase): """Integration tests for the proxy server""" async def get_application(self): """Create test application""" return create_app() @unittest_run_loop async def test_static_file_serving(self): """Test static file serving""" resp = await self.client.request('GET', '/index.html') self.assertEqual(resp.status, 200) self.assertIn('text/html', resp.headers['Content-Type']) text = await resp.text() self.assertIn('Rantii', text) @unittest_run_loop async def test_static_file_not_found(self): """Test 404 for non-existent files""" resp = await self.client.request('GET', '/nonexistent.html') self.assertEqual(resp.status, 404) @unittest_run_loop async def test_static_file_path_traversal_blocked(self): """Test path traversal attacks are blocked""" resp = await self.client.request('GET', '/../etc/passwd') self.assertEqual(resp.status, 403) @unittest_run_loop async def test_cors_headers_on_static(self): """Test CORS headers are added to static responses""" resp = await self.client.request('GET', '/index.html') self.assertEqual(resp.headers['Access-Control-Allow-Origin'], '*') @unittest_run_loop async def test_options_request(self): """Test OPTIONS requests are handled""" resp = await self.client.request('OPTIONS', '/api/devrant/rants') self.assertEqual(resp.status, 200) self.assertEqual(resp.headers['Access-Control-Allow-Origin'], '*') @unittest_run_loop async def test_api_proxy_invalid_path(self): """Test API proxy blocks invalid paths""" resp = await self.client.request('GET', '/api/../../../etc/passwd') self.assertEqual(resp.status, 400) data = await resp.json() self.assertFalse(data['success']) self.assertIn('Invalid path', data['error']) @unittest_run_loop async def test_image_proxy_missing_url(self): """Test image proxy requires url parameter""" resp = await self.client.request('GET', '/api/proxy-image') self.assertEqual(resp.status, 400) @unittest_run_loop async def test_image_proxy_invalid_host(self): """Test image proxy blocks invalid hosts""" resp = await self.client.request('GET', '/api/proxy-image?url=https://evil.com/image.jpg') self.assertEqual(resp.status, 403) @unittest_run_loop async def test_image_proxy_allowed_host(self): """Test image proxy allows valid hosts""" # This would normally proxy to devrant, but for testing we'll mock # Since it's integration, we might skip or use a test image URL resp = await self.client.request('GET', '/api/proxy-image?url=https://img.devrant.com/test.jpg') # In real scenario, this would attempt to fetch, but for test we check the attempt # Since we can't mock external requests easily in integration, we'll check the setup self.assertIn(resp.status, [200, 503]) # Either success or service unavailable if external fails @unittest_run_loop async def test_api_cors_headers(self): """Test CORS headers on API responses""" resp = await self.client.request('GET', '/api/devrant/rants?sort=recent&limit=1') # Even if the API call fails (external), CORS headers should be present self.assertEqual(resp.headers['Access-Control-Allow-Origin'], '*') self.assertIn('GET, POST, DELETE, OPTIONS', resp.headers['Access-Control-Allow-Methods']) @unittest_run_loop async def test_root_redirects_to_index(self): """Test root path serves index.html""" resp = await self.client.request('GET', '/') self.assertEqual(resp.status, 200) self.assertIn('text/html', resp.headers['Content-Type']) text = await resp.text() self.assertIn('Rantii', text) class TestProxyServerStartup: """Test server startup and configuration""" def test_app_creation(self): """Test application can be created""" app = create_app() self.assertIsInstance(app, web.Application) def test_app_routes_configured(self): """Test application has routes configured""" app = create_app() routes = [str(route) for route in app.router.routes()] self.assertTrue(any('GET' in route and '/api/' in route for route in routes)) self.assertTrue(any('POST' in route and '/api/' in route for route in routes)) self.assertTrue(any('OPTIONS' in route and '/api/' in route for route in routes)) if __name__ == '__main__': pytest.main([__file__])