Skip to content

Commit f7bf975

Browse files
Copilotpontemonti
andcommitted
Add unit tests for chat history models and operation result classes
Co-authored-by: pontemonti <7850950+pontemonti@users.noreply.github.com>
1 parent 70b54d2 commit f7bf975

6 files changed

Lines changed: 356 additions & 0 deletions

File tree

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# Copyright (c) Microsoft. All rights reserved.
2+
3+
"""Unit tests for OperationError class."""
4+
5+
import pytest
6+
from microsoft_agents_a365.runtime import OperationError
7+
8+
9+
class TestOperationError:
10+
"""Tests for OperationError class."""
11+
12+
def test_operation_error_can_be_instantiated(self):
13+
"""Test that OperationError can be instantiated with an exception."""
14+
# Arrange
15+
exception = Exception("Test error")
16+
17+
# Act
18+
error = OperationError(exception)
19+
20+
# Assert
21+
assert error is not None
22+
assert error.exception == exception
23+
assert error.message == "Test error"
24+
25+
def test_operation_error_requires_exception(self):
26+
"""Test that OperationError requires an exception."""
27+
# Act & Assert
28+
with pytest.raises(ValueError, match="exception cannot be None"):
29+
OperationError(None)
30+
31+
def test_operation_error_string_representation(self):
32+
"""Test that OperationError has correct string representation."""
33+
# Arrange
34+
exception = Exception("Test error message")
35+
error = OperationError(exception)
36+
37+
# Act
38+
result = str(error)
39+
40+
# Assert
41+
assert "Test error message" in result
42+
43+
def test_operation_error_with_different_exception_types(self):
44+
"""Test that OperationError works with different exception types."""
45+
# Arrange & Act
46+
value_error = OperationError(ValueError("Invalid value"))
47+
type_error = OperationError(TypeError("Invalid type"))
48+
runtime_error = OperationError(RuntimeError("Runtime issue"))
49+
50+
# Assert
51+
assert value_error.message == "Invalid value"
52+
assert type_error.message == "Invalid type"
53+
assert runtime_error.message == "Runtime issue"
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
# Copyright (c) Microsoft. All rights reserved.
2+
3+
"""Unit tests for OperationResult class."""
4+
5+
from microsoft_agents_a365.runtime import OperationError, OperationResult
6+
7+
8+
class TestOperationResult:
9+
"""Tests for OperationResult class."""
10+
11+
def test_operation_result_success(self):
12+
"""Test that OperationResult.success() returns a successful result."""
13+
# Act
14+
result = OperationResult.success()
15+
16+
# Assert
17+
assert result is not None
18+
assert result.succeeded is True
19+
assert len(result.errors) == 0
20+
21+
def test_operation_result_success_returns_singleton(self):
22+
"""Test that OperationResult.success() returns the same instance."""
23+
# Act
24+
result1 = OperationResult.success()
25+
result2 = OperationResult.success()
26+
27+
# Assert
28+
assert result1 is result2
29+
30+
def test_operation_result_failed_with_no_errors(self):
31+
"""Test that OperationResult.failed() without errors returns a failed result."""
32+
# Act
33+
result = OperationResult.failed()
34+
35+
# Assert
36+
assert result is not None
37+
assert result.succeeded is False
38+
assert len(result.errors) == 0
39+
40+
def test_operation_result_failed_with_single_error(self):
41+
"""Test that OperationResult.failed() with a single error works correctly."""
42+
# Arrange
43+
exception = Exception("Test error")
44+
error = OperationError(exception)
45+
46+
# Act
47+
result = OperationResult.failed(error)
48+
49+
# Assert
50+
assert result is not None
51+
assert result.succeeded is False
52+
assert len(result.errors) == 1
53+
assert result.errors[0] == error
54+
55+
def test_operation_result_failed_with_multiple_errors(self):
56+
"""Test that OperationResult.failed() with multiple errors works correctly."""
57+
# Arrange
58+
error1 = OperationError(Exception("Error 1"))
59+
error2 = OperationError(Exception("Error 2"))
60+
error3 = OperationError(Exception("Error 3"))
61+
62+
# Act
63+
result = OperationResult.failed(error1, error2, error3)
64+
65+
# Assert
66+
assert result is not None
67+
assert result.succeeded is False
68+
assert len(result.errors) == 3
69+
assert result.errors[0] == error1
70+
assert result.errors[1] == error2
71+
assert result.errors[2] == error3
72+
73+
def test_operation_result_success_string_representation(self):
74+
"""Test that successful OperationResult has correct string representation."""
75+
# Act
76+
result = OperationResult.success()
77+
78+
# Assert
79+
assert str(result) == "Succeeded"
80+
81+
def test_operation_result_failed_string_representation_no_errors(self):
82+
"""Test that failed OperationResult without errors has correct string representation."""
83+
# Act
84+
result = OperationResult.failed()
85+
86+
# Assert
87+
assert str(result) == "Failed : "
88+
89+
def test_operation_result_failed_string_representation_with_errors(self):
90+
"""Test that failed OperationResult with errors has correct string representation."""
91+
# Arrange
92+
error1 = OperationError(Exception("Error 1"))
93+
error2 = OperationError(Exception("Error 2"))
94+
95+
# Act
96+
result = OperationResult.failed(error1, error2)
97+
98+
# Assert
99+
result_str = str(result)
100+
assert "Failed" in result_str
101+
assert "Error 1" in result_str
102+
assert "Error 2" in result_str

