logboard/app/scripts/test_multi_view_ajax.py
Sergey Antropoff 6e51f00791 feat: Добавлено AJAX обновление логов и улучшения интерфейса
Основные изменения:
- Добавлено AJAX обновление логов с чекбоксом 'Auto-update logs'
- Добавлена опция 'All logs' в выпадающий список tail lines
- Исправлено отображение длинных названий контейнеров в multi-view режиме
- Восстановлена загрузка истории логов при включенном AJAX обновлении

Новые функции:
- Чекбокс 'Auto-update logs' в секции Options (включен по умолчанию)
- Настройка интервала обновления через LOGBOARD_AJAX_UPDATE_INTERVAL
- API эндпоинт /api/settings для получения настроек приложения
- Поддержка параметра tail=all для загрузки всех логов
- Автоматический запуск AJAX обновления при включении чекбокса

Исправления UI:
- Кнопки LogLevels не уезжают вправо при длинных названиях контейнеров
- Добавлено обрезание длинных названий с многоточием
- Фиксированная высота заголовков в multi-view режиме
- Защита от сжатия кнопок LogLevels

Тестирование:
- Добавлены тесты для AJAX обновления (test_ajax_update.py)
- Тест multi-view AJAX обновления (test_multi_view_ajax.py)
- Тест опции 'all logs' (test_all_logs.py)
- Тест отображения длинных названий (test_multi_view_layout.py)
- Команды make test-ajax, make test-multi-view-ajax, make test-all-logs, make test-multi-view-layout

Документация:
- Создана подробная документация AJAX обновления (app/docs/ajax-update.md)
- Обновлен CHANGELOG.md с версиями 1.3.0, 1.5.0, 1.6.0
- Обновлен README.md с описанием новых функций

Автор: Сергей Антропов
Сайт: https://devops.org.ru
2025-08-18 19:35:47 +03:00

