fix: Comprehensive validation and bug fixes

Infrastructure:
- Add Redis and Celery workers to all docker-compose files
- Fix celery migration race condition in entrypoint.sh
- Add healthchecks and resource limits to dev compose
- Update .env.template with Redis/Celery variables

Backend Models & Schemas:
- Rename Sprint.completed_points to velocity (per requirements)
- Add AgentInstance.name as required field
- Rename Issue external tracker fields for consistency
- Add IssueSource and TrackerType enums
- Add Project.default_tracker_type field

Backend Fixes:
- Add Celery retry configuration with exponential backoff
- Remove unused sequence counter from EventBus
- Add mypy overrides for test dependencies
- Fix test file using wrong schema (UserUpdate -> dict)

Frontend Fixes:
- Fix memory leak in useProjectEvents (proper cleanup)
- Fix race condition with stale closure in reconnection
- Sync TokenWithUser type with regenerated API client
- Fix expires_in null handling in useAuth
- Clean up unused imports in prototype pages
- Add ESLint relaxed rules for prototype files

CI/CD:
- Add E2E testing stage with Testcontainers
- Add security scanning with Trivy and pip-audit
- Add dependency caching for faster builds

Tests:
- Update all tests to use renamed fields (velocity, name, etc.)
- Fix 14 schema test failures
- All 1500 tests pass with 91% coverage

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-30 10:35:30 +01:00
parent 6ea9edf3d1
commit 742ce4c9c8
57 changed files with 1062 additions and 332 deletions

View File

@@ -50,7 +50,7 @@ class TestSprintModel:
assert retrieved.status == SprintStatus.PLANNED # Default
assert retrieved.goal is None
assert retrieved.planned_points is None
assert retrieved.completed_points is None
assert retrieved.velocity is None
def test_create_sprint_with_all_fields(self, db_session):
"""Test creating a sprint with all optional fields."""
@@ -75,7 +75,7 @@ class TestSprintModel:
end_date=today + timedelta(days=14),
status=SprintStatus.ACTIVE,
planned_points=34,
completed_points=21,
velocity=21,
)
db_session.add(sprint)
db_session.commit()
@@ -87,7 +87,7 @@ class TestSprintModel:
assert retrieved.goal == "Complete all authentication features"
assert retrieved.status == SprintStatus.ACTIVE
assert retrieved.planned_points == 34
assert retrieved.completed_points == 21
assert retrieved.velocity == 21
def test_sprint_timestamps(self, db_session):
"""Test that timestamps are automatically set."""
@@ -214,12 +214,12 @@ class TestSprintLifecycle:
# Complete the sprint
sprint.status = SprintStatus.COMPLETED
sprint.completed_points = 18
sprint.velocity = 18
db_session.commit()
retrieved = db_session.query(Sprint).filter_by(name="Sprint to Complete").first()
assert retrieved.status == SprintStatus.COMPLETED
assert retrieved.completed_points == 18
assert retrieved.velocity == 18
def test_cancel_sprint(self, db_session):
"""Test cancelling a sprint."""
@@ -338,14 +338,14 @@ class TestSprintPoints:
start_date=today,
end_date=today + timedelta(days=14),
planned_points=0,
completed_points=0,
velocity=0,
)
db_session.add(sprint)
db_session.commit()
retrieved = db_session.query(Sprint).filter_by(name="Zero Points Sprint").first()
assert retrieved.planned_points == 0
assert retrieved.completed_points == 0
assert retrieved.velocity == 0
def test_sprint_velocity_calculation(self, db_session):
"""Test that we can calculate velocity from points."""
@@ -363,16 +363,16 @@ class TestSprintPoints:
end_date=today + timedelta(days=14),
status=SprintStatus.COMPLETED,
planned_points=21,
completed_points=18,
velocity=18,
)
db_session.add(sprint)
db_session.commit()
retrieved = db_session.query(Sprint).filter_by(name="Velocity Sprint").first()
# Calculate velocity
velocity = retrieved.completed_points / retrieved.planned_points
assert velocity == pytest.approx(18 / 21, rel=0.01)
# Calculate completion ratio from velocity
completion_ratio = retrieved.velocity / retrieved.planned_points
assert completion_ratio == pytest.approx(18 / 21, rel=0.01)
def test_sprint_overdelivery(self, db_session):
"""Test sprint where completed > planned (stretch goals)."""
@@ -390,13 +390,13 @@ class TestSprintPoints:
end_date=today + timedelta(days=14),
status=SprintStatus.COMPLETED,
planned_points=20,
completed_points=25, # Completed more than planned
velocity=25, # Completed more than planned
)
db_session.add(sprint)
db_session.commit()
retrieved = db_session.query(Sprint).filter_by(name="Overdelivery Sprint").first()
assert retrieved.completed_points > retrieved.planned_points
assert retrieved.velocity > retrieved.planned_points
class TestSprintNumber: