Skip to content

Commit 6f3f366

Browse files
committed
Fix characterization tests: portable imports + tmp_path data_dir
Characterization tests were written during an old refactor and hardcoded: - sys.path manipulation referencing C:/ROAMPAL-REFACTOR (long-defunct dir) - data_dir="C:/ROAMPAL/ui-implementation/src-tauri/binaries/data" absolute path On CI runners (D:\a\...) both of these fail. Locally they worked only because those paths happened to exist on the dev machine. Changes: - Replaced get_original_memory_system() / get_original_action_outcome() / get_original_wilson_score() helpers with plain top-of-file imports. The sys.path juggling was scaffolding to swap between a pre-refactor tree and a post-refactor tree — that refactor shipped in v0.3.1 so the juggling is useless. - Replaced hardcoded data_dir with pytest's tmp_path fixture. Fixture scope bumped from "class" to default so each test gets a fresh temp directory. Kept every test assertion — threshold values, method existence, API shape checks, ActionOutcome dataclass behavior — because those are real regression guards worth keeping. 23 characterization tests pass (was 0 collectable on fresh env). Full backend: 529 passed / 1 skipped / 0 failed.
1 parent ebc2abb commit 6f3f366

2 files changed

Lines changed: 19 additions & 161 deletions

File tree

ui-implementation/src-tauri/backend/tests/characterization/test_outcome_behavior.py

