Skip to content

Commit cc74cc5

Browse files
committed
test ci
1 parent 092f560 commit cc74cc5

52 files changed

Lines changed: 2025 additions & 2205 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/ci.yml

Lines changed: 12 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -138,11 +138,11 @@ jobs:
138138
JWT_EXPIRE_MINUTES: 60
139139
ENVIRONMENT: test
140140
run: |
141-
pytest tests/ -v \
141+
pytest tests/unit -m unit -v \
142142
--cov=app \
143143
--cov-report=xml \
144144
--cov-report=html \
145-
--junitxml=test-results.xml \
145+
--junitxml=test-results-unit.xml \
146146
--maxfail=5 \
147147
--tb=short
148148
@@ -159,24 +159,13 @@ jobs:
159159
JWT_EXPIRE_MINUTES: 60
160160
ENVIRONMENT: test
161161
run: |
162-
pytest tests/integration -v --tb=short
163-
continue-on-error: true
164-
165-
- name: Run E2E tests
166-
working-directory: backend
167-
env:
168-
DATABASE_USER: postgres
169-
DATABASE_PASSWORD: postgres
170-
DATABASE_HOST: localhost
171-
DATABASE_PORT: 5432
172-
DATABASE_NAME: testdb
173-
JWT_SECRET_KEY: test-secret-key-for-ci
174-
JWT_ALGORITHM: HS256
175-
JWT_EXPIRE_MINUTES: 60
176-
ENVIRONMENT: test
177-
run: |
178-
pytest tests/e2e -v --tb=short
179-
continue-on-error: true
162+
pytest tests/integration -m integration -v \
163+
--cov=app \
164+
--cov-report=xml \
165+
--cov-report=html \
166+
--junitxml=test-results-integration.xml \
167+
--maxfail=5 \
168+
--tb=short
180169
181170
- name: Upload coverage to Codecov
182171
uses: codecov/codecov-action@v3
@@ -190,7 +179,9 @@ jobs:
190179
if: always()
191180
uses: EnricoMi/publish-unit-test-result-action@v2
192181
with:
193-
files: backend/test-results.xml
182+
files: |
183+
backend/test-results-unit.xml
184+
backend/test-results-integration.xml
194185
check_name: Backend Test Results
195186

196187
frontend:

.gitignore

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,27 @@
66

77
# ============================================================================
88
# ENVIRONMENT & SECRETS (CRITICAL - Never commit)
9-
# ============================================================================
9+
# ==============================================================================
10+
# Never commit actual .env files - they contain secrets and sensitive data
11+
# .example files ARE tracked (serve as templates)
12+
# config/ folder contains centralized environment configuration
13+
14+
# Root level (legacy - kept for backward compatibility)
1015
.env
1116
.env.local
1217
.env.docker
1318
.env.staging
1419
.env.prod
1520
.env.*.local
21+
22+
# config/ folder - centralized environment configuration
23+
# NOTE: .env.*.example files ARE tracked (they are templates)
24+
config/.env
25+
config/.env.*
26+
!config/.env.*.example
27+
!config/.env.*.md
28+
29+
# Environment files at any level
1630
*.pem
1731
*.key
1832
*.cert

Makefile

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,12 @@ help:
3535
@echo " $(YELLOW)make frontend$(NC) - Run frontend only"
3636
@echo ""
3737
@echo "$(GREEN)🧪 TESTING & QUALITY$(NC)"
38-
@echo " $(YELLOW)make test$(NC) - Run all tests"
39-
@echo " $(YELLOW)make backend-test$(NC) - Run backend tests with coverage"
40-
@echo " $(YELLOW)make backend-test-integration$(NC) - Integration tests only"
41-
@echo " $(YELLOW)make backend-test-e2e$(NC) - E2E tests only"
38+
@echo " $(YELLOW)make test$(NC) - Run all tests (unit + integration + frontend)"
39+
@echo " $(YELLOW)make backend-test$(NC) - Run all backend tests (unit + integration)"
40+
@echo " $(YELLOW)make backend-test-unit$(NC) - Unit tests only (no DB, fast)"
41+
@echo " $(YELLOW)make backend-test-integration$(NC) - Integration tests (real DB)"
42+
@echo " $(YELLOW)make backend-test-coverage$(NC) - Tests with coverage report"
43+
@echo " $(YELLOW)make backend-validate-tests$(NC) - Validate test configuration"
4244
@echo " $(YELLOW)make frontend-test$(NC) - Frontend tests"
4345
@echo " $(YELLOW)make lint$(NC) - Run linters (ruff, eslint)"
4446
@echo " $(YELLOW)make format$(NC) - Format code (black, prettier)"
@@ -81,7 +83,6 @@ docker-up:
8183
@echo " Frontend: $(BLUE)http://localhost:4200$(NC)"
8284
@echo " Backend: $(BLUE)http://localhost:8000$(NC)"
8385
@echo " Database: $(BLUE)postgresql://localhost:5432$(NC)"
84-
@echo " Adminer: $(BLUE)http://localhost:8080$(NC)"
8586
@echo ""
8687
@echo "$(YELLOW)Tip: Use 'make docker-logs' to view logs$(NC)"
8788

