Skip to content

Commit 1ece447

Browse files
wip
1 parent 6fad175 commit 1ece447

5 files changed

Lines changed: 489 additions & 25 deletions

File tree

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
name: Backend Tests (Parallel)
2+
3+
on:
4+
push:
5+
branches: [ main, develop, release-* ]
6+
paths:
7+
- 'backend/**'
8+
- '.github/workflows/backend-tests.yml'
9+
pull_request:
10+
branches: [ main, develop, release-* ]
11+
paths:
12+
- 'backend/**'
13+
- '.github/workflows/backend-tests.yml'
14+
15+
jobs:
16+
backend-tests:
17+
runs-on: ubuntu-latest
18+
strategy:
19+
fail-fast: false
20+
matrix:
21+
test-group:
22+
- "compliance_report"
23+
- "fuel_supply,final_supply_equipment,allocation_agreement"
24+
- "fuel_export,fuel_code,other_uses,notional_transfer"
25+
- "services,user,transfer,transaction"
26+
- "organization,organization_snapshot,notification,initiative_agreement"
27+
- "credit_ledger,calculator,audit_log,organizations,internal_comment,document,admin_adjustment"
28+
include:
29+
- test-group: "compliance_report"
30+
job-index: 1
31+
- test-group: "fuel_supply,final_supply_equipment,allocation_agreement"
32+
job-index: 2
33+
- test-group: "fuel_export,fuel_code,other_uses,notional_transfer"
34+
job-index: 3
35+
- test-group: "services,user,transfer,transaction"
36+
job-index: 4
37+
- test-group: "organization,organization_snapshot,notification,initiative_agreement"
38+
job-index: 5
39+
- test-group: "credit_ledger,calculator,audit_log,organizations,internal_comment,document,admin_adjustment"
40+
job-index: 6
41+
42+
services:
43+
postgres:
44+
image: postgres:15
45+
env:
46+
POSTGRES_USER: postgres
47+
POSTGRES_PASSWORD: postgres
48+
POSTGRES_DB: postgres
49+
options: >-
50+
--health-cmd pg_isready
51+
--health-interval 10s
52+
--health-timeout 5s
53+
--health-retries 5
54+
ports:
55+
- 5432:5432
56+
57+
redis:
58+
image: redis:7-alpine
59+
options: >-
60+
--health-cmd "redis-cli ping"
61+
--health-interval 10s
62+
--health-timeout 3s
63+
--health-retries 5
64+
ports:
65+
- 6379:6379
66+
67+
rabbitmq:
68+
image: rabbitmq:3-management-alpine
69+
env:
70+
RABBITMQ_DEFAULT_USER: rabbitmq
71+
RABBITMQ_DEFAULT_PASS: rabbitmq
72+
options: >-
73+
--health-cmd "rabbitmq-diagnostics -q ping"
74+
--health-interval 30s
75+
--health-timeout 10s
76+
--health-retries 5
77+
ports:
78+
- 5672:5672
79+
- 15672:15672
80+
81+
steps:
82+
- uses: actions/checkout@v4
83+
84+
- name: Set up Python 3.11
85+
uses: actions/setup-python@v4
86+
with:
87+
python-version: '3.11'
88+
89+
- name: Install Poetry
90+
uses: snok/install-poetry@v1
91+
with:
92+
version: latest
93+
virtualenvs-create: true
94+
virtualenvs-in-project: true
95+
installer: pip
96+
97+
- name: Load cached venv
98+
id: cached-poetry-dependencies
99+
uses: actions/cache@v3
100+
with:
101+
path: backend/.venv
102+
key: venv-${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('**/poetry.lock') }}
103+
104+
- name: Install dependencies
105+
if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true'
106+
working-directory: ./backend
107+
run: poetry install --no-interaction --no-root
108+
109+
- name: Install project
110+
working-directory: ./backend
111+
run: poetry install --no-interaction
112+
113+
- name: Wait for services to be ready
114+
run: |
115+
# Wait for PostgreSQL
116+
timeout 60 bash -c 'until pg_isready -h localhost -p 5432 -U postgres; do sleep 1; done'
117+
118+
# Wait for Redis
119+
timeout 60 bash -c 'until redis-cli -h localhost -p 6379 ping | grep -q PONG; do sleep 1; done'
120+
121+
# Wait for RabbitMQ
122+
timeout 60 bash -c 'until curl -f http://localhost:15672; do sleep 1; done'
123+
124+
- name: Run tests for ${{ matrix.test-group }}
125+
working-directory: ./backend
126+
env:
127+
# Database configuration with job-specific database name
128+
LCFS_DB_BASE: lcfs_test_job_${{ matrix.job-index }}
129+
LCFS_DB_HOST: localhost
130+
LCFS_DB_PORT: 5432
131+
LCFS_DB_USER: postgres
132+
LCFS_DB_PASS: postgres
133+
134+
# Redis configuration
135+
LCFS_REDIS_HOST: localhost
136+
LCFS_REDIS_PORT: 6379
137+
138+
# RabbitMQ configuration
139+
LCFS_RABBITMQ_HOST: localhost
140+
LCFS_RABBITMQ_PORT: 5672
141+
LCFS_RABBITMQ_USER: rabbitmq
142+
LCFS_RABBITMQ_PASS: rabbitmq
143+
144+
# Test environment
145+
APP_ENVIRONMENT: pytest
146+
147+
# Other test settings
148+
LCFS_RELOAD: false
149+
LCFS_LOG_LEVEL: INFO
150+
run: |
151+
# Parse test groups and run tests
152+
IFS=',' read -ra GROUPS <<< "${{ matrix.test-group }}"
153+
154+
for group in "${GROUPS[@]}"; do
155+
group=$(echo "$group" | xargs) # trim whitespace
156+
echo "Running tests for group: $group"
157+
158+
# Check if test directory exists
159+
if [ -d "lcfs/tests/${group}" ]; then
160+
poetry run pytest lcfs/tests/${group}/ \
161+
-v \
162+
--tb=short \
163+
--junitxml=pytest-results-${group}.xml \
164+
--cov=lcfs \
165+
--cov-report=xml:coverage-${group}.xml \
166+
--cov-report=term-missing
167+
else
168+
echo "Warning: Test directory lcfs/tests/${group}/ not found"
169+
fi
170+
done
171+
172+
- name: Upload test results
173+
uses: actions/upload-artifact@v3
174+
if: always()
175+
with:
176+
name: test-results-job-${{ matrix.job-index }}
177+
path: |
178+
backend/pytest-results-*.xml
179+
backend/coverage-*.xml
180+
181+
- name: Upload coverage to Codecov
182+
uses: codecov/codecov-action@v3
183+
if: always()
184+
with:
185+
file: ./backend/coverage-*.xml
186+
flags: backend
187+
name: backend-coverage-job-${{ matrix.job-index }}
188+
189+
# Aggregate results from all parallel jobs
190+
test-summary:
191+
needs: backend-tests
192+
runs-on: ubuntu-latest
193+
if: always()
194+
steps:
195+
- name: Check test results
196+
run: |
197+
if [[ "${{ needs.backend-tests.result }}" == "failure" ]]; then
198+
echo "Some backend tests failed"
199+
exit 1
200+
elif [[ "${{ needs.backend-tests.result }}" == "cancelled" ]]; then
201+
echo "Backend tests were cancelled"
202+
exit 1
203+
else
204+
echo "All backend tests passed successfully"
205+
fi

