| """Tests for the predictive needs model.""" |
|
|
| import os |
| import tempfile |
| from pathlib import Path |
|
|
| import pytest |
|
|
| from infj_bot.core.plugins.predictor import PredictiveNeeds |
|
|
|
|
| @pytest.fixture |
| def tmp_db(): |
| fd, path = tempfile.mkstemp(suffix=".db") |
| os.close(fd) |
| yield Path(path) |
| os.unlink(path) |
|
|
|
|
| def _make_emotion(label: str, intensity: float) -> dict: |
| return {"label": label, "intensity": intensity, "confidence": 0.8} |
|
|
|
|
| class TestPredictiveNeeds: |
| def test_init_creates_db(self, tmp_db): |
| PredictiveNeeds(db_path=tmp_db) |
| assert tmp_db.exists() |
|
|
| def test_record_interaction(self, tmp_db): |
| p = PredictiveNeeds(db_path=tmp_db) |
| p.record_interaction("I am stressed about work", _make_emotion("anxious", 0.7)) |
| patterns = p._load_recent_patterns(10) |
| assert len(patterns) == 1 |
| assert patterns[0]["emotion_label"] == "anxious" |
| assert patterns[0]["stress_score"] > 0 |
|
|
| def test_extract_topics(self, tmp_db): |
| p = PredictiveNeeds(db_path=tmp_db) |
| p.record_interaction( |
| "My partner and I are fighting about money", _make_emotion("sad", 0.5) |
| ) |
| patterns = p._load_recent_patterns(1) |
| topics = patterns[0]["topics"].split(",") |
| assert "relationship" in topics or "conflict" in topics or "security" in topics |
|
|
| def test_time_of_day_analysis(self, tmp_db): |
| p = PredictiveNeeds(db_path=tmp_db) |
| |
| for _ in range(3): |
| p.record_interaction( |
| "Morning anxiety about deadlines", _make_emotion("anxious", 0.6) |
| ) |
| patterns = p._load_recent_patterns(10) |
| result = p._analyze_time_of_day(patterns) |
| |
| for bucket, data in result.items(): |
| assert "typical_emotion" in data |
| assert "avg_stress" in data |
|
|
| def test_predict_current_need_insufficient_data(self, tmp_db): |
| p = PredictiveNeeds(db_path=tmp_db) |
| assert p.predict_current_need() is None |
|
|
| def test_predict_current_need_with_data(self, tmp_db): |
| p = PredictiveNeeds(db_path=tmp_db) |
| for i in range(8): |
| p.record_interaction( |
| f"I am stressed and overwhelmed at work {i}", |
| _make_emotion("overwhelmed", 0.7), |
| ) |
| pred = p.predict_current_need() |
| assert pred is not None |
| assert "prediction" in pred |
| assert "confidence" in pred |
| assert 0.0 <= pred["confidence"] <= 1.0 |
|
|
| def test_detect_anomaly_emotion_shift(self, tmp_db): |
| p = PredictiveNeeds(db_path=tmp_db) |
| |
| for _ in range(5): |
| p.record_interaction("All good", _make_emotion("neutral", 0.2)) |
| |
| for _ in range(3): |
| p.record_interaction("Feeling down", _make_emotion("sad", 0.6)) |
| anomaly = p.detect_anomaly() |
| assert anomaly is not None |
| assert anomaly["type"] == "emotion_shift" |
|
|
| def test_detect_anomaly_stress_spike(self, tmp_db): |
| p = PredictiveNeeds(db_path=tmp_db) |
| |
| for _ in range(5): |
| p.record_interaction("Normal day", _make_emotion("anxious", 0.2)) |
| for _ in range(3): |
| p.record_interaction( |
| "PANIC everything is broken deadline urgent", |
| _make_emotion("anxious", 0.9), |
| ) |
| anomaly = p.detect_anomaly() |
| assert anomaly is not None |
| assert anomaly["type"] == "stress_spike" |
|
|
| def test_proactive_suggestion(self, tmp_db): |
| p = PredictiveNeeds(db_path=tmp_db) |
| for _ in range(6): |
| p.record_interaction( |
| "I can't cope with this workload overwhelmed burned out", |
| _make_emotion("overwhelmed", 0.8), |
| ) |
| |
| for _ in range(3): |
| p.record_interaction( |
| "Actually things are okay", _make_emotion("neutral", 0.2) |
| ) |
| for _ in range(3): |
| p.record_interaction("I am so sad and stressed", _make_emotion("sad", 0.8)) |
| suggestion = p.proactive_suggestion() |
| assert suggestion is not None |
| assert len(suggestion) > 10 |
|
|
| def test_format_predictive_prompt_empty(self, tmp_db): |
| p = PredictiveNeeds(db_path=tmp_db) |
| assert p.format_predictive_prompt() == "" |
|
|
| def test_format_predictive_prompt_with_data(self, tmp_db): |
| p = PredictiveNeeds(db_path=tmp_db) |
| for _ in range(8): |
| p.record_interaction( |
| "I am stressed and overwhelmed at work", _make_emotion("anxious", 0.7) |
| ) |
| prompt = p.format_predictive_prompt() |
| assert "PREDICTIVE SENSE" in prompt |
| assert "wonder" in prompt.lower() |
|
|
| def test_gap_trend_stable(self, tmp_db): |
| p = PredictiveNeeds(db_path=tmp_db) |
| for i in range(6): |
| p.record_interaction(f"msg {i}", _make_emotion("neutral", 0.3)) |
| trend = p._analyze_gap_trend(p._load_recent_patterns(10)) |
| assert trend in ("stable", None) |
|
|
| def test_stress_signal_scoring(self, tmp_db): |
| p = PredictiveNeeds(db_path=tmp_db) |
| p.record_interaction( |
| "I am burned out and overwhelmed", _make_emotion("sad", 0.5) |
| ) |
| patterns = p._load_recent_patterns(1) |
| assert patterns[0]["stress_score"] > 0.4 |
|
|