1152 lines
35 KiB
Python
1152 lines
35 KiB
Python
"""
|
|
Comprehensive unit tests for App models.
|
|
|
|
This test suite covers:
|
|
- App configuration validation
|
|
- App-Message relationships
|
|
- Conversation model integrity
|
|
- Annotation model relationships
|
|
"""
|
|
|
|
import json
|
|
from datetime import UTC, datetime
|
|
from decimal import Decimal
|
|
from unittest.mock import MagicMock, patch
|
|
from uuid import uuid4
|
|
|
|
import pytest
|
|
|
|
from models.model import (
|
|
App,
|
|
AppAnnotationHitHistory,
|
|
AppAnnotationSetting,
|
|
AppMode,
|
|
AppModelConfig,
|
|
Conversation,
|
|
IconType,
|
|
Message,
|
|
MessageAnnotation,
|
|
Site,
|
|
)
|
|
|
|
|
|
class TestAppModelValidation:
|
|
"""Test suite for App model validation and basic operations."""
|
|
|
|
def test_app_creation_with_required_fields(self):
|
|
"""Test creating an app with all required fields."""
|
|
# Arrange
|
|
tenant_id = str(uuid4())
|
|
created_by = str(uuid4())
|
|
|
|
# Act
|
|
app = App(
|
|
tenant_id=tenant_id,
|
|
name="Test App",
|
|
mode=AppMode.CHAT,
|
|
enable_site=True,
|
|
enable_api=False,
|
|
created_by=created_by,
|
|
)
|
|
|
|
# Assert
|
|
assert app.name == "Test App"
|
|
assert app.tenant_id == tenant_id
|
|
assert app.mode == AppMode.CHAT
|
|
assert app.enable_site is True
|
|
assert app.enable_api is False
|
|
assert app.created_by == created_by
|
|
|
|
def test_app_creation_with_optional_fields(self):
|
|
"""Test creating an app with optional fields."""
|
|
# Arrange & Act
|
|
app = App(
|
|
tenant_id=str(uuid4()),
|
|
name="Test App",
|
|
mode=AppMode.COMPLETION,
|
|
enable_site=True,
|
|
enable_api=True,
|
|
created_by=str(uuid4()),
|
|
description="Test description",
|
|
icon_type=IconType.EMOJI,
|
|
icon="🤖",
|
|
icon_background="#FF5733",
|
|
is_demo=True,
|
|
is_public=False,
|
|
api_rpm=100,
|
|
api_rph=1000,
|
|
)
|
|
|
|
# Assert
|
|
assert app.description == "Test description"
|
|
assert app.icon_type == IconType.EMOJI
|
|
assert app.icon == "🤖"
|
|
assert app.icon_background == "#FF5733"
|
|
assert app.is_demo is True
|
|
assert app.is_public is False
|
|
assert app.api_rpm == 100
|
|
assert app.api_rph == 1000
|
|
|
|
def test_app_mode_validation(self):
|
|
"""Test app mode enum values."""
|
|
# Assert
|
|
expected_modes = {
|
|
"chat",
|
|
"completion",
|
|
"workflow",
|
|
"advanced-chat",
|
|
"agent-chat",
|
|
"channel",
|
|
"rag-pipeline",
|
|
}
|
|
assert {mode.value for mode in AppMode} == expected_modes
|
|
|
|
def test_app_mode_value_of(self):
|
|
"""Test AppMode.value_of method."""
|
|
# Act & Assert
|
|
assert AppMode.value_of("chat") == AppMode.CHAT
|
|
assert AppMode.value_of("completion") == AppMode.COMPLETION
|
|
assert AppMode.value_of("workflow") == AppMode.WORKFLOW
|
|
|
|
with pytest.raises(ValueError, match="invalid mode value"):
|
|
AppMode.value_of("invalid_mode")
|
|
|
|
def test_icon_type_validation(self):
|
|
"""Test icon type enum values."""
|
|
# Assert
|
|
assert {t.value for t in IconType} == {"image", "emoji"}
|
|
|
|
def test_app_desc_or_prompt_with_description(self):
|
|
"""Test desc_or_prompt property when description exists."""
|
|
# Arrange
|
|
app = App(
|
|
tenant_id=str(uuid4()),
|
|
name="Test App",
|
|
mode=AppMode.CHAT,
|
|
enable_site=True,
|
|
enable_api=False,
|
|
created_by=str(uuid4()),
|
|
description="App description",
|
|
)
|
|
|
|
# Act
|
|
result = app.desc_or_prompt
|
|
|
|
# Assert
|
|
assert result == "App description"
|
|
|
|
def test_app_desc_or_prompt_without_description(self):
|
|
"""Test desc_or_prompt property when description is empty."""
|
|
# Arrange
|
|
app = App(
|
|
tenant_id=str(uuid4()),
|
|
name="Test App",
|
|
mode=AppMode.CHAT,
|
|
enable_site=True,
|
|
enable_api=False,
|
|
created_by=str(uuid4()),
|
|
description="",
|
|
)
|
|
|
|
# Mock app_model_config property
|
|
with patch.object(App, "app_model_config", new_callable=lambda: property(lambda self: None)):
|
|
# Act
|
|
result = app.desc_or_prompt
|
|
|
|
# Assert
|
|
assert result == ""
|
|
|
|
def test_app_is_agent_property_false(self):
|
|
"""Test is_agent property returns False when not configured as agent."""
|
|
# Arrange
|
|
app = App(
|
|
tenant_id=str(uuid4()),
|
|
name="Test App",
|
|
mode=AppMode.CHAT,
|
|
enable_site=True,
|
|
enable_api=False,
|
|
created_by=str(uuid4()),
|
|
)
|
|
|
|
# Mock app_model_config to return None
|
|
with patch.object(App, "app_model_config", new_callable=lambda: property(lambda self: None)):
|
|
# Act
|
|
result = app.is_agent
|
|
|
|
# Assert
|
|
assert result is False
|
|
|
|
def test_app_mode_compatible_with_agent(self):
|
|
"""Test mode_compatible_with_agent property."""
|
|
# Arrange
|
|
app = App(
|
|
tenant_id=str(uuid4()),
|
|
name="Test App",
|
|
mode=AppMode.CHAT,
|
|
enable_site=True,
|
|
enable_api=False,
|
|
created_by=str(uuid4()),
|
|
)
|
|
|
|
# Mock is_agent to return False
|
|
with patch.object(App, "is_agent", new_callable=lambda: property(lambda self: False)):
|
|
# Act
|
|
result = app.mode_compatible_with_agent
|
|
|
|
# Assert
|
|
assert result == AppMode.CHAT
|
|
|
|
|
|
class TestAppModelConfig:
|
|
"""Test suite for AppModelConfig model."""
|
|
|
|
def test_app_model_config_creation(self):
|
|
"""Test creating an AppModelConfig."""
|
|
# Arrange
|
|
app_id = str(uuid4())
|
|
created_by = str(uuid4())
|
|
|
|
# Act
|
|
config = AppModelConfig(
|
|
app_id=app_id,
|
|
provider="openai",
|
|
model_id="gpt-4",
|
|
created_by=created_by,
|
|
)
|
|
|
|
# Assert
|
|
assert config.app_id == app_id
|
|
assert config.provider == "openai"
|
|
assert config.model_id == "gpt-4"
|
|
assert config.created_by == created_by
|
|
|
|
def test_app_model_config_with_configs_json(self):
|
|
"""Test AppModelConfig with JSON configs."""
|
|
# Arrange
|
|
configs = {"temperature": 0.7, "max_tokens": 1000}
|
|
|
|
# Act
|
|
config = AppModelConfig(
|
|
app_id=str(uuid4()),
|
|
provider="openai",
|
|
model_id="gpt-4",
|
|
created_by=str(uuid4()),
|
|
configs=configs,
|
|
)
|
|
|
|
# Assert
|
|
assert config.configs == configs
|
|
|
|
def test_app_model_config_model_dict_property(self):
|
|
"""Test model_dict property."""
|
|
# Arrange
|
|
model_data = {"provider": "openai", "name": "gpt-4"}
|
|
config = AppModelConfig(
|
|
app_id=str(uuid4()),
|
|
provider="openai",
|
|
model_id="gpt-4",
|
|
created_by=str(uuid4()),
|
|
model=json.dumps(model_data),
|
|
)
|
|
|
|
# Act
|
|
result = config.model_dict
|
|
|
|
# Assert
|
|
assert result == model_data
|
|
|
|
def test_app_model_config_model_dict_empty(self):
|
|
"""Test model_dict property when model is None."""
|
|
# Arrange
|
|
config = AppModelConfig(
|
|
app_id=str(uuid4()),
|
|
provider="openai",
|
|
model_id="gpt-4",
|
|
created_by=str(uuid4()),
|
|
model=None,
|
|
)
|
|
|
|
# Act
|
|
result = config.model_dict
|
|
|
|
# Assert
|
|
assert result == {}
|
|
|
|
def test_app_model_config_suggested_questions_list(self):
|
|
"""Test suggested_questions_list property."""
|
|
# Arrange
|
|
questions = ["What can you do?", "How does this work?"]
|
|
config = AppModelConfig(
|
|
app_id=str(uuid4()),
|
|
provider="openai",
|
|
model_id="gpt-4",
|
|
created_by=str(uuid4()),
|
|
suggested_questions=json.dumps(questions),
|
|
)
|
|
|
|
# Act
|
|
result = config.suggested_questions_list
|
|
|
|
# Assert
|
|
assert result == questions
|
|
|
|
def test_app_model_config_annotation_reply_dict_disabled(self):
|
|
"""Test annotation_reply_dict when annotation is disabled."""
|
|
# Arrange
|
|
config = AppModelConfig(
|
|
app_id=str(uuid4()),
|
|
provider="openai",
|
|
model_id="gpt-4",
|
|
created_by=str(uuid4()),
|
|
)
|
|
|
|
# Mock database query to return None
|
|
with patch("models.model.db.session.query") as mock_query:
|
|
mock_query.return_value.where.return_value.first.return_value = None
|
|
|
|
# Act
|
|
result = config.annotation_reply_dict
|
|
|
|
# Assert
|
|
assert result == {"enabled": False}
|
|
|
|
|
|
class TestConversationModel:
|
|
"""Test suite for Conversation model integrity."""
|
|
|
|
def test_conversation_creation_with_required_fields(self):
|
|
"""Test creating a conversation with required fields."""
|
|
# Arrange
|
|
app_id = str(uuid4())
|
|
from_end_user_id = str(uuid4())
|
|
|
|
# Act
|
|
conversation = Conversation(
|
|
app_id=app_id,
|
|
mode=AppMode.CHAT,
|
|
name="Test Conversation",
|
|
status="normal",
|
|
from_source="api",
|
|
from_end_user_id=from_end_user_id,
|
|
)
|
|
|
|
# Assert
|
|
assert conversation.app_id == app_id
|
|
assert conversation.mode == AppMode.CHAT
|
|
assert conversation.name == "Test Conversation"
|
|
assert conversation.status == "normal"
|
|
assert conversation.from_source == "api"
|
|
assert conversation.from_end_user_id == from_end_user_id
|
|
|
|
def test_conversation_with_inputs(self):
|
|
"""Test conversation inputs property."""
|
|
# Arrange
|
|
inputs = {"query": "Hello", "context": "test"}
|
|
conversation = Conversation(
|
|
app_id=str(uuid4()),
|
|
mode=AppMode.CHAT,
|
|
name="Test Conversation",
|
|
status="normal",
|
|
from_source="api",
|
|
from_end_user_id=str(uuid4()),
|
|
)
|
|
conversation._inputs = inputs
|
|
|
|
# Act
|
|
result = conversation.inputs
|
|
|
|
# Assert
|
|
assert result == inputs
|
|
|
|
def test_conversation_inputs_setter(self):
|
|
"""Test conversation inputs setter."""
|
|
# Arrange
|
|
conversation = Conversation(
|
|
app_id=str(uuid4()),
|
|
mode=AppMode.CHAT,
|
|
name="Test Conversation",
|
|
status="normal",
|
|
from_source="api",
|
|
from_end_user_id=str(uuid4()),
|
|
)
|
|
inputs = {"query": "Hello", "context": "test"}
|
|
|
|
# Act
|
|
conversation.inputs = inputs
|
|
|
|
# Assert
|
|
assert conversation._inputs == inputs
|
|
|
|
def test_conversation_summary_or_query_with_summary(self):
|
|
"""Test summary_or_query property when summary exists."""
|
|
# Arrange
|
|
conversation = Conversation(
|
|
app_id=str(uuid4()),
|
|
mode=AppMode.CHAT,
|
|
name="Test Conversation",
|
|
status="normal",
|
|
from_source="api",
|
|
from_end_user_id=str(uuid4()),
|
|
summary="Test summary",
|
|
)
|
|
|
|
# Act
|
|
result = conversation.summary_or_query
|
|
|
|
# Assert
|
|
assert result == "Test summary"
|
|
|
|
def test_conversation_summary_or_query_without_summary(self):
|
|
"""Test summary_or_query property when summary is empty."""
|
|
# Arrange
|
|
conversation = Conversation(
|
|
app_id=str(uuid4()),
|
|
mode=AppMode.CHAT,
|
|
name="Test Conversation",
|
|
status="normal",
|
|
from_source="api",
|
|
from_end_user_id=str(uuid4()),
|
|
summary=None,
|
|
)
|
|
|
|
# Mock first_message to return a message with query
|
|
mock_message = MagicMock()
|
|
mock_message.query = "First message query"
|
|
with patch.object(Conversation, "first_message", new_callable=lambda: property(lambda self: mock_message)):
|
|
# Act
|
|
result = conversation.summary_or_query
|
|
|
|
# Assert
|
|
assert result == "First message query"
|
|
|
|
def test_conversation_in_debug_mode(self):
|
|
"""Test in_debug_mode property."""
|
|
# Arrange
|
|
conversation = Conversation(
|
|
app_id=str(uuid4()),
|
|
mode=AppMode.CHAT,
|
|
name="Test Conversation",
|
|
status="normal",
|
|
from_source="api",
|
|
from_end_user_id=str(uuid4()),
|
|
override_model_configs='{"model": "gpt-4"}',
|
|
)
|
|
|
|
# Act
|
|
result = conversation.in_debug_mode
|
|
|
|
# Assert
|
|
assert result is True
|
|
|
|
def test_conversation_to_dict_serialization(self):
|
|
"""Test conversation to_dict method."""
|
|
# Arrange
|
|
app_id = str(uuid4())
|
|
from_end_user_id = str(uuid4())
|
|
conversation = Conversation(
|
|
app_id=app_id,
|
|
mode=AppMode.CHAT,
|
|
name="Test Conversation",
|
|
status="normal",
|
|
from_source="api",
|
|
from_end_user_id=from_end_user_id,
|
|
dialogue_count=5,
|
|
)
|
|
conversation.id = str(uuid4())
|
|
conversation._inputs = {"query": "test"}
|
|
|
|
# Act
|
|
result = conversation.to_dict()
|
|
|
|
# Assert
|
|
assert result["id"] == conversation.id
|
|
assert result["app_id"] == app_id
|
|
assert result["mode"] == AppMode.CHAT
|
|
assert result["name"] == "Test Conversation"
|
|
assert result["status"] == "normal"
|
|
assert result["from_source"] == "api"
|
|
assert result["from_end_user_id"] == from_end_user_id
|
|
assert result["dialogue_count"] == 5
|
|
assert result["inputs"] == {"query": "test"}
|
|
|
|
|
|
class TestMessageModel:
|
|
"""Test suite for Message model and App-Message relationships."""
|
|
|
|
def test_message_creation_with_required_fields(self):
|
|
"""Test creating a message with required fields."""
|
|
# Arrange
|
|
app_id = str(uuid4())
|
|
conversation_id = str(uuid4())
|
|
|
|
# Act
|
|
message = Message(
|
|
app_id=app_id,
|
|
conversation_id=conversation_id,
|
|
query="What is AI?",
|
|
message={"role": "user", "content": "What is AI?"},
|
|
answer="AI stands for Artificial Intelligence.",
|
|
message_unit_price=Decimal("0.0001"),
|
|
answer_unit_price=Decimal("0.0002"),
|
|
currency="USD",
|
|
from_source="api",
|
|
)
|
|
|
|
# Assert
|
|
assert message.app_id == app_id
|
|
assert message.conversation_id == conversation_id
|
|
assert message.query == "What is AI?"
|
|
assert message.answer == "AI stands for Artificial Intelligence."
|
|
assert message.currency == "USD"
|
|
assert message.from_source == "api"
|
|
|
|
def test_message_with_inputs(self):
|
|
"""Test message inputs property."""
|
|
# Arrange
|
|
inputs = {"query": "Hello", "context": "test"}
|
|
message = Message(
|
|
app_id=str(uuid4()),
|
|
conversation_id=str(uuid4()),
|
|
query="Test query",
|
|
message={"role": "user", "content": "Test"},
|
|
answer="Test answer",
|
|
message_unit_price=Decimal("0.0001"),
|
|
answer_unit_price=Decimal("0.0002"),
|
|
currency="USD",
|
|
from_source="api",
|
|
)
|
|
message._inputs = inputs
|
|
|
|
# Act
|
|
result = message.inputs
|
|
|
|
# Assert
|
|
assert result == inputs
|
|
|
|
def test_message_inputs_setter(self):
|
|
"""Test message inputs setter."""
|
|
# Arrange
|
|
message = Message(
|
|
app_id=str(uuid4()),
|
|
conversation_id=str(uuid4()),
|
|
query="Test query",
|
|
message={"role": "user", "content": "Test"},
|
|
answer="Test answer",
|
|
message_unit_price=Decimal("0.0001"),
|
|
answer_unit_price=Decimal("0.0002"),
|
|
currency="USD",
|
|
from_source="api",
|
|
)
|
|
inputs = {"query": "Hello", "context": "test"}
|
|
|
|
# Act
|
|
message.inputs = inputs
|
|
|
|
# Assert
|
|
assert message._inputs == inputs
|
|
|
|
def test_message_in_debug_mode(self):
|
|
"""Test message in_debug_mode property."""
|
|
# Arrange
|
|
message = Message(
|
|
app_id=str(uuid4()),
|
|
conversation_id=str(uuid4()),
|
|
query="Test query",
|
|
message={"role": "user", "content": "Test"},
|
|
answer="Test answer",
|
|
message_unit_price=Decimal("0.0001"),
|
|
answer_unit_price=Decimal("0.0002"),
|
|
currency="USD",
|
|
from_source="api",
|
|
override_model_configs='{"model": "gpt-4"}',
|
|
)
|
|
|
|
# Act
|
|
result = message.in_debug_mode
|
|
|
|
# Assert
|
|
assert result is True
|
|
|
|
def test_message_metadata_dict_property(self):
|
|
"""Test message_metadata_dict property."""
|
|
# Arrange
|
|
metadata = {"retriever_resources": ["doc1", "doc2"], "usage": {"tokens": 100}}
|
|
message = Message(
|
|
app_id=str(uuid4()),
|
|
conversation_id=str(uuid4()),
|
|
query="Test query",
|
|
message={"role": "user", "content": "Test"},
|
|
answer="Test answer",
|
|
message_unit_price=Decimal("0.0001"),
|
|
answer_unit_price=Decimal("0.0002"),
|
|
currency="USD",
|
|
from_source="api",
|
|
message_metadata=json.dumps(metadata),
|
|
)
|
|
|
|
# Act
|
|
result = message.message_metadata_dict
|
|
|
|
# Assert
|
|
assert result == metadata
|
|
|
|
def test_message_metadata_dict_empty(self):
|
|
"""Test message_metadata_dict when metadata is None."""
|
|
# Arrange
|
|
message = Message(
|
|
app_id=str(uuid4()),
|
|
conversation_id=str(uuid4()),
|
|
query="Test query",
|
|
message={"role": "user", "content": "Test"},
|
|
answer="Test answer",
|
|
message_unit_price=Decimal("0.0001"),
|
|
answer_unit_price=Decimal("0.0002"),
|
|
currency="USD",
|
|
from_source="api",
|
|
message_metadata=None,
|
|
)
|
|
|
|
# Act
|
|
result = message.message_metadata_dict
|
|
|
|
# Assert
|
|
assert result == {}
|
|
|
|
def test_message_to_dict_serialization(self):
|
|
"""Test message to_dict method."""
|
|
# Arrange
|
|
app_id = str(uuid4())
|
|
conversation_id = str(uuid4())
|
|
now = datetime.now(UTC)
|
|
|
|
message = Message(
|
|
app_id=app_id,
|
|
conversation_id=conversation_id,
|
|
query="Test query",
|
|
message={"role": "user", "content": "Test"},
|
|
answer="Test answer",
|
|
message_unit_price=Decimal("0.0001"),
|
|
answer_unit_price=Decimal("0.0002"),
|
|
total_price=Decimal("0.0003"),
|
|
currency="USD",
|
|
from_source="api",
|
|
status="normal",
|
|
)
|
|
message.id = str(uuid4())
|
|
message._inputs = {"query": "test"}
|
|
message.created_at = now
|
|
message.updated_at = now
|
|
|
|
# Act
|
|
result = message.to_dict()
|
|
|
|
# Assert
|
|
assert result["id"] == message.id
|
|
assert result["app_id"] == app_id
|
|
assert result["conversation_id"] == conversation_id
|
|
assert result["query"] == "Test query"
|
|
assert result["answer"] == "Test answer"
|
|
assert result["status"] == "normal"
|
|
assert result["from_source"] == "api"
|
|
assert result["inputs"] == {"query": "test"}
|
|
assert "created_at" in result
|
|
assert "updated_at" in result
|
|
|
|
def test_message_from_dict_deserialization(self):
|
|
"""Test message from_dict method."""
|
|
# Arrange
|
|
message_id = str(uuid4())
|
|
app_id = str(uuid4())
|
|
conversation_id = str(uuid4())
|
|
data = {
|
|
"id": message_id,
|
|
"app_id": app_id,
|
|
"conversation_id": conversation_id,
|
|
"model_id": "gpt-4",
|
|
"inputs": {"query": "test"},
|
|
"query": "Test query",
|
|
"message": {"role": "user", "content": "Test"},
|
|
"answer": "Test answer",
|
|
"total_price": Decimal("0.0003"),
|
|
"status": "normal",
|
|
"error": None,
|
|
"message_metadata": {"usage": {"tokens": 100}},
|
|
"from_source": "api",
|
|
"from_end_user_id": None,
|
|
"from_account_id": None,
|
|
"created_at": "2024-01-01T00:00:00",
|
|
"updated_at": "2024-01-01T00:00:00",
|
|
"agent_based": False,
|
|
"workflow_run_id": None,
|
|
}
|
|
|
|
# Act
|
|
message = Message.from_dict(data)
|
|
|
|
# Assert
|
|
assert message.id == message_id
|
|
assert message.app_id == app_id
|
|
assert message.conversation_id == conversation_id
|
|
assert message.query == "Test query"
|
|
assert message.answer == "Test answer"
|
|
|
|
|
|
class TestMessageAnnotation:
|
|
"""Test suite for MessageAnnotation and annotation relationships."""
|
|
|
|
def test_message_annotation_creation(self):
|
|
"""Test creating a message annotation."""
|
|
# Arrange
|
|
app_id = str(uuid4())
|
|
conversation_id = str(uuid4())
|
|
message_id = str(uuid4())
|
|
account_id = str(uuid4())
|
|
|
|
# Act
|
|
annotation = MessageAnnotation(
|
|
app_id=app_id,
|
|
conversation_id=conversation_id,
|
|
message_id=message_id,
|
|
question="What is AI?",
|
|
content="AI stands for Artificial Intelligence.",
|
|
account_id=account_id,
|
|
)
|
|
|
|
# Assert
|
|
assert annotation.app_id == app_id
|
|
assert annotation.conversation_id == conversation_id
|
|
assert annotation.message_id == message_id
|
|
assert annotation.question == "What is AI?"
|
|
assert annotation.content == "AI stands for Artificial Intelligence."
|
|
assert annotation.account_id == account_id
|
|
|
|
def test_message_annotation_without_message_id(self):
|
|
"""Test creating annotation without message_id."""
|
|
# Arrange
|
|
app_id = str(uuid4())
|
|
account_id = str(uuid4())
|
|
|
|
# Act
|
|
annotation = MessageAnnotation(
|
|
app_id=app_id,
|
|
question="What is AI?",
|
|
content="AI stands for Artificial Intelligence.",
|
|
account_id=account_id,
|
|
)
|
|
|
|
# Assert
|
|
assert annotation.app_id == app_id
|
|
assert annotation.message_id is None
|
|
assert annotation.conversation_id is None
|
|
assert annotation.question == "What is AI?"
|
|
assert annotation.content == "AI stands for Artificial Intelligence."
|
|
|
|
def test_message_annotation_hit_count_default(self):
|
|
"""Test annotation hit_count default value."""
|
|
# Arrange
|
|
annotation = MessageAnnotation(
|
|
app_id=str(uuid4()),
|
|
question="Test question",
|
|
content="Test content",
|
|
account_id=str(uuid4()),
|
|
)
|
|
|
|
# Act & Assert - default value is set by database
|
|
# Model instantiation doesn't set server defaults
|
|
assert hasattr(annotation, "hit_count")
|
|
|
|
|
|
class TestAppAnnotationSetting:
|
|
"""Test suite for AppAnnotationSetting model."""
|
|
|
|
def test_app_annotation_setting_creation(self):
|
|
"""Test creating an app annotation setting."""
|
|
# Arrange
|
|
app_id = str(uuid4())
|
|
collection_binding_id = str(uuid4())
|
|
created_user_id = str(uuid4())
|
|
updated_user_id = str(uuid4())
|
|
|
|
# Act
|
|
setting = AppAnnotationSetting(
|
|
app_id=app_id,
|
|
score_threshold=0.8,
|
|
collection_binding_id=collection_binding_id,
|
|
created_user_id=created_user_id,
|
|
updated_user_id=updated_user_id,
|
|
)
|
|
|
|
# Assert
|
|
assert setting.app_id == app_id
|
|
assert setting.score_threshold == 0.8
|
|
assert setting.collection_binding_id == collection_binding_id
|
|
assert setting.created_user_id == created_user_id
|
|
assert setting.updated_user_id == updated_user_id
|
|
|
|
def test_app_annotation_setting_score_threshold_validation(self):
|
|
"""Test score threshold values."""
|
|
# Arrange & Act
|
|
setting_high = AppAnnotationSetting(
|
|
app_id=str(uuid4()),
|
|
score_threshold=0.95,
|
|
collection_binding_id=str(uuid4()),
|
|
created_user_id=str(uuid4()),
|
|
updated_user_id=str(uuid4()),
|
|
)
|
|
setting_low = AppAnnotationSetting(
|
|
app_id=str(uuid4()),
|
|
score_threshold=0.5,
|
|
collection_binding_id=str(uuid4()),
|
|
created_user_id=str(uuid4()),
|
|
updated_user_id=str(uuid4()),
|
|
)
|
|
|
|
# Assert
|
|
assert setting_high.score_threshold == 0.95
|
|
assert setting_low.score_threshold == 0.5
|
|
|
|
|
|
class TestAppAnnotationHitHistory:
|
|
"""Test suite for AppAnnotationHitHistory model."""
|
|
|
|
def test_app_annotation_hit_history_creation(self):
|
|
"""Test creating an annotation hit history."""
|
|
# Arrange
|
|
app_id = str(uuid4())
|
|
annotation_id = str(uuid4())
|
|
message_id = str(uuid4())
|
|
account_id = str(uuid4())
|
|
|
|
# Act
|
|
history = AppAnnotationHitHistory(
|
|
app_id=app_id,
|
|
annotation_id=annotation_id,
|
|
source="api",
|
|
question="What is AI?",
|
|
account_id=account_id,
|
|
score=0.95,
|
|
message_id=message_id,
|
|
annotation_question="What is AI?",
|
|
annotation_content="AI stands for Artificial Intelligence.",
|
|
)
|
|
|
|
# Assert
|
|
assert history.app_id == app_id
|
|
assert history.annotation_id == annotation_id
|
|
assert history.source == "api"
|
|
assert history.question == "What is AI?"
|
|
assert history.account_id == account_id
|
|
assert history.score == 0.95
|
|
assert history.message_id == message_id
|
|
assert history.annotation_question == "What is AI?"
|
|
assert history.annotation_content == "AI stands for Artificial Intelligence."
|
|
|
|
def test_app_annotation_hit_history_score_values(self):
|
|
"""Test annotation hit history with different score values."""
|
|
# Arrange & Act
|
|
history_high = AppAnnotationHitHistory(
|
|
app_id=str(uuid4()),
|
|
annotation_id=str(uuid4()),
|
|
source="api",
|
|
question="Test",
|
|
account_id=str(uuid4()),
|
|
score=0.99,
|
|
message_id=str(uuid4()),
|
|
annotation_question="Test",
|
|
annotation_content="Content",
|
|
)
|
|
history_low = AppAnnotationHitHistory(
|
|
app_id=str(uuid4()),
|
|
annotation_id=str(uuid4()),
|
|
source="api",
|
|
question="Test",
|
|
account_id=str(uuid4()),
|
|
score=0.6,
|
|
message_id=str(uuid4()),
|
|
annotation_question="Test",
|
|
annotation_content="Content",
|
|
)
|
|
|
|
# Assert
|
|
assert history_high.score == 0.99
|
|
assert history_low.score == 0.6
|
|
|
|
|
|
class TestSiteModel:
|
|
"""Test suite for Site model."""
|
|
|
|
def test_site_creation_with_required_fields(self):
|
|
"""Test creating a site with required fields."""
|
|
# Arrange
|
|
app_id = str(uuid4())
|
|
|
|
# Act
|
|
site = Site(
|
|
app_id=app_id,
|
|
title="Test Site",
|
|
default_language="en-US",
|
|
customize_token_strategy="uuid",
|
|
)
|
|
|
|
# Assert
|
|
assert site.app_id == app_id
|
|
assert site.title == "Test Site"
|
|
assert site.default_language == "en-US"
|
|
assert site.customize_token_strategy == "uuid"
|
|
|
|
def test_site_creation_with_optional_fields(self):
|
|
"""Test creating a site with optional fields."""
|
|
# Arrange & Act
|
|
site = Site(
|
|
app_id=str(uuid4()),
|
|
title="Test Site",
|
|
default_language="en-US",
|
|
customize_token_strategy="uuid",
|
|
icon_type=IconType.EMOJI,
|
|
icon="🌐",
|
|
icon_background="#0066CC",
|
|
description="Test site description",
|
|
copyright="© 2024 Test",
|
|
privacy_policy="https://example.com/privacy",
|
|
)
|
|
|
|
# Assert
|
|
assert site.icon_type == IconType.EMOJI
|
|
assert site.icon == "🌐"
|
|
assert site.icon_background == "#0066CC"
|
|
assert site.description == "Test site description"
|
|
assert site.copyright == "© 2024 Test"
|
|
assert site.privacy_policy == "https://example.com/privacy"
|
|
|
|
def test_site_custom_disclaimer_setter(self):
|
|
"""Test site custom_disclaimer setter."""
|
|
# Arrange
|
|
site = Site(
|
|
app_id=str(uuid4()),
|
|
title="Test Site",
|
|
default_language="en-US",
|
|
customize_token_strategy="uuid",
|
|
)
|
|
|
|
# Act
|
|
site.custom_disclaimer = "This is a test disclaimer"
|
|
|
|
# Assert
|
|
assert site.custom_disclaimer == "This is a test disclaimer"
|
|
|
|
def test_site_custom_disclaimer_exceeds_limit(self):
|
|
"""Test site custom_disclaimer with excessive length."""
|
|
# Arrange
|
|
site = Site(
|
|
app_id=str(uuid4()),
|
|
title="Test Site",
|
|
default_language="en-US",
|
|
customize_token_strategy="uuid",
|
|
)
|
|
long_disclaimer = "x" * 513 # Exceeds 512 character limit
|
|
|
|
# Act & Assert
|
|
with pytest.raises(ValueError, match="Custom disclaimer cannot exceed 512 characters"):
|
|
site.custom_disclaimer = long_disclaimer
|
|
|
|
def test_site_generate_code(self):
|
|
"""Test Site.generate_code static method."""
|
|
# Mock database query to return 0 (no existing codes)
|
|
with patch("models.model.db.session.query") as mock_query:
|
|
mock_query.return_value.where.return_value.count.return_value = 0
|
|
|
|
# Act
|
|
code = Site.generate_code(8)
|
|
|
|
# Assert
|
|
assert isinstance(code, str)
|
|
assert len(code) == 8
|
|
|
|
|
|
class TestModelIntegration:
|
|
"""Test suite for model integration scenarios."""
|
|
|
|
def test_complete_app_conversation_message_hierarchy(self):
|
|
"""Test complete hierarchy from app to message."""
|
|
# Arrange
|
|
tenant_id = str(uuid4())
|
|
app_id = str(uuid4())
|
|
conversation_id = str(uuid4())
|
|
message_id = str(uuid4())
|
|
created_by = str(uuid4())
|
|
|
|
# Create app
|
|
app = App(
|
|
tenant_id=tenant_id,
|
|
name="Test App",
|
|
mode=AppMode.CHAT,
|
|
enable_site=True,
|
|
enable_api=True,
|
|
created_by=created_by,
|
|
)
|
|
app.id = app_id
|
|
|
|
# Create conversation
|
|
conversation = Conversation(
|
|
app_id=app_id,
|
|
mode=AppMode.CHAT,
|
|
name="Test Conversation",
|
|
status="normal",
|
|
from_source="api",
|
|
from_end_user_id=str(uuid4()),
|
|
)
|
|
conversation.id = conversation_id
|
|
|
|
# Create message
|
|
message = Message(
|
|
app_id=app_id,
|
|
conversation_id=conversation_id,
|
|
query="Test query",
|
|
message={"role": "user", "content": "Test"},
|
|
answer="Test answer",
|
|
message_unit_price=Decimal("0.0001"),
|
|
answer_unit_price=Decimal("0.0002"),
|
|
currency="USD",
|
|
from_source="api",
|
|
)
|
|
message.id = message_id
|
|
|
|
# Assert
|
|
assert app.id == app_id
|
|
assert conversation.app_id == app_id
|
|
assert message.app_id == app_id
|
|
assert message.conversation_id == conversation_id
|
|
assert app.mode == AppMode.CHAT
|
|
assert conversation.mode == AppMode.CHAT
|
|
|
|
def test_app_with_annotation_setting(self):
|
|
"""Test app with annotation setting."""
|
|
# Arrange
|
|
app_id = str(uuid4())
|
|
collection_binding_id = str(uuid4())
|
|
created_user_id = str(uuid4())
|
|
|
|
# Create app
|
|
app = App(
|
|
tenant_id=str(uuid4()),
|
|
name="Test App",
|
|
mode=AppMode.CHAT,
|
|
enable_site=True,
|
|
enable_api=True,
|
|
created_by=created_user_id,
|
|
)
|
|
app.id = app_id
|
|
|
|
# Create annotation setting
|
|
setting = AppAnnotationSetting(
|
|
app_id=app_id,
|
|
score_threshold=0.85,
|
|
collection_binding_id=collection_binding_id,
|
|
created_user_id=created_user_id,
|
|
updated_user_id=created_user_id,
|
|
)
|
|
|
|
# Assert
|
|
assert setting.app_id == app.id
|
|
assert setting.score_threshold == 0.85
|
|
|
|
def test_message_with_annotation(self):
|
|
"""Test message with annotation."""
|
|
# Arrange
|
|
app_id = str(uuid4())
|
|
conversation_id = str(uuid4())
|
|
message_id = str(uuid4())
|
|
account_id = str(uuid4())
|
|
|
|
# Create message
|
|
message = Message(
|
|
app_id=app_id,
|
|
conversation_id=conversation_id,
|
|
query="What is AI?",
|
|
message={"role": "user", "content": "What is AI?"},
|
|
answer="AI stands for Artificial Intelligence.",
|
|
message_unit_price=Decimal("0.0001"),
|
|
answer_unit_price=Decimal("0.0002"),
|
|
currency="USD",
|
|
from_source="api",
|
|
)
|
|
message.id = message_id
|
|
|
|
# Create annotation
|
|
annotation = MessageAnnotation(
|
|
app_id=app_id,
|
|
conversation_id=conversation_id,
|
|
message_id=message_id,
|
|
question="What is AI?",
|
|
content="AI stands for Artificial Intelligence.",
|
|
account_id=account_id,
|
|
)
|
|
|
|
# Assert
|
|
assert annotation.app_id == message.app_id
|
|
assert annotation.conversation_id == message.conversation_id
|
|
assert annotation.message_id == message.id
|
|
|
|
def test_annotation_hit_history_tracking(self):
|
|
"""Test annotation hit history tracking."""
|
|
# Arrange
|
|
app_id = str(uuid4())
|
|
annotation_id = str(uuid4())
|
|
message_id = str(uuid4())
|
|
account_id = str(uuid4())
|
|
|
|
# Create annotation
|
|
annotation = MessageAnnotation(
|
|
app_id=app_id,
|
|
question="What is AI?",
|
|
content="AI stands for Artificial Intelligence.",
|
|
account_id=account_id,
|
|
)
|
|
annotation.id = annotation_id
|
|
|
|
# Create hit history
|
|
history = AppAnnotationHitHistory(
|
|
app_id=app_id,
|
|
annotation_id=annotation_id,
|
|
source="api",
|
|
question="What is AI?",
|
|
account_id=account_id,
|
|
score=0.92,
|
|
message_id=message_id,
|
|
annotation_question="What is AI?",
|
|
annotation_content="AI stands for Artificial Intelligence.",
|
|
)
|
|
|
|
# Assert
|
|
assert history.app_id == annotation.app_id
|
|
assert history.annotation_id == annotation.id
|
|
assert history.score == 0.92
|
|
|
|
def test_app_with_site(self):
|
|
"""Test app with site."""
|
|
# Arrange
|
|
app_id = str(uuid4())
|
|
|
|
# Create app
|
|
app = App(
|
|
tenant_id=str(uuid4()),
|
|
name="Test App",
|
|
mode=AppMode.CHAT,
|
|
enable_site=True,
|
|
enable_api=True,
|
|
created_by=str(uuid4()),
|
|
)
|
|
app.id = app_id
|
|
|
|
# Create site
|
|
site = Site(
|
|
app_id=app_id,
|
|
title="Test Site",
|
|
default_language="en-US",
|
|
customize_token_strategy="uuid",
|
|
)
|
|
|
|
# Assert
|
|
assert site.app_id == app.id
|
|
assert app.enable_site is True
|