286 lines
13 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Тест AJAX обновления в multi-view режиме
Автор: Сергей Антропов
Сайт: https://devops.org.ru
"""
import asyncio
import aiohttp
import json
import time
from datetime import datetime
import os
import sys
async def test_multi_view_ajax():
"""Тестирование AJAX обновления в multi-view режиме"""
print("🔄 Тестирование AJAX обновления в multi-view режиме")
print("=" * 60)
# Настройки
base_url = "http://localhost:9001"
username = os.getenv("LOGBOARD_USER", "admin")
password = os.getenv("LOGBOARD_PASS", "admin")
async with aiohttp.ClientSession() as session:
try:
# 1. Получаем токен авторизации
print("🔐 Получение токена авторизации...")
auth_data = {
"username": username,
"password": password
}
async with session.post(f"{base_url}/api/auth/login", json=auth_data) as response:
if response.status != 200:
print(f"❌ Ошибка авторизации: {response.status}")
return False
auth_response = await response.json()
token = auth_response.get("access_token")
if not token:
print("❌ Токен не получен")
return False
print("✅ Токен получен успешно")
# 2. Получаем список контейнеров
print("\n📋 Получение списка контейнеров...")
headers = {"Authorization": f"Bearer {token}"}
async with session.get(f"{base_url}/api/services", headers=headers) as response:
if response.status != 200:
print(f"❌ Ошибка получения контейнеров: {response.status}")
return False
containers = await response.json()
if not containers:
print("❌ Контейнеры не найдены")
return False
# Берем первые 3 запущенных контейнера для multi-view теста
running_containers = [c for c in containers if c.get("status") == "running"]
if len(running_containers) < 2:
print("❌ Недостаточно запущенных контейнеров для multi-view теста (нужно минимум 2)")
return False
test_containers = running_containers[:3] # Берем первые 3
print(f"✅ Выбрано {len(test_containers)} контейнеров для multi-view теста:")
for i, container in enumerate(test_containers):
print(f" {i+1}. {container['name']} ({container['id'][:12]}...)")
# 3. Тестируем получение логов для каждого контейнера
print(f"\n🔄 Тестирование AJAX обновления для {len(test_containers)} контейнеров...")
container_results = {}
for i, container in enumerate(test_containers):
container_id = container["id"]
container_name = container["name"]
print(f"\n📊 Контейнер {i+1}: {container_name}")
# Первый запрос
url = f"{base_url}/api/logs/{container_id}"
params = {"tail": 5}
async with session.get(url, headers=headers, params=params) as response:
if response.status != 200:
print(f" ❌ Ошибка получения логов: {response.status}")
continue
data = await response.json()
first_count = data.get('total_lines', 0)
first_timestamp = data.get('timestamp')
print(f" ✅ Первый запрос: {first_count} строк, timestamp: {first_timestamp}")
# Ждем немного
await asyncio.sleep(1)
# Второй запрос с since
params = {"tail": 5, "since": first_timestamp}
async with session.get(url, headers=headers, params=params) as response:
if response.status != 200:
print(f" ❌ Ошибка получения новых логов: {response.status}")
continue
data = await response.json()
second_count = data.get('total_lines', 0)
second_timestamp = data.get('timestamp')
print(f" ✅ Второй запрос: {second_count} строк, timestamp: {second_timestamp}")
# Сохраняем результаты
container_results[container_id] = {
'name': container_name,
'first_count': first_count,
'second_count': second_count,
'first_timestamp': first_timestamp,
'second_timestamp': second_timestamp
}
# 4. Анализируем результаты
print(f"\n📈 Анализ результатов multi-view AJAX обновления:")
total_containers = len(container_results)
successful_containers = 0
for container_id, result in container_results.items():
print(f"\n 📦 {result['name']} ({container_id[:12]}...):")
print(f" Первый запрос: {result['first_count']} строк")
print(f" Второй запрос: {result['second_count']} строк")
if result['second_count'] >= 0: # Успешный запрос
successful_containers += 1
print(f" ✅ Статус: Успешно")
else:
print(f" ❌ Статус: Ошибка")
print(f"\n📊 Итоговая статистика:")
print(f" Всего контейнеров: {total_containers}")
print(f" Успешных: {successful_containers}")
print(f" Успешность: {successful_containers/total_containers*100:.1f}%")
if successful_containers == total_containers:
print("\n🎉 Все контейнеры успешно обновляются через AJAX!")
return True
else:
print(f"\n⚠️ {total_containers - successful_containers} контейнеров имеют проблемы")
return False
except Exception as e:
print(f"❌ Ошибка тестирования: {e}")
return False
async def test_concurrent_ajax_requests():
"""Тестирование одновременных AJAX запросов (имитация multi-view)"""
print(f"\n⚡ Тестирование одновременных AJAX запросов")
print("=" * 50)
# Настройки
base_url = "http://localhost:9001"
username = os.getenv("LOGBOARD_USER", "admin")
password = os.getenv("LOGBOARD_PASS", "admin")
async with aiohttp.ClientSession() as session:
try:
# Получаем токен
auth_data = {"username": username, "password": password}
async with session.post(f"{base_url}/api/auth/login", json=auth_data) as response:
auth_response = await response.json()
token = auth_response.get("access_token")
headers = {"Authorization": f"Bearer {token}"}
# Получаем контейнеры
async with session.get(f"{base_url}/api/services", headers=headers) as response:
containers = await response.json()
running_containers = [c for c in containers if c.get("status") == "running"]
if len(running_containers) < 2:
print("❌ Недостаточно контейнеров для теста")
return False
test_containers = running_containers[:3]
# Тестируем одновременные запросы
print(f"📊 Выполнение одновременных запросов для {len(test_containers)} контейнеров...")
start_time = time.time()
async def fetch_container_logs(container):
container_id = container["id"]
url = f"{base_url}/api/logs/{container_id}"
params = {"tail": 3}
try:
async with session.get(url, headers=headers, params=params) as response:
data = await response.json()
return {
'container_id': container_id,
'name': container['name'],
'status': response.status,
'lines': data.get('total_lines', 0),
'success': response.status == 200
}
except Exception as e:
return {
'container_id': container_id,
'name': container['name'],
'status': 'error',
'lines': 0,
'success': False,
'error': str(e)
}
# Выполняем запросы одновременно
tasks = [fetch_container_logs(container) for container in test_containers]
results = await asyncio.gather(*tasks)
total_time = time.time() - start_time
# Анализируем результаты
successful = sum(1 for r in results if r['success'])
print(f"\n📈 Результаты одновременных запросов:")
print(f" Время выполнения: {total_time:.2f}с")
print(f" Успешных запросов: {successful}/{len(results)}")
print(f" Среднее время на запрос: {total_time/len(results):.2f}с")
for result in results:
status_icon = "" if result['success'] else ""
print(f" {status_icon} {result['name']}: {result['lines']} строк")
if successful == len(results):
print("Все одновременные запросы выполнены успешно!")
return True
else:
print(f"⚠️ {len(results) - successful} запросов завершились с ошибкой")
return False
except Exception as e:
print(f"❌ Ошибка тестирования одновременных запросов: {e}")
return False
async def main():
"""Основная функция тестирования"""
print("🔄 Запуск тестов multi-view AJAX обновления")
print("=" * 70)
# Проверяем, что сервер запущен
try:
async with aiohttp.ClientSession() as session:
async with session.get("http://localhost:9001/healthz") as response:
if response.status != 200:
print("❌ Сервер LogBoard+ не запущен на порту 9001")
print(" Запустите сервер командой: make up")
return False
except Exception:
print("Не удается подключиться к серверу LogBoard+")
print(" Убедитесь, что сервер запущен: make up")
return False
# Запускаем тесты
success1 = await test_multi_view_ajax()
success2 = await test_concurrent_ajax_requests()
print("\n" + "=" * 70)
if success1 and success2:
print("🎉 Все тесты прошли успешно!")
print("✅ Multi-view AJAX обновление работает корректно")
return True
else:
print("❌ Некоторые тесты не прошли")
print("🔧 Проверьте логи сервера и настройки")
return False
if __name__ == "__main__":
result = asyncio.run(main())
sys.exit(0 if result else 1)