139 lines
4.8 KiB
Python
139 lines
4.8 KiB
Python
"""Tests for Mnemosyne CLI commands — path, touch, decay, vitality, fading, vibrant."""
|
|
|
|
import json
|
|
import tempfile
|
|
from pathlib import Path
|
|
from unittest.mock import patch
|
|
import sys
|
|
import io
|
|
|
|
import pytest
|
|
|
|
from nexus.mnemosyne.archive import MnemosyneArchive
|
|
from nexus.mnemosyne.entry import ArchiveEntry
|
|
|
|
|
|
@pytest.fixture
|
|
def archive(tmp_path):
|
|
path = tmp_path / "test_archive.json"
|
|
return MnemosyneArchive(archive_path=path)
|
|
|
|
|
|
@pytest.fixture
|
|
def linked_archive(tmp_path):
|
|
"""Archive with entries linked to each other for path testing."""
|
|
path = tmp_path / "test_archive.json"
|
|
arch = MnemosyneArchive(archive_path=path, auto_embed=False)
|
|
e1 = arch.add(ArchiveEntry(title="Alpha", content="first entry about python", topics=["code"]))
|
|
e2 = arch.add(ArchiveEntry(title="Beta", content="second entry about python coding", topics=["code"]))
|
|
e3 = arch.add(ArchiveEntry(title="Gamma", content="third entry about cooking recipes", topics=["food"]))
|
|
return arch, e1, e2, e3
|
|
|
|
|
|
class TestPathCommand:
|
|
def test_shortest_path_exists(self, linked_archive):
|
|
arch, e1, e2, e3 = linked_archive
|
|
path = arch.shortest_path(e1.id, e2.id)
|
|
assert path is not None
|
|
assert path[0] == e1.id
|
|
assert path[-1] == e2.id
|
|
|
|
def test_shortest_path_no_connection(self, linked_archive):
|
|
arch, e1, e2, e3 = linked_archive
|
|
# e3 (cooking) likely not linked to e1 (python coding)
|
|
path = arch.shortest_path(e1.id, e3.id)
|
|
# Path may or may not exist depending on linking threshold
|
|
# Either None or a list is valid
|
|
|
|
def test_shortest_path_same_entry(self, linked_archive):
|
|
arch, e1, _, _ = linked_archive
|
|
path = arch.shortest_path(e1.id, e1.id)
|
|
assert path == [e1.id]
|
|
|
|
def test_shortest_path_missing_entry(self, linked_archive):
|
|
arch, e1, _, _ = linked_archive
|
|
path = arch.shortest_path(e1.id, "nonexistent-id")
|
|
assert path is None
|
|
|
|
|
|
class TestTouchCommand:
|
|
def test_touch_boosts_vitality(self, archive):
|
|
entry = archive.add(ArchiveEntry(title="Test", content="Content"))
|
|
# Simulate time passing by setting old last_accessed
|
|
old_time = "2020-01-01T00:00:00+00:00"
|
|
entry.last_accessed = old_time
|
|
entry.vitality = 0.5
|
|
archive._save()
|
|
|
|
touched = archive.touch(entry.id)
|
|
assert touched.vitality > 0.5
|
|
assert touched.last_accessed != old_time
|
|
|
|
def test_touch_missing_entry(self, archive):
|
|
with pytest.raises(KeyError):
|
|
archive.touch("nonexistent-id")
|
|
|
|
|
|
class TestDecayCommand:
|
|
def test_apply_decay_returns_stats(self, archive):
|
|
archive.add(ArchiveEntry(title="Test", content="Content"))
|
|
result = archive.apply_decay()
|
|
assert result["total_entries"] == 1
|
|
assert "avg_vitality" in result
|
|
assert "fading_count" in result
|
|
assert "vibrant_count" in result
|
|
|
|
def test_decay_on_empty_archive(self, archive):
|
|
result = archive.apply_decay()
|
|
assert result["total_entries"] == 0
|
|
assert result["avg_vitality"] == 0.0
|
|
|
|
|
|
class TestVitalityCommand:
|
|
def test_get_vitality(self, archive):
|
|
entry = archive.add(ArchiveEntry(title="Test", content="Content"))
|
|
v = archive.get_vitality(entry.id)
|
|
assert v["entry_id"] == entry.id
|
|
assert v["title"] == "Test"
|
|
assert 0.0 <= v["vitality"] <= 1.0
|
|
assert v["age_days"] >= 0
|
|
|
|
def test_get_vitality_missing(self, archive):
|
|
with pytest.raises(KeyError):
|
|
archive.get_vitality("nonexistent-id")
|
|
|
|
|
|
class TestFadingVibrant:
|
|
def test_fading_returns_sorted_ascending(self, archive):
|
|
# Add entries with different vitalities
|
|
e1 = archive.add(ArchiveEntry(title="Vibrant", content="High energy"))
|
|
e2 = archive.add(ArchiveEntry(title="Fading", content="Low energy"))
|
|
e2.vitality = 0.1
|
|
e2.last_accessed = "2020-01-01T00:00:00+00:00"
|
|
archive._save()
|
|
|
|
results = archive.fading(limit=10)
|
|
assert len(results) == 2
|
|
assert results[0]["vitality"] <= results[1]["vitality"]
|
|
|
|
def test_vibrant_returns_sorted_descending(self, archive):
|
|
e1 = archive.add(ArchiveEntry(title="Fresh", content="New"))
|
|
e2 = archive.add(ArchiveEntry(title="Old", content="Ancient"))
|
|
e2.vitality = 0.1
|
|
e2.last_accessed = "2020-01-01T00:00:00+00:00"
|
|
archive._save()
|
|
|
|
results = archive.vibrant(limit=10)
|
|
assert len(results) == 2
|
|
assert results[0]["vitality"] >= results[1]["vitality"]
|
|
|
|
def test_fading_limit(self, archive):
|
|
for i in range(15):
|
|
archive.add(ArchiveEntry(title=f"Entry {i}", content=f"Content {i}"))
|
|
results = archive.fading(limit=5)
|
|
assert len(results) == 5
|
|
|
|
def test_vibrant_empty(self, archive):
|
|
results = archive.vibrant()
|
|
assert results == []
|