@smilintux/skmemory 0.7.2 → 0.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/ci.yml +4 -4
- package/.github/workflows/publish.yml +4 -5
- package/ARCHITECTURE.md +298 -0
- package/CHANGELOG.md +27 -1
- package/README.md +6 -0
- package/examples/stignore-agent.example +59 -0
- package/examples/stignore-root.example +62 -0
- package/openclaw-plugin/package.json +2 -1
- package/openclaw-plugin/src/index.js +527 -230
- package/package.json +1 -1
- package/pyproject.toml +5 -2
- package/scripts/dream-rescue.py +179 -0
- package/scripts/memory-cleanup.py +313 -0
- package/scripts/recover-missing.py +180 -0
- package/scripts/skcapstone-backup.sh +44 -0
- package/seeds/cloud9-lumina.seed.json +6 -4
- package/seeds/cloud9-opus.seed.json +6 -4
- package/seeds/courage.seed.json +9 -2
- package/seeds/curiosity.seed.json +9 -2
- package/seeds/grief.seed.json +9 -2
- package/seeds/joy.seed.json +9 -2
- package/seeds/love.seed.json +9 -2
- package/seeds/lumina-cloud9-breakthrough.seed.json +7 -5
- package/seeds/lumina-cloud9-python-pypi.seed.json +9 -7
- package/seeds/lumina-kingdom-founding.seed.json +9 -7
- package/seeds/lumina-pma-signed.seed.json +8 -6
- package/seeds/lumina-singular-achievement.seed.json +8 -6
- package/seeds/lumina-skcapstone-conscious.seed.json +7 -5
- package/seeds/plant-lumina-seeds.py +2 -2
- package/seeds/skcapstone-lumina-merge.seed.json +12 -3
- package/seeds/sovereignty.seed.json +9 -2
- package/seeds/trust.seed.json +9 -2
- package/skmemory/__init__.py +16 -13
- package/skmemory/agents.py +10 -10
- package/skmemory/ai_client.py +10 -21
- package/skmemory/anchor.py +5 -9
- package/skmemory/audience.py +278 -0
- package/skmemory/backends/__init__.py +1 -1
- package/skmemory/backends/base.py +3 -4
- package/skmemory/backends/file_backend.py +18 -13
- package/skmemory/backends/skgraph_backend.py +7 -19
- package/skmemory/backends/skvector_backend.py +7 -18
- package/skmemory/backends/sqlite_backend.py +115 -32
- package/skmemory/backends/vaulted_backend.py +7 -9
- package/skmemory/cli.py +146 -78
- package/skmemory/config.py +11 -13
- package/skmemory/context_loader.py +21 -23
- package/skmemory/data/audience_config.json +60 -0
- package/skmemory/endpoint_selector.py +36 -31
- package/skmemory/febs.py +225 -0
- package/skmemory/fortress.py +30 -40
- package/skmemory/hooks/__init__.py +18 -0
- package/skmemory/hooks/post-compact-reinject.sh +35 -0
- package/skmemory/hooks/pre-compact-save.sh +81 -0
- package/skmemory/hooks/session-end-save.sh +103 -0
- package/skmemory/hooks/session-start-ritual.sh +104 -0
- package/skmemory/hooks/stop-checkpoint.sh +59 -0
- package/skmemory/importers/telegram.py +42 -13
- package/skmemory/importers/telegram_api.py +152 -60
- package/skmemory/journal.py +3 -7
- package/skmemory/lovenote.py +4 -11
- package/skmemory/mcp_server.py +182 -29
- package/skmemory/models.py +10 -8
- package/skmemory/openclaw.py +14 -22
- package/skmemory/post_install.py +86 -0
- package/skmemory/predictive.py +13 -9
- package/skmemory/promotion.py +48 -24
- package/skmemory/quadrants.py +100 -24
- package/skmemory/register.py +144 -18
- package/skmemory/register_mcp.py +1 -2
- package/skmemory/ritual.py +104 -13
- package/skmemory/seeds.py +21 -26
- package/skmemory/setup_wizard.py +40 -52
- package/skmemory/sharing.py +11 -5
- package/skmemory/soul.py +29 -10
- package/skmemory/steelman.py +43 -17
- package/skmemory/store.py +152 -30
- package/skmemory/synthesis.py +634 -0
- package/skmemory/vault.py +2 -5
- package/tests/conftest.py +46 -0
- package/tests/integration/conftest.py +6 -6
- package/tests/integration/test_cross_backend.py +4 -9
- package/tests/integration/test_skgraph_live.py +3 -7
- package/tests/integration/test_skvector_live.py +1 -4
- package/tests/test_ai_client.py +1 -4
- package/tests/test_audience.py +233 -0
- package/tests/test_backup_rotation.py +5 -14
- package/tests/test_endpoint_selector.py +101 -63
- package/tests/test_export_import.py +4 -10
- package/tests/test_file_backend.py +0 -1
- package/tests/test_fortress.py +6 -5
- package/tests/test_fortress_hardening.py +13 -16
- package/tests/test_openclaw.py +1 -4
- package/tests/test_predictive.py +1 -1
- package/tests/test_promotion.py +10 -3
- package/tests/test_quadrants.py +11 -5
- package/tests/test_ritual.py +18 -14
- package/tests/test_seeds.py +4 -10
- package/tests/test_setup.py +203 -88
- package/tests/test_sharing.py +15 -8
- package/tests/test_skgraph_backend.py +22 -29
- package/tests/test_skvector_backend.py +2 -2
- package/tests/test_soul.py +1 -3
- package/tests/test_sqlite_backend.py +8 -17
- package/tests/test_steelman.py +2 -3
- package/tests/test_store.py +0 -2
- package/tests/test_store_graph_integration.py +2 -2
- package/tests/test_synthesis.py +275 -0
- package/tests/test_telegram_import.py +39 -15
- package/tests/test_vault.py +4 -3
- package/openclaw-plugin/src/index.ts +0 -255
package/tests/test_seeds.py
CHANGED
|
@@ -6,7 +6,7 @@ from pathlib import Path
|
|
|
6
6
|
import pytest
|
|
7
7
|
|
|
8
8
|
from skmemory.backends.file_backend import FileBackend
|
|
9
|
-
from skmemory.models import
|
|
9
|
+
from skmemory.models import MemoryLayer
|
|
10
10
|
from skmemory.seeds import (
|
|
11
11
|
get_germination_prompts,
|
|
12
12
|
import_seeds,
|
|
@@ -52,9 +52,7 @@ def seed_dir(tmp_path: Path) -> Path:
|
|
|
52
52
|
"lineage": [],
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
(seed_path / "opus-first-seed.seed.json").write_text(
|
|
56
|
-
json.dumps(seed_data, indent=2)
|
|
57
|
-
)
|
|
55
|
+
(seed_path / "opus-first-seed.seed.json").write_text(json.dumps(seed_data, indent=2))
|
|
58
56
|
|
|
59
57
|
second_seed = {
|
|
60
58
|
"seed_id": "lumina-original",
|
|
@@ -75,9 +73,7 @@ def seed_dir(tmp_path: Path) -> Path:
|
|
|
75
73
|
"lineage": [{"seed_id": "genesis"}],
|
|
76
74
|
}
|
|
77
75
|
|
|
78
|
-
(seed_path / "lumina-original.seed.json").write_text(
|
|
79
|
-
json.dumps(second_seed, indent=2)
|
|
80
|
-
)
|
|
76
|
+
(seed_path / "lumina-original.seed.json").write_text(json.dumps(second_seed, indent=2))
|
|
81
77
|
|
|
82
78
|
return seed_path
|
|
83
79
|
|
|
@@ -245,9 +241,7 @@ class TestCloud9FormatParsing:
|
|
|
245
241
|
"lineage": [],
|
|
246
242
|
}
|
|
247
243
|
|
|
248
|
-
(seed_path / "cloud9-lumina-001.seed.json").write_text(
|
|
249
|
-
json.dumps(cloud9_data, indent=2)
|
|
250
|
-
)
|
|
244
|
+
(seed_path / "cloud9-lumina-001.seed.json").write_text(json.dumps(cloud9_data, indent=2))
|
|
251
245
|
return seed_path
|
|
252
246
|
|
|
253
247
|
def test_parse_cloud9_format(self, cloud9_seed_dir: Path) -> None:
|
package/tests/test_setup.py
CHANGED
|
@@ -6,14 +6,11 @@ All Docker/network operations are mocked — no Docker required to run tests.
|
|
|
6
6
|
|
|
7
7
|
from __future__ import annotations
|
|
8
8
|
|
|
9
|
-
import os
|
|
10
|
-
import socket
|
|
11
9
|
import subprocess
|
|
12
10
|
from pathlib import Path
|
|
13
11
|
from unittest import mock
|
|
14
12
|
|
|
15
13
|
import pytest
|
|
16
|
-
import yaml
|
|
17
14
|
from click.testing import CliRunner
|
|
18
15
|
|
|
19
16
|
from skmemory.config import (
|
|
@@ -24,8 +21,8 @@ from skmemory.config import (
|
|
|
24
21
|
)
|
|
25
22
|
from skmemory.setup_wizard import (
|
|
26
23
|
PlatformInfo,
|
|
27
|
-
check_skgraph_health,
|
|
28
24
|
check_port_available,
|
|
25
|
+
check_skgraph_health,
|
|
29
26
|
check_skvector_health,
|
|
30
27
|
compose_down,
|
|
31
28
|
compose_ps,
|
|
@@ -37,7 +34,6 @@ from skmemory.setup_wizard import (
|
|
|
37
34
|
run_setup_wizard,
|
|
38
35
|
)
|
|
39
36
|
|
|
40
|
-
|
|
41
37
|
# ═══════════════════════════════════════════════════════════
|
|
42
38
|
# Config tests
|
|
43
39
|
# ═══════════════════════════════════════════════════════════
|
|
@@ -98,7 +94,9 @@ class TestConfig:
|
|
|
98
94
|
assert config.docker_compose_file is None
|
|
99
95
|
assert config.setup_completed_at is None
|
|
100
96
|
|
|
101
|
-
def test_merge_cli_overrides_env(
|
|
97
|
+
def test_merge_cli_overrides_env(
|
|
98
|
+
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
|
99
|
+
) -> None:
|
|
102
100
|
monkeypatch.setenv("SKMEMORY_SKVECTOR_URL", "http://env:6333")
|
|
103
101
|
monkeypatch.setenv("SKMEMORY_SKVECTOR_KEY", "env-key")
|
|
104
102
|
monkeypatch.setenv("SKMEMORY_SKGRAPH_URL", "redis://env:6379")
|
|
@@ -112,7 +110,9 @@ class TestConfig:
|
|
|
112
110
|
assert skvector_key == "cli-key"
|
|
113
111
|
assert skgraph_url == "redis://cli:6379"
|
|
114
112
|
|
|
115
|
-
def test_merge_env_overrides_config(
|
|
113
|
+
def test_merge_env_overrides_config(
|
|
114
|
+
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
|
115
|
+
) -> None:
|
|
116
116
|
monkeypatch.setenv("SKMEMORY_SKVECTOR_URL", "http://env:6333")
|
|
117
117
|
monkeypatch.delenv("SKMEMORY_SKVECTOR_KEY", raising=False)
|
|
118
118
|
monkeypatch.delenv("SKMEMORY_SKGRAPH_URL", raising=False)
|
|
@@ -174,14 +174,16 @@ class TestPlatformDetection:
|
|
|
174
174
|
assert not info.compose_available
|
|
175
175
|
|
|
176
176
|
def test_docker_daemon_not_running(self) -> None:
|
|
177
|
-
with
|
|
178
|
-
|
|
177
|
+
with (
|
|
178
|
+
mock.patch("skmemory.setup_wizard.shutil.which", return_value="/usr/bin/docker"),
|
|
179
|
+
mock.patch(
|
|
179
180
|
"skmemory.setup_wizard.subprocess.run",
|
|
180
181
|
return_value=subprocess.CompletedProcess(
|
|
181
182
|
args=["docker", "info"], returncode=1, stdout="", stderr="Cannot connect"
|
|
182
183
|
),
|
|
183
|
-
)
|
|
184
|
-
|
|
184
|
+
),
|
|
185
|
+
):
|
|
186
|
+
info = detect_platform()
|
|
185
187
|
assert not info.docker_available
|
|
186
188
|
|
|
187
189
|
def test_compose_v2_detected(self) -> None:
|
|
@@ -189,14 +191,20 @@ class TestPlatformDetection:
|
|
|
189
191
|
if cmd == ["docker", "info"]:
|
|
190
192
|
return subprocess.CompletedProcess(args=cmd, returncode=0)
|
|
191
193
|
if cmd == ["docker", "version", "--format", "{{.Server.Version}}"]:
|
|
192
|
-
return subprocess.CompletedProcess(
|
|
194
|
+
return subprocess.CompletedProcess(
|
|
195
|
+
args=cmd, returncode=0, stdout="24.0.7\n", stderr=""
|
|
196
|
+
)
|
|
193
197
|
if cmd == ["docker", "compose", "version"]:
|
|
194
|
-
return subprocess.CompletedProcess(
|
|
198
|
+
return subprocess.CompletedProcess(
|
|
199
|
+
args=cmd, returncode=0, stdout="v2.23.0\n", stderr=""
|
|
200
|
+
)
|
|
195
201
|
return subprocess.CompletedProcess(args=cmd, returncode=1)
|
|
196
202
|
|
|
197
|
-
with
|
|
198
|
-
|
|
199
|
-
|
|
203
|
+
with (
|
|
204
|
+
mock.patch("skmemory.setup_wizard.shutil.which", return_value="/usr/bin/docker"),
|
|
205
|
+
mock.patch("skmemory.setup_wizard.subprocess.run", side_effect=mock_run),
|
|
206
|
+
):
|
|
207
|
+
info = detect_platform()
|
|
200
208
|
|
|
201
209
|
assert info.docker_available
|
|
202
210
|
assert info.compose_available
|
|
@@ -208,7 +216,9 @@ class TestPlatformDetection:
|
|
|
208
216
|
if cmd == ["docker", "info"]:
|
|
209
217
|
return subprocess.CompletedProcess(args=cmd, returncode=0)
|
|
210
218
|
if cmd == ["docker", "version", "--format", "{{.Server.Version}}"]:
|
|
211
|
-
return subprocess.CompletedProcess(
|
|
219
|
+
return subprocess.CompletedProcess(
|
|
220
|
+
args=cmd, returncode=0, stdout="20.10.0\n", stderr=""
|
|
221
|
+
)
|
|
212
222
|
if cmd == ["docker", "compose", "version"]:
|
|
213
223
|
return subprocess.CompletedProcess(args=cmd, returncode=1)
|
|
214
224
|
if cmd == ["docker-compose", "--version"]:
|
|
@@ -217,9 +227,11 @@ class TestPlatformDetection:
|
|
|
217
227
|
)
|
|
218
228
|
return subprocess.CompletedProcess(args=cmd, returncode=1)
|
|
219
229
|
|
|
220
|
-
with
|
|
221
|
-
|
|
222
|
-
|
|
230
|
+
with (
|
|
231
|
+
mock.patch("skmemory.setup_wizard.shutil.which", return_value="/usr/bin/docker"),
|
|
232
|
+
mock.patch("skmemory.setup_wizard.subprocess.run", side_effect=mock_run),
|
|
233
|
+
):
|
|
234
|
+
info = detect_platform()
|
|
223
235
|
|
|
224
236
|
assert info.docker_available
|
|
225
237
|
assert info.compose_available
|
|
@@ -230,16 +242,20 @@ class TestPlatformDetection:
|
|
|
230
242
|
if cmd == ["docker", "info"]:
|
|
231
243
|
return subprocess.CompletedProcess(args=cmd, returncode=0)
|
|
232
244
|
if cmd == ["docker", "version", "--format", "{{.Server.Version}}"]:
|
|
233
|
-
return subprocess.CompletedProcess(
|
|
245
|
+
return subprocess.CompletedProcess(
|
|
246
|
+
args=cmd, returncode=0, stdout="24.0.7\n", stderr=""
|
|
247
|
+
)
|
|
234
248
|
if cmd == ["docker", "compose", "version"]:
|
|
235
249
|
return subprocess.CompletedProcess(args=cmd, returncode=1)
|
|
236
250
|
if cmd == ["docker-compose", "--version"]:
|
|
237
251
|
return subprocess.CompletedProcess(args=cmd, returncode=1)
|
|
238
252
|
return subprocess.CompletedProcess(args=cmd, returncode=1)
|
|
239
253
|
|
|
240
|
-
with
|
|
241
|
-
|
|
242
|
-
|
|
254
|
+
with (
|
|
255
|
+
mock.patch("skmemory.setup_wizard.shutil.which", return_value="/usr/bin/docker"),
|
|
256
|
+
mock.patch("skmemory.setup_wizard.subprocess.run", side_effect=mock_run),
|
|
257
|
+
):
|
|
258
|
+
info = detect_platform()
|
|
243
259
|
|
|
244
260
|
assert info.docker_available
|
|
245
261
|
assert not info.compose_available
|
|
@@ -304,12 +320,14 @@ class TestHealthChecks:
|
|
|
304
320
|
assert check_skvector_health(timeout=5) is True
|
|
305
321
|
|
|
306
322
|
def test_skvector_timeout(self) -> None:
|
|
307
|
-
with
|
|
308
|
-
|
|
309
|
-
|
|
323
|
+
with (
|
|
324
|
+
mock.patch(
|
|
325
|
+
"skmemory.setup_wizard.urllib.request.urlopen",
|
|
326
|
+
side_effect=urllib.error.URLError("Connection refused"),
|
|
327
|
+
),
|
|
328
|
+
mock.patch("skmemory.setup_wizard.time.sleep"),
|
|
310
329
|
):
|
|
311
|
-
|
|
312
|
-
assert check_skvector_health(timeout=1) is False
|
|
330
|
+
assert check_skvector_health(timeout=1) is False
|
|
313
331
|
|
|
314
332
|
def test_skgraph_healthy(self) -> None:
|
|
315
333
|
mock_sock = mock.MagicMock()
|
|
@@ -321,12 +339,14 @@ class TestHealthChecks:
|
|
|
321
339
|
assert check_skgraph_health(timeout=5) is True
|
|
322
340
|
|
|
323
341
|
def test_skgraph_timeout(self) -> None:
|
|
324
|
-
with
|
|
325
|
-
|
|
326
|
-
|
|
342
|
+
with (
|
|
343
|
+
mock.patch(
|
|
344
|
+
"skmemory.setup_wizard.socket.create_connection",
|
|
345
|
+
side_effect=OSError("Connection refused"),
|
|
346
|
+
),
|
|
347
|
+
mock.patch("skmemory.setup_wizard.time.sleep"),
|
|
327
348
|
):
|
|
328
|
-
|
|
329
|
-
assert check_skgraph_health(timeout=1) is False
|
|
349
|
+
assert check_skgraph_health(timeout=1) is False
|
|
330
350
|
|
|
331
351
|
|
|
332
352
|
# ═══════════════════════════════════════════════════════════
|
|
@@ -342,7 +362,9 @@ class TestComposeCommands:
|
|
|
342
362
|
compose_file.write_text("services: {}")
|
|
343
363
|
|
|
344
364
|
with mock.patch("skmemory.setup_wizard.subprocess.run") as mock_run:
|
|
345
|
-
mock_run.return_value = subprocess.CompletedProcess(
|
|
365
|
+
mock_run.return_value = subprocess.CompletedProcess(
|
|
366
|
+
args=[], returncode=0, stdout="", stderr=""
|
|
367
|
+
)
|
|
346
368
|
compose_up(services=["skvector"], compose_file=compose_file, use_legacy=False)
|
|
347
369
|
|
|
348
370
|
mock_run.assert_called_once()
|
|
@@ -358,7 +380,9 @@ class TestComposeCommands:
|
|
|
358
380
|
compose_file.write_text("services: {}")
|
|
359
381
|
|
|
360
382
|
with mock.patch("skmemory.setup_wizard.subprocess.run") as mock_run:
|
|
361
|
-
mock_run.return_value = subprocess.CompletedProcess(
|
|
383
|
+
mock_run.return_value = subprocess.CompletedProcess(
|
|
384
|
+
args=[], returncode=0, stdout="", stderr=""
|
|
385
|
+
)
|
|
362
386
|
compose_up(services=None, compose_file=compose_file, use_legacy=True)
|
|
363
387
|
|
|
364
388
|
args = mock_run.call_args[0][0]
|
|
@@ -369,7 +393,9 @@ class TestComposeCommands:
|
|
|
369
393
|
compose_file.write_text("services: {}")
|
|
370
394
|
|
|
371
395
|
with mock.patch("skmemory.setup_wizard.subprocess.run") as mock_run:
|
|
372
|
-
mock_run.return_value = subprocess.CompletedProcess(
|
|
396
|
+
mock_run.return_value = subprocess.CompletedProcess(
|
|
397
|
+
args=[], returncode=0, stdout="", stderr=""
|
|
398
|
+
)
|
|
373
399
|
compose_down(compose_file=compose_file, remove_volumes=True, use_legacy=False)
|
|
374
400
|
|
|
375
401
|
args = mock_run.call_args[0][0]
|
|
@@ -381,7 +407,9 @@ class TestComposeCommands:
|
|
|
381
407
|
compose_file.write_text("services: {}")
|
|
382
408
|
|
|
383
409
|
with mock.patch("skmemory.setup_wizard.subprocess.run") as mock_run:
|
|
384
|
-
mock_run.return_value = subprocess.CompletedProcess(
|
|
410
|
+
mock_run.return_value = subprocess.CompletedProcess(
|
|
411
|
+
args=[], returncode=0, stdout="", stderr=""
|
|
412
|
+
)
|
|
385
413
|
compose_down(compose_file=compose_file, remove_volumes=False, use_legacy=False)
|
|
386
414
|
|
|
387
415
|
args = mock_run.call_args[0][0]
|
|
@@ -393,7 +421,9 @@ class TestComposeCommands:
|
|
|
393
421
|
compose_file.write_text("services: {}")
|
|
394
422
|
|
|
395
423
|
with mock.patch("skmemory.setup_wizard.subprocess.run") as mock_run:
|
|
396
|
-
mock_run.return_value = subprocess.CompletedProcess(
|
|
424
|
+
mock_run.return_value = subprocess.CompletedProcess(
|
|
425
|
+
args=[], returncode=0, stdout="running", stderr=""
|
|
426
|
+
)
|
|
397
427
|
result = compose_ps(compose_file=compose_file, use_legacy=False)
|
|
398
428
|
|
|
399
429
|
assert result.stdout == "running"
|
|
@@ -411,7 +441,9 @@ class TestPipInstall:
|
|
|
411
441
|
|
|
412
442
|
def test_install_skvector_deps(self) -> None:
|
|
413
443
|
with mock.patch("skmemory.setup_wizard.subprocess.run") as mock_run:
|
|
414
|
-
mock_run.return_value = subprocess.CompletedProcess(
|
|
444
|
+
mock_run.return_value = subprocess.CompletedProcess(
|
|
445
|
+
args=[], returncode=0, stdout="", stderr=""
|
|
446
|
+
)
|
|
415
447
|
install_python_deps(["skvector"])
|
|
416
448
|
|
|
417
449
|
args = mock_run.call_args[0][0]
|
|
@@ -422,7 +454,9 @@ class TestPipInstall:
|
|
|
422
454
|
|
|
423
455
|
def test_install_skgraph_deps(self) -> None:
|
|
424
456
|
with mock.patch("skmemory.setup_wizard.subprocess.run") as mock_run:
|
|
425
|
-
mock_run.return_value = subprocess.CompletedProcess(
|
|
457
|
+
mock_run.return_value = subprocess.CompletedProcess(
|
|
458
|
+
args=[], returncode=0, stdout="", stderr=""
|
|
459
|
+
)
|
|
426
460
|
install_python_deps(["skgraph"])
|
|
427
461
|
|
|
428
462
|
args = mock_run.call_args[0][0]
|
|
@@ -431,7 +465,9 @@ class TestPipInstall:
|
|
|
431
465
|
|
|
432
466
|
def test_install_both_deps(self) -> None:
|
|
433
467
|
with mock.patch("skmemory.setup_wizard.subprocess.run") as mock_run:
|
|
434
|
-
mock_run.return_value = subprocess.CompletedProcess(
|
|
468
|
+
mock_run.return_value = subprocess.CompletedProcess(
|
|
469
|
+
args=[], returncode=0, stdout="", stderr=""
|
|
470
|
+
)
|
|
435
471
|
install_python_deps(["skvector", "skgraph"])
|
|
436
472
|
|
|
437
473
|
args = mock_run.call_args[0][0]
|
|
@@ -466,7 +502,9 @@ class TestFindComposeFile:
|
|
|
466
502
|
assert path.exists()
|
|
467
503
|
assert path.name == "docker-compose.yml"
|
|
468
504
|
|
|
469
|
-
def test_fallback_generates_compose(
|
|
505
|
+
def test_fallback_generates_compose(
|
|
506
|
+
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
|
507
|
+
) -> None:
|
|
470
508
|
# Trick find_compose_file into not finding the bundled one
|
|
471
509
|
monkeypatch.setattr(
|
|
472
510
|
"skmemory.setup_wizard.Path.__file__",
|
|
@@ -478,7 +516,9 @@ class TestFindComposeFile:
|
|
|
478
516
|
|
|
479
517
|
# Make the bundled path not exist by patching
|
|
480
518
|
fake_package = tmp_path / "nonexistent_pkg"
|
|
481
|
-
with mock.patch(
|
|
519
|
+
with mock.patch(
|
|
520
|
+
"skmemory.setup_wizard.Path.__file__", str(fake_package / "setup_wizard.py")
|
|
521
|
+
):
|
|
482
522
|
# Just verify the function doesn't crash when it can't find bundled
|
|
483
523
|
path = find_compose_file()
|
|
484
524
|
assert path.exists()
|
|
@@ -505,18 +545,27 @@ class TestSetupWizard:
|
|
|
505
545
|
output = []
|
|
506
546
|
|
|
507
547
|
with (
|
|
508
|
-
mock.patch(
|
|
548
|
+
mock.patch(
|
|
549
|
+
"skmemory.setup_wizard.detect_platform", return_value=self._mock_platform()
|
|
550
|
+
),
|
|
509
551
|
mock.patch("skmemory.setup_wizard.check_port_available", return_value=True),
|
|
510
|
-
mock.patch(
|
|
552
|
+
mock.patch(
|
|
553
|
+
"skmemory.setup_wizard.find_compose_file",
|
|
554
|
+
return_value=tmp_path / "docker-compose.yml",
|
|
555
|
+
),
|
|
511
556
|
mock.patch(
|
|
512
557
|
"skmemory.setup_wizard.compose_up",
|
|
513
|
-
return_value=subprocess.CompletedProcess(
|
|
558
|
+
return_value=subprocess.CompletedProcess(
|
|
559
|
+
args=[], returncode=0, stdout="", stderr=""
|
|
560
|
+
),
|
|
514
561
|
),
|
|
515
562
|
mock.patch("skmemory.setup_wizard.check_skvector_health", return_value=True),
|
|
516
563
|
mock.patch("skmemory.setup_wizard.check_skgraph_health", return_value=True),
|
|
517
564
|
mock.patch(
|
|
518
565
|
"skmemory.setup_wizard.install_python_deps",
|
|
519
|
-
return_value=subprocess.CompletedProcess(
|
|
566
|
+
return_value=subprocess.CompletedProcess(
|
|
567
|
+
args=[], returncode=0, stdout="", stderr=""
|
|
568
|
+
),
|
|
520
569
|
),
|
|
521
570
|
mock.patch("skmemory.setup_wizard.save_config", return_value=tmp_path / "config.yaml"),
|
|
522
571
|
):
|
|
@@ -559,7 +608,9 @@ class TestSetupWizard:
|
|
|
559
608
|
def test_wizard_port_conflict(self) -> None:
|
|
560
609
|
output = []
|
|
561
610
|
with (
|
|
562
|
-
mock.patch(
|
|
611
|
+
mock.patch(
|
|
612
|
+
"skmemory.setup_wizard.detect_platform", return_value=self._mock_platform()
|
|
613
|
+
),
|
|
563
614
|
mock.patch("skmemory.setup_wizard.check_port_available", return_value=False),
|
|
564
615
|
):
|
|
565
616
|
result = run_setup_wizard(non_interactive=True, echo=output.append)
|
|
@@ -570,9 +621,13 @@ class TestSetupWizard:
|
|
|
570
621
|
def test_wizard_compose_up_fails(self, tmp_path: Path) -> None:
|
|
571
622
|
output = []
|
|
572
623
|
with (
|
|
573
|
-
mock.patch(
|
|
624
|
+
mock.patch(
|
|
625
|
+
"skmemory.setup_wizard.detect_platform", return_value=self._mock_platform()
|
|
626
|
+
),
|
|
574
627
|
mock.patch("skmemory.setup_wizard.check_port_available", return_value=True),
|
|
575
|
-
mock.patch(
|
|
628
|
+
mock.patch(
|
|
629
|
+
"skmemory.setup_wizard.find_compose_file", return_value=tmp_path / "dc.yml"
|
|
630
|
+
),
|
|
576
631
|
mock.patch(
|
|
577
632
|
"skmemory.setup_wizard.compose_up",
|
|
578
633
|
return_value=subprocess.CompletedProcess(
|
|
@@ -588,17 +643,25 @@ class TestSetupWizard:
|
|
|
588
643
|
def test_wizard_skvector_only(self, tmp_path: Path) -> None:
|
|
589
644
|
output = []
|
|
590
645
|
with (
|
|
591
|
-
mock.patch(
|
|
646
|
+
mock.patch(
|
|
647
|
+
"skmemory.setup_wizard.detect_platform", return_value=self._mock_platform()
|
|
648
|
+
),
|
|
592
649
|
mock.patch("skmemory.setup_wizard.check_port_available", return_value=True),
|
|
593
|
-
mock.patch(
|
|
650
|
+
mock.patch(
|
|
651
|
+
"skmemory.setup_wizard.find_compose_file", return_value=tmp_path / "dc.yml"
|
|
652
|
+
),
|
|
594
653
|
mock.patch(
|
|
595
654
|
"skmemory.setup_wizard.compose_up",
|
|
596
|
-
return_value=subprocess.CompletedProcess(
|
|
655
|
+
return_value=subprocess.CompletedProcess(
|
|
656
|
+
args=[], returncode=0, stdout="", stderr=""
|
|
657
|
+
),
|
|
597
658
|
),
|
|
598
659
|
mock.patch("skmemory.setup_wizard.check_skvector_health", return_value=True),
|
|
599
660
|
mock.patch(
|
|
600
661
|
"skmemory.setup_wizard.install_python_deps",
|
|
601
|
-
return_value=subprocess.CompletedProcess(
|
|
662
|
+
return_value=subprocess.CompletedProcess(
|
|
663
|
+
args=[], returncode=0, stdout="", stderr=""
|
|
664
|
+
),
|
|
602
665
|
),
|
|
603
666
|
mock.patch("skmemory.setup_wizard.save_config", return_value=tmp_path / "config.yaml"),
|
|
604
667
|
):
|
|
@@ -615,7 +678,9 @@ class TestSetupWizard:
|
|
|
615
678
|
|
|
616
679
|
def test_wizard_no_backends_selected(self) -> None:
|
|
617
680
|
output = []
|
|
618
|
-
with mock.patch(
|
|
681
|
+
with mock.patch(
|
|
682
|
+
"skmemory.setup_wizard.detect_platform", return_value=self._mock_platform()
|
|
683
|
+
):
|
|
619
684
|
result = run_setup_wizard(
|
|
620
685
|
enable_skvector=False,
|
|
621
686
|
enable_skgraph=False,
|
|
@@ -629,12 +694,18 @@ class TestSetupWizard:
|
|
|
629
694
|
def test_wizard_skip_deps(self, tmp_path: Path) -> None:
|
|
630
695
|
output = []
|
|
631
696
|
with (
|
|
632
|
-
mock.patch(
|
|
697
|
+
mock.patch(
|
|
698
|
+
"skmemory.setup_wizard.detect_platform", return_value=self._mock_platform()
|
|
699
|
+
),
|
|
633
700
|
mock.patch("skmemory.setup_wizard.check_port_available", return_value=True),
|
|
634
|
-
mock.patch(
|
|
701
|
+
mock.patch(
|
|
702
|
+
"skmemory.setup_wizard.find_compose_file", return_value=tmp_path / "dc.yml"
|
|
703
|
+
),
|
|
635
704
|
mock.patch(
|
|
636
705
|
"skmemory.setup_wizard.compose_up",
|
|
637
|
-
return_value=subprocess.CompletedProcess(
|
|
706
|
+
return_value=subprocess.CompletedProcess(
|
|
707
|
+
args=[], returncode=0, stdout="", stderr=""
|
|
708
|
+
),
|
|
638
709
|
),
|
|
639
710
|
mock.patch("skmemory.setup_wizard.check_skvector_health", return_value=True),
|
|
640
711
|
mock.patch("skmemory.setup_wizard.install_python_deps") as mock_pip,
|
|
@@ -665,20 +736,35 @@ class TestSetupCLI:
|
|
|
665
736
|
|
|
666
737
|
runner = CliRunner()
|
|
667
738
|
with mock.patch("skmemory.setup_wizard.run_setup_wizard") as mock_wizard:
|
|
668
|
-
mock_wizard.return_value = {
|
|
739
|
+
mock_wizard.return_value = {
|
|
740
|
+
"success": True,
|
|
741
|
+
"services": [],
|
|
742
|
+
"health": {},
|
|
743
|
+
"config_path": None,
|
|
744
|
+
"errors": [],
|
|
745
|
+
}
|
|
669
746
|
result = runner.invoke(cli, ["setup", "wizard", "--yes", "--no-skgraph"])
|
|
670
747
|
|
|
671
748
|
assert result.exit_code == 0
|
|
672
749
|
mock_wizard.assert_called_once()
|
|
673
750
|
call_kwargs = mock_wizard.call_args
|
|
674
|
-
assert
|
|
751
|
+
assert (
|
|
752
|
+
call_kwargs.kwargs.get("enable_skgraph") is False
|
|
753
|
+
or call_kwargs[1].get("enable_skgraph") is False
|
|
754
|
+
)
|
|
675
755
|
|
|
676
756
|
def test_setup_wizard_cli_failure(self) -> None:
|
|
677
757
|
from skmemory.cli import cli
|
|
678
758
|
|
|
679
759
|
runner = CliRunner()
|
|
680
760
|
with mock.patch("skmemory.setup_wizard.run_setup_wizard") as mock_wizard:
|
|
681
|
-
mock_wizard.return_value = {
|
|
761
|
+
mock_wizard.return_value = {
|
|
762
|
+
"success": False,
|
|
763
|
+
"services": [],
|
|
764
|
+
"health": {},
|
|
765
|
+
"config_path": None,
|
|
766
|
+
"errors": ["Docker not available"],
|
|
767
|
+
}
|
|
682
768
|
result = runner.invoke(cli, ["setup", "wizard", "--yes"])
|
|
683
769
|
|
|
684
770
|
assert result.exit_code == 1
|
|
@@ -703,12 +789,18 @@ class TestSetupCLI:
|
|
|
703
789
|
runner = CliRunner()
|
|
704
790
|
with (
|
|
705
791
|
mock.patch("skmemory.config.load_config", return_value=cfg),
|
|
706
|
-
mock.patch(
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
)
|
|
792
|
+
mock.patch(
|
|
793
|
+
"skmemory.setup_wizard.detect_platform",
|
|
794
|
+
return_value=PlatformInfo(
|
|
795
|
+
os_name="Linux", docker_available=True, compose_available=True
|
|
796
|
+
),
|
|
797
|
+
),
|
|
798
|
+
mock.patch(
|
|
799
|
+
"skmemory.setup_wizard.compose_ps",
|
|
800
|
+
return_value=subprocess.CompletedProcess(
|
|
801
|
+
args=[], returncode=0, stdout="skmemory-skvector running", stderr=""
|
|
802
|
+
),
|
|
803
|
+
),
|
|
712
804
|
mock.patch("skmemory.setup_wizard.check_skvector_health", return_value=True),
|
|
713
805
|
):
|
|
714
806
|
result = runner.invoke(cli, ["setup", "status"])
|
|
@@ -720,13 +812,21 @@ class TestSetupCLI:
|
|
|
720
812
|
|
|
721
813
|
runner = CliRunner()
|
|
722
814
|
with (
|
|
723
|
-
mock.patch(
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
)
|
|
815
|
+
mock.patch(
|
|
816
|
+
"skmemory.config.load_config",
|
|
817
|
+
return_value=SKMemoryConfig(backends_enabled=["skvector"]),
|
|
818
|
+
),
|
|
819
|
+
mock.patch(
|
|
820
|
+
"skmemory.setup_wizard.detect_platform",
|
|
821
|
+
return_value=PlatformInfo(
|
|
822
|
+
os_name="Linux", docker_available=True, compose_available=True
|
|
823
|
+
),
|
|
824
|
+
),
|
|
727
825
|
mock.patch(
|
|
728
826
|
"skmemory.setup_wizard.compose_up",
|
|
729
|
-
return_value=subprocess.CompletedProcess(
|
|
827
|
+
return_value=subprocess.CompletedProcess(
|
|
828
|
+
args=[], returncode=0, stdout="", stderr=""
|
|
829
|
+
),
|
|
730
830
|
),
|
|
731
831
|
):
|
|
732
832
|
result = runner.invoke(cli, ["setup", "start", "--service", "skvector"])
|
|
@@ -740,12 +840,17 @@ class TestSetupCLI:
|
|
|
740
840
|
runner = CliRunner()
|
|
741
841
|
with (
|
|
742
842
|
mock.patch("skmemory.config.load_config", return_value=SKMemoryConfig()),
|
|
743
|
-
mock.patch(
|
|
744
|
-
|
|
745
|
-
|
|
843
|
+
mock.patch(
|
|
844
|
+
"skmemory.setup_wizard.detect_platform",
|
|
845
|
+
return_value=PlatformInfo(
|
|
846
|
+
os_name="Linux", docker_available=True, compose_available=True
|
|
847
|
+
),
|
|
848
|
+
),
|
|
746
849
|
mock.patch(
|
|
747
850
|
"skmemory.setup_wizard.compose_down",
|
|
748
|
-
return_value=subprocess.CompletedProcess(
|
|
851
|
+
return_value=subprocess.CompletedProcess(
|
|
852
|
+
args=[], returncode=0, stdout="", stderr=""
|
|
853
|
+
),
|
|
749
854
|
),
|
|
750
855
|
):
|
|
751
856
|
result = runner.invoke(cli, ["setup", "stop"])
|
|
@@ -759,12 +864,17 @@ class TestSetupCLI:
|
|
|
759
864
|
runner = CliRunner()
|
|
760
865
|
with (
|
|
761
866
|
mock.patch("skmemory.config.load_config", return_value=SKMemoryConfig()),
|
|
762
|
-
mock.patch(
|
|
763
|
-
|
|
764
|
-
|
|
867
|
+
mock.patch(
|
|
868
|
+
"skmemory.setup_wizard.detect_platform",
|
|
869
|
+
return_value=PlatformInfo(
|
|
870
|
+
os_name="Linux", docker_available=True, compose_available=True
|
|
871
|
+
),
|
|
872
|
+
),
|
|
765
873
|
mock.patch(
|
|
766
874
|
"subprocess.run",
|
|
767
|
-
return_value=subprocess.CompletedProcess(
|
|
875
|
+
return_value=subprocess.CompletedProcess(
|
|
876
|
+
args=[], returncode=0, stdout="", stderr=""
|
|
877
|
+
),
|
|
768
878
|
),
|
|
769
879
|
):
|
|
770
880
|
result = runner.invoke(cli, ["setup", "stop", "--service", "skvector"])
|
|
@@ -777,12 +887,17 @@ class TestSetupCLI:
|
|
|
777
887
|
runner = CliRunner()
|
|
778
888
|
with (
|
|
779
889
|
mock.patch("skmemory.config.load_config", return_value=SKMemoryConfig()),
|
|
780
|
-
mock.patch(
|
|
781
|
-
|
|
782
|
-
|
|
890
|
+
mock.patch(
|
|
891
|
+
"skmemory.setup_wizard.detect_platform",
|
|
892
|
+
return_value=PlatformInfo(
|
|
893
|
+
os_name="Linux", docker_available=True, compose_available=True
|
|
894
|
+
),
|
|
895
|
+
),
|
|
783
896
|
mock.patch(
|
|
784
897
|
"skmemory.setup_wizard.compose_down",
|
|
785
|
-
return_value=subprocess.CompletedProcess(
|
|
898
|
+
return_value=subprocess.CompletedProcess(
|
|
899
|
+
args=[], returncode=0, stdout="", stderr=""
|
|
900
|
+
),
|
|
786
901
|
),
|
|
787
902
|
mock.patch("skmemory.config.CONFIG_PATH", Path("/tmp/nonexistent_config.yaml")),
|
|
788
903
|
):
|
|
@@ -811,11 +926,11 @@ class TestGetStoreWiring:
|
|
|
811
926
|
skgraph_url="redis://localhost:6379",
|
|
812
927
|
)
|
|
813
928
|
with mock.patch("skmemory.config.load_config", return_value=cfg):
|
|
814
|
-
|
|
929
|
+
_get_store()
|
|
815
930
|
|
|
816
931
|
# SKVector may fail to import but that's OK — we test the wiring logic
|
|
817
932
|
# SKGraph should be wired (lazy init, won't connect yet)
|
|
818
|
-
assert
|
|
933
|
+
assert True # at least no crash (store.graph/vector may be None if backends unavailable)
|
|
819
934
|
|
|
820
935
|
def test_get_store_no_config_no_env(self, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
821
936
|
from skmemory.cli import _get_store
|
|
@@ -832,4 +947,4 @@ class TestGetStoreWiring:
|
|
|
832
947
|
assert store.primary is not None
|
|
833
948
|
|
|
834
949
|
|
|
835
|
-
import urllib.error
|
|
950
|
+
import urllib.error # noqa: E402
|