|
|
|
""" |
|
Comprehensive test for bounds checking fixes in crossword generator. |
|
""" |
|
|
|
import asyncio |
|
import sys |
|
import pytest |
|
from pathlib import Path |
|
|
|
|
|
project_root = Path(__file__).parent.parent |
|
sys.path.insert(0, str(project_root)) |
|
|
|
from src.services.crossword_generator_fixed import CrosswordGeneratorFixed |
|
|
|
class TestBoundsChecking: |
|
"""Test all bounds checking in crossword generator.""" |
|
|
|
def setup_method(self): |
|
"""Setup test instance.""" |
|
self.generator = CrosswordGeneratorFixed(vector_service=None) |
|
|
|
def test_can_place_word_bounds_horizontal(self): |
|
"""Test _can_place_word bounds checking for horizontal placement.""" |
|
|
|
grid = [["." for _ in range(5)] for _ in range(5)] |
|
|
|
|
|
assert not self.generator._can_place_word(grid, "TOOLONG", 2, 1, "horizontal") |
|
assert not self.generator._can_place_word(grid, "TEST", -1, 1, "horizontal") |
|
assert not self.generator._can_place_word(grid, "TEST", 1, -1, "horizontal") |
|
assert not self.generator._can_place_word(grid, "TEST", 5, 1, "horizontal") |
|
assert not self.generator._can_place_word(grid, "TEST", 1, 5, "horizontal") |
|
assert not self.generator._can_place_word(grid, "TEST", 1, 3, "horizontal") |
|
|
|
|
|
assert self.generator._can_place_word(grid, "TEST", 2, 1, "horizontal") |
|
assert self.generator._can_place_word(grid, "A", 0, 0, "horizontal") |
|
|
|
def test_can_place_word_bounds_vertical(self): |
|
"""Test _can_place_word bounds checking for vertical placement.""" |
|
|
|
grid = [["." for _ in range(5)] for _ in range(5)] |
|
|
|
|
|
assert not self.generator._can_place_word(grid, "TOOLONG", 1, 2, "vertical") |
|
assert not self.generator._can_place_word(grid, "TEST", -1, 1, "vertical") |
|
assert not self.generator._can_place_word(grid, "TEST", 1, -1, "vertical") |
|
assert not self.generator._can_place_word(grid, "TEST", 5, 1, "vertical") |
|
assert not self.generator._can_place_word(grid, "TEST", 1, 5, "vertical") |
|
assert not self.generator._can_place_word(grid, "TEST", 3, 1, "vertical") |
|
|
|
|
|
assert self.generator._can_place_word(grid, "TEST", 1, 2, "vertical") |
|
assert self.generator._can_place_word(grid, "A", 0, 0, "vertical") |
|
|
|
def test_place_word_bounds_horizontal(self): |
|
"""Test _place_word bounds checking for horizontal placement.""" |
|
grid = [["." for _ in range(5)] for _ in range(5)] |
|
|
|
|
|
original_state = self.generator._place_word(grid, "TEST", 2, 1, "horizontal") |
|
assert len(original_state) == 4 |
|
assert grid[2][1] == "T" |
|
assert grid[2][4] == "T" |
|
|
|
|
|
with pytest.raises(IndexError): |
|
self.generator._place_word(grid, "TOOLONG", 2, 1, "horizontal") |
|
|
|
with pytest.raises(IndexError): |
|
self.generator._place_word(grid, "TEST", -1, 1, "horizontal") |
|
|
|
with pytest.raises(IndexError): |
|
self.generator._place_word(grid, "TEST", 5, 1, "horizontal") |
|
|
|
with pytest.raises(IndexError): |
|
self.generator._place_word(grid, "TEST", 1, 5, "horizontal") |
|
|
|
def test_place_word_bounds_vertical(self): |
|
"""Test _place_word bounds checking for vertical placement.""" |
|
grid = [["." for _ in range(5)] for _ in range(5)] |
|
|
|
|
|
original_state = self.generator._place_word(grid, "TEST", 1, 2, "vertical") |
|
assert len(original_state) == 4 |
|
assert grid[1][2] == "T" |
|
assert grid[4][2] == "T" |
|
|
|
|
|
with pytest.raises(IndexError): |
|
self.generator._place_word(grid, "TOOLONG", 1, 2, "vertical") |
|
|
|
with pytest.raises(IndexError): |
|
self.generator._place_word(grid, "TEST", -1, 2, "vertical") |
|
|
|
with pytest.raises(IndexError): |
|
self.generator._place_word(grid, "TEST", 5, 2, "vertical") |
|
|
|
with pytest.raises(IndexError): |
|
self.generator._place_word(grid, "TEST", 2, 5, "vertical") |
|
|
|
def test_remove_word_bounds(self): |
|
"""Test _remove_word bounds checking.""" |
|
grid = [["." for _ in range(5)] for _ in range(5)] |
|
|
|
|
|
original_state = self.generator._place_word(grid, "TEST", 2, 1, "horizontal") |
|
|
|
|
|
self.generator._remove_word(grid, original_state) |
|
assert grid[2][1] == "." |
|
|
|
|
|
bad_state = [{"row": -1, "col": 1, "value": "."}] |
|
with pytest.raises(IndexError): |
|
self.generator._remove_word(grid, bad_state) |
|
|
|
bad_state = [{"row": 5, "col": 1, "value": "."}] |
|
with pytest.raises(IndexError): |
|
self.generator._remove_word(grid, bad_state) |
|
|
|
bad_state = [{"row": 1, "col": -1, "value": "."}] |
|
with pytest.raises(IndexError): |
|
self.generator._remove_word(grid, bad_state) |
|
|
|
bad_state = [{"row": 1, "col": 5, "value": "."}] |
|
with pytest.raises(IndexError): |
|
self.generator._remove_word(grid, bad_state) |
|
|
|
def test_create_simple_cross_bounds(self): |
|
"""Test _create_simple_cross bounds checking.""" |
|
|
|
word_list = ["CAT", "TOY"] |
|
word_objs = [{"word": w, "clue": f"Clue for {w}"} for w in word_list] |
|
|
|
|
|
result = self.generator._create_simple_cross(word_list, word_objs) |
|
assert result is not None |
|
assert len(result["placed_words"]) == 2 |
|
|
|
|
|
word_list = ["A", "A"] |
|
word_objs = [{"word": w, "clue": f"Clue for {w}"} for w in word_list] |
|
|
|
|
|
result = self.generator._create_simple_cross(word_list, word_objs) |
|
|
|
|
|
def test_trim_grid_bounds(self): |
|
"""Test _trim_grid bounds checking.""" |
|
|
|
grid = [["." for _ in range(10)] for _ in range(10)] |
|
|
|
|
|
grid[5][3] = "T" |
|
grid[5][4] = "E" |
|
grid[5][5] = "S" |
|
grid[5][6] = "T" |
|
|
|
placed_words = [{ |
|
"word": "TEST", |
|
"row": 5, |
|
"col": 3, |
|
"direction": "horizontal", |
|
"number": 1 |
|
}] |
|
|
|
|
|
result = self.generator._trim_grid(grid, placed_words) |
|
assert result is not None |
|
assert "grid" in result |
|
assert "placed_words" in result |
|
|
|
|
|
placed_words = [{ |
|
"word": "A", |
|
"row": 0, |
|
"col": 0, |
|
"direction": "horizontal", |
|
"number": 1 |
|
}] |
|
|
|
grid[0][0] = "A" |
|
result = self.generator._trim_grid(grid, placed_words) |
|
assert result is not None |
|
|
|
def test_calculation_placement_score_bounds(self): |
|
"""Test _calculate_placement_score bounds checking.""" |
|
grid = [["." for _ in range(5)] for _ in range(5)] |
|
|
|
|
|
grid[2][2] = "T" |
|
grid[2][3] = "E" |
|
|
|
placement = {"row": 2, "col": 2, "direction": "horizontal"} |
|
placed_words = [] |
|
|
|
|
|
score = self.generator._calculate_placement_score(grid, "TEST", placement, placed_words) |
|
assert isinstance(score, int) |
|
|
|
|
|
placement = {"row": 4, "col": 3, "direction": "horizontal"} |
|
score = self.generator._calculate_placement_score(grid, "TEST", placement, placed_words) |
|
assert isinstance(score, int) |
|
|
|
|
|
placement = {"row": -1, "col": 0, "direction": "horizontal"} |
|
score = self.generator._calculate_placement_score(grid, "TEST", placement, placed_words) |
|
assert isinstance(score, int) |
|
|
|
async def test_full_generation_stress(): |
|
"""Stress test full generation to catch index errors.""" |
|
generator = CrosswordGeneratorFixed(vector_service=None) |
|
|
|
|
|
test_words = [ |
|
{"word": "CAT", "clue": "Feline pet"}, |
|
{"word": "DOG", "clue": "Man's best friend"}, |
|
{"word": "BIRD", "clue": "Flying animal"}, |
|
{"word": "FISH", "clue": "Aquatic animal"}, |
|
{"word": "ELEPHANT", "clue": "Large mammal"}, |
|
{"word": "TIGER", "clue": "Striped cat"}, |
|
{"word": "HORSE", "clue": "Riding animal"}, |
|
{"word": "BEAR", "clue": "Large carnivore"}, |
|
{"word": "WOLF", "clue": "Pack animal"}, |
|
{"word": "LION", "clue": "King of jungle"} |
|
] |
|
|
|
generator._select_words = lambda topics, difficulty, use_ai: test_words |
|
|
|
|
|
for i in range(20): |
|
try: |
|
result = await generator.generate_puzzle(["animals"], "medium", use_ai=False) |
|
if result: |
|
print(f"✅ Generation {i+1} succeeded") |
|
else: |
|
print(f"⚠️ Generation {i+1} returned None") |
|
except IndexError as e: |
|
print(f"❌ Index error in generation {i+1}: {e}") |
|
raise |
|
except Exception as e: |
|
print(f"⚠️ Other error in generation {i+1}: {e}") |
|
|
|
|
|
print("✅ All stress test generations completed without index errors!") |
|
|
|
if __name__ == "__main__": |
|
|
|
print("🧪 Running comprehensive bounds checking tests...") |
|
|
|
|
|
import subprocess |
|
result = subprocess.run([sys.executable, "-m", "pytest", __file__, "-v"], |
|
capture_output=True, text=True) |
|
|
|
print("STDOUT:", result.stdout) |
|
if result.stderr: |
|
print("STDERR:", result.stderr) |
|
|
|
|
|
print("\n🏋️ Running stress test...") |
|
asyncio.run(test_full_generation_stress()) |