Files
basil/TESTING.md
Paul R Kartchner 554b53bec7
Some checks failed
CI Pipeline / Lint Code (push) Has been cancelled
CI Pipeline / Test API Package (push) Has been cancelled
CI Pipeline / Test Web Package (push) Has been cancelled
CI Pipeline / Test Shared Package (push) Has been cancelled
CI Pipeline / Build All Packages (push) Has been cancelled
CI Pipeline / Generate Coverage Report (push) Has been cancelled
Docker Build & Deploy / Build Docker Images (push) Has been cancelled
Docker Build & Deploy / Push Docker Images (push) Has been cancelled
Docker Build & Deploy / Deploy to Staging (push) Has been cancelled
Docker Build & Deploy / Deploy to Production (push) Has been cancelled
E2E Tests / End-to-End Tests (push) Has been cancelled
E2E Tests / E2E Tests (Mobile) (push) Has been cancelled
Security Scanning / NPM Audit (push) Has been cancelled
Security Scanning / Dependency License Check (push) Has been cancelled
Security Scanning / Code Quality Scan (push) Has been cancelled
Security Scanning / Docker Image Security (push) Has been cancelled
Security Scanning / Security Summary (push) Has been cancelled
feat: add comprehensive testing infrastructure
- Add Vitest for unit testing across all packages
- Add Playwright for E2E testing
- Add sample tests for API, Web, and Shared packages
- Configure Gitea Actions CI/CD workflows (ci, e2e, security, docker)
- Add testing documentation (TESTING.md)
- Add Gitea Actions setup guide
- Update .gitignore for test artifacts
- Add test environment configuration
2025-10-28 02:03:52 -06:00

622 lines
13 KiB
Markdown

