File size: 9,003 Bytes
dbb04e4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c3a3710
 
dbb04e4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c3a3710
 
dbb04e4
 
 
 
 
 
 
 
 
 
 
 
c3a3710
 
dbb04e4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c3a3710
 
dbb04e4
 
 
 
 
 
 
 
 
 
 
 
c3a3710
 
dbb04e4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
"""

Tests for Dependency Injection Migration

=========================================

Verifies that the singleton pattern has been properly removed

and replaced with dependency injection.

"""

import pytest
from unittest.mock import MagicMock, AsyncMock, patch


class TestAsyncRedisStorageDI:
    """Tests for AsyncRedisStorage dependency injection."""

    def test_no_get_instance_method(self):
        """AsyncRedisStorage should not have get_instance class method."""
        from mnemocore.core.async_storage import AsyncRedisStorage
        assert not hasattr(AsyncRedisStorage, 'get_instance'), \
            "AsyncRedisStorage should not have get_instance method"

    def test_constructor_accepts_parameters(self):
        """AsyncRedisStorage constructor should accept explicit parameters."""
        from mnemocore.core.async_storage import AsyncRedisStorage

        # Create with explicit parameters to verify they work
        storage = AsyncRedisStorage(
            url="redis://test:6379/0",
            stream_key="test:stream",
            max_connections=5,
            socket_timeout=10,
            password="testpass",
        )

        # Verify attributes are set
        assert storage.stream_key == "test:stream"

    def test_constructor_with_mock_client(self):
        """AsyncRedisStorage should accept a mock client for testing."""
        from mnemocore.core.async_storage import AsyncRedisStorage

        mock_client = MagicMock()
        storage = AsyncRedisStorage(client=mock_client)

        assert storage.redis_client is mock_client


class TestQdrantStoreDI:
    """Tests for QdrantStore dependency injection."""

    def test_no_get_instance_method(self):
        """QdrantStore should not have get_instance class method."""
        from mnemocore.core.qdrant_store import QdrantStore
        assert not hasattr(QdrantStore, 'get_instance'), \
            "QdrantStore should not have get_instance method"

    def test_constructor_accepts_parameters(self):
        """QdrantStore constructor should accept explicit parameters."""
        from mnemocore.core.qdrant_store import QdrantStore

        store = QdrantStore(
            url="http://test:6333",
            api_key="test-key",
            dimensionality=8192,
            collection_hot="test_hot",
            collection_warm="test_warm",
        )

        assert store.url == "http://test:6333"
        assert store.api_key == "test-key"
        assert store.dim == 8192
        assert store.collection_hot == "test_hot"
        assert store.collection_warm == "test_warm"


class TestContainer:
    """Tests for the dependency injection container."""

    def test_container_exists(self):
        """Container module should exist and be importable."""
        from mnemocore.core.container import Container, build_container
        assert Container is not None
        assert build_container is not None

    def test_build_container_creates_dependencies(self):
        """build_container should create all required dependencies."""
        from mnemocore.core.container import build_container
        from mnemocore.core.config import HAIMConfig

        # Create a minimal config
        config = HAIMConfig()

        with patch('mnemocore.core.container.AsyncRedisStorage') as mock_redis_class, \
             patch('mnemocore.core.container.QdrantStore') as mock_qdrant_class:

            mock_redis_class.return_value = MagicMock()
            mock_qdrant_class.return_value = MagicMock()

            container = build_container(config)

        assert container.config is config
        assert container.redis_storage is not None
        assert container.qdrant_store is not None

    def test_container_dataclass_fields(self):
        """Container should have expected fields."""
        from mnemocore.core.container import Container
        from mnemocore.core.config import HAIMConfig

        config = HAIMConfig()
        container = Container(config=config)

        assert hasattr(container, 'config')
        assert hasattr(container, 'redis_storage')
        assert hasattr(container, 'qdrant_store')


class TestTierManagerDI:
    """Tests for TierManager dependency injection."""

    def test_constructor_accepts_config(self):
        """TierManager constructor should accept config parameter."""
        from mnemocore.core.tier_manager import TierManager
        from mnemocore.core.config import HAIMConfig

        config = HAIMConfig()

        with patch('mnemocore.core.tier_manager.HNSW_AVAILABLE', False), \
             patch('mnemocore.core.tier_manager.FAISS_AVAILABLE', False):
            manager = TierManager(config=config)

        assert manager.config is config

    def test_constructor_accepts_qdrant_store(self):
        """TierManager constructor should accept qdrant_store parameter."""
        from mnemocore.core.tier_manager import TierManager
        from mnemocore.core.config import HAIMConfig

        config = HAIMConfig()
        mock_qdrant = MagicMock()

        with patch('mnemocore.core.tier_manager.HNSW_AVAILABLE', False), \
             patch('mnemocore.core.tier_manager.FAISS_AVAILABLE', False):
            manager = TierManager(config=config, qdrant_store=mock_qdrant)

        assert manager.qdrant is mock_qdrant
        assert manager.use_qdrant is True


class TestHAIMEngineDI:
    """Tests for HAIMEngine dependency injection."""

    def test_constructor_accepts_config(self):
        """HAIMEngine constructor should accept config parameter."""
        from mnemocore.core.engine import HAIMEngine
        from mnemocore.core.config import HAIMConfig

        config = HAIMConfig()

        # Patch at tier_manager level since that's where HNSW/FAISS is used
        with patch('mnemocore.core.tier_manager.HNSW_AVAILABLE', False), \
             patch('mnemocore.core.tier_manager.FAISS_AVAILABLE', False):
            engine = HAIMEngine(config=config)

        assert engine.config is config

    def test_constructor_accepts_tier_manager(self):
        """HAIMEngine constructor should accept tier_manager parameter."""
        from mnemocore.core.engine import HAIMEngine
        from mnemocore.core.config import HAIMConfig
        from mnemocore.core.tier_manager import TierManager

        config = HAIMConfig()

        with patch('mnemocore.core.tier_manager.HNSW_AVAILABLE', False), \
             patch('mnemocore.core.tier_manager.FAISS_AVAILABLE', False):
            tier_manager = TierManager(config=config)
            engine = HAIMEngine(config=config, tier_manager=tier_manager)

        assert engine.tier_manager is tier_manager


class TestConsolidationWorkerDI:
    """Tests for ConsolidationWorker dependency injection."""

    def test_constructor_accepts_storage(self):
        """ConsolidationWorker constructor should accept storage parameter."""
        from mnemocore.core.consolidation_worker import ConsolidationWorker

        mock_storage = MagicMock()
        mock_tier_manager = MagicMock()

        worker = ConsolidationWorker(
            storage=mock_storage,
            tier_manager=mock_tier_manager,
        )

        assert worker.storage is mock_storage
        assert worker.tier_manager is mock_tier_manager


class TestNoSingletonPattern:
    """Tests to ensure singleton pattern is fully removed."""

    def test_no_singleton_instances(self):
        """Classes should not have _instance class attribute for singletons."""
        from mnemocore.core.async_storage import AsyncRedisStorage
        from mnemocore.core.qdrant_store import QdrantStore

        # _instance is the typical singleton storage attribute
        assert not hasattr(AsyncRedisStorage, '_instance') or \
               AsyncRedisStorage._instance is None or \
               '_instance' not in AsyncRedisStorage.__dict__

        # Note: QdrantStore might have _instance from object base,
        # but shouldn't have it defined explicitly for singleton use
        if hasattr(QdrantStore, '_instance'):
            # Check it's not being used as singleton storage
            assert '_instance' not in QdrantStore.__dict__ or \
                   QdrantStore.__dict__['_instance'] is None

    def test_multiple_instances_independent(self):
        """Creating multiple instances should work independently."""
        from mnemocore.core.async_storage import AsyncRedisStorage

        mock_client1 = MagicMock()
        mock_client2 = MagicMock()

        storage1 = AsyncRedisStorage(client=mock_client1)
        storage2 = AsyncRedisStorage(client=mock_client2)

        # Each should have its own client
        assert storage1.redis_client is mock_client1
        assert storage2.redis_client is mock_client2
        assert storage1 is not storage2