PARALLEL_TESTING.md

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
# Parallel Backend Testing Setup
2+
3+
This document explains the parallel test execution setup for the LCFS backend tests.
4+
5+
## Overview
6+
7+
The backend tests have been configured to run in parallel using GitHub Actions matrix strategy. This reduces test execution time from ~15-20 minutes to ~3-5 minutes by running tests across 6 parallel jobs.
8+
9+
## How It Works
10+
11+
### Job Distribution
12+
13+
Tests are distributed across 6 parallel jobs by domain/business functionality:
14+
15+
1. **Job 1**: `compliance_report` (10 test files - largest module)
16+
2. **Job 2**: `fuel_supply,final_supply_equipment,allocation_agreement` (15 files)
17+
3. **Job 3**: `fuel_export,fuel_code,other_uses,notional_transfer` (16 files)
18+
4. **Job 4**: `services,user,transfer,transaction` (12 files)
19+
5. **Job 5**: `organization,organization_snapshot,notification,initiative_agreement` (9 files)
20+
6. **Job 6**: `credit_ledger,calculator,audit_log,organizations,internal_comment,document,admin_adjustment` (remaining files)
21+
22+
### Database Isolation
23+
24+
Each parallel job uses its own isolated database:
25+
- Job 1: `lcfs_test_job_1`
26+
- Job 2: `lcfs_test_job_2`
27+
- Job 3: `lcfs_test_job_3`
28+
- Job 4: `lcfs_test_job_4`
29+
- Job 5: `lcfs_test_job_5`
30+
- Job 6: `lcfs_test_job_6`
31+
32+
This prevents conflicts between tests running simultaneously.
33+
34+
## Configuration Files
35+
36+
### GitHub Actions Workflow
37+
- **File**: `.github/workflows/backend-tests.yml`
38+
- **Purpose**: Defines the parallel job matrix and execution strategy
39+
- **Services**: PostgreSQL, Redis, RabbitMQ (each job gets isolated access)
40+
41+
### Backend Configuration
42+
- **File**: `backend/lcfs/conftest.py`
43+
- **Changes**: Modified `_engine` fixture to support dynamic database names
44+
- **Environment**: Uses `LCFS_DB_BASE` environment variable for database naming
45+
46+
### Pytest Configuration
47+
- **File**: `backend/pyproject.toml`
48+
- **Changes**: Updated pytest options for better parallel execution
49+
- **Discovery**: Enhanced test discovery patterns
50+
51+
## Local Development
52+
53+
### Running Tests Locally (Sequential)
54+
```bash
55+
cd backend
56+
poetry run pytest lcfs/tests/
57+
```
58+
59+
### Testing Parallel Setup Locally
60+
```bash
61+
# Test a specific group with custom database name
62+
export LCFS_DB_BASE=lcfs_test_local_1
63+
cd backend
64+
poetry run pytest lcfs/tests/compliance_report/ -v
65+
```
66+
67+
### Testing Multiple Groups Locally
68+
```bash
69+
# Simulate parallel execution (requires multiple terminal windows)
70+
# Terminal 1:
71+
export LCFS_DB_BASE=lcfs_test_local_1
72+
poetry run pytest lcfs/tests/compliance_report/
73+
74+
# Terminal 2:
75+
export LCFS_DB_BASE=lcfs_test_local_2
76+
poetry run pytest lcfs/tests/fuel_supply/
77+
```
78+
79+
## GitHub Actions Usage
80+
81+
### Triggering Parallel Tests
82+
The workflow automatically triggers on:
83+
- Push to `main`, `develop`, or `release-*` branches (when backend files change)
84+
- Pull requests to `main`, `develop`, or `release-*` branches (when backend files change)
85+
86+
### Monitoring Execution
87+
1. Go to GitHub Actions tab in the repository
88+
2. Look for "Backend Tests (Parallel)" workflow
89+
3. Each job shows individual progress and logs
90+
4. Test results are aggregated in the final summary
91+
92+
### Adding New Test Modules
93+
When adding new test modules:
94+
95+
1. Create test directory: `backend/lcfs/tests/new_module/`
96+
2. Add test files following naming convention: `test_*.py`
97+
3. Update workflow grouping in `.github/workflows/backend-tests.yml` if needed
98+
4. Tests will be automatically discovered and executed
99+
100+
## Performance Benefits
101+
102+
- **Execution Time**: Reduced from 15-20 minutes to 3-5 minutes
103+
- **Parallel Jobs**: 6 simultaneous executions
104+
- **Speedup Factor**: ~4-5x improvement
105+
- **Resource Usage**: Better CI resource utilization
106+
107+
## Troubleshooting
108+
109+
### Database Connection Issues
110+
- Check PostgreSQL service is running in CI
111+
- Verify database permissions for test user
112+
- Ensure each job has unique database name
113+
114+
### Test Failures in Parallel Mode
115+
- Check for shared state dependencies between tests
116+
- Verify test isolation (no cross-test data dependencies)
117+
- Review database rollback behavior in fixtures
118+
119+
### Job Imbalance
120+
- Monitor job execution times in GitHub Actions
121+
- Redistribute test groups if some jobs take significantly longer
122+
- Consider test complexity when grouping modules
123+
124+
## Migration from Sequential to Parallel
125+
126+
### Backward Compatibility
127+
- Local development remains unchanged (runs sequentially by default)
128+
- Existing test commands continue to work
129+
- No changes required to individual test files
130+
131+
### Environment Variables
132+
- `LCFS_DB_BASE`: Database name (defaults to `lcfs_test`)
133+
- `APP_ENVIRONMENT`: Always set to `pytest` for tests
134+
- Other settings remain unchanged
135+
136+
## Future Enhancements
137+
138+
### Phase 2 Optimizations
139+
- Add pytest-xdist for within-job parallelization
140+
- Implement UUID-based IDs to eliminate sequence conflicts
141+
- Add job-level retry logic for transient failures
142+
- Optimize fixture setup for faster database operations
143+
144+
### Monitoring
145+
- Add execution time tracking per job
146+
- Implement test result analytics
147+
- Monitor for test flakiness in parallel mode

0 commit comments

Comments
 (0)