@@ -148,30 +149,39 @@ frontend:
148149
# TESTING & CODE QUALITY
149150
# ============================================================================
150151

151-
test: backend-test frontend-test
152+
test: backend-test-unit backend-test-integration frontend-test
152153
@echo "$(GREEN)✅ All tests completed!$(NC)"
153154

154-
backend-test:
155-
@echo "$(GREEN)🧪 Running backend tests with coverage...$(NC)"
156-
cd backend && python -m pytest tests/ -v --tb=short --cov=app --cov-report=html
157-
@echo "$(GREEN)✅ Backend tests passed!$(NC)"
155+
backend-test: backend-test-unit backend-test-integration
156+
@echo "$(GREEN)✅ All backend tests passed!$(NC)"
157+
158+
backend-test-unit:
159+
@echo "$(GREEN)🧪 Running backend unit tests (no DB)...$(NC)"
160+
cd backend && python -m pytest tests/unit -m unit -v --tb=short --cov=app --cov-report=html
161+
@echo "$(GREEN)✅ Unit tests passed!$(NC)"
158162
@echo "$(BLUE)Coverage report: backend/htmlcov/index.html$(NC)"
159163

160164
backend-test-integration:
161-
@echo "$(GREEN)🧪 Running backend integration tests...$(NC)"
162-
cd backend && python -m pytest tests/integration -v --tb=short
165+
@echo "$(GREEN)🧪 Running backend integration tests (with real DB)...$(NC)"
166+
cd backend && python -m pytest tests/integration -m integration -v --tb=short
163167
@echo "$(GREEN)✅ Integration tests passed!$(NC)"
164168

165-
backend-test-e2e:
166-
@echo "$(GREEN)🧪 Running backend E2E tests...$(NC)"
167-
cd backend && python -m pytest tests/e2e -v --tb=short
168-
@echo "$(GREEN)✅ E2E tests passed!$(NC)"
169+
backend-test-coverage:
170+
@echo "$(GREEN)🧪 Running all backend tests with coverage report...$(NC)"
171+
cd backend && python -m pytest tests/unit tests/integration -v --tb=short --cov=app --cov-report=html --cov-report=term-missing
172+
@echo "$(GREEN)✅ Tests completed!$(NC)"
173+
@echo "$(BLUE)Coverage report: backend/htmlcov/index.html$(NC)"
169174

170175
frontend-test:
171176
@echo "$(GREEN)🧪 Running frontend tests...$(NC)"
172177
cd frontend && npm run test -- --watch=false --coverage
173178
@echo "$(GREEN)✅ Frontend tests passed!$(NC)"
174179

180+
backend-validate-tests:
181+
@echo "$(GREEN)✓ Validating test configuration...$(NC)"
182+
cd backend && python validate_tests.py
183+
@echo "$(GREEN)✅ Test configuration valid!$(NC)"
184+
175185
lint:
176186
@echo "$(GREEN)🔍 Running linters...$(NC)"
177187
cd backend && ruff check app/ --output-format=github
@@ -268,6 +278,4 @@ clean:
268278
@find . -type d -name .angular -exec rm -rf {} + 2>/dev/null || true
269279
@echo "$(GREEN)✅ Cleanup complete!$(NC)"
270280

271-
.DEFAULT_GOAL := help
272-
@rm -f .env .env.local
273-
@echo "✅ Cleanup complete!"
281+
.DEFAULT_GOAL := help

