Files
TimeTracker/tests/test_basic.py
Dries Peeters 113a57d2eb testing updates
2025-10-10 11:37:23 +02:00

225 lines
6.3 KiB
Python

import pytest
from app import db
from app.models import User, Project, TimeEntry, Settings, Client
from datetime import datetime, timedelta
from decimal import Decimal
# Note: All fixtures are now imported from conftest.py
# No duplicate fixtures needed here
@pytest.mark.smoke
@pytest.mark.unit
def test_app_creation(app):
"""Test that the app can be created"""
assert app is not None
assert app.config['TESTING'] is True
@pytest.mark.unit
@pytest.mark.database
def test_database_creation(app):
"""Test that database tables can be created"""
with app.app_context():
# Check that tables exist using inspect
from sqlalchemy import inspect
inspector = inspect(db.engine)
tables = inspector.get_table_names()
assert 'users' in tables
assert 'projects' in tables
assert 'time_entries' in tables
assert 'settings' in tables
@pytest.mark.unit
@pytest.mark.models
def test_user_creation(app):
"""Test user creation"""
with app.app_context():
user = User(username='testuser', role='user')
db.session.add(user)
db.session.commit()
assert user.id is not None
assert user.username == 'testuser'
assert user.role == 'user'
assert user.is_admin is False
@pytest.mark.unit
@pytest.mark.models
def test_admin_user(app):
"""Test admin user properties"""
with app.app_context():
admin = User(username='admin', role='admin')
db.session.add(admin)
db.session.commit()
assert admin.is_admin is True
@pytest.mark.unit
@pytest.mark.models
def test_project_creation(app):
"""Test project creation"""
with app.app_context():
# Create a client first
client = Client(name='Test Client', default_hourly_rate=Decimal('50.00'))
db.session.add(client)
db.session.commit()
project = Project(
name='Test Project',
client_id=client.id,
description='Test description',
billable=True,
hourly_rate=Decimal('50.00')
)
db.session.add(project)
db.session.commit()
assert project.id is not None
assert project.name == 'Test Project'
assert project.client_id == client.id
assert project.billable is True
assert float(project.hourly_rate) == 50.00
@pytest.mark.unit
@pytest.mark.models
def test_time_entry_creation(app, user, project):
"""Test time entry creation"""
start_time = datetime.utcnow()
end_time = start_time + timedelta(hours=2)
entry = TimeEntry(
user_id=user.id,
project_id=project.id,
start_time=start_time,
end_time=end_time,
notes='Test entry',
tags='test,work',
source='manual'
)
db.session.add(entry)
db.session.commit()
assert entry.id is not None
assert entry.duration_hours == 2.0
assert entry.duration_formatted == '02:00:00'
assert entry.tag_list == ['test', 'work']
@pytest.mark.unit
@pytest.mark.models
def test_active_timer(app, user, project):
"""Test active timer functionality"""
# Create active timer
timer = TimeEntry(
user_id=user.id,
project_id=project.id,
start_time=datetime.utcnow(),
source='auto'
)
db.session.add(timer)
db.session.commit()
assert timer.is_active is True
assert timer.end_time is None
# Stop timer
timer.stop_timer()
db.session.commit()
assert timer.is_active is False
assert timer.end_time is not None
assert timer.duration_seconds > 0
@pytest.mark.unit
@pytest.mark.models
def test_user_active_timer_property(app, user, project):
"""Test user active timer property"""
# Refresh user to check initial state
db.session.refresh(user)
# Create active timer
timer = TimeEntry(
user_id=user.id,
project_id=project.id,
start_time=datetime.utcnow(),
source='auto'
)
db.session.add(timer)
db.session.commit()
# Refresh user to load relationships
db.session.expire(user)
db.session.refresh(user)
# Check active timer
assert user.active_timer is not None
assert user.active_timer.id == timer.id
@pytest.mark.integration
@pytest.mark.models
def test_project_totals(app, user, project):
"""Test project total calculations"""
# Create time entries
start_time = datetime.utcnow()
entry1 = TimeEntry(
user_id=user.id,
project_id=project.id,
start_time=start_time,
end_time=start_time + timedelta(hours=2),
source='manual',
billable=True
)
entry2 = TimeEntry(
user_id=user.id,
project_id=project.id,
start_time=start_time + timedelta(hours=3),
end_time=start_time + timedelta(hours=5),
source='manual',
billable=True
)
db.session.add_all([entry1, entry2])
db.session.commit()
# Refresh project to load relationships
db.session.expire(project)
db.session.refresh(project)
# Check totals
assert project.total_hours == 4.0
assert project.total_billable_hours == 4.0
expected_cost = 4.0 * float(project.hourly_rate)
assert float(project.estimated_cost) == expected_cost
@pytest.mark.unit
@pytest.mark.models
def test_settings_singleton(app):
"""Test settings singleton pattern"""
with app.app_context():
# Get settings (should create if not exists)
settings1 = Settings.get_settings()
settings2 = Settings.get_settings()
assert settings1.id == settings2.id
assert settings1 is settings2
@pytest.mark.smoke
@pytest.mark.routes
def test_health_check(client):
"""Test health check endpoint"""
response = client.get('/_health')
assert response.status_code == 200
data = response.get_json()
assert data['status'] == 'healthy'
@pytest.mark.smoke
@pytest.mark.routes
def test_login_page(client):
"""Test login page accessibility"""
response = client.get('/login')
assert response.status_code == 200
@pytest.mark.unit
@pytest.mark.routes
def test_protected_route_redirect(client):
"""Test that protected routes redirect to login"""
response = client.get('/dashboard', follow_redirects=False)
assert response.status_code == 302
assert '/login' in response.location