Security Fixes

This commit is contained in:
Eric Gullickson
2025-08-24 14:39:50 -05:00
parent 000e71a026
commit e22d643ae3
19 changed files with 187 additions and 8838 deletions

View File

@@ -25,7 +25,8 @@
"Bash(git add:*)", "Bash(git add:*)",
"Bash(git commit:*)", "Bash(git commit:*)",
"Bash(git tag:*)", "Bash(git tag:*)",
"Bash(docker build:*)" "Bash(docker build:*)",
"Bash(docker run:*)"
], ],
"deny": [] "deny": []
} }

View File

@@ -4,7 +4,12 @@
Vehicle management platform using Modified Feature Capsules. Each feature in backend/src/features/[name]/ is 100% self-contained with API, domain, data, migrations, external integrations, tests, and docs. Single directory load gives complete context. No shared business logic, only pure utilities in shared-minimal/. Vehicle management platform using Modified Feature Capsules. Each feature in backend/src/features/[name]/ is 100% self-contained with API, domain, data, migrations, external integrations, tests, and docs. Single directory load gives complete context. No shared business logic, only pure utilities in shared-minimal/.
## Architecture Philosophy ## Architecture Philosophy
Each feature is a complete, self-contained capsule. Load ONE directory for 100% context. Evaluate every feature if it should be in it's own Docker container. This is a microservices based archiecture where production will be run on k8s. - Each feature is a complete, self-contained capsule.
- Load ONE directory for 100% context.
- Evaluate every feature if it should be in it's own Docker container.
- This is a microservices based archiecture where production will be run on k8s.
- This is a production only application architecture.
- Always assume this application is going into production when you are done. All security and linting should pass.
## Navigation & Quick Tasks ## Navigation & Quick Tasks
@@ -36,25 +41,26 @@ cd backend/src/features/[feature-name]/
# Creates complete capsule structure with all subdirectories # Creates complete capsule structure with all subdirectories
``` ```
### Running Feature Migrations ### Running Migrations
```bash ```bash
# Single feature # All features (in dependency order)
npm run migrate:feature [feature-name]
# All features (respects dependencies)
npm run migrate:all npm run migrate:all
# From project root using Docker
make migrate
``` ```
Note: Single-feature migration is not implemented yet. Run the full migration set.
### Testing Strategy ### Testing Strategy
```bash ```bash
# Test single feature (complete isolation) # Run all tests (from project root)
make test
# In backend container shell
make shell-backend
npm test # all tests
npm test -- features/[feature-name] npm test -- features/[feature-name]
# Test feature integration
npm test -- features/[feature-name]/tests/integration npm test -- features/[feature-name]/tests/integration
# Test everything
npm test
``` ```
### Docker Development Workflow ### Docker Development Workflow
@@ -128,10 +134,14 @@ features/[name]/
## Development Environment ## Development Environment
All development happens in Docker containers: All development happens in Docker containers:
- **Development**: Dockerfile.dev with npm install during container build - **Development**: `make dev` builds and runs the stack
- **Testing**: make test runs tests in container - **Testing**: `make test` runs backend tests in the container
- **Rebuilding**: make rebuild for code changes - **Rebuilding**: `make rebuild` for code/dependency changes
- **Package changes**: Container rebuild required - **Package changes**: Rebuild backend/frontend containers as needed
## Authentication (Current State)
- Backend uses a Fastify auth plugin that injects a mock user in development/test.
- JWT validation via Auth0 is planned; production configuration will enforce it.
## External Services ## External Services
- **PostgreSQL**: Primary database (port 5432) - **PostgreSQL**: Primary database (port 5432)
@@ -152,4 +162,4 @@ Features must be migrated in dependency order:
1. **vehicles** (base feature) 1. **vehicles** (base feature)
2. **fuel-logs** (depends on vehicles) 2. **fuel-logs** (depends on vehicles)
3. **maintenance** (depends on vehicles) 3. **maintenance** (depends on vehicles)
4. **stations** (independent) 4. **stations** (independent)

View File

