QuantEngine MudBlazor UI: Complete Phase 1-8 Implementation #14
@@ -0,0 +1,401 @@
|
|||||||
|
# QuantEngine - Testing & Deployment Guide
|
||||||
|
|
||||||
|
**Status**: Phase 6 (Testing) & Phase 8 (Deployment) - Configuration & Documentation
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 6: Testing & Optimization
|
||||||
|
|
||||||
|
### 6.1 Unit Testing (bUnit)
|
||||||
|
|
||||||
|
#### Setup
|
||||||
|
```bash
|
||||||
|
cd src/dotnet
|
||||||
|
dotnet add package bunit
|
||||||
|
dotnet add package bunit.web
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Example Test: Dashboard Component
|
||||||
|
```csharp
|
||||||
|
// Tests/Pages/DashboardTests.cs
|
||||||
|
[TestFixture]
|
||||||
|
public class DashboardTests
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void Dashboard_Renders_KPICards()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var cut = new TestContext().RenderComponent<Dashboard>();
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
var kpiCards = cut.FindAll(".mud-card-kpi");
|
||||||
|
kpiCards.Count.Should().Be(4);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task Dashboard_LoadsAssets_OnInitialize()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var httpClient = new HttpClientStub();
|
||||||
|
var cut = new TestContext();
|
||||||
|
cut.Services.AddScoped(sp => httpClient);
|
||||||
|
var dashboard = cut.RenderComponent<Dashboard>();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await Task.Delay(100); // Wait for async init
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
httpClient.Requests.Should().Contain(r => r.Url.Contains("/api/portfolio"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Test Coverage Targets
|
||||||
|
- Dashboard rendering (4 KPI cards)
|
||||||
|
- Users list (search, filter, pagination)
|
||||||
|
- Portfolio components (asset table, categories)
|
||||||
|
- Form fields (all input types)
|
||||||
|
- Dialogs (confirm/cancel actions)
|
||||||
|
|
||||||
|
#### Run Tests
|
||||||
|
```bash
|
||||||
|
dotnet test src/dotnet/QuantEngine.Web.Client.Tests
|
||||||
|
dotnet test src/dotnet/QuantEngine.Web.Tests
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6.2 Integration Tests
|
||||||
|
|
||||||
|
#### Database Test Setup
|
||||||
|
```csharp
|
||||||
|
[TestFixture]
|
||||||
|
public class RepositoryIntegrationTests
|
||||||
|
{
|
||||||
|
private IDbConnectionFactory _connectionFactory;
|
||||||
|
private ICollectionRepository _repository;
|
||||||
|
|
||||||
|
[OneTimeSetUp]
|
||||||
|
public void OneTimeSetUp()
|
||||||
|
{
|
||||||
|
_connectionFactory = new DbConnectionFactory(
|
||||||
|
"Host=localhost;Database=quantengine_test;..."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task SaveCollectionRun_Persists_ToDatabase()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var run = new CollectionRun { RunId = Guid.NewGuid().ToString(), ... };
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await _repository.SaveRunAsync(run);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var retrieved = await _repository.GetRunAsync(run.RunId);
|
||||||
|
retrieved.Should().NotBeNull();
|
||||||
|
retrieved.RunId.Should().Be(run.RunId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6.3 Performance Optimization
|
||||||
|
|
||||||
|
#### Bundle Size Optimization
|
||||||
|
```bash
|
||||||
|
# Check bundle sizes
|
||||||
|
dotnet publish -c Release --output ./publish
|
||||||
|
du -sh publish/wwwroot/_framework/*
|
||||||
|
```
|
||||||
|
|
||||||
|
**Targets**:
|
||||||
|
- dotnet.wasm: < 2MB
|
||||||
|
- app.js: < 500KB
|
||||||
|
- Total: < 5MB
|
||||||
|
|
||||||
|
#### Loading Time Optimization
|
||||||
|
```csharp
|
||||||
|
// Use lazy loading for pages
|
||||||
|
[lazy: Dashboard]
|
||||||
|
@rendermode InteractiveWebAssembly
|
||||||
|
|
||||||
|
// Pre-load critical resources
|
||||||
|
<link rel="prefetch" href="/_framework/QuantEngine.Web.Client.wasm" />
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6.4 Accessibility Testing (WCAG 2.1 AA)
|
||||||
|
|
||||||
|
#### Automated Checks
|
||||||
|
```bash
|
||||||
|
dotnet add package Deque.AxeCore.Selenium
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Manual Checklist
|
||||||
|
- [ ] Keyboard navigation (Tab, Enter, Escape)
|
||||||
|
- [ ] Screen reader support (NVDA, JAWS)
|
||||||
|
- [ ] Color contrast (4.5:1 for text)
|
||||||
|
- [ ] Form labels properly associated
|
||||||
|
- [ ] Error messages clear and descriptive
|
||||||
|
- [ ] Focus indicators visible
|
||||||
|
- [ ] No automatic content changes
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 8: Deployment & Operations
|
||||||
|
|
||||||
|
### 8.1 Production Build
|
||||||
|
|
||||||
|
#### Release Build Configuration
|
||||||
|
```bash
|
||||||
|
# Build Release configuration
|
||||||
|
cd src/dotnet
|
||||||
|
dotnet build -c Release
|
||||||
|
|
||||||
|
# Publish for deployment
|
||||||
|
dotnet publish -c Release -o ./publish/quantengine
|
||||||
|
|
||||||
|
# Size check
|
||||||
|
ls -lh publish/quantengine/
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Build Output
|
||||||
|
- `publish/quantengine/` - Complete deployment package
|
||||||
|
- `publish/quantengine/wwwroot/` - Static assets
|
||||||
|
- `publish/quantengine/QuantEngine.Web.exe` - Server executable
|
||||||
|
- `publish/quantengine/appsettings.production.json` - Configuration
|
||||||
|
|
||||||
|
### 8.2 Docker Deployment
|
||||||
|
|
||||||
|
#### Dockerfile
|
||||||
|
```dockerfile
|
||||||
|
FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base
|
||||||
|
WORKDIR /app
|
||||||
|
EXPOSE 80 443
|
||||||
|
|
||||||
|
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
|
||||||
|
WORKDIR /src
|
||||||
|
COPY ["src/dotnet/QuantEngine.Web/QuantEngine.Web.csproj", "QuantEngine.Web/"]
|
||||||
|
RUN dotnet restore "QuantEngine.Web/QuantEngine.Web.csproj"
|
||||||
|
|
||||||
|
COPY src/dotnet/ .
|
||||||
|
RUN dotnet build "QuantEngine.Web/QuantEngine.Web.csproj" -c Release -o /app/build
|
||||||
|
|
||||||
|
FROM build AS publish
|
||||||
|
RUN dotnet publish "QuantEngine.Web/QuantEngine.Web.csproj" -c Release -o /app/publish
|
||||||
|
|
||||||
|
FROM base AS final
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=publish /app/publish .
|
||||||
|
ENTRYPOINT ["dotnet", "QuantEngine.Web.dll"]
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Docker Build & Run
|
||||||
|
```bash
|
||||||
|
# Build image
|
||||||
|
docker build -t quantengine:latest .
|
||||||
|
|
||||||
|
# Run container
|
||||||
|
docker run -d \
|
||||||
|
-p 5265:80 \
|
||||||
|
-e ConnectionStrings__DefaultConnection="Host=db;Database=quantenginedb;..." \
|
||||||
|
-e ASPNETCORE_ENVIRONMENT=Production \
|
||||||
|
quantengine:latest
|
||||||
|
|
||||||
|
# Check logs
|
||||||
|
docker logs -f <container_id>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 8.3 Nginx Reverse Proxy
|
||||||
|
|
||||||
|
#### Nginx Configuration
|
||||||
|
```nginx
|
||||||
|
upstream quantengine {
|
||||||
|
server 127.0.0.1:5000;
|
||||||
|
server 127.0.0.1:5001;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name quantengine.example.com;
|
||||||
|
|
||||||
|
# Redirect to HTTPS
|
||||||
|
return 301 https://$server_name$request_uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 443 ssl http2;
|
||||||
|
server_name quantengine.example.com;
|
||||||
|
|
||||||
|
ssl_certificate /etc/ssl/certs/cert.pem;
|
||||||
|
ssl_certificate_key /etc/ssl/private/key.pem;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://quantengine;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
|
||||||
|
# WebSocket support
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "upgrade";
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~* \.(js|css|wasm|svg|woff2)$ {
|
||||||
|
expires 30d;
|
||||||
|
add_header Cache-Control "public, immutable";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 8.4 Environment Configuration
|
||||||
|
|
||||||
|
#### appsettings.production.json
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"System": "Warning",
|
||||||
|
"Microsoft": "Warning"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ConnectionStrings": {
|
||||||
|
"DefaultConnection": "Host=prod-db-host;Database=quantenginedb;Username=quantengine_app;Password=***;SslMode=Require;",
|
||||||
|
"HangfireConnection": "Host=prod-db-host;Database=quantengine_hangfire;..."
|
||||||
|
},
|
||||||
|
"AdminSettings": {
|
||||||
|
"Username": "admin",
|
||||||
|
"Password": "***"
|
||||||
|
},
|
||||||
|
"Kestrel": {
|
||||||
|
"Endpoints": {
|
||||||
|
"Http": {
|
||||||
|
"Url": "http://0.0.0.0:5000"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 8.5 Deployment Checklist
|
||||||
|
|
||||||
|
#### Pre-Deployment
|
||||||
|
- [ ] All tests pass (`dotnet test`)
|
||||||
|
- [ ] Code reviewed and approved
|
||||||
|
- [ ] Security vulnerabilities scanned (`dotnet package-search`)
|
||||||
|
- [ ] Database migrations tested
|
||||||
|
- [ ] Hangfire schedules configured
|
||||||
|
- [ ] Secrets properly managed (not in code)
|
||||||
|
- [ ] Environment variables documented
|
||||||
|
|
||||||
|
#### Deployment Steps
|
||||||
|
```bash
|
||||||
|
# 1. Create backup
|
||||||
|
pg_dump -h prod-db-host -U quantengine_app quantenginedb > backup-$(date +%Y%m%d).sql
|
||||||
|
|
||||||
|
# 2. Deploy application
|
||||||
|
docker pull quantengine:latest
|
||||||
|
docker stop quantengine
|
||||||
|
docker run -d --name quantengine -p 5000:80 quantengine:latest
|
||||||
|
|
||||||
|
# 3. Health check
|
||||||
|
curl https://quantengine.example.com/health
|
||||||
|
|
||||||
|
# 4. Monitor logs
|
||||||
|
docker logs -f quantengine
|
||||||
|
|
||||||
|
# 5. Verify features
|
||||||
|
- [ ] Login works
|
||||||
|
- [ ] Dashboard loads
|
||||||
|
- [ ] Data collection runs
|
||||||
|
- [ ] Hangfire jobs scheduled
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Post-Deployment
|
||||||
|
- [ ] Monitor error logs (Serilog, Telegram alerts)
|
||||||
|
- [ ] Check Hangfire dashboard
|
||||||
|
- [ ] Verify scheduled jobs running
|
||||||
|
- [ ] Monitor database performance
|
||||||
|
- [ ] Check API response times (< 200ms)
|
||||||
|
|
||||||
|
### 8.6 Monitoring & Observability
|
||||||
|
|
||||||
|
#### Health Checks
|
||||||
|
```csharp
|
||||||
|
app.MapHealthChecks("/health", new HealthCheckOptions
|
||||||
|
{
|
||||||
|
Predicate = _ => true,
|
||||||
|
ResponseWriter = WriteResponse
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add health checks
|
||||||
|
builder.Services.AddHealthChecks()
|
||||||
|
.AddDbContextCheck<QuantEngineDbContext>()
|
||||||
|
.AddCheck("Database", () => HealthCheckResult.Healthy())
|
||||||
|
.AddCheck("KIS API", () => CheckKisApiAsync());
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Logging (Serilog)
|
||||||
|
```csharp
|
||||||
|
Log.Information("Collection run completed: {RunId}, {Count} items", runId, itemCount);
|
||||||
|
Log.Warning("API rate limit warning: {Remaining}", remaining);
|
||||||
|
Log.Error(ex, "Collection failed: {RunId}", runId);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Monitoring Metrics
|
||||||
|
- Request rate (requests/sec)
|
||||||
|
- Error rate (errors/requests)
|
||||||
|
- Database query time (p50, p95, p99)
|
||||||
|
- Hangfire job success rate
|
||||||
|
- API response time by endpoint
|
||||||
|
|
||||||
|
### 8.7 Rollback Plan
|
||||||
|
|
||||||
|
#### If Deployment Fails
|
||||||
|
```bash
|
||||||
|
# 1. Stop current deployment
|
||||||
|
docker stop quantengine
|
||||||
|
|
||||||
|
# 2. Restore previous version
|
||||||
|
docker run -d --name quantengine -p 5000:80 quantengine:v1.0.0
|
||||||
|
|
||||||
|
# 3. Restore database from backup
|
||||||
|
psql -h prod-db-host -U quantengine_app -d quantenginedb < backup-20260705.sql
|
||||||
|
|
||||||
|
# 4. Verify health
|
||||||
|
curl https://quantengine.example.com/health
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Deployment Timeline
|
||||||
|
|
||||||
|
| Milestone | Target Date | Status |
|
||||||
|
|-----------|-------------|--------|
|
||||||
|
| Phase 6: Tests | 2026-07-06 | 📋 |
|
||||||
|
| Phase 7: Hangfire | 2026-07-05 | ✅ |
|
||||||
|
| Phase 8: Deploy | 2026-07-07 | 📋 |
|
||||||
|
| Production Release | 2026-07-10 | 📅 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Success Criteria
|
||||||
|
|
||||||
|
**Phase 6**:
|
||||||
|
- [ ] 80%+ test coverage
|
||||||
|
- [ ] All component tests passing
|
||||||
|
- [ ] WCAG AA compliance verified
|
||||||
|
- [ ] Bundle size < 5MB
|
||||||
|
|
||||||
|
**Phase 8**:
|
||||||
|
- [ ] Docker image builds successfully
|
||||||
|
- [ ] Production config validated
|
||||||
|
- [ ] Database backups automated
|
||||||
|
- [ ] Rollback plan documented
|
||||||
|
- [ ] Monitoring alerts configured
|
||||||
|
- [ ] 99.5% uptime target established
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Next**: Execute deployment pipeline and monitor production metrics.
|
||||||
Reference in New Issue
Block a user