Lines changed: 8 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -2,88 +2,24 @@
22
Characterization Tests for UnifiedMemorySystem outcome recording.
33
44
These tests capture the CURRENT behavior of record_outcome() and related methods.
5-
They serve as a regression safety net during refactoring.
6-
7-
Uses lazy imports inside fixtures to ensure path is set correctly.
5+
They serve as a regression safety net against accidental threshold / API drift.
86
"""
97

108
import asyncio
119
from datetime import datetime
1210

1311
import pytest
1412

15-
16-
def clear_memory_modules():
17-
"""Clear all cached modules.memory.* modules to force fresh import."""
18-
import sys
19-
to_delete = [key for key in sys.modules if key.startswith("modules.memory") or key == "modules"]
20-
for key in to_delete:
21-
del sys.modules[key]
22-
23-
24-
def get_original_memory_system():
25-
"""Import and return the original UnifiedMemorySystem.
26-
27-
Uses importlib to ensure fresh import with correct path.
28-
"""
29-
import sys
30-
31-
# Clear cached modules first
32-
clear_memory_modules()
33-
34-
# Remove refactor path if present and ensure original is first
35-
refactor_path = "C:/ROAMPAL-REFACTOR"
36-
backend_path = "C:/ROAMPAL/ui-implementation/src-tauri/backend"
37-
38-
# Remove refactor path if it's there
39-
while refactor_path in sys.path:
40-
sys.path.remove(refactor_path)
41-
42-
# Ensure original backend path is first
43-
if backend_path not in sys.path:
44-
sys.path.insert(0, backend_path)
45-
elif sys.path.index(backend_path) != 0:
46-
sys.path.remove(backend_path)
47-
sys.path.insert(0, backend_path)
48-
49-
from modules.memory.unified_memory_system import UnifiedMemorySystem
50-
return UnifiedMemorySystem
51-
52-
53-
def get_original_action_outcome():
54-
"""Import and return the original ActionOutcome dataclass."""
55-
import sys
56-
57-
# Clear cached modules first
58-
clear_memory_modules()
59-
60-
# Remove refactor path if present and ensure original is first
61-
refactor_path = "C:/ROAMPAL-REFACTOR"
62-
backend_path = "C:/ROAMPAL/ui-implementation/src-tauri/backend"
63-
64-
# Remove refactor path if it's there
65-
while refactor_path in sys.path:
66-
sys.path.remove(refactor_path)
67-
68-
# Ensure original backend path is first
69-
if backend_path not in sys.path:
70-
sys.path.insert(0, backend_path)
71-
elif sys.path.index(backend_path) != 0:
72-
sys.path.remove(backend_path)
73-
sys.path.insert(0, backend_path)
74-
75-
from modules.memory.unified_memory_system import ActionOutcome
76-
return ActionOutcome
13+
from modules.memory.unified_memory_system import UnifiedMemorySystem, ActionOutcome
7714

7815

7916
class TestOutcomeBehavior:
8017
"""Capture current outcome recording behavior."""
8118

82-
@pytest.fixture(scope="class")
83-
def memory_system(self):
84-
UnifiedMemorySystem = get_original_memory_system()
19+
@pytest.fixture
20+
def memory_system(self, tmp_path):
8521
ms = UnifiedMemorySystem(
86-
data_dir="C:/ROAMPAL/ui-implementation/src-tauri/binaries/data",
22+
data_dir=str(tmp_path),
8723
use_server=False
8824
)
8925
return ms
@@ -139,11 +75,10 @@ def test_threshold_values(self, memory_system):
13975
class TestPromotionBehavior:
14076
"""Capture current promotion/demotion behavior."""
14177

142-
@pytest.fixture(scope="class")
143-
def memory_system(self):
144-
UnifiedMemorySystem = get_original_memory_system()
78+
@pytest.fixture
79+
def memory_system(self, tmp_path):
14580
ms = UnifiedMemorySystem(
146-
data_dir="C:/ROAMPAL/ui-implementation/src-tauri/binaries/data",
81+
data_dir=str(tmp_path),
14782
use_server=False
14883
)
14984
return ms
@@ -168,12 +103,10 @@ class TestActionOutcome:
168103

169104
def test_action_outcome_import(self):
170105
"""ActionOutcome should be importable."""
171-
ActionOutcome = get_original_action_outcome()
172106
assert ActionOutcome is not None
173107

174108
def test_action_outcome_fields(self):
175109
"""ActionOutcome should have expected fields."""
176-
ActionOutcome = get_original_action_outcome()
177110

178111
# Create instance with actual API (v0.2.1 Causal Learning)
179112
ao = ActionOutcome(
@@ -188,7 +121,6 @@ def test_action_outcome_fields(self):
188121

189122
def test_action_outcome_to_dict(self):
190123
"""ActionOutcome.to_dict() should work."""
191-
ActionOutcome = get_original_action_outcome()
192124

193125
ao = ActionOutcome(
194126
action_type="search_memory",
@@ -205,7 +137,6 @@ def test_action_outcome_to_dict(self):
205137

206138
def test_action_outcome_from_dict(self):
207139
"""ActionOutcome.from_dict() should work."""
208-
ActionOutcome = get_original_action_outcome()
209140

210141
data = {
211142
"action_type": "search_memory",

ui-implementation/src-tauri/backend/tests/characterization/test_search_behavior.py

Lines changed: 11 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,8 @@
1-
r"""
1+
"""
22
Characterization Tests for UnifiedMemorySystem.search()
33
44
These tests capture the CURRENT behavior of the search() method.
5-
They serve as a regression safety net during refactoring.
6-
7-
Uses lazy imports inside fixtures to ensure path is set correctly.
8-
9-
Usage:
10-
cd C:/ROAMPAL-REFACTOR
11-
python -m pytest tests/characterization/ -v
5+
They serve as a regression safety net against API / return-shape drift.
126
"""
137

148
import asyncio
@@ -18,68 +12,8 @@
1812

1913
import pytest
2014

