mirror of
https://github.com/DRYTRIX/TimeTracker.git
synced 2025-12-20 10:19:56 -06:00
225 lines
6.3 KiB
Python
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
|