Files
leocrm/tests/test_custom_fields.py
T

165 lines
6.1 KiB
Python
Raw Normal View History

"""Custom fields tests — plugin-defined custom fields on contacts."""
from __future__ import annotations
import pytest
from httpx import AsyncClient
from tests.conftest import ORIGIN_HEADER, login_client, seed_tenant_and_users
@pytest.mark.asyncio
class TestCustomFieldsGet:
"""GET /api/v1/contacts/{id}/custom-fields"""
async def test_get_custom_fields_returns_200(self, client: AsyncClient, db_session):
"""GET custom fields for a contact returns 200 with merged definitions."""
await seed_tenant_and_users(db_session)
await login_client(client, "admin@tenanta.com")
# Create a contact first
resp = await client.post(
"/api/v1/contacts",
json={"type": "company", "name": "Test Corp", "displayname": "Test Corp"},
headers=ORIGIN_HEADER,
)
assert resp.status_code == 201
contact_id = resp.json()["id"]
resp = await client.get(
f"/api/v1/contacts/{contact_id}/custom-fields",
headers=ORIGIN_HEADER,
)
assert resp.status_code == 200
data = resp.json()
assert "fields" in data
assert isinstance(data["fields"], list)
async def test_get_custom_fields_invalid_uuid_returns_400(self, client: AsyncClient, db_session):
"""GET custom fields with invalid UUID returns 400."""
await seed_tenant_and_users(db_session)
await login_client(client, "admin@tenanta.com")
resp = await client.get(
"/api/v1/contacts/not-a-uuid/custom-fields",
headers=ORIGIN_HEADER,
)
assert resp.status_code == 400
async def test_get_custom_fields_not_found_returns_404(self, client: AsyncClient, db_session):
"""GET custom fields for non-existent contact returns 404."""
await seed_tenant_and_users(db_session)
await login_client(client, "admin@tenanta.com")
resp = await client.get(
"/api/v1/contacts/00000000-0000-0000-0000-000000000000/custom-fields",
headers=ORIGIN_HEADER,
)
assert resp.status_code == 404
@pytest.mark.asyncio
class TestCustomFieldsUpdate:
"""PATCH /api/v1/contacts/{id}/custom-fields"""
async def test_update_custom_fields_returns_200(self, client: AsyncClient, db_session):
"""PATCH custom fields stores values in contacts.custom JSONB."""
await seed_tenant_and_users(db_session)
await login_client(client, "admin@tenanta.com")
# Create a contact
resp = await client.post(
"/api/v1/contacts",
json={"type": "company", "name": "Custom Corp", "displayname": "Custom Corp"},
headers=ORIGIN_HEADER,
)
contact_id = resp.json()["id"]
# Update custom fields (empty values is valid since no plugin defines fields)
resp = await client.patch(
f"/api/v1/contacts/{contact_id}/custom-fields",
json={"values": {"test_field": "test_value"}},
headers=ORIGIN_HEADER,
)
# Without plugin definitions, unknown fields should be rejected
assert resp.status_code == 400
async def test_update_custom_fields_empty_values_returns_200(self, client: AsyncClient, db_session):
"""PATCH custom fields with empty values dict returns 200."""
await seed_tenant_and_users(db_session)
await login_client(client, "admin@tenanta.com")
resp = await client.post(
"/api/v1/contacts",
json={"type": "company", "name": "Empty Corp", "displayname": "Empty Corp"},
headers=ORIGIN_HEADER,
)
contact_id = resp.json()["id"]
resp = await client.patch(
f"/api/v1/contacts/{contact_id}/custom-fields",
json={"values": {}},
headers=ORIGIN_HEADER,
)
assert resp.status_code == 200
assert "fields" in resp.json()
async def test_update_custom_fields_not_found_returns_404(self, client: AsyncClient, db_session):
"""PATCH custom fields for non-existent contact returns 404."""
await seed_tenant_and_users(db_session)
await login_client(client, "admin@tenanta.com")
resp = await client.patch(
"/api/v1/contacts/00000000-0000-0000-0000-000000000000/custom-fields",
json={"values": {}},
headers=ORIGIN_HEADER,
)
assert resp.status_code == 404
@pytest.mark.asyncio
class TestCustomFieldDefinition:
"""Test CustomFieldDefinition model in manifest."""
async def test_custom_field_definition_creation(self):
"""CustomFieldDefinition can be created with valid field types."""
from app.plugins.manifest import CustomFieldDefinition
cf = CustomFieldDefinition(
name="lead_source",
label="Lead Source",
label_key="custom.leadSource",
field_type="select",
options=["website", "referral", "cold_call"],
required=False,
entity="contact",
)
assert cf.name == "lead_source"
assert cf.field_type == "select"
assert len(cf.options) == 3
async def test_custom_field_definition_invalid_type_raises(self):
"""CustomFieldDefinition rejects invalid field_type."""
from app.plugins.manifest import CustomFieldDefinition
from pydantic import ValidationError
with pytest.raises(ValidationError):
CustomFieldDefinition(
name="bad_field",
field_type="invalid_type",
)
async def test_plugin_manifest_with_custom_fields(self):
"""PluginManifest accepts custom_fields list."""
from app.plugins.manifest import PluginManifest, CustomFieldDefinition
manifest = PluginManifest(
name="test_plugin",
version="1.0.0",
display_name="Test Plugin",
custom_fields=[
CustomFieldDefinition(
name="score",
label="Score",
field_type="number",
entity="contact",
),
],
)
assert len(manifest.custom_fields) == 1
assert manifest.custom_fields[0].name == "score"