README.md

Lines changed: 95 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,30 @@ make deploy ENV=staging IMAGE_TAG=v1.0.0
7777
7878
### Principle: **Secrets in Code = Never**
7979
80+
All environment configuration is centralized in `config/` folder:
81+
82+
**Tracked in Git (✅):**
83+
- `config/.env.*.example` - Templates for each environment
84+
- `.gitignore` - Security rules
85+
86+
**Never in Git (❌):**
87+
- `config/.env.dev` - Development settings
88+
- `config/.env.test` - Testing settings
89+
- `config/.env.local` - Local overrides
90+
- `config/.env.prod` - Production settings
91+
92+
**For CI/CD:**
93+
- GitHub Secrets stored in repository settings
94+
- Used via `${{ secrets.SECRET_NAME }}` in workflows
95+
- OIDC token for AWS authentication (no hardcoded credentials)
96+
97+
**For Runtime:**
98+
- AWS Secrets Manager for sensitive data
99+
- ECS task definitions inject secrets as environment variables
100+
- Application reads from environment at runtime
101+
102+
See [docs/ENVIRONMENT_CONFIGURATION.md](docs/ENVIRONMENT_CONFIGURATION.md) for complete setup guide.
103+
80104
**For CI/CD:**
81105
- GitHub Secrets stored in repository settings (AWS_ROLE_TO_ASSUME, DB passwords, etc.)
82106
- Used via `${{ secrets.SECRET_NAME }}` in workflows
@@ -89,23 +113,85 @@ make deploy ENV=staging IMAGE_TAG=v1.0.0
89113
- Terraform manages secrets creation in AWS
90114
91115
**For Local Development:**
92-
- Copy `config/.env.example` to `.env.local`
93-
- Fill in local values (never commit `.env.local`)
94-
- Used by scripts via `source .env.local`
116+
- Copy `config/.env.dev.example` to `config/.env.dev`
117+
- Copy `config/.env.test.example` to `config/.env.test`
118+
- Optional: `config/.env.local` for local overrides
119+
- All .env files in `config/` are git-ignored (never committed)
95120
96-
See [docs/SECRETS_MANAGEMENT.md](docs/SECRETS_MANAGEMENT.md) for detailed patterns.
121+
See [docs/SECRETS_MANAGEMENT.md](docs/SECRETS_MANAGEMENT.md) for detailed patterns and [docs/ENVIRONMENT_CONFIGURATION.md](docs/ENVIRONMENT_CONFIGURATION.md) for environment file structure.
97122
98123
## 🧪 Testing Strategy
99124
100-
### Backend
125+
### Backend Testing
126+
127+
#### Layer-Based Test Architecture
128+
Tests are organized into two layers:
129+
- **Unit Tests** (mocked, no database): Fast validation of business logic (~5 seconds)
130+
- **Integration Tests** (real database, auto-rollback): End-to-end flow validation (~15-30 seconds)
131+
132+
#### Quick Local Testing
133+
```bash
134+
# Run unit tests only (mocked, no DB access)
135+
make backend-test-unit
136+
137+
# Run integration tests only (full DB operations)
138+
make backend-test-integration
139+
140+
# Run all tests with coverage report
141+
make backend-test-coverage
142+
143+
# Run all tests
144+
make backend-test
145+
```
146+
147+
#### Using Helper Scripts
101148
```bash
102-
# Unit tests
103-
cd backend && pytest
149+
# Run unit tests
150+
bash scripts/run-tests.sh unit
151+
152+
# Run integration tests
153+
bash scripts/run-tests.sh integration
104154

105-
# With coverage
106-
pytest --cov=app --cov-report=html
155+
# Run all tests (unit + integration)
156+
bash scripts/run-tests.sh all
157+
158+
# Generate coverage HTML report
159+
bash scripts/run-tests.sh coverage
107160
```
108161

