Files
TimeTracker/tests/test_analytics.py
Dries Peeters 0752332ed6 feat: Implement comprehensive CI/CD pipeline with GitHub Actions
Implement a complete, production-ready CI/CD pipeline that runs 100% on
GitHub Actions with zero external dependencies. This replaces and consolidates
existing workflows with an optimized, streamlined pipeline.

## Major Changes
- Add 3 new workflows (ci-comprehensive, cd-development, cd-release)
- Remove 2 redundant workflows (backed up)
- Add 130+ tests across 4 new test files
- Add 8 documentation guides (60+ KB)
- Add developer tools and scripts
2025-10-09 13:02:39 +02:00

233 lines
7.6 KiB
Python

import pytest
from app import create_app, db
from app.models import User, Project, TimeEntry
from datetime import datetime, timedelta
from flask_login import login_user
@pytest.fixture
def app():
app = create_app()
app.config['TESTING'] = True
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///:memory:'
with app.app_context():
db.create_all()
yield app
db.drop_all()
@pytest.fixture
def client(app):
return app.test_client()
@pytest.fixture
def sample_data(app):
with app.app_context():
# Create test user
user = User(username='testuser', role='user')
user.is_active = True
db.session.add(user)
# Create test project
project = Project(name='Test Project', client='Test Client')
db.session.add(project)
db.session.commit()
# Create test time entries
base_time = datetime.now() - timedelta(days=5)
for i in range(5):
entry = TimeEntry(
user_id=user.id,
project_id=project.id,
start_time=base_time + timedelta(days=i),
end_time=base_time + timedelta(days=i, hours=8),
duration_seconds=28800, # 8 hours
billable=True
)
db.session.add(entry)
db.session.commit()
return {'user': user, 'project': project}
@pytest.mark.integration
@pytest.mark.routes
def test_analytics_dashboard_requires_login(client):
"""Test that analytics dashboard requires authentication"""
response = client.get('/analytics')
assert response.status_code == 302 # Redirect to login
@pytest.mark.integration
@pytest.mark.routes
def test_analytics_dashboard_accessible_when_logged_in(client, app, sample_data):
"""Test that analytics dashboard is accessible when logged in"""
with app.app_context():
with client.session_transaction() as sess:
# Simulate login
user = sample_data['user']
login_user(user)
sess['_user_id'] = user.id
response = client.get('/analytics')
assert response.status_code == 200
assert b'Analytics Dashboard' in response.data
@pytest.mark.integration
@pytest.mark.api
def test_hours_by_day_api(client, app, sample_data):
"""Test hours by day API endpoint"""
with app.app_context():
with client.session_transaction() as sess:
user = sample_data['user']
login_user(user)
sess['_user_id'] = user.id
response = client.get('/api/analytics/hours-by-day?days=7')
assert response.status_code == 200
data = response.get_json()
assert 'labels' in data
assert 'datasets' in data
assert len(data['datasets']) > 0
@pytest.mark.integration
@pytest.mark.api
def test_hours_by_project_api(client, app, sample_data):
"""Test hours by project API endpoint"""
with app.app_context():
with client.session_transaction() as sess:
user = sample_data['user']
login_user(user)
sess['_user_id'] = user.id
response = client.get('/api/analytics/hours-by-project?days=7')
assert response.status_code == 200
data = response.get_json()
assert 'labels' in data
assert 'datasets' in data
assert len(data['labels']) > 0
@pytest.mark.integration
@pytest.mark.api
def test_billable_vs_nonbillable_api(client, app, sample_data):
"""Test billable vs non-billable API endpoint"""
with app.app_context():
with client.session_transaction() as sess:
user = sample_data['user']
login_user(user)
sess['_user_id'] = user.id
response = client.get('/api/analytics/billable-vs-nonbillable?days=7')
assert response.status_code == 200
data = response.get_json()
assert 'labels' in data
assert 'datasets' in data
assert len(data['labels']) == 2 # Billable and Non-Billable
@pytest.mark.integration
@pytest.mark.api
def test_hours_by_hour_api(client, app, sample_data):
"""Test hours by hour API endpoint"""
with app.app_context():
with client.session_transaction() as sess:
user = sample_data['user']
login_user(user)
sess['_user_id'] = user.id
response = client.get('/api/analytics/hours-by-hour?days=7')
assert response.status_code == 200
data = response.get_json()
assert 'labels' in data
assert 'datasets' in data
assert len(data['labels']) == 24 # 24 hours
@pytest.mark.integration
@pytest.mark.api
def test_weekly_trends_api(client, app, sample_data):
"""Test weekly trends API endpoint"""
with app.app_context():
with client.session_transaction() as sess:
user = sample_data['user']
login_user(user)
sess['_user_id'] = user.id
response = client.get('/api/analytics/weekly-trends?weeks=4')
assert response.status_code == 200
data = response.get_json()
assert 'labels' in data
assert 'datasets' in data
@pytest.mark.integration
@pytest.mark.api
def test_project_efficiency_api(client, app, sample_data):
"""Test project efficiency API endpoint"""
with app.app_context():
with client.session_transaction() as sess:
user = sample_data['user']
login_user(user)
sess['_user_id'] = user.id
response = client.get('/api/analytics/project-efficiency?days=7')
assert response.status_code == 200
data = response.get_json()
assert 'labels' in data
assert 'datasets' in data
@pytest.mark.integration
@pytest.mark.api
@pytest.mark.security
def test_user_performance_api_requires_admin(client, app, sample_data):
"""Test that user performance API requires admin access"""
with app.app_context():
with client.session_transaction() as sess:
user = sample_data['user']
login_user(user)
sess['_user_id'] = user.id
response = client.get('/api/analytics/hours-by-user?days=7')
assert response.status_code == 403 # Forbidden for non-admin users
@pytest.mark.integration
@pytest.mark.api
def test_user_performance_api_accessible_by_admin(client, app, sample_data):
"""Test that user performance API is accessible by admin users"""
with app.app_context():
# Make user admin
user = sample_data['user']
user.role = 'admin'
db.session.commit()
with client.session_transaction() as sess:
login_user(user)
sess['_user_id'] = user.id
response = client.get('/api/analytics/hours-by-user?days=7')
assert response.status_code == 200
data = response.get_json()
assert 'labels' in data
assert 'datasets' in data
@pytest.mark.integration
@pytest.mark.api
def test_api_endpoints_with_invalid_parameters(client, app, sample_data):
"""Test API endpoints with invalid parameters"""
with app.app_context():
with client.session_transaction() as sess:
user = sample_data['user']
login_user(user)
sess['_user_id'] = user.id
# Test with invalid days parameter
response = client.get('/api/analytics/hours-by-day?days=invalid')
assert response.status_code == 500 # Should handle invalid parameter gracefully
# Test with missing parameter (should use default)
response = client.get('/api/analytics/hours-by-day')
assert response.status_code == 200