@@ -1,10 +1,10 @@
.PHONY: help setup dev stop clean test logs shell-backend shell-frontend migrate rebuild .PHONY: help setup start dev stop clean test logs shell-backend shell-frontend migrate rebuild
help: help:
@echo "MotoVaultPro - Fully Containerized Modified Feature Capsule Architecture" @echo "MotoVaultPro - Production-Ready Modified Feature Capsule Architecture"
@echo "Commands:" @echo "Commands:"
@echo " make setup - Initial project setup" @echo " make setup - Initial project setup"
@echo " make dev - Start all services in development mode" @echo " make start - Start all services"
@echo " make rebuild - Rebuild and restart containers (for code changes)" @echo " make rebuild - Rebuild and restart containers (for code changes)"
@echo " make stop - Stop all services" @echo " make stop - Stop all services"
@echo " make clean - Clean all data and volumes" @echo " make clean - Clean all data and volumes"
@@ -27,14 +27,17 @@ setup:
@echo "Backend: http://localhost:3001" @echo "Backend: http://localhost:3001"
@echo "MinIO Console: http://localhost:9001" @echo "MinIO Console: http://localhost:9001"
dev: start:
@echo "Starting development environment..." @echo "Starting application services..."
@docker compose up -d --build @docker compose up -d --build
@echo "✅ Development environment running!" @echo "✅ Application running!"
@echo "Frontend: http://localhost:3000" @echo "Frontend: http://localhost:3000"
@echo "Backend: http://localhost:3001/health" @echo "Backend: http://localhost:3001/health"
@echo "View logs with: make logs" @echo "View logs with: make logs"
# Alias for backward compatibility
dev: start
stop: stop:
@docker compose down @docker compose down

View File

@@ -32,11 +32,17 @@ Load `docs/database-schema.md` for complete schema overview
Load `docs/testing.md` for Docker-based testing workflow Load `docs/testing.md` for Docker-based testing workflow
Only use docker containers for testing. Never install local tools if they do not exist already. Only use docker containers for testing. Never install local tools if they do not exist already.
### 4. Development Environment (1 command) ### 4. Development Environment (Docker-first)
```bash ```bash
make dev # Starts complete Docker environment # One-time setup (copies .env and builds containers)
make setup
# Start/rebuild the full environment
make dev
``` ```
Note: The frontend runs behind nginx with HTTPS in dev. You must provide local certificates in `./certs` (see SSL section below) or the frontend container will fail to start.
### 5. Key Principles ### 5. Key Principles
- **Docker-First**: All development in containers, no local installs - **Docker-First**: All development in containers, no local installs
- **Feature Independence**: Each feature is completely isolated - **Feature Independence**: Each feature is completely isolated
@@ -45,12 +51,16 @@ make dev # Starts complete Docker environment
### 6. Common Tasks ### 6. Common Tasks
```bash ```bash
# Test specific feature # Run all migrations (inside containers)
npm test -- features/vehicles
# Run migrations
make migrate make migrate
# Run all backend tests (inside containers)
make test
# Run tests for a specific feature (from backend container shell)
make shell-backend
npm test -- features/vehicles
# View logs # View logs
make logs make logs
@@ -60,10 +70,21 @@ make shell-backend
### 7. Feature Status ### 7. Feature Status
- **vehicles**: Complete (primary entity, VIN decoding) - **vehicles**: Complete (primary entity, VIN decoding)
- **fuel-logs**: Implemented (depends on vehicles) - **fuel-logs**: Implemented (depends on vehicles); tests pending
- **maintenance**: Scaffolded (depends on vehicles) - **maintenance**: Scaffolded (depends on vehicles)
- **stations**: Partial (Google Maps integration) - **stations**: Partial (Google Maps integration)
## SSL for Frontend (Local Dev)
- Place `motovaultpro.com.crt` and `motovaultpro.com.key` in `./certs`.
- To generate self-signed dev certs:
```bash
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout certs/motovaultpro.com.key \
-out certs/motovaultpro.com.crt \
-subj "/CN=localhost"
```
- Access frontend at `https://localhost:3443` (HTTP on `:3000` redirects to HTTPS).
## Architecture Summary ## Architecture Summary
Vehicle management platform using Modified Feature Capsule design where each feature is self-contained with API, domain logic, database layer, migrations, external integrations, tests, and documentation in a single directory. Built for AI maintainability with Docker-first development. Vehicle management platform using Modified Feature Capsule design where each feature is self-contained with API, domain logic, database layer, migrations, external integrations, tests, and documentation in a single directory. Built for AI maintainability with Docker-first development.
@@ -72,4 +93,4 @@ Vehicle management platform using Modified Feature Capsule design where each fea
- **Features**: backend/src/features/[name]/README.md - **Features**: backend/src/features/[name]/README.md
- **Database**: docs/database-schema.md - **Database**: docs/database-schema.md
- **Testing**: docs/testing.md - **Testing**: docs/testing.md
- **Security**: docs/security.md - **Security**: docs/security.md

