393 lines
11 KiB
Python
393 lines
11 KiB
Python
"""
|
|
Unit tests for Log Reader service.
|
|
|
|
Tests cover:
|
|
- Log file discovery
|
|
- Log event parsing
|
|
- Event subscription
|
|
- Pattern matching
|
|
- Statistics tracking
|
|
"""
|
|
|
|
import pytest
|
|
from pathlib import Path
|
|
from unittest.mock import MagicMock, patch, mock_open
|
|
from datetime import datetime
|
|
|
|
from core.log_reader import LogReader, LogEvent, get_log_reader
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestLogReaderInitialization:
|
|
"""Test LogReader initialization."""
|
|
|
|
def test_default_log_path(self):
|
|
"""Test default log path detection."""
|
|
with patch.object(Path, 'exists', return_value=True):
|
|
reader = LogReader()
|
|
# Should find one of the default paths
|
|
assert reader.log_path is not None
|
|
|
|
def test_custom_log_path(self, tmp_path):
|
|
"""Test custom log path."""
|
|
log_file = tmp_path / "custom.log"
|
|
log_file.write_text("test log content")
|
|
|
|
reader = LogReader(log_path=log_file)
|
|
|
|
assert reader.log_path == log_file
|
|
|
|
def test_find_log_file(self, tmp_path):
|
|
"""Test log file discovery."""
|
|
log_file = tmp_path / "chat.log"
|
|
log_file.write_text("test")
|
|
|
|
reader = LogReader()
|
|
found_path = reader._find_log_file()
|
|
|
|
# May or may not find depending on system
|
|
# Just verify method doesn't raise
|
|
assert found_path is None or isinstance(found_path, Path)
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestLogEvent:
|
|
"""Test LogEvent dataclass."""
|
|
|
|
def test_log_event_creation(self):
|
|
"""Test LogEvent creation."""
|
|
event = LogEvent(
|
|
timestamp=datetime.now(),
|
|
raw_line="Test line",
|
|
event_type="skill_gain",
|
|
data={"skill": "Rifle", "gain": 0.01}
|
|
)
|
|
|
|
assert event.raw_line == "Test line"
|
|
assert event.event_type == "skill_gain"
|
|
assert event.data["skill"] == "Rifle"
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestPatternMatching:
|
|
"""Test log pattern matching."""
|
|
|
|
def test_skill_gain_pattern(self):
|
|
"""Test skill gain pattern matching."""
|
|
reader = LogReader()
|
|
|
|
line = "2024-01-01 12:00:00 Rifle has improved by 0.01 points"
|
|
event = reader._parse_event(line)
|
|
|
|
assert event is not None
|
|
assert event.event_type == "skill_gain"
|
|
assert "Rifle" in event.data['groups']
|
|
assert "0.01" in event.data['groups']
|
|
|
|
def test_loot_pattern(self):
|
|
"""Test loot pattern matching."""
|
|
reader = LogReader()
|
|
|
|
line = "2024-01-01 12:00:00 You received Animal Oil x 1"
|
|
event = reader._parse_event(line)
|
|
|
|
assert event is not None
|
|
assert event.event_type == "loot"
|
|
|
|
def test_global_pattern(self):
|
|
"""Test global pattern matching."""
|
|
reader = LogReader()
|
|
|
|
line = "2024-01-01 12:00:00 Player received something from Mob worth 1000 PED"
|
|
event = reader._parse_event(line)
|
|
|
|
assert event is not None
|
|
assert event.event_type == "global"
|
|
|
|
def test_damage_pattern(self):
|
|
"""Test damage pattern matching."""
|
|
reader = LogReader()
|
|
|
|
line = "2024-01-01 12:00:00 You hit for 150 damage"
|
|
event = reader._parse_event(line)
|
|
|
|
assert event is not None
|
|
assert event.event_type == "damage"
|
|
|
|
def test_damage_taken_pattern(self):
|
|
"""Test damage taken pattern matching."""
|
|
reader = LogReader()
|
|
|
|
line = "2024-01-01 12:00:00 You were hit for 50 damage"
|
|
event = reader._parse_event(line)
|
|
|
|
assert event is not None
|
|
assert event.event_type == "damage_taken"
|
|
|
|
def test_heal_pattern(self):
|
|
"""Test heal pattern matching."""
|
|
reader = LogReader()
|
|
|
|
line = "2024-01-01 12:00:00 You healed 100 health"
|
|
event = reader._parse_event(line)
|
|
|
|
assert event is not None
|
|
assert event.event_type == "heal"
|
|
|
|
def test_mission_complete_pattern(self):
|
|
"""Test mission complete pattern matching."""
|
|
reader = LogReader()
|
|
|
|
line = "2024-01-01 12:00:00 Mission completed: Test Mission"
|
|
event = reader._parse_event(line)
|
|
|
|
assert event is not None
|
|
assert event.event_type == "mission_complete"
|
|
|
|
def test_tier_increase_pattern(self):
|
|
"""Test tier increase pattern matching."""
|
|
reader = LogReader()
|
|
|
|
line = "2024-01-01 12:00:00 Your Weapon has reached tier 5"
|
|
event = reader._parse_event(line)
|
|
|
|
assert event is not None
|
|
assert event.event_type == "tier_increase"
|
|
|
|
def test_enhancer_break_pattern(self):
|
|
"""Test enhancer break pattern matching."""
|
|
reader = LogReader()
|
|
|
|
line = "2024-01-01 12:00:00 Your Enhancer broke"
|
|
event = reader._parse_event(line)
|
|
|
|
assert event is not None
|
|
assert event.event_type == "enhancer_break"
|
|
|
|
def test_no_match(self):
|
|
"""Test line that doesn't match any pattern."""
|
|
reader = LogReader()
|
|
|
|
line = "2024-01-01 12:00:00 Some random log line"
|
|
event = reader._parse_event(line)
|
|
|
|
assert event is None
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestSubscription:
|
|
"""Test event subscription."""
|
|
|
|
def test_subscribe_to_event_type(self):
|
|
"""Test subscribing to specific event type."""
|
|
reader = LogReader()
|
|
received = []
|
|
|
|
def callback(event):
|
|
received.append(event)
|
|
|
|
reader.subscribe("skill_gain", callback)
|
|
|
|
# Simulate event
|
|
event = LogEvent(
|
|
timestamp=datetime.now(),
|
|
raw_line="Test",
|
|
event_type="skill_gain",
|
|
data={}
|
|
)
|
|
reader._notify_subscribers(event)
|
|
|
|
assert len(received) == 1
|
|
|
|
def test_subscribe_all(self):
|
|
"""Test subscribing to all events."""
|
|
reader = LogReader()
|
|
received = []
|
|
|
|
def callback(event):
|
|
received.append(event)
|
|
|
|
reader.subscribe_all(callback)
|
|
|
|
# Simulate events
|
|
for event_type in ["skill_gain", "loot", "damage"]:
|
|
event = LogEvent(
|
|
timestamp=datetime.now(),
|
|
raw_line="Test",
|
|
event_type=event_type,
|
|
data={}
|
|
)
|
|
reader._notify_subscribers(event)
|
|
|
|
assert len(received) == 3
|
|
|
|
def test_unsubscribe(self):
|
|
"""Test unsubscribing from events."""
|
|
reader = LogReader()
|
|
received = []
|
|
|
|
def callback(event):
|
|
received.append(event)
|
|
|
|
reader.subscribe("skill_gain", callback)
|
|
|
|
# First event
|
|
event = LogEvent(
|
|
timestamp=datetime.now(),
|
|
raw_line="Test",
|
|
event_type="skill_gain",
|
|
data={}
|
|
)
|
|
reader._notify_subscribers(event)
|
|
assert len(received) == 1
|
|
|
|
# Unsubscribe
|
|
reader.unsubscribe("skill_gain", callback)
|
|
|
|
# Second event
|
|
reader._notify_subscribers(event)
|
|
assert len(received) == 1 # No new events
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestReadLines:
|
|
"""Test reading log lines."""
|
|
|
|
def test_read_lines(self):
|
|
"""Test reading recent lines."""
|
|
reader = LogReader()
|
|
reader._recent_lines = ["line1", "line2", "line3", "line4", "line5"]
|
|
|
|
lines = reader.read_lines(count=3)
|
|
|
|
assert len(lines) == 3
|
|
assert lines == ["line3", "line4", "line5"]
|
|
|
|
def test_read_lines_with_filter(self):
|
|
"""Test reading lines with filter."""
|
|
reader = LogReader()
|
|
reader._recent_lines = [
|
|
"skill gain line",
|
|
"combat line",
|
|
"another skill gain",
|
|
"loot line"
|
|
]
|
|
|
|
lines = reader.read_lines(count=10, filter_text="skill")
|
|
|
|
assert len(lines) == 2
|
|
assert all("skill" in line.lower() for line in lines)
|
|
|
|
def test_read_lines_more_than_available(self):
|
|
"""Test reading more lines than available."""
|
|
reader = LogReader()
|
|
reader._recent_lines = ["line1", "line2"]
|
|
|
|
lines = reader.read_lines(count=10)
|
|
|
|
assert len(lines) == 2
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestStatistics:
|
|
"""Test statistics tracking."""
|
|
|
|
def test_stats_initialization(self):
|
|
"""Test stats initialization."""
|
|
reader = LogReader()
|
|
|
|
assert reader.stats['lines_read'] == 0
|
|
assert reader.stats['events_parsed'] == 0
|
|
assert reader.stats['start_time'] is None
|
|
|
|
def test_stats_tracking(self):
|
|
"""Test stats tracking during operation."""
|
|
reader = LogReader()
|
|
|
|
# Simulate processing lines
|
|
reader._process_line("Line 1")
|
|
reader._process_line("Line 2")
|
|
reader._process_line("Rifle has improved by 0.01 points") # Matches pattern
|
|
|
|
assert reader.stats['lines_read'] == 3
|
|
assert reader.stats['events_parsed'] == 1
|
|
|
|
def test_get_stats(self):
|
|
"""Test getting stats copy."""
|
|
reader = LogReader()
|
|
reader.stats['lines_read'] = 100
|
|
|
|
stats = reader.get_stats()
|
|
|
|
assert stats['lines_read'] == 100
|
|
|
|
# Modifying returned stats shouldn't affect original
|
|
stats['lines_read'] = 200
|
|
assert reader.stats['lines_read'] == 100
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestAvailability:
|
|
"""Test availability checking."""
|
|
|
|
def test_is_available_true(self, tmp_path):
|
|
"""Test availability when log exists."""
|
|
log_file = tmp_path / "chat.log"
|
|
log_file.write_text("test")
|
|
|
|
reader = LogReader(log_path=log_file)
|
|
|
|
assert reader.is_available() is True
|
|
|
|
def test_is_available_false(self):
|
|
"""Test availability when log doesn't exist."""
|
|
reader = LogReader(log_path=Path("/nonexistent/path/chat.log"))
|
|
|
|
assert reader.is_available() is False
|
|
|
|
def test_is_available_no_path(self):
|
|
"""Test availability when no path set."""
|
|
reader = LogReader()
|
|
reader.log_path = None
|
|
|
|
assert reader.is_available() is False
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestStartStop:
|
|
"""Test starting and stopping the reader."""
|
|
|
|
def test_start_with_valid_log(self, tmp_path):
|
|
"""Test starting with valid log file."""
|
|
log_file = tmp_path / "chat.log"
|
|
log_file.write_text("test content here")
|
|
|
|
reader = LogReader(log_path=log_file)
|
|
result = reader.start()
|
|
|
|
assert result is True
|
|
assert reader.running is True
|
|
assert reader.thread is not None
|
|
|
|
reader.stop()
|
|
|
|
def test_start_with_invalid_log(self):
|
|
"""Test starting with invalid log file."""
|
|
reader = LogReader(log_path=Path("/nonexistent/chat.log"))
|
|
result = reader.start()
|
|
|
|
assert result is False
|
|
assert reader.running is False
|
|
|
|
def test_stop(self, tmp_path):
|
|
"""Test stopping the reader."""
|
|
log_file = tmp_path / "chat.log"
|
|
log_file.write_text("test")
|
|
|
|
reader = LogReader(log_path=log_file)
|
|
reader.start()
|
|
|
|
reader.stop()
|
|
|
|
assert reader.running is False
|