mirror of
https://github.com/DRYTRIX/TimeTracker.git
synced 2025-12-31 00:09:58 -06:00
553 lines
16 KiB
Python
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
|
|
|