View File

@@ -19,7 +19,7 @@ RUN npm install && npm cache clean --force
COPY . . COPY . .
# Build the application # Build the application
RUN npm run build:docker RUN npm run build
# Stage 2: Production runtime # Stage 2: Production runtime
FROM node:20-alpine AS production FROM node:20-alpine AS production
@@ -30,8 +30,9 @@ RUN apk add --no-cache dumb-init
# Set working directory # Set working directory
WORKDIR /app WORKDIR /app
# Copy package files # Copy package files and any lock file generated in builder stage
COPY package*.json ./ COPY package*.json ./
COPY --from=builder /app/package-lock.json ./
# Install only production dependencies # Install only production dependencies
RUN npm ci --omit=dev && npm cache clean --force RUN npm ci --omit=dev && npm cache clean --force

View File

@@ -56,8 +56,8 @@ make test
- `database.ts` - PostgreSQL connection pool - `database.ts` - PostgreSQL connection pool
- `redis.ts` - Redis client and cache service - `redis.ts` - Redis client and cache service
### Security (`src/core/security/`) ### Security (Fastify Plugin)
- `auth.middleware.ts` - JWT authentication via Auth0 - `src/core/plugins/auth.plugin.ts` - Auth plugin (mock user in dev; plan for Auth0 JWT)
### Logging (`src/core/logging/`) ### Logging (`src/core/logging/`)
- `logger.ts` - Structured logging with Winston - `logger.ts` - Structured logging with Winston
@@ -89,7 +89,7 @@ Run tests:
npm test npm test
# Specific feature # Specific feature
npm run test:feature -- --feature=vehicles npm test -- features/vehicles
# Watch mode # Watch mode
npm run test:watch npm run test:watch
@@ -100,5 +100,5 @@ npm run test:watch
See `.env.example` for required variables. Key variables: See `.env.example` for required variables. Key variables:
- Database connection (DB_*) - Database connection (DB_*)
- Redis connection (REDIS_*) - Redis connection (REDIS_*)
- Auth0 configuration (AUTH0_*) - Auth0 configuration (AUTH0_*) — backend currently uses mock auth; JWT enforcement planned
- External API keys - External API keys