# Testing Documentation
This document provides comprehensive guidance on testing in the Basil project, including unit tests, integration tests, E2E tests, and security testing.
## Table of Contents
1. [Overview](#overview)
2. [Testing Stack](#testing-stack)
3. [Running Tests](#running-tests)
4. [Unit Testing](#unit-testing)
5. [Integration Testing](#integration-testing)
6. [E2E Testing](#e2e-testing)
7. [Security Testing](#security-testing)
8. [CI/CD Integration](#cicd-integration)
9. [Writing Tests](#writing-tests)
10. [Best Practices](#best-practices)
11. [Troubleshooting](#troubleshooting)
## Overview
The Basil project uses a comprehensive testing strategy covering all layers:
- **Unit Tests**: Test individual functions and components in isolation
- **Integration Tests**: Test API endpoints and database interactions
- **E2E Tests**: Test complete user workflows across the full stack
- **Security Tests**: Scan for vulnerabilities and security issues
## Testing Stack
### Unit & Integration Tests
- **Vitest**: Fast unit testing framework with TypeScript support
- **@testing-library/react**: React component testing utilities
- **@testing-library/jest-dom**: Custom Jest matchers for DOM assertions
- **Supertest**: HTTP assertion library for API testing
### E2E Tests
- **Playwright**: Cross-browser end-to-end testing framework
- Supports Chrome, Firefox, Safari, and mobile browsers
### Security Testing
- **npm audit**: Built-in npm vulnerability scanner
- ESLint with security rules
- Docker image scanning
## Running Tests
### Run All Tests
```bash
# Run all unit tests across all packages
npm test
# Run with coverage
npm run test:coverage
# Run tests in watch mode (development)
npm run test:watch
```
### Package-Specific Tests
#### API Package
```bash
cd packages/api
# Run unit tests
npm test
# Run with coverage
npm run test:coverage
# Run in watch mode
npm run test:watch
# Run with UI
npm run test:ui
```
#### Web Package
```bash
cd packages/web
# Run component tests
npm test
# Run with coverage
npm run test:coverage
# Run in watch mode
npm run test:watch
```
#### Shared Package
```bash
cd packages/shared
# Run type validation tests
npm test
```
### E2E Tests
```bash
# Run all E2E tests (headless)
npm run test:e2e
# Run with UI mode (interactive)
npm run test:e2e:ui
# Run in headed mode (see browser)
npm run test:e2e:headed
# Run specific test file
npx playwright test e2e/recipes.spec.ts
# Run specific browser
npx playwright test --project=chromium
# Run mobile tests only
npx playwright test --project="Mobile Chrome"
```
## Unit Testing
### API Unit Tests
Located in `packages/api/src/**/*.test.ts`
**Example: Testing a Service**
```typescript
import { describe, it, expect, vi } from 'vitest';
import { StorageService } from './storage.service';
describe('StorageService', () => {
it('should save file locally', async () => {
const service = StorageService.getInstance();
const mockFile = createMockFile();
const result = await service.saveFile(mockFile, 'recipes');
expect(result).toMatch(/^\/uploads\/recipes\//);
});
});
```
**Running API Tests**
```bash
cd packages/api
npm test
# Run specific test file
npm test -- storage.service.test.ts
# Run with coverage
npm run test:coverage
```
### Web Unit Tests
Located in `packages/web/src/**/*.test.{ts,tsx}`
**Example: Testing a React Component**
```typescript
import { render, screen, waitFor } from '@testing-library/react';
import { describe, it, expect, vi } from 'vitest';
import RecipeList from './RecipeList';
describe('RecipeList', () => {
it('should display recipes', async () => {
render(<RecipeList />);
await waitFor(() => {
expect(screen.getByText('Recipe Title')).toBeInTheDocument();
});
});
});
```
## Integration Testing
Integration tests verify API endpoints and database interactions.
### API Integration Tests
Located in `packages/api/src/routes/**/*.test.ts`
**Example: Testing an API Endpoint**
```typescript
import request from 'supertest';
import { describe, it, expect } from 'vitest';
import app from '../index';
describe('GET /api/recipes', () => {
it('should return paginated recipes', async () => {
const response = await request(app)
.get('/api/recipes')
.expect(200);
expect(response.body).toHaveProperty('data');
expect(response.body).toHaveProperty('total');
});
});
```
### Database Testing
Integration tests use a test database. Configure in `.env.test`:
```env
DATABASE_URL=postgresql://basil:basil@localhost:5432/basil_test
```
**Setup Test Database**
```bash
cd packages/api
# Run migrations on test database
DATABASE_URL="postgresql://basil:basil@localhost:5432/basil_test" npm run prisma:migrate
# Seed test data (if needed)
DATABASE_URL="postgresql://basil:basil@localhost:5432/basil_test" npm run prisma:seed
```
## E2E Testing
E2E tests validate complete user workflows using Playwright.
### Configuration
Playwright configuration: `playwright.config.ts`
```typescript
export default defineConfig({
testDir: './e2e',
baseURL: 'http://localhost:5173',
webServer: {
command: 'npm run dev',
url: 'http://localhost:5173',
},
});
```
### Writing E2E Tests
Located in `e2e/**/*.spec.ts`
**Example: Testing Recipe Workflow**
```typescript
import { test, expect } from '@playwright/test';
test('should create and view recipe', async ({ page }) => {
// Navigate to app
await page.goto('/');
// Click create button
await page.click('button:has-text("Create Recipe")');
// Fill form
await page.fill('input[name="title"]', 'Test Recipe');
await page.fill('textarea[name="description"]', 'Test Description');
// Submit
await page.click('button[type="submit"]');
// Verify recipe was created
await expect(page.locator('h1')).toContainText('Test Recipe');
});
```
### E2E Test Best Practices
1. **Use data-testid attributes** for reliable selectors
2. **Mock external APIs** to avoid flaky tests
3. **Clean up test data** after each test
4. **Use page object pattern** for complex pages
5. **Test critical user paths** first
### Debugging E2E Tests
```bash
# Run with headed browser
npm run test:e2e:headed
# Run with UI mode (interactive debugging)
npm run test:e2e:ui
# Generate trace for failed tests
npx playwright test --trace on
# View trace
npx playwright show-trace trace.zip
```
## Security Testing
### NPM Audit
```bash
# Run security audit
npm audit
# Fix automatically fixable vulnerabilities
npm audit fix
# Audit specific package
cd packages/api && npm audit
```
### Dependency Scanning
Monitor for outdated and vulnerable dependencies:
```bash
# Check for outdated packages
npm outdated
# Update dependencies
npm update
```
### Code Scanning
ESLint is configured with security rules:
```bash
# Run linter
npm run lint
# Fix auto-fixable issues
npm run lint -- --fix
```
### Docker Security
Scan Docker images for vulnerabilities:
```bash
# Build images
docker-compose build
# Optional: Use Trivy for scanning
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
aquasec/trivy image basil-api:latest
```
## CI/CD Integration
### Gitea Actions Workflows
The project includes comprehensive CI/CD workflows in `.gitea/workflows/`:
#### 1. CI Pipeline (`ci.yml`)
Runs on every push and PR:
- Linting
- Unit tests for all packages
- Build verification
- Coverage reporting
#### 2. E2E Tests (`e2e.yml`)
Runs on main branch and nightly:
- Full E2E test suite
- Mobile browser testing
- Screenshot and trace artifacts
#### 3. Security Scanning (`security.yml`)
Runs weekly and on main branch:
- NPM audit
- Dependency checking
- Code quality scanning
- Docker image security
#### 4. Docker Build & Deploy (`docker.yml`)
Runs on main branch and version tags:
- Build Docker images
- Push to registry
- Deploy to staging/production
### Setting Up Gitea Actions
1. **Enable Gitea Actions** in your Gitea instance settings
2. **Configure Runners** (act_runner or Gitea Actions runner)
3. **Set Repository Secrets**:
```
DOCKER_REGISTRY=registry.example.com
DOCKER_USERNAME=your-username
DOCKER_PASSWORD=your-password
```
4. **Verify Workflows**: Push to trigger pipelines
### CI Environment Variables
Configure these in your Gitea repository settings:
```env
# Database (for CI)
DATABASE_URL=postgresql://basil:basil@localhost:5432/basil_test
# Docker Registry
DOCKER_REGISTRY=your-registry.com
DOCKER_USERNAME=your-username
DOCKER_PASSWORD=your-token
# Optional: External Services
SENTRY_DSN=your-sentry-dsn
ANALYTICS_KEY=your-analytics-key
```
## Writing Tests
### General Guidelines
1. **Follow AAA Pattern**: Arrange, Act, Assert
```typescript
it('should do something', () => {
// Arrange
const input = setupTestData();
// Act
const result = functionUnderTest(input);
// Assert
expect(result).toBe(expected);
});
```
2. **Use Descriptive Names**: Test names should describe behavior
```typescript
// Good
it('should return 404 when recipe not found')
// Bad
it('test recipe endpoint')
```
3. **Test One Thing**: Each test should verify one specific behavior
4. **Mock External Dependencies**: Don't rely on external services
```typescript
vi.mock('axios');
vi.mocked(axios.get).mockResolvedValue({ data: mockData });
```
5. **Clean Up**: Reset mocks and state after each test
```typescript
afterEach(() => {
vi.clearAllMocks();
});
```
### Test Structure
```
packages/
├── api/
│ └── src/
│ ├── services/
│ │ ├── storage.service.ts
│ │ └── storage.service.test.ts
│ └── routes/
│ ├── recipes.routes.ts
│ └── recipes.routes.test.ts
├── web/
│ └── src/
│ ├── components/
│ │ ├── RecipeCard.tsx
│ │ └── RecipeCard.test.tsx
│ └── services/
│ ├── api.ts
│ └── api.test.ts
└── shared/
└── src/
├── types.ts
└── types.test.ts
```
## Best Practices
### Unit Tests
✅ **Do:**
- Test pure functions and logic
- Mock external dependencies
- Use factory functions for test data
- Test edge cases and error conditions
- Keep tests fast (< 50ms per test)
❌ **Don't:**
- Test implementation details
- Make network requests
- Access real databases
- Test framework code
- Write brittle tests tied to DOM structure
### Integration Tests
✅ **Do:**
- Test API endpoints end-to-end
- Use test database with migrations
- Test authentication and authorization
- Verify database state after operations
- Test error responses
❌ **Don't:**
- Test external APIs directly
- Share state between tests
- Skip cleanup after tests
- Hard-code test data IDs
### E2E Tests
✅ **Do:**
- Test critical user workflows
- Use stable selectors (data-testid)
- Mock external APIs
- Run in CI pipeline
- Take screenshots on failure
❌ **Don't:**
- Test every possible path (use unit tests)
- Rely on timing (use waitFor)
- Share test data between tests
- Test implementation details
- Make tests too granular
## Coverage Goals
Aim for the following coverage targets:
- **Unit Tests**: 80%+ coverage
- **Integration Tests**: Cover all API endpoints
- **E2E Tests**: Cover critical user paths
Check coverage:
```bash
# API Package
cd packages/api && npm run test:coverage
# Web Package
cd packages/web && npm run test:coverage
# View HTML coverage report
open packages/api/coverage/index.html
```
## Troubleshooting
### Common Issues
#### Tests Failing Locally
1. **Database connection errors**
```bash
# Ensure PostgreSQL is running
docker-compose up -d postgres
# Run migrations
cd packages/api && npm run prisma:migrate
```
2. **Module resolution errors**
```bash
# Clear node_modules and reinstall
rm -rf node_modules package-lock.json
npm install
```
3. **Port already in use**
```bash
# Kill process on port 3001
lsof -ti:3001 | xargs kill -9
```
#### E2E Tests Timing Out
1. **Increase timeout** in playwright.config.ts
2. **Check webServer** is starting correctly
3. **Verify database** migrations ran successfully
#### CI Tests Failing
1. **Check logs** in Gitea Actions UI
2. **Verify secrets** are configured correctly
3. **Run tests locally** with same Node version
4. **Check database** service is healthy
### Getting Help
- Review test logs and error messages
- Check existing tests for examples
- Consult Vitest docs: https://vitest.dev
- Consult Playwright docs: https://playwright.dev
## Continuous Improvement
Testing is an ongoing process. Regularly:
1. **Review test coverage** and add tests for uncovered code
2. **Refactor flaky tests** to be more reliable
3. **Update tests** when requirements change
4. **Add tests** for bug fixes to prevent regressions
5. **Monitor CI pipeline** performance and optimize slow tests
---
**Last Updated**: 2025-10-24
**Maintained By**: Basil Development Team