Sprint 16 — постоянный regression-контур: flows + visual + nightly +
CI workflow + README badges.
Ключевые цифры:
- 35 flow-тестов: 35/35 ✓ за ~30 секунд (workers=2 локально).
- 60 visual snapshot'ов (15 страниц × 2 темы × 2 viewport'a):
60/60 ✓ за ~4 минуты с retries=1.
- Полный регресс прогон: ~5 минут (цель была < 15).
Что сделано:
1. tests/regression/ — Playwright + factories + 8 spec-файлов.
OrgFactory builder создаёт org через API за O(N) HTTP вызовов
(signup → token → refs → products → counterparties → posted supplies).
Каждый flow независим, использует свой fresh-org.
2. tests/regression/visual/ — 15 страниц × 2 темы × 2 viewport'a.
Маски на динамический контент (артикулы с Date.now, KPI'ы,
delta-стрелки) чтобы 0.2% threshold не флакал. snapshotPathTemplate
c {projectName} — desktop+mobile не затирают друг друга.
3. tests/regression/factories/OrgFactory.ts — builder с .withProducts
.withCounterparties .withSupplies. Retry signup'a на 429.
4. .forgejo/workflows/regression.yml — on workflow_run после
Docker API/Web; cache на pnpm-store + Playwright-browsers;
артефакты при failure; Telegram-уведомление в обоих случаях.
5. ~/nightly-verify.sh + cron `0 4 * * *`: health → redeploy если
нужно → smoke flows; в воскресенье полный flows+visual. Логи с
ротацией 14 дней. Telegram на провал (~/.fm-watchdog/telegram-*).
6. scripts/generate-badges.sh — coverage из cobertura.xml в SVG через
shields.io (offline fallback). 4 CI-status badge + coverage badge в
README; CI step «Update coverage badge» авто-коммитит обновлённый
SVG на push в main.
Локальное число flake'ов: 1/60 visual на retry=1 (product-new light) —
случайная гонка маски, retry'ит и проходит.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
92 lines
4 KiB
Markdown
92 lines
4 KiB
Markdown
# food-market
|
||
|
||

|
||

|
||

|
||

|
||

|
||
|
||
Аналог системы МойСклад для розничной торговли в Казахстане.
|
||
|
||
## Состав системы
|
||
|
||
- **Сервер** (ASP.NET Core 8 + PostgreSQL) — мультитенантный API, web-админка на React
|
||
- **Web-админка** (React 18 + Vite + shadcn/ui) — управление магазином, справочниками, документами, отчётами
|
||
- **Кассовая программа** (WPF на .NET 8) — офлайн-работоспособная касса для Windows 10+, синхронизируется с сервером, работает с весами (Масса-К в первую очередь)
|
||
|
||
## Структура репозитория
|
||
|
||
```
|
||
food-market/
|
||
├── src/
|
||
│ ├── food-market.domain/ # доменные сущности, enum'ы, события
|
||
│ ├── food-market.application/ # use cases (MediatR), DTO, интерфейсы
|
||
│ ├── food-market.infrastructure/ # EF Core, PostgreSQL, внешние сервисы
|
||
│ ├── food-market.api/ # ASP.NET Core + OpenIddict + SignalR
|
||
│ ├── food-market.web/ # React + Vite + shadcn/ui (SPA)
|
||
│ ├── food-market.shared/ # DTO-контракты сервер ↔ POS
|
||
│ ├── food-market.pos.core/ # логика POS (независима от UI)
|
||
│ └── food-market.pos/ # WPF + .NET 8 кассовая программа
|
||
├── tests/
|
||
├── deploy/
|
||
│ ├── docker-compose.yml # PostgreSQL для локальной разработки
|
||
│ └── Dockerfile.api
|
||
└── docs/
|
||
```
|
||
|
||
## Именование
|
||
|
||
- **Папки, проекты, csproj, docker-образы, URL** — lowercase: `food-market`, `food-market.api`
|
||
- **C# namespace** — `foodmarket.Api`, `foodmarket.Domain` (lowercase root; C# не допускает дефис в идентификаторах)
|
||
- **Отображаемое имя в UI** — "Food Market"
|
||
|
||
## Стек
|
||
|
||
### Сервер
|
||
- .NET 8 LTS (до ноября 2026), ASP.NET Core Minimal APIs + Controllers
|
||
- EF Core 8 + Npgsql + PostgreSQL 16
|
||
- OpenIddict 5 (OAuth2/OIDC — password + refresh tokens)
|
||
- MediatR + FluentValidation (CQRS-lite)
|
||
- SignalR (real-time синхронизация)
|
||
- Hangfire (фоновые задачи)
|
||
- Serilog (структурированное логирование)
|
||
|
||
### Web
|
||
- React 18 + Vite + TypeScript
|
||
- shadcn/ui + Tailwind CSS
|
||
- TanStack Query + TanStack Table
|
||
- AG Grid Community (для тяжёлых grid'ов)
|
||
- Recharts / Tremor (графики)
|
||
- react-hook-form + Zod
|
||
|
||
### POS
|
||
- .NET 8 WPF, Windows 10+
|
||
- CommunityToolkit.Mvvm (source-generated MVVM)
|
||
- SQLite (локальная БД)
|
||
- Refit + Polly (API-клиент с retry)
|
||
- System.IO.Ports (драйверы весов: Масса-К и др.)
|
||
|
||
## Мультитенантность
|
||
|
||
Каждая сущность имеет `OrganizationId`. Пользователь scoped к организации. EF Core query filter автоматически изолирует данные между тенантами.
|
||
|
||
## Локальная разработка
|
||
|
||
```bash
|
||
# Поднять PostgreSQL
|
||
cd deploy && docker compose up -d
|
||
|
||
# Мигрировать БД
|
||
cd src/food-market.api && dotnet ef database update
|
||
|
||
# Запустить API
|
||
dotnet run --project src/food-market.api
|
||
|
||
# Запустить Web
|
||
cd src/food-market.web && pnpm install && pnpm dev
|
||
```
|
||
|
||
## Статус
|
||
|
||
🚧 Phase 0: фундамент (scaffolding, auth, multi-tenancy)
|