Files
TimeTracker/tests/test_client_note_model.py
2025-10-24 17:48:19 +02:00

553 lines
16 KiB
Python

"""
Test suite for ClientNote model.
Tests model creation, relationships, properties, and business logic.
"""
import pytest
from datetime import datetime
from app.models import ClientNote, Client, User
from app import db
# ============================================================================
# ClientNote Model Tests
# ============================================================================
@pytest.mark.unit
@pytest.mark.models
@pytest.mark.smoke
def test_client_note_creation(app, user, test_client):
"""Test basic client note creation."""
with app.app_context():
note = ClientNote(
content="Important note about the client",
user_id=user.id,
client_id=test_client.id,
is_important=False
)
db.session.add(note)
db.session.commit()
assert note.id is not None
assert note.content == "Important note about the client"
assert note.user_id == user.id
assert note.client_id == test_client.id
assert note.is_important is False
assert note.created_at is not None
assert note.updated_at is not None
@pytest.mark.unit
@pytest.mark.models
def test_client_note_requires_client(app, user):
"""Test that client note requires a client."""
with app.app_context():
with pytest.raises(ValueError, match="Note must be associated with a client"):
note = ClientNote(
content="Note without client",
user_id=user.id,
client_id=None
)
@pytest.mark.unit
@pytest.mark.models
def test_client_note_requires_content(app, user, test_client):
"""Test that client note requires content."""
with app.app_context():
with pytest.raises(ValueError, match="Note content cannot be empty"):
note = ClientNote(
content="",
user_id=user.id,
client_id=test_client.id
)
@pytest.mark.unit
@pytest.mark.models
def test_client_note_strips_content(app, user, test_client):
"""Test that client note content is stripped of whitespace."""
with app.app_context():
note = ClientNote(
content=" Note with spaces ",
user_id=user.id,
client_id=test_client.id
)
db.session.add(note)
db.session.commit()
assert note.content == "Note with spaces"
@pytest.mark.unit
@pytest.mark.models
def test_client_note_author_relationship(app, user, test_client):
"""Test client note author relationship."""
with app.app_context():
note = ClientNote(
content="Test note",
user_id=user.id,
client_id=test_client.id
)
db.session.add(note)
db.session.commit()
db.session.refresh(note)
assert note.author is not None
assert note.author.id == user.id
assert note.author.username == user.username
@pytest.mark.unit
@pytest.mark.models
def test_client_note_client_relationship(app, user, test_client):
"""Test client note client relationship."""
with app.app_context():
note = ClientNote(
content="Test note",
user_id=user.id,
client_id=test_client.id
)
db.session.add(note)
db.session.commit()
db.session.refresh(note)
assert note.client is not None
assert note.client.id == test_client.id
assert note.client.name == test_client.name
@pytest.mark.unit
@pytest.mark.models
def test_client_has_notes_relationship(app, user, test_client):
"""Test that client has notes relationship."""
with app.app_context():
# Re-query the client to ensure it's in the current session
from app.models import Client
client = Client.query.get(test_client.id)
note1 = ClientNote(
content="First note",
user_id=user.id,
client_id=client.id
)
note2 = ClientNote(
content="Second note",
user_id=user.id,
client_id=client.id,
is_important=True
)
db.session.add_all([note1, note2])
db.session.commit()
db.session.refresh(client)
assert len(client.notes) == 2
@pytest.mark.unit
@pytest.mark.models
def test_client_note_author_name_property(app, test_client):
"""Test client note author_name property."""
with app.app_context():
# Test with username only (no full_name)
user_without_fullname = User(
username='usernoname',
email='noname@example.com',
role='user'
)
user_without_fullname.is_active = True
db.session.add(user_without_fullname)
db.session.commit()
note1 = ClientNote(
content="Test note 1",
user_id=user_without_fullname.id,
client_id=test_client.id
)
db.session.add(note1)
db.session.commit()
db.session.refresh(note1)
assert note1.author_name == "usernoname"
# Test with full name
user_with_fullname = User(
username='userwithname',
email='withname@example.com',
role='user'
)
user_with_fullname.full_name = "Test User Full Name"
user_with_fullname.is_active = True
db.session.add(user_with_fullname)
db.session.commit()
note2 = ClientNote(
content="Test note 2",
user_id=user_with_fullname.id,
client_id=test_client.id
)
db.session.add(note2)
db.session.commit()
db.session.refresh(note2)
assert note2.author_name == "Test User Full Name"
@pytest.mark.unit
@pytest.mark.models
def test_client_note_client_name_property(app, user, test_client):
"""Test client note client_name property."""
with app.app_context():
note = ClientNote(
content="Test note",
user_id=user.id,
client_id=test_client.id
)
db.session.add(note)
db.session.commit()
db.session.refresh(note)
assert note.client_name == test_client.name
@pytest.mark.unit
@pytest.mark.models
def test_client_note_can_edit(app, user, admin_user, test_client):
"""Test client note can_edit permission."""
with app.app_context():
note = ClientNote(
content="Test note",
user_id=user.id,
client_id=test_client.id
)
db.session.add(note)
db.session.commit()
# Author can edit
assert note.can_edit(user) is True
# Admin can edit
assert note.can_edit(admin_user) is True
# Other user cannot edit
other_user = User(username='otheruser', role='user')
other_user.is_active = True
db.session.add(other_user)
db.session.commit()
assert note.can_edit(other_user) is False
@pytest.mark.unit
@pytest.mark.models
def test_client_note_can_delete(app, user, admin_user, test_client):
"""Test client note can_delete permission."""
with app.app_context():
note = ClientNote(
content="Test note",
user_id=user.id,
client_id=test_client.id
)
db.session.add(note)
db.session.commit()
# Author can delete
assert note.can_delete(user) is True
# Admin can delete
assert note.can_delete(admin_user) is True
# Other user cannot delete
other_user = User(username='otheruser', role='user')
other_user.is_active = True
db.session.add(other_user)
db.session.commit()
assert note.can_delete(other_user) is False
@pytest.mark.unit
@pytest.mark.models
def test_client_note_edit_content(app, user, test_client):
"""Test editing client note content."""
with app.app_context():
note = ClientNote(
content="Original content",
user_id=user.id,
client_id=test_client.id,
is_important=False
)
db.session.add(note)
db.session.commit()
# Edit content
note.edit_content("Updated content", user, is_important=True)
db.session.commit()
assert note.content == "Updated content"
assert note.is_important is True
@pytest.mark.unit
@pytest.mark.models
def test_client_note_edit_content_permission_denied(app, user, test_client):
"""Test editing client note without permission."""
with app.app_context():
note = ClientNote(
content="Original content",
user_id=user.id,
client_id=test_client.id
)
db.session.add(note)
db.session.commit()
# Create another user
other_user = User(username='otheruser', role='user')
other_user.is_active = True
db.session.add(other_user)
db.session.commit()
# Try to edit as other user
with pytest.raises(PermissionError, match="User does not have permission to edit this note"):
note.edit_content("Hacked content", other_user)
@pytest.mark.unit
@pytest.mark.models
def test_client_note_edit_content_empty_fails(app, user, test_client):
"""Test editing client note with empty content fails."""
with app.app_context():
note = ClientNote(
content="Original content",
user_id=user.id,
client_id=test_client.id
)
db.session.add(note)
db.session.commit()
# Try to edit with empty content
with pytest.raises(ValueError, match="Note content cannot be empty"):
note.edit_content("", user)
@pytest.mark.unit
@pytest.mark.models
def test_client_note_to_dict(app, user, test_client):
"""Test client note serialization to dictionary."""
with app.app_context():
note = ClientNote(
content="Test note",
user_id=user.id,
client_id=test_client.id,
is_important=True
)
db.session.add(note)
db.session.commit()
db.session.refresh(note)
note_dict = note.to_dict()
assert 'id' in note_dict
assert 'content' in note_dict
assert 'client_id' in note_dict
assert 'client_name' in note_dict
assert 'user_id' in note_dict
assert 'author' in note_dict
assert 'author_name' in note_dict
assert 'is_important' in note_dict
assert 'created_at' in note_dict
assert 'updated_at' in note_dict
assert note_dict['content'] == "Test note"
assert note_dict['is_important'] is True
@pytest.mark.unit
@pytest.mark.models
def test_get_client_notes(app, user, test_client):
"""Test getting notes for a client."""
with app.app_context():
# Create multiple notes
note1 = ClientNote(
content="First note",
user_id=user.id,
client_id=test_client.id,
is_important=False
)
note2 = ClientNote(
content="Second note",
user_id=user.id,
client_id=test_client.id,
is_important=True
)
note3 = ClientNote(
content="Third note",
user_id=user.id,
client_id=test_client.id,
is_important=False
)
db.session.add_all([note1, note2, note3])
db.session.commit()
# Get all notes
notes = ClientNote.get_client_notes(test_client.id)
assert len(notes) == 3
# Get notes ordered by importance
notes_ordered = ClientNote.get_client_notes(test_client.id, order_by_important=True)
assert len(notes_ordered) == 3
# Important note should be first
assert notes_ordered[0].is_important is True
@pytest.mark.unit
@pytest.mark.models
def test_get_important_notes(app, user, test_client):
"""Test getting only important notes."""
with app.app_context():
# Create multiple notes
note1 = ClientNote(
content="Regular note",
user_id=user.id,
client_id=test_client.id,
is_important=False
)
note2 = ClientNote(
content="Important note 1",
user_id=user.id,
client_id=test_client.id,
is_important=True
)
note3 = ClientNote(
content="Important note 2",
user_id=user.id,
client_id=test_client.id,
is_important=True
)
db.session.add_all([note1, note2, note3])
db.session.commit()
# Get all important notes
important_notes = ClientNote.get_important_notes()
assert len(important_notes) == 2
assert all(note.is_important for note in important_notes)
# Get important notes for specific client
client_important = ClientNote.get_important_notes(client_id=test_client.id)
assert len(client_important) == 2
@pytest.mark.unit
@pytest.mark.models
def test_get_user_notes(app, user, test_client):
"""Test getting notes by a specific user."""
with app.app_context():
# Create notes by user
note1 = ClientNote(
content="User note 1",
user_id=user.id,
client_id=test_client.id
)
note2 = ClientNote(
content="User note 2",
user_id=user.id,
client_id=test_client.id
)
db.session.add_all([note1, note2])
# Create note by other user
other_user = User(username='otheruser', role='user')
other_user.is_active = True
db.session.add(other_user)
db.session.commit()
note3 = ClientNote(
content="Other user note",
user_id=other_user.id,
client_id=test_client.id
)
db.session.add(note3)
db.session.commit()
# Get notes by specific user
user_notes = ClientNote.get_user_notes(user.id)
assert len(user_notes) == 2
assert all(note.user_id == user.id for note in user_notes)
# Test with limit
limited_notes = ClientNote.get_user_notes(user.id, limit=1)
assert len(limited_notes) == 1
@pytest.mark.unit
@pytest.mark.models
def test_get_recent_notes(app, user, test_client):
"""Test getting recent notes across all clients."""
with app.app_context():
# Create multiple notes
for i in range(15):
note = ClientNote(
content=f"Note {i}",
user_id=user.id,
client_id=test_client.id
)
db.session.add(note)
db.session.commit()
# Get recent notes with default limit
recent_notes = ClientNote.get_recent_notes()
assert len(recent_notes) == 10
# Get recent notes with custom limit
recent_notes_5 = ClientNote.get_recent_notes(limit=5)
assert len(recent_notes_5) == 5
@pytest.mark.unit
@pytest.mark.models
def test_client_note_repr(app, user, test_client):
"""Test client note string representation."""
with app.app_context():
note = ClientNote(
content="Test note",
user_id=user.id,
client_id=test_client.id
)
db.session.add(note)
db.session.commit()
db.session.refresh(note)
repr_str = repr(note)
assert 'ClientNote' in repr_str
assert user.username in repr_str
assert str(test_client.id) in repr_str
@pytest.mark.unit
@pytest.mark.models
def test_client_note_cascade_delete(app, user, test_client):
"""Test that notes are deleted when client is deleted."""
with app.app_context():
# Re-query the client to ensure it's in the current session
from app.models import Client
client = Client.query.get(test_client.id)
note = ClientNote(
content="Test note",
user_id=user.id,
client_id=client.id
)
db.session.add(note)
db.session.commit()
note_id = note.id
# Delete client
db.session.delete(client)
db.session.commit()
# Note should be deleted
deleted_note = ClientNote.query.get(note_id)
assert deleted_note is None