Files
wehub-resource-sync fbfefa28d3
CodeQL / Analyze (python) (push) Failing after 0s
Release / Build (push) Failing after 1s
Test Suite / Unit Tests (push) Failing after 0s
Release / Release (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:18:10 +08:00

347 lines
11 KiB
Python

"""
Mock HTTP server for consistent testing without external dependencies.
This server provides:
- Static HTML pages with predictable content
- JSON/XML/CSV endpoints
- Rate limiting simulation
- Error condition simulation
- Dynamic content generation
"""
import json
import time
from http.server import BaseHTTPRequestHandler, HTTPServer
from threading import Thread
from typing import Dict, Optional
from urllib.parse import parse_qs, urlparse
class MockHTTPRequestHandler(BaseHTTPRequestHandler):
"""Request handler for the mock HTTP server."""
# Track request count for rate limiting simulation
request_count: Dict[str, int] = {}
def log_message(self, format, *args):
"""Suppress default logging."""
pass
def do_GET(self):
"""Handle GET requests."""
parsed_path = urlparse(self.path)
path = parsed_path.path
query_params = parse_qs(parsed_path.query)
# Route requests
if path == "/":
self._serve_home()
elif path == "/products":
self._serve_products()
elif path == "/projects":
self._serve_projects()
elif path == "/api/data.json":
self._serve_json_data()
elif path == "/api/data.xml":
self._serve_xml_data()
elif path == "/api/data.csv":
self._serve_csv_data()
elif path == "/slow":
self._serve_slow_response()
elif path == "/error/404":
self._serve_404()
elif path == "/error/500":
self._serve_500()
elif path == "/rate-limited":
self._serve_rate_limited()
elif path == "/dynamic":
self._serve_dynamic_content()
elif path == "/pagination":
self._serve_pagination(query_params)
else:
self._serve_404()
def _serve_home(self):
"""Serve home page."""
html = """
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Mock Test Website</title>
</head>
<body>
<h1>Welcome to Mock Test Website</h1>
<p>This is a test website for ScrapeGraphAI testing.</p>
<nav>
<a href="/products">Products</a>
<a href="/projects">Projects</a>
</nav>
</body>
</html>
"""
self._send_html_response(html)
def _serve_products(self):
"""Serve products page."""
html = """
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Products</title>
</head>
<body>
<h1>Our Products</h1>
<div class="products">
<article class="product" data-id="1">
<h2 class="product-name">Product Alpha</h2>
<p class="product-description">High-quality product for testing</p>
<span class="product-price">$99.99</span>
<span class="product-stock">In Stock</span>
</article>
<article class="product" data-id="2">
<h2 class="product-name">Product Beta</h2>
<p class="product-description">Another great product</p>
<span class="product-price">$149.99</span>
<span class="product-stock">Limited Stock</span>
</article>
<article class="product" data-id="3">
<h2 class="product-name">Product Gamma</h2>
<p class="product-description">Premium product option</p>
<span class="product-price">$199.99</span>
<span class="product-stock">Out of Stock</span>
</article>
</div>
</body>
</html>
"""
self._send_html_response(html)
def _serve_projects(self):
"""Serve projects page."""
html = """
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Projects</title>
</head>
<body>
<h1>Our Projects</h1>
<div class="projects">
<article class="project">
<h2>Project Alpha</h2>
<p class="description">A comprehensive web scraping solution</p>
<a href="https://github.com/example/alpha">GitHub</a>
</article>
<article class="project">
<h2>Project Beta</h2>
<p class="description">AI-powered data extraction tool</p>
<a href="https://github.com/example/beta">GitHub</a>
</article>
</div>
</body>
</html>
"""
self._send_html_response(html)
def _serve_json_data(self):
"""Serve JSON endpoint."""
data = {
"company": "Test Company",
"description": "A mock company for testing",
"employees": [
{"name": "Alice", "role": "Engineer", "department": "Engineering"},
{"name": "Bob", "role": "Designer", "department": "Design"},
{"name": "Charlie", "role": "Manager", "department": "Operations"},
],
"founded": "2020",
"location": "San Francisco",
}
self._send_json_response(data)
def _serve_xml_data(self):
"""Serve XML endpoint."""
xml = """<?xml version="1.0" encoding="UTF-8"?>
<company>
<name>Test Company</name>
<description>A mock company for testing</description>
<employees>
<employee>
<name>Alice</name>
<role>Engineer</role>
</employee>
<employee>
<name>Bob</name>
<role>Designer</role>
</employee>
</employees>
</company>
"""
self._send_xml_response(xml)
def _serve_csv_data(self):
"""Serve CSV endpoint."""
csv = """name,role,department
Alice,Engineer,Engineering
Bob,Designer,Design
Charlie,Manager,Operations"""
self._send_csv_response(csv)
def _serve_slow_response(self):
"""Simulate a slow response."""
time.sleep(2) # 2 second delay
self._send_html_response("<html><body><h1>Slow Response</h1></body></html>")
def _serve_404(self):
"""Serve 404 error."""
self.send_response(404)
self.send_header("Content-type", "text/html")
self.end_headers()
self.wfile.write(b"<html><body><h1>404 Not Found</h1></body></html>")
def _serve_500(self):
"""Serve 500 error."""
self.send_response(500)
self.send_header("Content-type", "text/html")
self.end_headers()
self.wfile.write(b"<html><body><h1>500 Internal Server Error</h1></body></html>")
def _serve_rate_limited(self):
"""Simulate rate limiting."""
client_ip = self.client_address[0]
self.request_count[client_ip] = self.request_count.get(client_ip, 0) + 1
if self.request_count[client_ip] > 5:
self.send_response(429)
self.send_header("Content-type", "text/html")
self.send_header("Retry-After", "60")
self.end_headers()
self.wfile.write(b"<html><body><h1>429 Too Many Requests</h1></body></html>")
else:
self._send_html_response("<html><body><h1>Rate Limited Endpoint</h1></body></html>")
def _serve_dynamic_content(self):
"""Serve dynamically generated content."""
timestamp = int(time.time())
html = f"""
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Dynamic Content</title>
</head>
<body>
<h1>Dynamic Content</h1>
<p class="timestamp">Generated at: {timestamp}</p>
<p class="random-data">Random value: {timestamp % 1000}</p>
</body>
</html>
"""
self._send_html_response(html)
def _serve_pagination(self, query_params):
"""Serve paginated content."""
page = int(query_params.get("page", ["1"])[0])
per_page = 10
total_items = 50
items = []
start = (page - 1) * per_page
end = min(start + per_page, total_items)
for i in range(start, end):
items.append(f'<li class="item">Item {i + 1}</li>')
next_page = page + 1 if end < total_items else None
prev_page = page - 1 if page > 1 else None
html = f"""
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Pagination - Page {page}</title>
</head>
<body>
<h1>Paginated Content - Page {page}</h1>
<ul class="items">
{''.join(items)}
</ul>
<nav class="pagination">
{f'<a href="/pagination?page={prev_page}">Previous</a>' if prev_page else ''}
<span>Page {page}</span>
{f'<a href="/pagination?page={next_page}">Next</a>' if next_page else ''}
</nav>
</body>
</html>
"""
self._send_html_response(html)
def _send_html_response(self, html: str, status: int = 200):
"""Send HTML response."""
self.send_response(status)
self.send_header("Content-type", "text/html; charset=utf-8")
self.end_headers()
self.wfile.write(html.encode("utf-8"))
def _send_json_response(self, data: dict, status: int = 200):
"""Send JSON response."""
self.send_response(status)
self.send_header("Content-type", "application/json")
self.end_headers()
self.wfile.write(json.dumps(data).encode("utf-8"))
def _send_xml_response(self, xml: str, status: int = 200):
"""Send XML response."""
self.send_response(status)
self.send_header("Content-type", "application/xml")
self.end_headers()
self.wfile.write(xml.encode("utf-8"))
def _send_csv_response(self, csv: str, status: int = 200):
"""Send CSV response."""
self.send_response(status)
self.send_header("Content-type", "text/csv")
self.end_headers()
self.wfile.write(csv.encode("utf-8"))
class MockHTTPServer:
"""Mock HTTP server for testing."""
def __init__(self, host: str = "localhost", port: int = 8888):
self.host = host
self.port = port
self.server: Optional[HTTPServer] = None
self.thread: Optional[Thread] = None
def start(self):
"""Start the mock server in a background thread."""
self.server = HTTPServer((self.host, self.port), MockHTTPRequestHandler)
self.thread = Thread(target=self.server.serve_forever, daemon=True)
self.thread.start()
time.sleep(0.1) # Give server time to start
def stop(self):
"""Stop the mock server."""
if self.server:
self.server.shutdown()
self.server.server_close()
if self.thread:
self.thread.join(timeout=1)
def get_url(self, path: str = "") -> str:
"""Get full URL for a given path."""
return f"http://{self.host}:{self.port}{path}"
def __enter__(self):
"""Context manager entry."""
self.start()
return self
def __exit__(self, exc_type, exc_val, exc_tb):
"""Context manager exit."""
self.stop()