Commit 46f27ebd authored by avelichko's avatar avelichko

Initial commit

parents
This diff is collapsed.
from typing import Any
from dataclasses import dataclass
@dataclass
class Result:
data: Any
@dataclass
class Metrics:
time: int
resources: dict[str, int]
@dataclass
class ComparisonReport:
improvements: dict[str, float]
class TestScenario:
def __init__(self, id: str, name: str, configuration: dict[str, Any]) -> None:
self.id = id
self.name = name
self.configuration = configuration
def execute(self) -> Result:
pass
class Optimizer:
def optimize(self, scenario: TestScenario) -> TestScenario:
pass
class MetricsCollector:
def collect_metrics(self, result: Result) -> Metrics:
pass
class ComparisonTool:
def compare(self, before: Metrics, after: Metrics) -> ComparisonReport:
pass
class RecommendationGenerator:
def generate_recommendations(self, report: ComparisonReport) -> list[str]:
pass
class WebPortal:
def show_results(self, report: ComparisonReport) -> None:
pass
def show_recommendations(self, recommendations: list[str]) -> None:
pass
\ No newline at end of file
@startuml ActivityDiagram
title Диаграмма активности процесса тестирования Serverless
start
:Настроить тестовый сценарий;
:Оптимизировать сценарий;
:Запустить оптимизированный тест;
:Собрать метрики оптимизированного теста;
:Запустить оригинальный тест;
:Собрать метрики оригинального теста;
:Сравнить метрики;
:Сгенерировать рекомендации;
:Отобразить результаты;
stop
@enduml
\ No newline at end of file
@startuml ClassDiagram
title Диаграмма классов системы тестирования Serverless
class СценарийТеста {
+id: String
+имя: String
+конфигурация: Map<String, Object>
+выполнить(): Результат
}
class Оптимизатор {
+оптимизировать(сценарий: СценарийТеста): СценарийТеста
}
class СборщикМетрик {
+собратьМетрики(результат: Результат): Метрики
}
class ИнструментСравнения {
+сравнить(до: Метрики, после: Метрики): ОтчетСравнения
}
class ГенераторРекомендаций {
+сгенерироватьРекомендации(отчет: ОтчетСравнения): List<String>
}
class ВебПортал {
+показатьРезультаты(отчет: ОтчетСравнения)
+показатьРекомендации(рекомендации: List<String>)
}
class Результат {
+данные: Object
}
class Метрики {
+время: Integer
+ресурсы: Map<String, Integer>
}
class ОтчетСравнения {
+улучшения: Map<String, Double>
}
СценарийТеста --> Результат : "1"
Оптимизатор --> СценарийТеста : оптимизирует
СборщикМетрик --> Метрики : собирает
ИнструментСравнения --> ОтчетСравнения : создает отчет
ГенераторРекомендаций --> "List<String>" : рекомендации
ВебПортал --> ОтчетСравнения : отображает отчет
ВебПортал --> "List<String>" : отображает рекомендации
@enduml
\ No newline at end of file
@startuml SequenceDiagram
title Диаграмма последовательности выполнения тестов Serverless
actor ОблачныйИнженер
participant ВебПортал
participant ДвижокТестирования as TestEngine
participant Оптимизатор
participant СборщикМетрик
participant ИнструментСравнения
participant ГенераторРекомендаций
ОблачныйИнженер -> ВебПортал: настроитьТест(конфигСценария)
ВебПортал -> ДвижокТестирования: запуститьТест(конфигСценария)
ДвижокТестирования -> Оптимизатор: оптимизировать(сценарий)
Оптимизатор --> ДвижокТестирования: оптимизированныйСценарий
ДвижокТестирования -> СборщикМетрик: собратьМетрики(оптимизированныйСценарий)
СборщикМетрик --> ДвижокТестирования: метрикиОптимизированного
ДвижокТестирования --> ВебПортал: результатыОптимизированного
ВебПортал -> ДвижокТестирования: запуститьТест(оригСценарий)
ДвижокТестирования -> СборщикМетрик: собратьМетрики(оригСценарий)
СборщикМетрик --> ДвижокТестирования: метрикиОригинала
ДвижокТестирования -> ИнструментСравнения: сравнить(метрикиОригинала, метрикиОптимизированного)
ИнструментСравнения --> ВебПортал: отчетСравнения
ВебПортал -> ГенераторРекомендаций: сгенерироватьРекомендации(отчетСравнения)
ГенераторРекомендаций --> ВебПортал: рекомендации
ВебПортал --> ОблачныйИнженер: отобразить(отчетСравнения, рекомендации)
@enduml
\ No newline at end of file
@startuml StateDiagram
title Диаграмма состояний процесса тестирования Serverless
[*] --> Создано
Создано --> Оптимизировано : оптимизировать()
Оптимизировано --> Выполняется : выполнить()
Выполняется --> МетрикиСобраны : собратьМетрики()
МетрикиСобраны --> Сравнение : сравнить()
Сравнение --> Рекомендации : сгенерироватьРекомендации()
Рекомендации --> Просмотр : просмотреть()
Просмотр --> [*]
@enduml
\ No newline at end of file
@startuml UseCase
title Диаграмма вариантов использования комплекса тестирования Serverless
left to right direction
actor ОблачныйИнженер as "Облачный инженер"
rectangle "Комплекс тестирования Serverless" {
(Настроить тестовые сценарии) as UC1
(Запустить тесты) as UC2
(Просмотреть результаты) as UC3
(Сгенерировать рекомендации) as UC4
}
ОблачныйИнженер --> UC1
ОблачныйИнженер --> UC2
ОблачныйИнженер --> UC3
ОблачныйИнженер --> UC4
@enduml
\ No newline at end of file
node_modules
npm-debug.log
build
.dockerignore
Dockerfile
.git
.github
.gitignore
README.md
docker-compose.yml
**/node_modules
**/npm-debug.log
**/.git
**/.DS_Store
\ No newline at end of file
# Stage 1: Build the React application
FROM node:20-alpine as build
WORKDIR /app
# Copy package.json and package-lock.json
COPY package*.json ./
# Install dependencies
RUN npm install
# Copy all files
COPY . .
# Build the application
RUN npm run build
# Stage 2: Serve the built application
FROM nginx:alpine
# Copy built application from Stage 1
COPY --from=build /app/build /usr/share/nginx/html
# Copy custom nginx configuration if needed
# COPY nginx.conf /etc/nginx/conf.d/default.conf
# Expose port 80
EXPOSE 80
# Start nginx
CMD ["nginx", "-g", "daemon off;"]
\ No newline at end of file
version: '3.8'
services:
frontend:
build: .
ports:
- "8080:80"
restart: unless-stopped
networks:
- serverless-net
networks:
serverless-net:
driver: bridge
\ No newline at end of file
{
"name": "serverless-testing-platform",
"version": "0.1.0",
"private": true,
"dependencies": {
"@emotion/react": "^11.11.0",
"@emotion/styled": "^11.11.0",
"@mui/icons-material": "^5.11.16",
"@mui/material": "^5.13.2",
"chart.js": "^4.3.0",
"react": "^18.2.0",
"react-chartjs-2": "^5.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.11.2",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
\ No newline at end of file
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Платформа для тестирования serverless-функций в облачной инфраструктуре импортозамещения"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" />
<title>Платформа тестирования Serverless</title>
</head>
<body>
<noscript>Для работы приложения необходимо включить JavaScript.</noscript>
<div id="root"></div>
</body>
</html>
\ No newline at end of file
{
"short_name": "ServerlessTest",
"name": "Платформа тестирования Serverless",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#1976d2",
"background_color": "#ffffff"
}
\ No newline at end of file
import React from 'react';
import { Routes, Route } from 'react-router-dom';
import { ThemeProvider, createTheme } from '@mui/material/styles';
import CssBaseline from '@mui/material/CssBaseline';
import Layout from './components/Layout';
import HomePage from './pages/HomePage';
import TestingPage from './pages/TestingPage';
import ResultsPage from './pages/ResultsPage';
import ConfigurationPage from './pages/ConfigurationPage';
const theme = createTheme({
palette: {
primary: {
main: '#1976d2',
},
secondary: {
main: '#dc004e',
},
background: {
default: '#f5f5f5',
},
},
typography: {
fontFamily: 'Roboto, Arial, sans-serif',
},
});
function App() {
return (
<ThemeProvider theme={theme}>
<CssBaseline />
<Layout>
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="/testing" element={<TestingPage />} />
<Route path="/results" element={<ResultsPage />} />
<Route path="/configuration" element={<ConfigurationPage />} />
</Routes>
</Layout>
</ThemeProvider>
);
}
export default App;
\ No newline at end of file
import React, { useState } from 'react';
import { useNavigate, useLocation } from 'react-router-dom';
import {
AppBar,
Box,
Drawer,
IconButton,
List,
ListItem,
ListItemIcon,
ListItemText,
Toolbar,
Typography,
useMediaQuery,
useTheme,
} from '@mui/material';
import MenuIcon from '@mui/icons-material/Menu';
import HomeIcon from '@mui/icons-material/Home';
import SpeedIcon from '@mui/icons-material/Speed';
import BarChartIcon from '@mui/icons-material/BarChart';
import SettingsIcon from '@mui/icons-material/Settings';
const drawerWidth = 240;
const menuItems = [
{ text: 'Главная', icon: <HomeIcon />, path: '/' },
{ text: 'Тестирование', icon: <SpeedIcon />, path: '/testing' },
{ text: 'Результаты', icon: <BarChartIcon />, path: '/results' },
{ text: 'Конфигурация', icon: <SettingsIcon />, path: '/configuration' },
];
function Layout({ children }) {
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down('md'));
const [mobileOpen, setMobileOpen] = useState(false);
const navigate = useNavigate();
const location = useLocation();
const handleDrawerToggle = () => {
setMobileOpen(!mobileOpen);
};
const drawer = (
<div>
<Toolbar sx={{ justifyContent: 'center' }}>
<Typography variant="h6" component="div" fontWeight="bold">
ServerlessTest
</Typography>
</Toolbar>
<List>
{menuItems.map((item) => (
<ListItem
button
key={item.text}
onClick={() => navigate(item.path)}
selected={location.pathname === item.path}
>
<ListItemIcon>{item.icon}</ListItemIcon>
<ListItemText primary={item.text} />
</ListItem>
))}
</List>
</div>
);
return (
<Box sx={{ display: 'flex' }}>
<AppBar
position="fixed"
sx={{
width: { sm: `calc(100% - ${drawerWidth}px)` },
ml: { sm: `${drawerWidth}px` },
}}
>
<Toolbar>
<IconButton
color="inherit"
aria-label="open drawer"
edge="start"
onClick={handleDrawerToggle}
sx={{ mr: 2, display: { sm: 'none' } }}
>
<MenuIcon />
</IconButton>
<Typography variant="h6" component="div" sx={{ flexGrow: 1 }}>
Тестирование Serverless-функций
</Typography>
</Toolbar>
</AppBar>
<Box
component="nav"
sx={{ width: { sm: drawerWidth }, flexShrink: { sm: 0 } }}
>
<Drawer
variant={isMobile ? 'temporary' : 'permanent'}
open={mobileOpen}
onClose={handleDrawerToggle}
ModalProps={{
keepMounted: true,
}}
sx={{
'& .MuiDrawer-paper': { boxSizing: 'border-box', width: drawerWidth },
}}
>
{drawer}
</Drawer>
</Box>
<Box
component="main"
sx={{
flexGrow: 1,
p: 3,
width: { sm: `calc(100% - ${drawerWidth}px)` },
}}
>
<Toolbar />
{children}
</Box>
</Box>
);
}
export default Layout;
\ No newline at end of file
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}
.dashboard-card {
transition: all 0.3s;
}
.dashboard-card:hover {
transform: translateY(-5px);
box-shadow: 0px 10px 20px rgba(0, 0, 0, 0.1);
}
.chart-container {
height: 300px;
position: relative;
}
\ No newline at end of file
import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</React.StrictMode>
);
reportWebVitals();
\ No newline at end of file
This diff is collapsed.
import React from 'react';
import { useNavigate } from 'react-router-dom';
import {
Box,
Button,
Card,
CardActionArea,
CardContent,
Container,
Grid,
Typography,
Paper,
} from '@mui/material';
import SpeedIcon from '@mui/icons-material/Speed';
import MemoryIcon from '@mui/icons-material/Memory';
import StorageIcon from '@mui/icons-material/Storage';
import TimelineIcon from '@mui/icons-material/Timeline';
const performanceData = [
{ title: 'Время ответа', icon: <TimelineIcon fontSize="large" color="primary" />, value: '124 мс', change: '-23%' },
{ title: 'Использование CPU', icon: <SpeedIcon fontSize="large" color="primary" />, value: '32%', change: '-12%' },
{ title: 'Использование RAM', icon: <MemoryIcon fontSize="large" color="primary" />, value: '256 MB', change: '-18%' },
{ title: 'Хранилище', icon: <StorageIcon fontSize="large" color="primary" />, value: '45 MB', change: '-8%' },
];
function HomePage() {
const navigate = useNavigate();
return (
<Container maxWidth="lg">
<Box sx={{ my: 4 }}>
<Typography variant="h4" component="h1" gutterBottom>
Тестирование и оптимизация Serverless-функций
</Typography>
<Typography variant="body1" paragraph>
Добро пожаловать в платформу для автоматизированного запуска и замера производительности
serverless-функций на облачной инфраструктуре импортозамещения.
</Typography>
<Paper sx={{ p: 3, mb: 4, bgcolor: 'primary.main', color: 'white' }}>
<Typography variant="h5" gutterBottom>
Текущий статус:
</Typography>
<Typography variant="body1">
Проведено 153 тестов Оптимизировано 27 функций Среднее улучшение: 24%
</Typography>
<Button
variant="contained"
color="secondary"
sx={{ mt: 2 }}
onClick={() => navigate('/testing')}
>
Запустить новый тест
</Button>
</Paper>
<Typography variant="h5" gutterBottom>
Сводка производительности
</Typography>
<Grid container spacing={3}>
{performanceData.map((item) => (
<Grid item xs={12} sm={6} md={3} key={item.title} sx={{ display: 'flex' }}>
<Card className="dashboard-card" sx={{ width: '100%', height: '100%' }}>
<CardActionArea sx={{ height: '100%' }} onClick={() => navigate('/results')}>
<CardContent sx={{ textAlign: 'center', display: 'flex', flexDirection: 'column', height: '100%' }}>
<Box sx={{ mb: 2 }}>{item.icon}</Box>
<Typography variant="h6" component="div">
{item.title}
</Typography>
<Typography variant="h4" color="text.primary" sx={{ mt: 1 }}>
{item.value}
</Typography>
<Box sx={{ flexGrow: 1 }} />
<Typography
variant="body2"
color={item.change.startsWith('-') ? 'success.main' : 'error.main'}
sx={{ mt: 2 }}
>
{item.change} после оптимизации
</Typography>
</CardContent>
</CardActionArea>
</Card>
</Grid>
))}
</Grid>
<Grid container spacing={4} sx={{ mt: 2 }}>
<Grid item xs={12} md={6} sx={{ display: 'flex' }}>
<Card sx={{ width: '100%', height: '100%' }}>
<CardContent sx={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
<Typography variant="h6" gutterBottom>
Последние тесты
</Typography>
<Typography variant="body2">
Тест обработки изображений (завершен 12.05.2024)
</Typography>
<Typography variant="body2">
Тест API Gateway с аутентификацией (завершен 10.05.2024)
</Typography>
<Typography variant="body2">
Тест базы данных PostgreSQL (завершен 08.05.2024)
</Typography>
<Box sx={{ flexGrow: 1 }} />
<Button
variant="outlined"
size="small"
sx={{ mt: 2 }}
onClick={() => navigate('/results')}
>
Все результаты
</Button>
</CardContent>
</Card>
</Grid>
<Grid item xs={12} md={6} sx={{ display: 'flex' }}>
<Card sx={{ width: '100%', height: '100%' }}>
<CardContent sx={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
<Typography variant="h6" gutterBottom>
Рекомендации
</Typography>
<Typography variant="body2">
Оптимизировать использование памяти в функции обработки изображений
</Typography>
<Typography variant="body2">
Кэшировать результаты HTTP-запросов для улучшения времени отклика
</Typography>
<Typography variant="body2">
Уменьшить размер пакета зависимостей для функции аналитики
</Typography>
<Box sx={{ flexGrow: 1 }} />
<Button
variant="outlined"
size="small"
sx={{ mt: 2 }}
onClick={() => navigate('/configuration')}
>
Настроить оптимизации
</Button>
</CardContent>
</Card>
</Grid>
</Grid>
</Box>
</Container>
);
}
export default HomePage;
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
const reportWebVitals = (onPerfEntry) => {
if (onPerfEntry && onPerfEntry instanceof Function) {
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
getCLS(onPerfEntry);
getFID(onPerfEntry);
getFCP(onPerfEntry);
getLCP(onPerfEntry);
getTTFB(onPerfEntry);
});
}
};
export default reportWebVitals;
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment