mirror of
https://github.com/DRYTRIX/TimeTracker.git
synced 2026-05-24 07:10:21 -05:00
ed934100a0
Four integration tests fell behind code changes in app/integrations/ and app/models. They were asserting against the old data shapes / patching the old mock targets and only surfaced now that v5.5.6's fixture refresh re-enabled them. ### tests/test_integration/test_caldav_integration.py test_sync_data_imports_events still expected the connector to write TimeEntry rows and IntegrationExternalEventLink rows. The connector in app/integrations/caldav_calendar.py:803 now writes CalendarEvent rows and tracks imports via a [CalDAV: <uid>] marker embedded in the description. Update the assertions to query CalendarEvent and look for the marker. Add CalendarEvent to the model imports. ### tests/test_integration/test_activitywatch_integration.py Four tests in this file patched `app.integrations.activitywatch.requests.get`. The connector at app/integrations/activitywatch.py:78-79 was refactored to route HTTP through `integration_session()` + `session_request()`, so requests.get is never called and the mocks never fire. Tests that relied on the mock then attempted real HTTP to localhost:5600 and failed. Repoint all four @patch decorators to `app.integrations.activitywatch.session_request`. Update the one positional-argument assertion in test_test_connection_success — the URL moves from arg[0] to arg[2] because session_request's signature is (session, method, url, **kw). ### tests/test_custom_field_definitions.py test_delete_custom_field_preserves_other_fields called `test_client.set_custom_field(...)` directly on the fixture instance which may belong to a different SQLAlchemy session by the time the test runs. The two JSON mutations weren't reliably persisted, so only one of the two fields survived to the assertions. Re-query the client via `Client.query.get(test_client.id)` before mutating — matches the pattern already used in the surrounding tests in the same file. Test plan - pytest tests/test_integration/test_caldav_integration.py::TestCalDAVConnector::test_sync_data_imports_events - pytest tests/test_integration/test_activitywatch_integration.py::TestActivityWatchConnector - pytest tests/test_custom_field_definitions.py::test_delete_custom_field_preserves_other_fields
320 lines
10 KiB
Python
320 lines
10 KiB
Python
"""
|
|
Test suite for Custom Field Definitions.
|
|
Tests model creation, deletion, and client field cleanup.
|
|
"""
|
|
|
|
import pytest
|
|
from app.models import CustomFieldDefinition, Client
|
|
from app import db
|
|
|
|
|
|
# ============================================================================
|
|
# CustomFieldDefinition Model Tests
|
|
# ============================================================================
|
|
|
|
|
|
@pytest.mark.unit
|
|
@pytest.mark.models
|
|
@pytest.mark.smoke
|
|
def test_custom_field_definition_creation(app, admin_user):
|
|
"""Test basic custom field definition creation."""
|
|
with app.app_context():
|
|
definition = CustomFieldDefinition(
|
|
field_key="debtor_number",
|
|
label="Debtor Number",
|
|
description="Client's debtor number in ERP system",
|
|
is_mandatory=False,
|
|
is_active=True,
|
|
order=1,
|
|
created_by=admin_user.id,
|
|
)
|
|
db.session.add(definition)
|
|
db.session.commit()
|
|
|
|
assert definition.id is not None
|
|
assert definition.field_key == "debtor_number"
|
|
assert definition.label == "Debtor Number"
|
|
assert definition.is_mandatory is False
|
|
assert definition.is_active is True
|
|
assert definition.order == 1
|
|
assert definition.created_at is not None
|
|
assert definition.updated_at is not None
|
|
|
|
|
|
@pytest.mark.unit
|
|
@pytest.mark.models
|
|
def test_count_clients_with_value_no_clients(app, admin_user):
|
|
"""Test counting clients with value when no clients have the field."""
|
|
with app.app_context():
|
|
definition = CustomFieldDefinition(
|
|
field_key="test_field",
|
|
label="Test Field",
|
|
created_by=admin_user.id,
|
|
)
|
|
db.session.add(definition)
|
|
db.session.commit()
|
|
|
|
count = definition.count_clients_with_value()
|
|
assert count == 0
|
|
|
|
|
|
@pytest.mark.unit
|
|
@pytest.mark.models
|
|
def test_count_clients_with_value_with_clients(app, admin_user, test_client):
|
|
"""Test counting clients with value when clients have the field."""
|
|
with app.app_context():
|
|
definition = CustomFieldDefinition(
|
|
field_key="test_field",
|
|
label="Test Field",
|
|
created_by=admin_user.id,
|
|
)
|
|
db.session.add(definition)
|
|
db.session.commit()
|
|
|
|
# Re-query client so it's in the current session
|
|
client = Client.query.get(test_client.id)
|
|
client.set_custom_field("test_field", "test_value")
|
|
db.session.commit()
|
|
|
|
count = definition.count_clients_with_value()
|
|
assert count == 1
|
|
|
|
# Add another client with the field
|
|
client2 = Client(name="Test Client 2")
|
|
db.session.add(client2)
|
|
client2.set_custom_field("test_field", "another_value")
|
|
db.session.commit()
|
|
|
|
count = definition.count_clients_with_value()
|
|
assert count == 2
|
|
|
|
|
|
@pytest.mark.unit
|
|
@pytest.mark.models
|
|
def test_count_clients_with_value_ignores_empty(app, admin_user, test_client):
|
|
"""Test that empty values are not counted."""
|
|
with app.app_context():
|
|
definition = CustomFieldDefinition(
|
|
field_key="test_field",
|
|
label="Test Field",
|
|
created_by=admin_user.id,
|
|
)
|
|
db.session.add(definition)
|
|
db.session.commit()
|
|
|
|
# Re-query client so it's in the current session
|
|
client = Client.query.get(test_client.id)
|
|
# Set empty value
|
|
client.set_custom_field("test_field", "")
|
|
db.session.commit()
|
|
|
|
count = definition.count_clients_with_value()
|
|
assert count == 0
|
|
|
|
# Set whitespace-only value
|
|
client.set_custom_field("test_field", " ")
|
|
db.session.commit()
|
|
|
|
count = definition.count_clients_with_value()
|
|
assert count == 0
|
|
|
|
# Set actual value
|
|
client.set_custom_field("test_field", "valid_value")
|
|
db.session.commit()
|
|
|
|
count = definition.count_clients_with_value()
|
|
assert count == 1
|
|
|
|
|
|
@pytest.mark.unit
|
|
@pytest.mark.models
|
|
def test_count_clients_with_value_ignores_other_fields(app, admin_user, test_client):
|
|
"""Test that only the specific field is counted."""
|
|
with app.app_context():
|
|
definition = CustomFieldDefinition(
|
|
field_key="test_field",
|
|
label="Test Field",
|
|
created_by=admin_user.id,
|
|
)
|
|
db.session.add(definition)
|
|
db.session.commit()
|
|
|
|
# Re-query client so it's in the current session
|
|
client = Client.query.get(test_client.id)
|
|
# Set a different field
|
|
client.set_custom_field("other_field", "value")
|
|
db.session.commit()
|
|
|
|
count = definition.count_clients_with_value()
|
|
assert count == 0
|
|
|
|
# Set the correct field
|
|
client.set_custom_field("test_field", "value")
|
|
db.session.commit()
|
|
|
|
count = definition.count_clients_with_value()
|
|
assert count == 1
|
|
|
|
|
|
# ============================================================================
|
|
# Custom Field Definition Deletion Tests
|
|
# ============================================================================
|
|
|
|
|
|
@pytest.mark.integration
|
|
@pytest.mark.database
|
|
def test_delete_custom_field_removes_from_clients(
|
|
app, admin_user, test_client, admin_authenticated_client
|
|
):
|
|
"""Test that deleting a custom field definition removes it from all clients."""
|
|
with app.app_context():
|
|
# Create custom field definition
|
|
definition = CustomFieldDefinition(
|
|
field_key="debtor_number",
|
|
label="Debtor Number",
|
|
created_by=admin_user.id,
|
|
)
|
|
db.session.add(definition)
|
|
db.session.commit()
|
|
|
|
# Set custom field value for client
|
|
test_client.set_custom_field("debtor_number", "12345")
|
|
db.session.commit()
|
|
|
|
# Verify client has the field
|
|
assert test_client.get_custom_field("debtor_number") == "12345"
|
|
|
|
# Delete the definition via route
|
|
response = admin_authenticated_client.post(
|
|
f"/admin/custom-field-definitions/{definition.id}/delete",
|
|
follow_redirects=True,
|
|
)
|
|
|
|
assert response.status_code == 200
|
|
|
|
# Verify definition is deleted
|
|
deleted_definition = CustomFieldDefinition.query.get(definition.id)
|
|
assert deleted_definition is None
|
|
|
|
# Verify field is removed from client (re-query; test_client may be detached after request)
|
|
client_after = Client.query.get(test_client.id)
|
|
assert client_after.get_custom_field("debtor_number") is None
|
|
assert "debtor_number" not in (client_after.custom_fields or {})
|
|
|
|
|
|
@pytest.mark.integration
|
|
@pytest.mark.database
|
|
def test_delete_custom_field_multiple_clients(
|
|
app, admin_user, test_client, admin_authenticated_client
|
|
):
|
|
"""Test that deleting a custom field removes it from multiple clients."""
|
|
with app.app_context():
|
|
# Create custom field definition
|
|
definition = CustomFieldDefinition(
|
|
field_key="erp_id",
|
|
label="ERP ID",
|
|
created_by=admin_user.id,
|
|
)
|
|
db.session.add(definition)
|
|
db.session.commit()
|
|
|
|
# Create multiple clients with the field
|
|
client1 = Client(name="Client 1")
|
|
client2 = Client(name="Client 2")
|
|
client3 = Client(name="Client 3")
|
|
db.session.add_all([client1, client2, client3])
|
|
client1.set_custom_field("erp_id", "ERP001")
|
|
client2.set_custom_field("erp_id", "ERP002")
|
|
client3.set_custom_field("erp_id", "ERP003")
|
|
db.session.commit()
|
|
|
|
# Delete the definition
|
|
response = admin_authenticated_client.post(
|
|
f"/admin/custom-field-definitions/{definition.id}/delete",
|
|
follow_redirects=True,
|
|
)
|
|
|
|
assert response.status_code == 200
|
|
|
|
# Verify field is removed from all clients
|
|
db.session.refresh(client1)
|
|
db.session.refresh(client2)
|
|
db.session.refresh(client3)
|
|
|
|
assert client1.get_custom_field("erp_id") is None
|
|
assert client2.get_custom_field("erp_id") is None
|
|
assert client3.get_custom_field("erp_id") is None
|
|
|
|
|
|
@pytest.mark.integration
|
|
@pytest.mark.database
|
|
def test_delete_custom_field_no_clients_affected(
|
|
app, admin_user, admin_authenticated_client
|
|
):
|
|
"""Test deleting a custom field when no clients have values."""
|
|
with app.app_context():
|
|
# Create custom field definition
|
|
definition = CustomFieldDefinition(
|
|
field_key="unused_field",
|
|
label="Unused Field",
|
|
created_by=admin_user.id,
|
|
)
|
|
db.session.add(definition)
|
|
db.session.commit()
|
|
|
|
# Delete the definition
|
|
response = admin_authenticated_client.post(
|
|
f"/admin/custom-field-definitions/{definition.id}/delete",
|
|
follow_redirects=True,
|
|
)
|
|
|
|
assert response.status_code == 200
|
|
|
|
# Verify definition is deleted
|
|
deleted_definition = CustomFieldDefinition.query.get(definition.id)
|
|
assert deleted_definition is None
|
|
|
|
|
|
@pytest.mark.integration
|
|
@pytest.mark.database
|
|
def test_delete_custom_field_preserves_other_fields(
|
|
app, admin_user, test_client, admin_authenticated_client
|
|
):
|
|
"""Test that deleting one custom field doesn't affect other custom fields."""
|
|
with app.app_context():
|
|
# Create two custom field definitions
|
|
definition1 = CustomFieldDefinition(
|
|
field_key="field1",
|
|
label="Field 1",
|
|
created_by=admin_user.id,
|
|
)
|
|
definition2 = CustomFieldDefinition(
|
|
field_key="field2",
|
|
label="Field 2",
|
|
created_by=admin_user.id,
|
|
)
|
|
db.session.add_all([definition1, definition2])
|
|
db.session.commit()
|
|
|
|
# Set both fields for client. Re-query the client into the current
|
|
# session so the two JSON mutations are written back through the
|
|
# active session's identity map; the `test_client` fixture instance
|
|
# may belong to a different session after the fixture commit.
|
|
client = Client.query.get(test_client.id)
|
|
client.set_custom_field("field1", "value1")
|
|
client.set_custom_field("field2", "value2")
|
|
db.session.commit()
|
|
|
|
# Delete only definition1
|
|
response = admin_authenticated_client.post(
|
|
f"/admin/custom-field-definitions/{definition1.id}/delete",
|
|
follow_redirects=True,
|
|
)
|
|
|
|
assert response.status_code == 200
|
|
|
|
# Verify field1 is removed but field2 remains (re-query; test_client may be detached after request)
|
|
client_after = Client.query.get(test_client.id)
|
|
assert client_after.get_custom_field("field1") is None
|
|
assert client_after.get_custom_field("field2") == "value2"
|