forked from cardosofelipe/fast-next-template
- Create docs/development/WORKFLOW.md with branch strategy, issue management, testing requirements, and code review process - Create docs/development/CODING_STANDARDS.md with technical patterns, auth DI pattern, testing patterns, and security guidelines - Streamline CLAUDE.md to link to detailed documentation instead of embedding all content - Add branch/issue workflow rules: single branch per feature for both design and implementation phases 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
313 lines
7.6 KiB
Markdown
313 lines
7.6 KiB
Markdown
# 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 <package>` or `uv add --dev <package>`
|
|
|
|
```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 `<AuthProvider store={mockStore}>`
|
|
- 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.
|