8723
backend/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -5,8 +5,7 @@
"main": "dist/index.js", "main": "dist/index.js",
"scripts": { "scripts": {
"dev": "nodemon --watch src --exec ts-node src/index.ts", "dev": "nodemon --watch src --exec ts-node src/index.ts",
"build": "tsc", "build": "tsc --project tsconfig.build.json",
"build:docker": "tsc --project tsconfig.build.json",
"start": "node dist/index.js", "start": "node dist/index.js",
"test": "jest", "test": "jest",
"test:watch": "jest --watch", "test:watch": "jest --watch",
@@ -39,7 +38,8 @@
"@fastify/type-provider-typebox": "^4.0.0", "@fastify/type-provider-typebox": "^4.0.0",
"@sinclair/typebox": "^0.31.28", "@sinclair/typebox": "^0.31.28",
"fastify-plugin": "^4.5.1", "fastify-plugin": "^4.5.1",
"@fastify/autoload": "^5.8.0" "@fastify/autoload": "^5.8.0",
"get-jwks": "^9.0.0"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^20.10.0", "@types/node": "^20.10.0",

View File

@@ -8,7 +8,7 @@ import * as dotenv from 'dotenv';
dotenv.config(); dotenv.config();
const envSchema = z.object({ const envSchema = z.object({
NODE_ENV: z.enum(['development', 'test', 'production']).default('development'), NODE_ENV: z.string().default('development'),
PORT: z.string().transform(Number).default('3001'), PORT: z.string().transform(Number).default('3001'),
// Database // Database
@@ -22,11 +22,11 @@ const envSchema = z.object({
REDIS_HOST: z.string().default('localhost'), REDIS_HOST: z.string().default('localhost'),
REDIS_PORT: z.string().transform(Number).default('6379'), REDIS_PORT: z.string().transform(Number).default('6379'),
// Auth0 // Auth0 - Required for JWT validation
AUTH0_DOMAIN: z.string().default('localhost'), AUTH0_DOMAIN: z.string().min(1, 'AUTH0_DOMAIN is required for JWT authentication'),
AUTH0_CLIENT_ID: z.string().default('development'), AUTH0_CLIENT_ID: z.string().min(1, 'AUTH0_CLIENT_ID is required'),
AUTH0_CLIENT_SECRET: z.string().default('development'), AUTH0_CLIENT_SECRET: z.string().min(1, 'AUTH0_CLIENT_SECRET is required'),
AUTH0_AUDIENCE: z.string().default('https://api.motovaultpro.com'), AUTH0_AUDIENCE: z.string().min(1, 'AUTH0_AUDIENCE is required for JWT validation'),
// External APIs // External APIs
GOOGLE_MAPS_API_KEY: z.string().default('development'), GOOGLE_MAPS_API_KEY: z.string().default('development'),
@@ -45,7 +45,4 @@ export type Environment = z.infer<typeof envSchema>;
// Validate and export - now with defaults for build-time compilation // Validate and export - now with defaults for build-time compilation
export const env = envSchema.parse(process.env); export const env = envSchema.parse(process.env);
// Convenience exports // Environment configuration validated and exported
export const isDevelopment = env.NODE_ENV === 'development';
export const isProduction = env.NODE_ENV === 'production';
export const isTest = env.NODE_ENV === 'test';

View File

@@ -3,10 +3,9 @@
* @ai-context All features use this for consistent logging * @ai-context All features use this for consistent logging
*/ */
import * as winston from 'winston'; import * as winston from 'winston';
import { env, isDevelopment } from '../config/environment';
export const logger = winston.createLogger({ export const logger = winston.createLogger({
level: isDevelopment ? 'debug' : 'info', level: 'info',
format: winston.format.combine( format: winston.format.combine(
winston.format.timestamp(), winston.format.timestamp(),
winston.format.errors({ stack: true }), winston.format.errors({ stack: true }),
@@ -14,16 +13,10 @@ export const logger = winston.createLogger({
), ),
defaultMeta: { defaultMeta: {
service: 'motovaultpro-backend', service: 'motovaultpro-backend',
environment: env.NODE_ENV,
}, },
transports: [ transports: [
new winston.transports.Console({ new winston.transports.Console({
format: isDevelopment format: winston.format.json(),
? winston.format.combine(
winston.format.colorize(),
winston.format.simple()
)
: winston.format.json(),
}), }),
], ],
}); });

View File

@@ -1,9 +1,10 @@
/** /**
* @ai-summary Fastify JWT authentication plugin using Auth0 * @ai-summary Fastify JWT authentication plugin using Auth0
* @ai-context Validates JWT tokens in production, mocks in development * @ai-context Validates JWT tokens against Auth0 JWKS endpoint
*/ */
import { FastifyPluginAsync, FastifyRequest, FastifyReply } from 'fastify'; import { FastifyPluginAsync, FastifyRequest, FastifyReply } from 'fastify';
import fp from 'fastify-plugin'; import fp from 'fastify-plugin';
import buildGetJwks from 'get-jwks';
import { env } from '../config/environment'; import { env } from '../config/environment';
import { logger } from '../logging/logger'; import { logger } from '../logging/logger';
@@ -11,20 +12,67 @@ declare module 'fastify' {
interface FastifyInstance { interface FastifyInstance {
authenticate: (request: FastifyRequest, reply: FastifyReply) => Promise<void>; authenticate: (request: FastifyRequest, reply: FastifyReply) => Promise<void>;
} }
interface FastifyRequest {
jwtVerify(): Promise<void>;
user?: any;
}
} }
const authPlugin: FastifyPluginAsync = async (fastify) => { const authPlugin: FastifyPluginAsync = async (fastify) => {
// For now, use mock authentication in all environments // Initialize JWKS client for Auth0 public key retrieval
// The frontend Auth0 flow should work independently const getJwks = buildGetJwks({
// TODO: Implement proper JWKS validation when needed for API security ttl: 60 * 60 * 1000, // 1 hour cache
});
fastify.decorate('authenticate', async (request: FastifyRequest, _reply: FastifyReply) => {
(request as any).user = { sub: 'dev-user-123' }; // Register @fastify/jwt with Auth0 JWKS validation
await fastify.register(require('@fastify/jwt'), {
if (env.NODE_ENV === 'development') { decode: { complete: true },
logger.debug('Using mock user for development', { userId: 'dev-user-123' }); secret: async (_request: FastifyRequest, token: any) => {
} else { try {
logger.info('Using mock authentication - Auth0 handled by frontend', { userId: 'dev-user-123' }); const { header: { kid, alg }, payload: { iss } } = token;
// Validate issuer matches Auth0 domain
const expectedIssuer = `https://${env.AUTH0_DOMAIN}/`;
if (iss !== expectedIssuer) {
throw new Error(`Invalid issuer: ${iss}`);
}
// Get public key from Auth0 JWKS endpoint
return getJwks.getPublicKey({ kid, domain: env.AUTH0_DOMAIN, alg });
} catch (error) {
logger.error('JWKS key retrieval failed', {
error: error instanceof Error ? error.message : 'Unknown error',
domain: env.AUTH0_DOMAIN
});
throw error;
}
},
verify: {
allowedIss: `https://${env.AUTH0_DOMAIN}/`,
allowedAud: env.AUTH0_AUDIENCE,
},
});
// Decorate with authenticate function that validates JWT
fastify.decorate('authenticate', async function(request: FastifyRequest, reply: FastifyReply) {
try {
await request.jwtVerify();
logger.info('JWT authentication successful', {
userId: request.user?.sub?.substring(0, 8) + '...',
audience: env.AUTH0_AUDIENCE
});
} catch (error) {
logger.warn('JWT authentication failed', {
path: request.url,
method: request.method,
error: error instanceof Error ? error.message : 'Unknown error',
});
reply.code(401).send({
error: 'Unauthorized',
message: 'Invalid or missing JWT token'
});
} }
}); });
}; };

View File

@@ -17,7 +17,6 @@ const errorPlugin: FastifyPluginAsync = async (fastify) => {
reply.status(500).send({ reply.status(500).send({
error: 'Internal server error', error: 'Internal server error',
message: process.env.NODE_ENV === 'development' ? error.message : undefined,
}); });
}); });
}; };

View File

@@ -10,13 +10,17 @@ import { cacheService } from '../../../../core/config/redis';
import { readFileSync } from 'fs'; import { readFileSync } from 'fs';
import { join } from 'path'; import { join } from 'path';
// Mock auth middleware to bypass JWT validation in tests // Mock auth plugin to bypass JWT validation in tests
jest.mock('../../../../core/security/auth.middleware', () => ({ jest.mock('../../../../core/plugins/auth.plugin', () => {
authMiddleware: (req: any, _res: any, next: any) => { const fastifyPlugin = require('fastify-plugin');
req.user = { sub: 'test-user-123' }; return {
next(); default: fastifyPlugin(async function(fastify) {
} fastify.decorate('authenticate', async function(request, _reply) {
})); request.user = { sub: 'test-user-123' };
});
}, { name: 'auth-plugin' })
};
});
// Mock external VIN decoder // Mock external VIN decoder
jest.mock('../../external/vpic/vpic.client', () => ({ jest.mock('../../external/vpic/vpic.client', () => ({

View File

@@ -57,7 +57,6 @@ services:
- node:20-alpine - node:20-alpine
container_name: mvp-backend container_name: mvp-backend
environment: environment:
NODE_ENV: development
PORT: 3001 PORT: 3001
DB_HOST: postgres DB_HOST: postgres
DB_PORT: 5432 DB_PORT: 5432
@@ -104,7 +103,6 @@ services:
VITE_API_BASE_URL: ${VITE_API_BASE_URL:-http://backend:3001/api} VITE_API_BASE_URL: ${VITE_API_BASE_URL:-http://backend:3001/api}
container_name: mvp-frontend container_name: mvp-frontend
environment: environment:
NODE_ENV: development
VITE_API_BASE_URL: http://backend:3001/api VITE_API_BASE_URL: http://backend:3001/api
VITE_AUTH0_DOMAIN: ${VITE_AUTH0_DOMAIN:-motovaultpro.us.auth0.com} VITE_AUTH0_DOMAIN: ${VITE_AUTH0_DOMAIN:-motovaultpro.us.auth0.com}
VITE_AUTH0_CLIENT_ID: ${VITE_AUTH0_CLIENT_ID:-yspR8zdnSxmV8wFIghHynQ08iXAPoQJ3} VITE_AUTH0_CLIENT_ID: ${VITE_AUTH0_CLIENT_ID:-yspR8zdnSxmV8wFIghHynQ08iXAPoQJ3}

View File

@@ -12,8 +12,8 @@ Complete database schema for MotoVaultPro Modified Feature Capsule architecture.
### Migration Tracking ### Migration Tracking
- **Table**: `_migrations` - **Table**: `_migrations`
- **Purpose**: Tracks executed migrations per feature - **Purpose**: Created by `backend/src/_system/migrations/run-all.ts` (not yet used for skipping executed files)
- **Location**: Created by `backend/src/_system/migrations/run-all.ts` - **Note**: Some SQL files use `IF NOT EXISTS`. Re-running all migrations may fail on indexes without `IF NOT EXISTS`.
## Core Tables ## Core Tables
@@ -183,17 +183,7 @@ npm run migrate:all
# Via Docker # Via Docker
make migrate make migrate
``` ```
Single-feature migration is not implemented yet.
### Run Single Feature
```bash
# In container
npm run migrate:feature vehicles
# Individual features
npm run migrate:feature fuel-logs
npm run migrate:feature maintenance
npm run migrate:feature stations
```
### Migration Files ### Migration Files
- **Location**: `backend/src/features/[feature]/migrations/` - **Location**: `backend/src/features/[feature]/migrations/`
@@ -226,4 +216,4 @@ npm run migrate:feature stations
- Regular pg_dump backups - Regular pg_dump backups
- Point-in-time recovery - Point-in-time recovery
- Read replicas for analytics - Read replicas for analytics
- Connection pooling (PgBouncer) - Connection pooling (PgBouncer)

View File

@@ -2,7 +2,11 @@
## Authentication & Authorization ## Authentication & Authorization
### Protected Endpoints ### Current State (MVP / Dev)
- Backend uses a Fastify authentication plugin that injects a mock user for development/test.
- JWT validation via Auth0 is not yet enabled on the backend; the frontend Auth0 flow works independently.
### Intended Production Behavior
All vehicle CRUD operations require JWT authentication via Auth0: All vehicle CRUD operations require JWT authentication via Auth0:
- `POST /api/vehicles` - Create vehicle - `POST /api/vehicles` - Create vehicle
- `GET /api/vehicles` - Get user vehicles - `GET /api/vehicles` - Get user vehicles
@@ -37,7 +41,7 @@ GET /api/vehicles/dropdown/trims
4. **Information Disclosure**: Exposes system capabilities to unauthenticated users 4. **Information Disclosure**: Exposes system capabilities to unauthenticated users
**Recommended Mitigations for Production:** **Recommended Mitigations for Production:**
1. **Rate Limiting**: Implement express-rate-limit (e.g., 100 requests/hour per IP) 1. **Rate Limiting**: Implement request rate limiting (e.g., 100 requests/hour per IP)
2. **Input Validation**: Sanitize make parameter in controller 2. **Input Validation**: Sanitize make parameter in controller
3. **CORS Restrictions**: Limit to application domain 3. **CORS Restrictions**: Limit to application domain
4. **Monitoring**: Add abuse detection logging 4. **Monitoring**: Add abuse detection logging
@@ -74,4 +78,4 @@ GET /api/vehicles/dropdown/trims
- Separate authenticated/unauthenticated HTTP clients - Separate authenticated/unauthenticated HTTP clients
- Request/response interceptors for error handling - Request/response interceptors for error handling
- Timeout configurations to prevent hanging requests - Timeout configurations to prevent hanging requests
- Auth token handling via Auth0 wrapper - Auth token handling via Auth0 wrapper

View File

@@ -27,7 +27,7 @@ backend/src/features/[name]/tests/
make test make test
``` ```
This executes: `docker-compose exec backend npm test` This executes: `docker compose exec backend npm test`
### Feature-Specific Testing ### Feature-Specific Testing
```bash ```bash
@@ -45,9 +45,9 @@ npm test -- features/vehicles --coverage
### Test Environment Setup ### Test Environment Setup
1. **Container-Based**: All tests run inside Docker containers 1. **Container-Based**: All tests run inside Docker containers
2. **Test Database**: Isolated test database per feature 2. **Database**: Uses the development database in the stack (`motovaultpro`)
3. **Mock External APIs**: No real API calls during testing 3. **Mock External APIs**: No real API calls during testing (where implemented)
4. **Cleanup**: Automatic test data cleanup after each test 4. **Cleanup**: Prefer transactions/cleanup per test; see feature tests for patterns
## Test Types ## Test Types
@@ -149,10 +149,14 @@ make clean && make dev
**Coverage**: Exclude node_modules, include src only **Coverage**: Exclude node_modules, include src only
### Database Testing ### Database Testing
- **Test DB**: Same as development (motovaultpro) - **DB**: Same as development (`motovaultpro`) within Docker
- **Transactions**: Each test runs in transaction, rolled back after - **Transactions**: Recommended pattern is one transaction per test
- **Isolation**: Tests cannot interfere with each other - **Isolation**: Keep tests independent; avoid shared state
- **Seeding**: Minimal seed data, test-specific fixtures - **Seeding**: Use feature-level fixtures when needed
### Coverage and Availability
- Full test suite exists for `vehicles`.
- Other features (e.g., `fuel-logs`, `stations`, `maintenance`) have placeholders and are being built out.
### Mock Strategy ### Mock Strategy
- **External APIs**: Completely mocked (vPIC, Google Maps) - **External APIs**: Completely mocked (vPIC, Google Maps)
@@ -214,7 +218,7 @@ make rebuild
#### Database Connection Issues #### Database Connection Issues
```bash ```bash
# Check postgres container # Check postgres container
docker-compose logs postgres docker compose logs postgres
# Reset database # Reset database
make clean && make dev make clean && make dev
@@ -288,4 +292,4 @@ describe('Error Handling', () => {
- Mock API failures to test error handling - Mock API failures to test error handling
- Test timeout scenarios - Test timeout scenarios
- Test network connectivity issues - Test network connectivity issues
- Verify graceful degradation paths - Verify graceful degradation paths

View File

@@ -4,8 +4,7 @@
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
"build": "tsc && vite build", "build": "tsc --project tsconfig.build.json && vite build",
"build:docker": "tsc --project tsconfig.build.json && vite build",
"preview": "vite preview", "preview": "vite preview",
"test": "vitest", "test": "vitest",
"test:ui": "vitest --ui", "test:ui": "vitest --ui",

View File

@@ -59,8 +59,8 @@ cat > "$BACKEND_DIR/README.md" << EOF
# Run feature tests # Run feature tests
npm test -- features/$FEATURE_NAME npm test -- features/$FEATURE_NAME
# Run feature migrations # Run migrations (all features)
npm run migrate:feature $FEATURE_NAME npm run migrate:all
\`\`\` \`\`\`
EOF EOF
@@ -92,4 +92,4 @@ echo "1. Implement business logic in domain/${FEATURE_CAMEL}.service.ts"
echo "2. Add database columns to migrations/" echo "2. Add database columns to migrations/"
echo "3. Implement API validation" echo "3. Implement API validation"
echo "4. Add tests" echo "4. Add tests"
echo "5. Register routes in backend/src/app.ts" echo "5. Register routes in backend/src/app.ts"