# Coding Standards Technical coding standards and patterns for Syndarix development. ## Table of Contents - [Backend Standards](#backend-standards) - [Frontend Standards](#frontend-standards) - [Testing Patterns](#testing-patterns) - [Security Guidelines](#security-guidelines) --- ## Backend Standards ### Dependency Management - Use **uv** (modern Python package manager), not pip - Always use `uv run` prefix for commands - Add dependencies: `uv add ` or `uv add --dev ` ```bash # Running tests IS_TEST=True uv run pytest # Or use Makefile make test make install-dev ``` ### Database Migrations - Use the `migrate.py` helper script, not Alembic directly - Never commit migrations without testing them first ```bash # Generate and apply migration python migrate.py auto "message" # Check current state python migrate.py current ``` ### Authentication Testing Fixtures Backend fixtures in `tests/conftest.py`: | Fixture | Purpose | |---------|---------| | `async_test_db` | Fresh SQLite per test | | `async_test_user` | Pre-created regular user | | `async_test_superuser` | Pre-created admin user | | `user_token` | Access token for regular user | | `superuser_token` | Access token for admin | ```python # Always use these decorators for async tests @pytest.mark.asyncio async def test_something(): pass # Use for async fixtures @pytest_asyncio.fixture async def my_fixture(): pass ``` ### Database Exception Mocking ```python from unittest.mock import patch, AsyncMock async def mock_commit(): raise OperationalError("Connection lost", {}, Exception()) with patch.object(session, 'commit', side_effect=mock_commit): with patch.object(session, 'rollback', new_callable=AsyncMock) as mock_rollback: with pytest.raises(OperationalError): await crud_method(session, obj_in=data) mock_rollback.assert_called_once() ``` ### Security Considerations - Backend has comprehensive security tests (JWT attacks, session hijacking) - Never skip security headers in production - Rate limiting: `@limiter.limit("10/minute")` - Session revocation is database-backed, not just JWT expiry --- ## Frontend Standards ### Auth Store Dependency Injection **CRITICAL: ALWAYS use `useAuth()` from `AuthContext`, NEVER import `useAuthStore` directly!** ```typescript // ❌ WRONG - Bypasses dependency injection import { useAuthStore } from '@/lib/stores/authStore'; const { user, isAuthenticated } = useAuthStore(); // ✅ CORRECT - Uses dependency injection import { useAuth } from '@/lib/auth/AuthContext'; const { user, isAuthenticated } = useAuth(); ``` **Why This Matters:** - E2E tests inject mock stores via `window.__TEST_AUTH_STORE__` - Unit tests inject via `` - Direct `useAuthStore` imports bypass this injection → **tests fail** - ESLint will catch violations **Exceptions:** 1. `AuthContext.tsx` - DI boundary, legitimately needs real store 2. `client.ts` - Non-React context, uses dynamic import + `__TEST_AUTH_STORE__` check ### API Client Generation ```bash # After backend schema changes npm run generate:api ``` - Client is auto-generated from OpenAPI spec - Located in `frontend/src/lib/api/generated/` - **NEVER manually edit generated files** ### Component Development - Follow design system docs in `frontend/docs/design-system/` - Read `08-ai-guidelines.md` for AI code generation rules - Use parent-controlled spacing (see `04-spacing-philosophy.md`) - WCAG AA compliance required (see `07-accessibility.md`) ### Route Groups Structure ``` src/app/[locale]/ ├── (auth)/ # Public auth pages (login, register, password reset) │ # Uses AuthLayoutClient - redirects authenticated users ├── (authenticated)/ # Protected app pages │ # Uses AuthGuard + Header/Footer ├── admin/ # Admin pages with sidebar │ # Uses AuthGuard requireAdmin ├── dev/ # Design system documentation └── prototypes/ # UI prototypes for approval ``` --- ## Testing Patterns ### Frontend Unit Tests ```typescript // Mock useAuth correctly jest.mock('@/lib/auth/AuthContext', () => ({ useAuth: () => ({ user: { id: '1', email: 'test@example.com' }, isAuthenticated: true, }), })); ``` ### E2E Test Best Practices (Playwright) **Navigation Pattern:** ```typescript // ✅ CORRECT - Use Promise.all for Next.js Link clicks await Promise.all([ page.waitForURL('/target', { timeout: 10000 }), link.click() ]); ``` **Selectors:** - Use ID-based selectors for validation errors: `#email-error` - Error IDs use dashes not underscores: `#new-password-error` - Target `.border-destructive[role="alert"]` to avoid Next.js route announcer conflicts - Avoid generic `[role="alert"]` which matches multiple elements **URL Assertions:** ```typescript // ✅ Use regex to handle query params await expect(page).toHaveURL(/\/auth\/login/); // ❌ Don't use exact strings (fails with query params) await expect(page).toHaveURL('/auth/login'); ``` **Configuration:** - Uses 12 workers in non-CI mode - Reduces to 2 workers in CI for stability ### Backend E2E Testing Requires Docker for Testcontainers. ```bash # Install deps make install-e2e # Run all E2E tests make test-e2e # Run schema tests only make test-e2e-schema # Run all tests (unit + E2E) make test-all ``` Uses: - Testcontainers (real PostgreSQL) - Schemathesis (OpenAPI contract testing) Markers: - `@pytest.mark.e2e` - `@pytest.mark.postgres` - `@pytest.mark.schemathesis` See: `backend/docs/E2E_TESTING.md` for complete guide. --- ## Security Guidelines ### OWASP Top 10 Compliance All code must be reviewed for: - Injection vulnerabilities (SQL, command, XSS) - Broken authentication - Sensitive data exposure - Security misconfiguration - Insecure deserialization ### Rate Limiting ```python from app.core.limiter import limiter @router.post("/endpoint") @limiter.limit("10/minute") async def endpoint(): pass ``` ### JWT Security - Tokens stored securely (httpOnly cookies preferred) - Short expiration times - Refresh token rotation - Database-backed session revocation --- ## Common Workflows ### Adding a New Feature 1. Start with backend schema and CRUD 2. Implement API route with proper authorization 3. Write backend tests (aim for >90% coverage) 4. Generate frontend API client: `npm run generate:api` 5. Implement frontend components 6. Write frontend unit tests 7. Add E2E tests for critical flows 8. Update relevant documentation ### Fixing Tests | Layer | Check | |-------|-------| | Backend | Test database isolation, async fixture usage | | Frontend unit | Mock `useAuth()` not `useAuthStore` | | E2E | Use `Promise.all()` pattern, regex URL assertions | ### Debugging | Layer | Action | |-------|--------| | Backend | Check `IS_TEST=True` environment variable | | Frontend | Run `npm run type-check` first | | E2E | Use `npm run test:e2e:debug` for step-by-step | | All | Check logs - backend has detailed error logging | --- ## Demo Mode Frontend-only showcase mode for demos without backend. ```bash # Enable echo "NEXT_PUBLIC_DEMO_MODE=true" > frontend/.env.local ``` - Uses MSW (Mock Service Worker) to intercept API calls - Zero backend required - perfect for Vercel deployments - MSW handlers auto-generated from OpenAPI spec - Run `npm run generate:api` → updates both API client AND MSW handlers **Demo Credentials:** - User: `demo@example.com` / `DemoPass123` - Admin: `admin@example.com` / `AdminPass123` **Safety:** - MSW never runs during tests (Jest or Playwright) - Mock files excluded from linting and coverage See: `frontend/docs/DEMO_MODE.md` for complete guide.