21-
22-
def clear_memory_modules():
23-
"""Clear all cached modules.memory.* modules to force fresh import."""
24-
import sys
25-
to_delete = [key for key in sys.modules if key.startswith("modules.memory") or key == "modules"]
26-
for key in to_delete:
27-
del sys.modules[key]
28-
29-
30-
def get_original_memory_system():
31-
"""Import and return the original UnifiedMemorySystem.
32-
33-
Uses importlib to ensure fresh import with correct path.
34-
"""
35-
import sys
36-
37-
# Clear cached modules first
38-
clear_memory_modules()
39-
40-
# Remove refactor path if present and ensure original is first
41-
refactor_path = "C:/ROAMPAL-REFACTOR"
42-
backend_path = "C:/ROAMPAL/ui-implementation/src-tauri/backend"
43-
44-
# Remove refactor path if it's there
45-
while refactor_path in sys.path:
46-
sys.path.remove(refactor_path)
47-
48-
# Ensure original backend path is first
49-
if backend_path not in sys.path:
50-
sys.path.insert(0, backend_path)
51-
elif sys.path.index(backend_path) != 0:
52-
sys.path.remove(backend_path)
53-
sys.path.insert(0, backend_path)
54-
55-
from modules.memory.unified_memory_system import UnifiedMemorySystem
56-
return UnifiedMemorySystem
57-
58-
59-
def get_original_wilson_score():
60-
"""Import and return the original wilson_score_lower function."""
61-
import sys
62-
63-
# Clear cached modules first
64-
clear_memory_modules()
65-
66-
# Remove refactor path if present and ensure original is first
67-
refactor_path = "C:/ROAMPAL-REFACTOR"
68-
backend_path = "C:/ROAMPAL/ui-implementation/src-tauri/backend"
69-
70-
# Remove refactor path if it's there
71-
while refactor_path in sys.path:
72-
sys.path.remove(refactor_path)
73-
74-
# Ensure original backend path is first
75-
if backend_path not in sys.path:
76-
sys.path.insert(0, backend_path)
77-
elif sys.path.index(backend_path) != 0:
78-
sys.path.remove(backend_path)
79-
sys.path.insert(0, backend_path)
80-
81-
from modules.memory.scoring_service import wilson_score_lower
82-
return wilson_score_lower
15+
from modules.memory.unified_memory_system import UnifiedMemorySystem
16+
from modules.memory.scoring_service import wilson_score_lower
8317

8418

8519
# Characterization queries from the plan
@@ -98,13 +32,11 @@ def get_original_wilson_score():
9832
class TestSearchBehavior:
9933
"""Capture current search() behavior for regression testing."""
10034

101-
@pytest.fixture(scope="class")
102-
def memory_system(self):
103-
"""Create a single instance for all tests in this class."""
104-
UnifiedMemorySystem = get_original_memory_system()
105-
# Use production data directory (read-only access)
35+
@pytest.fixture
36+
def memory_system(self, tmp_path):
37+
"""Create a single instance for each test."""
10638
ms = UnifiedMemorySystem(
107-
data_dir="C:/ROAMPAL/ui-implementation/src-tauri/binaries/data",
39+
data_dir=str(tmp_path),
10840
use_server=False # Don't start embedding server
10941
)
11042
return ms
@@ -191,18 +123,15 @@ class TestWilsonScore:
191123

192124
def test_wilson_score_import(self):
193125
"""Wilson score function should be importable."""
194-
wilson_score_lower = get_original_wilson_score()
195126
assert callable(wilson_score_lower)
196127

197128
def test_wilson_score_zero_uses(self):
198129
"""Zero uses should return 0.5 (neutral)."""
199-
wilson_score_lower = get_original_wilson_score()
200130
score = wilson_score_lower(0, 0)
201131
assert score == 0.5, f"Expected 0.5 for zero uses, got {score}"
202132

203133
def test_wilson_score_perfect_record(self):
204134
"""Perfect record with few uses should be lower than proven record."""
205-
wilson_score_lower = get_original_wilson_score()
206135

207136
# 1/1 = 100% but low confidence
208137
new_score = wilson_score_lower(1, 1)
@@ -216,7 +145,6 @@ def test_wilson_score_perfect_record(self):
216145

217146
def test_wilson_score_range(self):
218147
"""Wilson score should always be between 0 and 1."""
219-
wilson_score_lower = get_original_wilson_score()
220148

221149
test_cases = [
222150
(0, 1),
@@ -235,11 +163,10 @@ def test_wilson_score_range(self):
235163
class TestMCPToolShapes:
236164
"""Verify MCP tool response shapes match expected format."""
237165

238-
@pytest.fixture(scope="class")
239-
def memory_system(self):
240-
UnifiedMemorySystem = get_original_memory_system()
166+
@pytest.fixture
167+
def memory_system(self, tmp_path):
241168
ms = UnifiedMemorySystem(
242-
data_dir="C:/ROAMPAL/ui-implementation/src-tauri/binaries/data",
169+
data_dir=str(tmp_path),
243170
use_server=False
244171
)
245172
return ms

0 commit comments

Comments
 (0)