tests/tooling/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Copyright (c) Microsoft. All rights reserved.

tests/tooling/models/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Copyright (c) Microsoft. All rights reserved.
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# Copyright (c) Microsoft. All rights reserved.
2+
3+
"""Unit tests for ChatHistoryMessage class."""
4+
5+
from datetime import datetime, timezone
6+
7+
import pytest
8+
from microsoft_agents_a365.tooling.models import ChatHistoryMessage
9+
10+
11+
class TestChatHistoryMessage:
12+
"""Tests for ChatHistoryMessage class."""
13+
14+
def test_chat_history_message_can_be_instantiated(self):
15+
"""Test that ChatHistoryMessage can be instantiated with valid parameters."""
16+
# Arrange & Act
17+
timestamp = datetime.now(timezone.utc)
18+
message = ChatHistoryMessage("msg-123", "user", "Hello, world!", timestamp)
19+
20+
# Assert
21+
assert message is not None
22+
assert message.id == "msg-123"
23+
assert message.role == "user"
24+
assert message.content == "Hello, world!"
25+
assert message.timestamp == timestamp
26+
27+
def test_chat_history_message_to_dict(self):
28+
"""Test that ChatHistoryMessage converts to dictionary correctly."""
29+
# Arrange
30+
timestamp = datetime(2024, 1, 15, 10, 30, 0, tzinfo=timezone.utc)
31+
message = ChatHistoryMessage("msg-456", "assistant", "How can I help you?", timestamp)
32+
33+
# Act
34+
result = message.to_dict()
35+
36+
# Assert
37+
assert result["id"] == "msg-456"
38+
assert result["role"] == "assistant"
39+
assert result["content"] == "How can I help you?"
40+
assert result["timestamp"] == "2024-01-15T10:30:00+00:00"
41+
42+
def test_chat_history_message_requires_non_empty_id(self):
43+
"""Test that ChatHistoryMessage requires a non-empty id."""
44+
# Arrange
45+
timestamp = datetime.now(timezone.utc)
46+
47+
# Act & Assert
48+
with pytest.raises(ValueError, match="id cannot be empty"):
49+
ChatHistoryMessage("", "user", "Test content", timestamp)
50+
51+
def test_chat_history_message_requires_non_empty_role(self):
52+
"""Test that ChatHistoryMessage requires a non-empty role."""
53+
# Arrange
54+
timestamp = datetime.now(timezone.utc)
55+
56+
# Act & Assert
57+
with pytest.raises(ValueError, match="role cannot be empty"):
58+
ChatHistoryMessage("msg-001", "", "Test content", timestamp)
59+
60+
def test_chat_history_message_requires_non_empty_content(self):
61+
"""Test that ChatHistoryMessage requires a non-empty content."""
62+
# Arrange
63+
timestamp = datetime.now(timezone.utc)
64+
65+
# Act & Assert
66+
with pytest.raises(ValueError, match="content cannot be empty"):
67+
ChatHistoryMessage("msg-001", "user", "", timestamp)
68+
69+
def test_chat_history_message_requires_timestamp(self):
70+
"""Test that ChatHistoryMessage requires a timestamp."""
71+
# Act & Assert
72+
with pytest.raises(ValueError, match="timestamp cannot be empty"):
73+
ChatHistoryMessage("msg-001", "user", "Test content", None)
74+
75+
def test_chat_history_message_supports_system_role(self):
76+
"""Test that ChatHistoryMessage supports system role."""
77+
# Arrange & Act
78+
timestamp = datetime.now(timezone.utc)
79+
message = ChatHistoryMessage("sys-001", "system", "You are a helpful assistant.", timestamp)
80+
81+
# Assert
82+
assert message.role == "system"
83+
84+
def test_chat_history_message_preserves_timestamp_precision(self):
85+
"""Test that ChatHistoryMessage preserves timestamp precision."""
86+
# Arrange
87+
timestamp = datetime(2024, 1, 15, 10, 30, 45, 123000, tzinfo=timezone.utc)
88+
message = ChatHistoryMessage("msg-001", "user", "Test", timestamp)
89+
90+
# Act
91+
message_dict = message.to_dict()
92+
93+
# Assert
94+
assert message.timestamp == timestamp
95+
assert "2024-01-15T10:30:45.123000" in message_dict["timestamp"]
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
# Copyright (c) Microsoft. All rights reserved.
2+
3+
"""Unit tests for ChatMessageRequest class."""
4+
5+
from datetime import datetime, timezone
6+
7+
import pytest
8+
from microsoft_agents_a365.tooling.models import ChatHistoryMessage, ChatMessageRequest
9+
10+
11+
class TestChatMessageRequest:
12+
"""Tests for ChatMessageRequest class."""
13+
14+
def test_chat_message_request_can_be_instantiated(self):
15+
"""Test that ChatMessageRequest can be instantiated with valid parameters."""
16+
# Arrange
17+
timestamp = datetime.now(timezone.utc)
18+
message1 = ChatHistoryMessage("msg-1", "user", "Hello", timestamp)
19+
message2 = ChatHistoryMessage("msg-2", "assistant", "Hi there!", timestamp)
20+
chat_history = [message1, message2]
21+
22+
# Act
23+
request = ChatMessageRequest("conv-123", "msg-456", "How are you?", chat_history)
24+
25+
# Assert
26+
assert request is not None
27+
assert request.conversation_id == "conv-123"
28+
assert request.message_id == "msg-456"
29+
assert request.user_message == "How are you?"
30+
assert request.chat_history == chat_history
31+
32+
def test_chat_message_request_to_dict(self):
33+
"""Test that ChatMessageRequest converts to dictionary correctly."""
34+
# Arrange
35+
timestamp = datetime(2024, 1, 15, 10, 30, 0, tzinfo=timezone.utc)
36+
message = ChatHistoryMessage("msg-1", "user", "Hello", timestamp)
37+
request = ChatMessageRequest("conv-123", "msg-456", "How are you?", [message])
38+
39+
# Act
40+
result = request.to_dict()
41+
42+
# Assert
43+
assert result["conversationId"] == "conv-123"
44+
assert result["messageId"] == "msg-456"
45+
assert result["userMessage"] == "How are you?"
46+
assert len(result["chatHistory"]) == 1
47+
assert result["chatHistory"][0]["id"] == "msg-1"
48+
assert result["chatHistory"][0]["role"] == "user"
49+
assert result["chatHistory"][0]["content"] == "Hello"
50+
51+
def test_chat_message_request_requires_non_empty_conversation_id(self):
52+
"""Test that ChatMessageRequest requires a non-empty conversation_id."""
53+
# Arrange
54+
timestamp = datetime.now(timezone.utc)
55+
message = ChatHistoryMessage("msg-1", "user", "Hello", timestamp)
56+
57+
# Act & Assert
58+
with pytest.raises(ValueError, match="conversation_id cannot be empty"):
59+
ChatMessageRequest("", "msg-456", "How are you?", [message])
60+
61+
def test_chat_message_request_requires_non_empty_message_id(self):
62+
"""Test that ChatMessageRequest requires a non-empty message_id."""
63+
# Arrange
64+
timestamp = datetime.now(timezone.utc)
65+
message = ChatHistoryMessage("msg-1", "user", "Hello", timestamp)
66+
67+
# Act & Assert
68+
with pytest.raises(ValueError, match="message_id cannot be empty"):
69+
ChatMessageRequest("conv-123", "", "How are you?", [message])
70+
71+
def test_chat_message_request_requires_non_empty_user_message(self):
72+
"""Test that ChatMessageRequest requires a non-empty user_message."""
73+
# Arrange
74+
timestamp = datetime.now(timezone.utc)
75+
message = ChatHistoryMessage("msg-1", "user", "Hello", timestamp)
76+
77+
# Act & Assert
78+
with pytest.raises(ValueError, match="user_message cannot be empty"):
79+
ChatMessageRequest("conv-123", "msg-456", "", [message])
80+
81+
def test_chat_message_request_requires_non_empty_chat_history(self):
82+
"""Test that ChatMessageRequest requires a non-empty chat_history."""
83+
# Act & Assert
84+
with pytest.raises(ValueError, match="chat_history cannot be empty"):
85+
ChatMessageRequest("conv-123", "msg-456", "How are you?", [])
86+
87+
def test_chat_message_request_with_multiple_messages(self):
88+
"""Test that ChatMessageRequest handles multiple messages correctly."""
89+
# Arrange
90+
timestamp = datetime(2024, 1, 15, 10, 30, 0, tzinfo=timezone.utc)
91+
message1 = ChatHistoryMessage("msg-1", "user", "Hello", timestamp)
92+
message2 = ChatHistoryMessage("msg-2", "assistant", "Hi!", timestamp)
93+
message3 = ChatHistoryMessage("msg-3", "user", "How are you?", timestamp)
94+
chat_history = [message1, message2, message3]
95+
96+
# Act
97+
request = ChatMessageRequest("conv-123", "msg-456", "What can you do?", chat_history)
98+
result = request.to_dict()
99+
100+
# Assert
101+
assert len(result["chatHistory"]) == 3
102+
assert result["chatHistory"][0]["id"] == "msg-1"
103+
assert result["chatHistory"][1]["id"] == "msg-2"
104+
assert result["chatHistory"][2]["id"] == "msg-3"

0 commit comments

Comments
 (0)