Fix Solr configuration and verify all services
- Add stopwords.txt and synonyms.txt for Solr - Remove unsupported handlers from solrconfig.xml for Solr 9.x - Add comprehensive test suite for all backend services - Verify all 15 containers are running properly - All services pass health checks successfully 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -146,9 +146,7 @@
|
|||||||
</lst>
|
</lst>
|
||||||
</requestHandler>
|
</requestHandler>
|
||||||
|
|
||||||
<!-- Schema handler -->
|
<!-- Schema handler (removed for Solr 9.x compatibility) -->
|
||||||
<requestHandler name="/schema" class="solr.SchemaHandler"/>
|
|
||||||
|
|
||||||
<!-- Config handler -->
|
<!-- Config handler (removed for Solr 9.x compatibility) -->
|
||||||
<requestHandler name="/config" class="solr.ConfigHandler"/>
|
|
||||||
</config>
|
</config>
|
||||||
35
services/search/solr-config/conf/stopwords.txt
Normal file
35
services/search/solr-config/conf/stopwords.txt
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
# Licensed to the Apache Software Foundation (ASF)
|
||||||
|
# Standard English stop words
|
||||||
|
a
|
||||||
|
an
|
||||||
|
and
|
||||||
|
are
|
||||||
|
as
|
||||||
|
at
|
||||||
|
be
|
||||||
|
but
|
||||||
|
by
|
||||||
|
for
|
||||||
|
if
|
||||||
|
in
|
||||||
|
into
|
||||||
|
is
|
||||||
|
it
|
||||||
|
no
|
||||||
|
not
|
||||||
|
of
|
||||||
|
on
|
||||||
|
or
|
||||||
|
such
|
||||||
|
that
|
||||||
|
the
|
||||||
|
their
|
||||||
|
then
|
||||||
|
there
|
||||||
|
these
|
||||||
|
they
|
||||||
|
this
|
||||||
|
to
|
||||||
|
was
|
||||||
|
will
|
||||||
|
with
|
||||||
38
services/search/solr-config/conf/synonyms.txt
Normal file
38
services/search/solr-config/conf/synonyms.txt
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
# Synonyms for site11 search
|
||||||
|
# Format: term1, term2, term3 => all are synonyms
|
||||||
|
# Or: term1, term2 => term1 is replaced by term2
|
||||||
|
|
||||||
|
# Technology synonyms
|
||||||
|
javascript, js
|
||||||
|
typescript, ts
|
||||||
|
python, py
|
||||||
|
golang, go
|
||||||
|
database, db
|
||||||
|
kubernetes, k8s
|
||||||
|
docker, container, containerization
|
||||||
|
|
||||||
|
# Common terms
|
||||||
|
search, find, query, lookup
|
||||||
|
upload, import, add
|
||||||
|
download, export, get
|
||||||
|
delete, remove, erase
|
||||||
|
update, modify, edit, change
|
||||||
|
create, make, new, add
|
||||||
|
|
||||||
|
# File related
|
||||||
|
document, doc, file
|
||||||
|
image, picture, photo, img
|
||||||
|
video, movie, clip
|
||||||
|
audio, sound, music
|
||||||
|
|
||||||
|
# User related
|
||||||
|
user, member, account
|
||||||
|
admin, administrator, moderator
|
||||||
|
profile, account, user
|
||||||
|
|
||||||
|
# Status
|
||||||
|
active, enabled, live
|
||||||
|
inactive, disabled, offline
|
||||||
|
pending, waiting, processing
|
||||||
|
complete, done, finished
|
||||||
|
error, failed, failure
|
||||||
416
test_all_services.py
Normal file
416
test_all_services.py
Normal file
@ -0,0 +1,416 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Complete test suite for all backend services
|
||||||
|
"""
|
||||||
|
import asyncio
|
||||||
|
import httpx
|
||||||
|
import json
|
||||||
|
from datetime import datetime
|
||||||
|
import base64
|
||||||
|
import os
|
||||||
|
|
||||||
|
# Service endpoints
|
||||||
|
SERVICES = {
|
||||||
|
"users": "http://localhost:8001",
|
||||||
|
"images": "http://localhost:8002",
|
||||||
|
"oauth": "http://localhost:8003",
|
||||||
|
"console": "http://localhost:8011",
|
||||||
|
"statistics": "http://localhost:8012",
|
||||||
|
"notifications": "http://localhost:8013",
|
||||||
|
"files": "http://localhost:8014",
|
||||||
|
"search": "http://localhost:8015"
|
||||||
|
}
|
||||||
|
|
||||||
|
def print_section(title):
|
||||||
|
"""Print section header"""
|
||||||
|
print(f"\n{'='*60}")
|
||||||
|
print(f" {title}")
|
||||||
|
print(f"{'='*60}")
|
||||||
|
|
||||||
|
def print_test(name, status, details=""):
|
||||||
|
"""Print test result"""
|
||||||
|
icon = "✅" if status else "❌"
|
||||||
|
print(f"{icon} {name}: {details}")
|
||||||
|
|
||||||
|
async def test_health_endpoints():
|
||||||
|
"""Test all health endpoints"""
|
||||||
|
print_section("1. HEALTH CHECK ENDPOINTS")
|
||||||
|
|
||||||
|
async with httpx.AsyncClient(timeout=10.0) as client:
|
||||||
|
results = {}
|
||||||
|
for service, url in SERVICES.items():
|
||||||
|
try:
|
||||||
|
response = await client.get(f"{url}/health")
|
||||||
|
if response.status_code == 200:
|
||||||
|
data = response.json()
|
||||||
|
status = data.get("status", "unknown")
|
||||||
|
print_test(f"{service.upper()} Health", True, f"Status: {status}")
|
||||||
|
results[service] = True
|
||||||
|
else:
|
||||||
|
print_test(f"{service.upper()} Health", False, f"HTTP {response.status_code}")
|
||||||
|
results[service] = False
|
||||||
|
except Exception as e:
|
||||||
|
print_test(f"{service.upper()} Health", False, f"Error: {str(e)}")
|
||||||
|
results[service] = False
|
||||||
|
return results
|
||||||
|
|
||||||
|
async def test_users_service():
|
||||||
|
"""Test Users Service API"""
|
||||||
|
print_section("2. USERS SERVICE TESTS")
|
||||||
|
|
||||||
|
async with httpx.AsyncClient(timeout=10.0) as client:
|
||||||
|
base_url = SERVICES["users"]
|
||||||
|
|
||||||
|
# Create user
|
||||||
|
user_data = {
|
||||||
|
"username": f"testuser_{datetime.now().timestamp()}",
|
||||||
|
"email": f"test_{datetime.now().timestamp()}@example.com",
|
||||||
|
"password": "Test123!@#",
|
||||||
|
"full_name": "Test User"
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = await client.post(f"{base_url}/api/users/register", json=user_data)
|
||||||
|
if response.status_code == 200:
|
||||||
|
user = response.json()
|
||||||
|
print_test("Create User", True, f"User ID: {user.get('id')}")
|
||||||
|
|
||||||
|
# Get user
|
||||||
|
response = await client.get(f"{base_url}/api/users/{user['id']}")
|
||||||
|
print_test("Get User", response.status_code == 200, f"Username: {user.get('username')}")
|
||||||
|
|
||||||
|
# List users
|
||||||
|
response = await client.get(f"{base_url}/api/users")
|
||||||
|
data = response.json()
|
||||||
|
print_test("List Users", response.status_code == 200, f"Total: {data.get('total', 0)} users")
|
||||||
|
|
||||||
|
# Update user
|
||||||
|
update_data = {"bio": "Updated bio"}
|
||||||
|
response = await client.put(f"{base_url}/api/users/{user['id']}", json=update_data)
|
||||||
|
print_test("Update User", response.status_code == 200)
|
||||||
|
|
||||||
|
# Delete user
|
||||||
|
response = await client.delete(f"{base_url}/api/users/{user['id']}")
|
||||||
|
print_test("Delete User", response.status_code == 200)
|
||||||
|
else:
|
||||||
|
print_test("Create User", False, f"HTTP {response.status_code}")
|
||||||
|
except Exception as e:
|
||||||
|
print_test("Users Service", False, f"Error: {str(e)}")
|
||||||
|
|
||||||
|
async def test_oauth_service():
|
||||||
|
"""Test OAuth Service"""
|
||||||
|
print_section("3. OAUTH SERVICE TESTS")
|
||||||
|
|
||||||
|
async with httpx.AsyncClient(timeout=10.0) as client:
|
||||||
|
base_url = SERVICES["oauth"]
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Test OAuth providers
|
||||||
|
response = await client.get(f"{base_url}/api/oauth/providers")
|
||||||
|
if response.status_code == 200:
|
||||||
|
providers = response.json()
|
||||||
|
print_test("Get OAuth Providers", True, f"Providers: {', '.join(providers.get('providers', []))}")
|
||||||
|
else:
|
||||||
|
print_test("Get OAuth Providers", False, f"HTTP {response.status_code}")
|
||||||
|
|
||||||
|
# Test Google OAuth URL
|
||||||
|
response = await client.get(f"{base_url}/api/oauth/google/authorize")
|
||||||
|
print_test("Google OAuth URL", response.status_code == 200, "Authorization URL generated")
|
||||||
|
|
||||||
|
# Test GitHub OAuth URL
|
||||||
|
response = await client.get(f"{base_url}/api/oauth/github/authorize")
|
||||||
|
print_test("GitHub OAuth URL", response.status_code == 200, "Authorization URL generated")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print_test("OAuth Service", False, f"Error: {str(e)}")
|
||||||
|
|
||||||
|
async def test_images_service():
|
||||||
|
"""Test Images Service"""
|
||||||
|
print_section("4. IMAGES SERVICE TESTS")
|
||||||
|
|
||||||
|
async with httpx.AsyncClient(timeout=10.0) as client:
|
||||||
|
base_url = SERVICES["images"]
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Create test image (1x1 pixel PNG)
|
||||||
|
image_data = base64.b64decode(
|
||||||
|
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg=="
|
||||||
|
)
|
||||||
|
|
||||||
|
# Upload image
|
||||||
|
files = {"file": ("test.png", image_data, "image/png")}
|
||||||
|
response = await client.post(f"{base_url}/api/images/upload", files=files)
|
||||||
|
|
||||||
|
if response.status_code == 200:
|
||||||
|
image = response.json()
|
||||||
|
print_test("Upload Image", True, f"Image ID: {image.get('id')}")
|
||||||
|
|
||||||
|
# Get image metadata
|
||||||
|
response = await client.get(f"{base_url}/api/images/{image['id']}/metadata")
|
||||||
|
print_test("Get Image Metadata", response.status_code == 200)
|
||||||
|
|
||||||
|
# List images
|
||||||
|
response = await client.get(f"{base_url}/api/images")
|
||||||
|
data = response.json()
|
||||||
|
print_test("List Images", response.status_code == 200, f"Total: {data.get('total', 0)} images")
|
||||||
|
|
||||||
|
# Delete image
|
||||||
|
response = await client.delete(f"{base_url}/api/images/{image['id']}")
|
||||||
|
print_test("Delete Image", response.status_code == 200)
|
||||||
|
else:
|
||||||
|
print_test("Upload Image", False, f"HTTP {response.status_code}")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print_test("Images Service", False, f"Error: {str(e)}")
|
||||||
|
|
||||||
|
async def test_files_service():
|
||||||
|
"""Test Files Service"""
|
||||||
|
print_section("5. FILES SERVICE TESTS")
|
||||||
|
|
||||||
|
async with httpx.AsyncClient(timeout=30.0) as client:
|
||||||
|
base_url = SERVICES["files"]
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Upload test file
|
||||||
|
test_content = b"Test file content for MinIO storage"
|
||||||
|
files = {"file": ("test.txt", test_content, "text/plain")}
|
||||||
|
|
||||||
|
response = await client.post(f"{base_url}/api/files/upload", files=files)
|
||||||
|
|
||||||
|
if response.status_code == 200:
|
||||||
|
file_info = response.json()
|
||||||
|
print_test("Upload File", True, f"File ID: {file_info.get('file_id')}")
|
||||||
|
|
||||||
|
# Get file info
|
||||||
|
response = await client.get(f"{base_url}/api/files/{file_info['file_id']}")
|
||||||
|
print_test("Get File Info", response.status_code == 200)
|
||||||
|
|
||||||
|
# List files
|
||||||
|
response = await client.get(f"{base_url}/api/files")
|
||||||
|
data = response.json()
|
||||||
|
print_test("List Files", response.status_code == 200, f"Total: {data.get('total', 0)} files")
|
||||||
|
|
||||||
|
# Delete file
|
||||||
|
response = await client.delete(f"{base_url}/api/files/{file_info['file_id']}")
|
||||||
|
print_test("Delete File", response.status_code == 200)
|
||||||
|
else:
|
||||||
|
print_test("Upload File", False, f"HTTP {response.status_code}")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print_test("Files Service", False, f"Error: {str(e)}")
|
||||||
|
|
||||||
|
async def test_notifications_service():
|
||||||
|
"""Test Notifications Service"""
|
||||||
|
print_section("6. NOTIFICATIONS SERVICE TESTS")
|
||||||
|
|
||||||
|
async with httpx.AsyncClient(timeout=10.0) as client:
|
||||||
|
base_url = SERVICES["notifications"]
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Send email notification
|
||||||
|
email_data = {
|
||||||
|
"to": "test@example.com",
|
||||||
|
"subject": "Test Email",
|
||||||
|
"body": "This is a test email",
|
||||||
|
"template": "default"
|
||||||
|
}
|
||||||
|
|
||||||
|
response = await client.post(f"{base_url}/api/notifications/email", json=email_data)
|
||||||
|
print_test("Send Email", response.status_code == 200, "Email queued")
|
||||||
|
|
||||||
|
# Send SMS notification
|
||||||
|
sms_data = {
|
||||||
|
"to": "+1234567890",
|
||||||
|
"message": "Test SMS message"
|
||||||
|
}
|
||||||
|
|
||||||
|
response = await client.post(f"{base_url}/api/notifications/sms", json=sms_data)
|
||||||
|
print_test("Send SMS", response.status_code == 200, "SMS queued")
|
||||||
|
|
||||||
|
# Send push notification
|
||||||
|
push_data = {
|
||||||
|
"user_id": "test_user",
|
||||||
|
"title": "Test Push",
|
||||||
|
"body": "Test push notification",
|
||||||
|
"data": {"key": "value"}
|
||||||
|
}
|
||||||
|
|
||||||
|
response = await client.post(f"{base_url}/api/notifications/push", json=push_data)
|
||||||
|
print_test("Send Push", response.status_code == 200, "Push notification queued")
|
||||||
|
|
||||||
|
# Get notification history
|
||||||
|
response = await client.get(f"{base_url}/api/notifications/history?user_id=test_user")
|
||||||
|
data = response.json()
|
||||||
|
print_test("Get History", response.status_code == 200, f"Total: {data.get('total', 0)} notifications")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print_test("Notifications Service", False, f"Error: {str(e)}")
|
||||||
|
|
||||||
|
async def test_statistics_service():
|
||||||
|
"""Test Statistics Service"""
|
||||||
|
print_section("7. STATISTICS SERVICE TESTS")
|
||||||
|
|
||||||
|
async with httpx.AsyncClient(timeout=10.0) as client:
|
||||||
|
base_url = SERVICES["statistics"]
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Track event
|
||||||
|
event_data = {
|
||||||
|
"event_type": "page_view",
|
||||||
|
"user_id": "test_user",
|
||||||
|
"metadata": {
|
||||||
|
"page": "/home",
|
||||||
|
"referrer": "google.com"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
response = await client.post(f"{base_url}/api/statistics/events", json=event_data)
|
||||||
|
print_test("Track Event", response.status_code == 200, "Event tracked")
|
||||||
|
|
||||||
|
# Get user statistics
|
||||||
|
response = await client.get(f"{base_url}/api/statistics/users/test_user")
|
||||||
|
print_test("Get User Stats", response.status_code == 200)
|
||||||
|
|
||||||
|
# Get system statistics
|
||||||
|
response = await client.get(f"{base_url}/api/statistics/system")
|
||||||
|
if response.status_code == 200:
|
||||||
|
stats = response.json()
|
||||||
|
print_test("System Stats", True, f"Total events: {stats.get('total_events', 0)}")
|
||||||
|
else:
|
||||||
|
print_test("System Stats", False, f"HTTP {response.status_code}")
|
||||||
|
|
||||||
|
# Get analytics
|
||||||
|
response = await client.get(f"{base_url}/api/statistics/analytics?period=day")
|
||||||
|
print_test("Get Analytics", response.status_code == 200, "Daily analytics retrieved")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print_test("Statistics Service", False, f"Error: {str(e)}")
|
||||||
|
|
||||||
|
async def test_search_service():
|
||||||
|
"""Test Search Service"""
|
||||||
|
print_section("8. SEARCH SERVICE TESTS")
|
||||||
|
|
||||||
|
async with httpx.AsyncClient(timeout=10.0) as client:
|
||||||
|
base_url = SERVICES["search"]
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Index test document
|
||||||
|
doc = {
|
||||||
|
"id": f"test_doc_{datetime.now().timestamp()}",
|
||||||
|
"doc_type": "test",
|
||||||
|
"title": "Test Document",
|
||||||
|
"content": "This is a test document for search functionality",
|
||||||
|
"tags": ["test", "search", "solr"],
|
||||||
|
"created_at": datetime.utcnow().isoformat()
|
||||||
|
}
|
||||||
|
|
||||||
|
response = await client.post(f"{base_url}/api/search/index", json=doc)
|
||||||
|
print_test("Index Document", response.status_code == 200, f"Document ID: {doc['id']}")
|
||||||
|
|
||||||
|
# Wait for indexing
|
||||||
|
await asyncio.sleep(2)
|
||||||
|
|
||||||
|
# Search documents
|
||||||
|
response = await client.get(f"{base_url}/api/search", params={"q": "test"})
|
||||||
|
if response.status_code == 200:
|
||||||
|
data = response.json()
|
||||||
|
print_test("Search Documents", True, f"Found {data.get('total', 0)} results")
|
||||||
|
else:
|
||||||
|
print_test("Search Documents", False, f"HTTP {response.status_code}")
|
||||||
|
|
||||||
|
# Get suggestions
|
||||||
|
response = await client.get(f"{base_url}/api/search/suggest", params={"q": "tes"})
|
||||||
|
if response.status_code == 200:
|
||||||
|
data = response.json()
|
||||||
|
suggestions = data.get("suggestions", [])
|
||||||
|
print_test("Get Suggestions", True, f"{len(suggestions)} suggestions")
|
||||||
|
else:
|
||||||
|
print_test("Get Suggestions", False, f"HTTP {response.status_code}")
|
||||||
|
|
||||||
|
# Get statistics
|
||||||
|
response = await client.get(f"{base_url}/api/search/stats")
|
||||||
|
if response.status_code == 200:
|
||||||
|
stats = response.json()
|
||||||
|
print_test("Search Stats", True, f"Total docs: {stats.get('statistics', {}).get('total_documents', 0)}")
|
||||||
|
else:
|
||||||
|
print_test("Search Stats", False, f"HTTP {response.status_code}")
|
||||||
|
|
||||||
|
# Delete document
|
||||||
|
response = await client.delete(f"{base_url}/api/search/document/{doc['id']}")
|
||||||
|
print_test("Delete Document", response.status_code == 200)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print_test("Search Service", False, f"Error: {str(e)}")
|
||||||
|
|
||||||
|
async def test_console_service():
|
||||||
|
"""Test Console Service"""
|
||||||
|
print_section("9. CONSOLE SERVICE TESTS")
|
||||||
|
|
||||||
|
async with httpx.AsyncClient(timeout=10.0) as client:
|
||||||
|
base_url = SERVICES["console"]
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Get services status
|
||||||
|
response = await client.get(f"{base_url}/api/console/services")
|
||||||
|
if response.status_code == 200:
|
||||||
|
services = response.json()
|
||||||
|
print_test("Get Services", True, f"{len(services.get('services', []))} services")
|
||||||
|
else:
|
||||||
|
print_test("Get Services", False, f"HTTP {response.status_code}")
|
||||||
|
|
||||||
|
# Get system info
|
||||||
|
response = await client.get(f"{base_url}/api/console/system")
|
||||||
|
print_test("System Info", response.status_code == 200, "System information retrieved")
|
||||||
|
|
||||||
|
# Get logs
|
||||||
|
response = await client.get(f"{base_url}/api/console/logs?service=users&limit=10")
|
||||||
|
print_test("Get Logs", response.status_code == 200, "Logs retrieved")
|
||||||
|
|
||||||
|
# Get metrics
|
||||||
|
response = await client.get(f"{base_url}/api/console/metrics")
|
||||||
|
if response.status_code == 200:
|
||||||
|
metrics = response.json()
|
||||||
|
print_test("Get Metrics", True, f"CPU: {metrics.get('metrics', {}).get('cpu_usage', 'N/A')}%")
|
||||||
|
else:
|
||||||
|
print_test("Get Metrics", False, f"HTTP {response.status_code}")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print_test("Console Service", False, f"Error: {str(e)}")
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
"""Run all tests"""
|
||||||
|
print("="*60)
|
||||||
|
print(" BACKEND SERVICES COMPREHENSIVE TEST SUITE")
|
||||||
|
print("="*60)
|
||||||
|
print(f"Started at: {datetime.now().isoformat()}")
|
||||||
|
|
||||||
|
# Test health endpoints first
|
||||||
|
health_results = await test_health_endpoints()
|
||||||
|
|
||||||
|
# Test individual services
|
||||||
|
await test_users_service()
|
||||||
|
await test_oauth_service()
|
||||||
|
await test_images_service()
|
||||||
|
await test_files_service()
|
||||||
|
await test_notifications_service()
|
||||||
|
await test_statistics_service()
|
||||||
|
await test_search_service()
|
||||||
|
await test_console_service()
|
||||||
|
|
||||||
|
# Summary
|
||||||
|
print_section("TEST SUMMARY")
|
||||||
|
healthy_count = sum(1 for v in health_results.values() if v)
|
||||||
|
total_count = len(health_results)
|
||||||
|
|
||||||
|
print(f"\n📊 Services Health: {healthy_count}/{total_count} services are healthy")
|
||||||
|
print(f"✅ Healthy Services: {', '.join([k for k, v in health_results.items() if v])}")
|
||||||
|
|
||||||
|
if healthy_count < total_count:
|
||||||
|
unhealthy = [k for k, v in health_results.items() if not v]
|
||||||
|
print(f"❌ Unhealthy Services: {', '.join(unhealthy)}")
|
||||||
|
|
||||||
|
print(f"\n🎉 Test suite completed at: {datetime.now().isoformat()}")
|
||||||
|
print("="*60)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
asyncio.run(main())
|
||||||
Reference in New Issue
Block a user