ai-assistant-discord-bot/tests/test_response_formatter.py
Claude Discord Bot 4c00cd97e6 Week 2 complete: Discord bot MVP with full integration
Completed HIGH-001 through HIGH-004:

HIGH-001: Discord bot with channel message routing
- bot.py: 244 lines with ClaudeCoordinator class
- @mention trigger mode for safe operation
- Session lifecycle integration with SessionManager
- Typing indicators and error handling
- 20/20 tests passing

HIGH-002: Response formatter with intelligent chunking
- response_formatter.py: expanded to 329 lines
- format_response() with smart boundary detection
- Code block preservation and splitting
- 26/26 tests passing

HIGH-003: Slash commands for bot management
- commands.py: 411 lines with ClaudeCommands cog
- /reset with interactive confirmation dialog
- /status with Discord embed display
- /model for runtime model switching
- 18/18 tests passing

HIGH-004: Concurrent message handling
- Per-channel asyncio.Lock implementation
- Same-channel serialization (prevents race conditions)
- Cross-channel parallelization (maintains performance)
- 7/7 concurrency tests passing

Total: 134/135 tests passing (99.3%)
Production-ready Discord bot MVP

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 18:42:50 +00:00

381 lines
12 KiB
Python

"""Comprehensive tests for Discord response formatter."""
import pytest
from claude_coordinator.response_formatter import ResponseFormatter
class TestResponseFormatterBasics:
"""Test basic functionality of ResponseFormatter."""
def test_short_response_single_message(self):
"""Short response (<2000 chars) returns single message."""
formatter = ResponseFormatter()
text = "This is a short response."
result = formatter.format_response(text)
assert len(result) == 1
assert result[0] == text
def test_empty_input_returns_empty_list(self):
"""Empty input returns empty list."""
formatter = ResponseFormatter()
result = formatter.format_response("")
assert result == []
def test_whitespace_only_returns_empty_list(self):
"""Whitespace-only input returns empty list."""
formatter = ResponseFormatter()
result = formatter.format_response(" \n\t \n ")
assert result == []
def test_exactly_max_length_single_message(self):
"""Text exactly at max_length returns single message."""
formatter = ResponseFormatter()
text = "a" * 2000
result = formatter.format_response(text, max_length=2000)
assert len(result) == 1
assert result[0] == text
class TestSmartChunking:
"""Test intelligent chunking on natural boundaries."""
def test_long_response_without_code_blocks(self):
"""Long response without code blocks is intelligently chunked."""
formatter = ResponseFormatter()
# Create text longer than 2000 chars with paragraphs
paragraphs = [
"This is paragraph one with some content. " * 30,
"This is paragraph two with more content. " * 30,
"This is paragraph three with even more content. " * 30,
]
text = "\n\n".join(paragraphs)
result = formatter.format_response(text, max_length=2000)
# Should split into multiple messages
assert len(result) > 1
# Each chunk should be under max_length
for chunk in result:
assert len(chunk) <= 2000
def test_split_on_paragraph_boundaries(self):
"""Text splits on paragraph boundaries (double newlines)."""
formatter = ResponseFormatter()
# Create text with clear paragraph breaks
paragraph1 = "A" * 1100 + "\n\n"
paragraph2 = "B" * 1100
text = paragraph1 + paragraph2
result = formatter.format_response(text, max_length=2000)
# Should split into 2 chunks at paragraph boundary
assert len(result) == 2
assert "A" in result[0] and "B" not in result[0]
assert "B" in result[1] and "A" not in result[1]
def test_split_on_sentence_boundaries(self):
"""Text splits on sentence boundaries when no paragraph breaks."""
formatter = ResponseFormatter()
# Create long sentences
sentence1 = "This is the first sentence. " * 40
sentence2 = "This is the second sentence. " * 40
text = sentence1 + sentence2
result = formatter.format_response(text, max_length=1500)
# Should split into multiple messages
assert len(result) >= 2
# Each chunk should be under max_length
for chunk in result:
assert len(chunk) <= 1500
def test_split_on_word_boundaries(self):
"""Text splits on word boundaries when no sentence breaks."""
formatter = ResponseFormatter()
# Create text with unique words to detect mid-word splits
text = " ".join([f"testword{i}" for i in range(400)]) # ~4000 chars
result = formatter.format_response(text, max_length=2000)
# Should split into chunks
assert len(result) >= 2
# All chunks should be under max length
for chunk in result:
assert len(chunk) <= 2000
def test_very_long_single_line(self):
"""Single line longer than max_length is force-split."""
formatter = ResponseFormatter()
# Create one continuous line with no spaces
text = "a" * 3000
result = formatter.format_response(text, max_length=2000)
# Should split into 2 chunks
assert len(result) == 2
assert len(result[0]) == 2000
assert len(result[1]) == 1000
class TestCodeBlockPreservation:
"""Test code block handling and preservation."""
def test_single_code_block_preserved(self):
"""Response with single code block preserves it."""
formatter = ResponseFormatter()
text = "Here's the code:\n\n```python\ndef hello():\n return 'world'\n```\n\nThat's it!"
result = formatter.format_response(text)
# Should keep code block intact
assert len(result) == 1
assert "```python" in result[0]
assert "def hello():" in result[0]
assert "```" in result[0]
def test_multiple_code_blocks_preserved(self):
"""Response with multiple code blocks preserves all."""
formatter = ResponseFormatter()
text = """First block:
```python
def func1():
pass
```
Second block:
```javascript
function func2() {}
```
Done!"""
result = formatter.format_response(text)
# Should preserve both code blocks
full_text = " ".join(result)
assert "```python" in full_text
assert "```javascript" in full_text
assert full_text.count("```") >= 4 # 2 opening + 2 closing
def test_code_block_at_chunk_boundary(self):
"""Code block at chunk boundary is properly split and closed."""
formatter = ResponseFormatter()
# Create text with code block that causes splitting
prefix = "A" * 1000 + "\n\n"
code_block = "```python\n" + ("print('test')\n" * 100) + "```"
text = prefix + code_block
result = formatter.format_response(text, max_length=2000)
# Should split into multiple chunks
assert len(result) >= 2
# Each chunk should have valid markdown
for chunk in result:
assert len(chunk) <= 2000
def test_large_code_block_split_correctly(self):
"""Code block larger than 2000 chars splits with markers."""
formatter = ResponseFormatter()
# Create huge code block
code_lines = "\n".join([f"line_{i} = {i}" for i in range(200)])
text = f"```python\n{code_lines}\n```"
result = formatter.format_response(text, max_length=2000)
# Should split into multiple chunks
assert len(result) >= 2
# Each chunk should have code block markers
for chunk in result:
assert chunk.startswith("```python")
assert chunk.endswith("```")
assert len(chunk) <= 2000
def test_code_block_without_language(self):
"""Code blocks without language identifier are handled."""
formatter = ResponseFormatter()
text = "Code:\n\n```\nsome code here\nmore code\n```\n\nDone."
result = formatter.format_response(text)
assert len(result) == 1
assert "```" in result[0]
assert "some code here" in result[0]
class TestMixedContent:
"""Test responses with mixed markdown and code."""
def test_mixed_markdown_preserved(self):
"""Response with bold, italic, lists is preserved."""
formatter = ResponseFormatter()
text = """**Bold text** and *italic text*
- List item 1
- List item 2
- List item 3
Regular text after list."""
result = formatter.format_response(text)
assert len(result) == 1
assert "**Bold text**" in result[0]
assert "*italic text*" in result[0]
assert "- List item 1" in result[0]
def test_multiple_paragraphs_chunked_correctly(self):
"""Response with multiple paragraphs splits on paragraph boundaries."""
formatter = ResponseFormatter()
# Create multiple substantial paragraphs
paragraphs = []
for i in range(5):
paragraphs.append(f"Paragraph {i+1}. " + ("Content. " * 50))
text = "\n\n".join(paragraphs)
result = formatter.format_response(text, max_length=2000)
# Should split into multiple messages
assert len(result) >= 2
# Verify content distribution
for chunk in result:
assert len(chunk) <= 2000
class TestCodeBlockSplitting:
"""Test code block splitting behavior with split_on_code_blocks flag."""
def test_split_on_code_blocks_false(self):
"""With split_on_code_blocks=False, uses simple splitting."""
formatter = ResponseFormatter()
text = "Text before\n\n```python\ndef func():\n pass\n```\n\nText after" * 50
result = formatter.format_response(text, max_length=2000, split_on_code_blocks=False)
# Should split into chunks
assert len(result) >= 2
# May split code blocks (not preserving them)
for chunk in result:
assert len(chunk) <= 2000
def test_split_on_code_blocks_true_preserves(self):
"""With split_on_code_blocks=True, preserves code block integrity."""
formatter = ResponseFormatter()
code = "```python\ndef hello():\n return 'world'\n```"
text = "Intro text\n\n" + code + "\n\nOutro text"
result = formatter.format_response(text, max_length=2000, split_on_code_blocks=True)
# Code block should be intact in one of the chunks
full_text = "".join(result)
assert "```python\ndef hello():\n return 'world'\n```" in full_text
class TestEdgeCases:
"""Test edge cases and error handling."""
def test_single_code_block_exactly_max_length(self):
"""Code block exactly at max_length is handled."""
formatter = ResponseFormatter()
# Create code block that's exactly 2000 chars including markers
code_content = "x" * (2000 - len("```python\n\n```"))
text = f"```python\n{code_content}\n```"
result = formatter.format_response(text, max_length=2000)
assert len(result) == 1
assert len(result[0]) <= 2000
def test_consecutive_code_blocks(self):
"""Multiple consecutive code blocks are preserved."""
formatter = ResponseFormatter()
text = """```python
code1 = 1
```
```javascript
code2 = 2
```
```bash
code3 = 3
```"""
result = formatter.format_response(text)
full_text = " ".join(result)
assert "```python" in full_text
assert "```javascript" in full_text
assert "```bash" in full_text
def test_very_long_single_word(self):
"""Single word longer than max_length is force-split."""
formatter = ResponseFormatter()
text = "a" * 2500 # Single "word" with no spaces
result = formatter.format_response(text, max_length=2000)
assert len(result) == 2
assert len(result[0]) == 2000
assert len(result[1]) == 500
def test_custom_max_length(self):
"""Custom max_length parameter is respected."""
formatter = ResponseFormatter()
text = "word " * 300 # ~1500 chars
result = formatter.format_response(text, max_length=500)
# Should split into multiple chunks
assert len(result) >= 3
for chunk in result:
assert len(chunk) <= 500
class TestHelperMethods:
"""Test existing helper methods still work."""
def test_format_code_block_with_language(self):
"""format_code_block() creates proper code block."""
formatter = ResponseFormatter()
result = formatter.format_code_block("print('test')", "python")
assert result == "```python\nprint('test')\n```"
def test_format_code_block_without_language(self):
"""format_code_block() works without language."""
formatter = ResponseFormatter()
result = formatter.format_code_block("some code")
assert result == "```\nsome code\n```"
def test_format_error(self):
"""format_error() creates proper error message."""
formatter = ResponseFormatter()
result = formatter.format_error("Something went wrong")
assert ":warning:" in result
assert "Error:" in result
assert "Something went wrong" in result
assert "```" in result
def test_chunk_response_basic(self):
"""chunk_response() splits on line boundaries."""
formatter = ResponseFormatter()
text = "line1\nline2\nline3\n" + ("x" * 2000)
result = formatter.chunk_response(text, max_length=2000)
assert len(result) >= 2
for chunk in result:
assert len(chunk) <= 2000