162+
#### Database Setup (Local Development)
163+
```bash
164+
# Setup test database for local development
165+
bash scripts/setup-test-db.sh local
166+
167+
# Or in Docker environment
168+
bash scripts/setup-test-db.sh docker
169+
```
170+
171+
#### Coverage Reports
172+
```bash
173+
# HTML coverage report
174+
make backend-test-coverage
175+
176+
# View report
177+
open htmlcov/index.html
178+
```
179+
180+
#### Test Configuration Files
181+
- `.env.test`: Test environment variables (DB URL, secrets)
182+
- `pytest.ini`: Pytest configuration with `asyncio_mode=auto` and markers
183+
- `tests/validate_tests.py`: Configuration validator script
184+
185+
#### Key Features
186+
- ✅ Automatic database transaction rollback between tests (no data pollution)
187+
- ✅ AsyncClient for testing async endpoints
188+
- ✅ Fixture-based dependency injection (database, HTTP client, auth)
189+
- ✅ Pre-created test users and JWT tokens
190+
- ✅ No database mocking in integration tests (real DB with rollback)
191+
- ✅ No router testing in unit tests (only business logic)
192+
193+
See [Backend Testing Guide](docs/INFRASTRUCTURE_ADAPTATION_SUMMARY.md) for detailed documentation.
194+
109195
### Frontend
110196
```bash
111197
# Unit and integration tests

backend/Dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ FROM python:3.12-slim as builder
33

44
WORKDIR /build
55

6-
# Install build dependencies
6+
# Install minimal build dependency required for some wheels
77
RUN apt-get update && apt-get install -y --no-install-recommends \
88
gcc \
99
&& rm -rf /var/lib/apt/lists/*
@@ -15,7 +15,7 @@ COPY pyproject.toml poetry.lock ./
1515
RUN pip install --upgrade pip && \
1616
pip install poetry && \
1717
poetry config virtualenvs.create false && \
18-
poetry install --no-dev --no-directory
18+
poetry install --no-interaction --no-ansi --with dev --no-root
1919

2020
# Production stage
2121
FROM python:3.12-slim

backend/app/core/config.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
class Environment(str, Enum):
2626
"""Application environment."""
2727
DEVELOPMENT = "development"
28+
TEST = "test"
2829
STAGING = "staging"
2930
PRODUCTION = "production"
3031

backend/app/db/session.py

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,29 @@
11
from sqlalchemy import create_engine
2-
from sqlalchemy.orm import sessionmaker
3-
from sqlalchemy.ext.declarative import declarative_base
2+
from sqlalchemy.orm import sessionmaker, declarative_base
43
from app.core.config import settings
54

6-
SQLALCHEMY_DATABASE_URL = 'postgresql://postgres:postgres@localhost:5432/mydatabase'
7-
5+
# SQLAlchemy 2.0 / "future" style engine
86
engine = create_engine(
97
settings.DATABASE_URL,
108
echo=settings.DEBUG,
11-
connect_args={"check_same_thread": False} if "sqlite" in settings.DATABASE_URL else {},
12-
pool_pre_ping=True
9+
future=True,
10+
pool_pre_ping=True,
1311
)
1412

15-
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
13+
# Session factory (SQLAlchemy 2.0 style)
14+
SessionLocal = sessionmaker(
15+
bind=engine,
16+
autoflush=False,
17+
expire_on_commit=False,
18+
future=True,
19+
)
1620

21+
# Declarative base
1722
Base = declarative_base()
1823

19-
# Dependency for FastAPI
24+
2025
def get_db():
26+
"""FastAPI dependency that yields a SQLAlchemy Session (2.0 style)."""
2127
db = SessionLocal()
2228
try:
2329
yield db

backend/main.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,18 @@
2525
# ============================================================================
2626
# Load environment variables in order of precedence:
2727
# 1. System environment variables (already set)
28-
# 2. .env (environment-specific)
29-
# 3. .env.local (local overrides)
30-
env_files = [".env", ".env.local"]
31-
for env_file in env_files:
32-
env_path = Path(env_file)
28+
# 2. config/.env.dev (development) or config/.env.test (testing)
29+
# 3. config/.env.local (local overrides)
30+
env_files = [
31+
Path(__file__).parent.parent / "config" / ".env.dev",
32+
Path(__file__).parent.parent / "config" / ".env.local",
33+
]
34+
for env_path in env_files:
3335
if env_path.exists():
3436
load_dotenv(env_path, override=False)
35-
print(f"Loaded environment from {env_file}")
37+
print(f"Loaded environment from {env_path}")
38+
else:
39+
print(f"⚠ {env_path} not found, skipping")
3640

3741
# ============================================================================
3842
# LOGGING SETUP

0 commit comments

Comments
 (0)