You need to sign in or sign up before continuing.
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