seren-margin 2.3.0__tar.gz → 2.4.0__tar.gz
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.
- {seren_margin-2.3.0/seren_margin.egg-info → seren_margin-2.4.0}/PKG-INFO +1 -1
- {seren_margin-2.3.0 → seren_margin-2.4.0}/seren_margin/_version.py +3 -3
- {seren_margin-2.3.0 → seren_margin-2.4.0}/seren_margin/app.py +13 -1
- {seren_margin-2.3.0 → seren_margin-2.4.0/seren_margin.egg-info}/PKG-INFO +1 -1
- seren_margin-2.4.0/seren_margin.egg-info/scm_version.json +8 -0
- {seren_margin-2.3.0 → seren_margin-2.4.0}/tests/test_stores_snapshot.py +18 -0
- seren_margin-2.3.0/seren_margin.egg-info/scm_version.json +0 -8
- {seren_margin-2.3.0 → seren_margin-2.4.0}/LICENSE +0 -0
- {seren_margin-2.3.0 → seren_margin-2.4.0}/SerenMargin.pyproj +0 -0
- {seren_margin-2.3.0 → seren_margin-2.4.0}/pyproject.toml +0 -0
- {seren_margin-2.3.0 → seren_margin-2.4.0}/seren-margin.service.sample +0 -0
- {seren_margin-2.3.0 → seren_margin-2.4.0}/seren-margin.yaml.sample +0 -0
- {seren_margin-2.3.0 → seren_margin-2.4.0}/seren_margin/__init__.py +0 -0
- {seren_margin-2.3.0 → seren_margin-2.4.0}/seren_margin/__main__.py +0 -0
- {seren_margin-2.3.0 → seren_margin-2.4.0}/seren_margin/_diag.py +0 -0
- {seren_margin-2.3.0 → seren_margin-2.4.0}/seren_margin/bookmark.py +0 -0
- {seren_margin-2.3.0 → seren_margin-2.4.0}/seren_margin/config.py +0 -0
- {seren_margin-2.3.0 → seren_margin-2.4.0}/seren_margin/mcp/__init__.py +0 -0
- {seren_margin-2.3.0 → seren_margin-2.4.0}/seren_margin/mcp/server.py +0 -0
- {seren_margin-2.3.0 → seren_margin-2.4.0}/seren_margin/mcp/tools.py +0 -0
- {seren_margin-2.3.0 → seren_margin-2.4.0}/seren_margin/mcp-manifest.yaml +0 -0
- {seren_margin-2.3.0 → seren_margin-2.4.0}/seren_margin/models.py +0 -0
- {seren_margin-2.3.0 → seren_margin-2.4.0}/seren_margin/store.py +0 -0
- {seren_margin-2.3.0 → seren_margin-2.4.0}/seren_margin.egg-info/SOURCES.txt +0 -0
- {seren_margin-2.3.0 → seren_margin-2.4.0}/seren_margin.egg-info/dependency_links.txt +0 -0
- {seren_margin-2.3.0 → seren_margin-2.4.0}/seren_margin.egg-info/entry_points.txt +0 -0
- {seren_margin-2.3.0 → seren_margin-2.4.0}/seren_margin.egg-info/requires.txt +0 -0
- {seren_margin-2.3.0 → seren_margin-2.4.0}/seren_margin.egg-info/scm_file_list.json +0 -0
- {seren_margin-2.3.0 → seren_margin-2.4.0}/seren_margin.egg-info/top_level.txt +0 -0
- {seren_margin-2.3.0 → seren_margin-2.4.0}/setup.cfg +0 -0
- {seren_margin-2.3.0 → seren_margin-2.4.0}/tests/__init__.py +0 -0
- {seren_margin-2.3.0 → seren_margin-2.4.0}/tests/conftest.py +0 -0
- {seren_margin-2.3.0 → seren_margin-2.4.0}/tests/test_amend_and_topics.py +0 -0
- {seren_margin-2.3.0 → seren_margin-2.4.0}/tests/test_bookmark_and_letters.py +0 -0
- {seren_margin-2.3.0 → seren_margin-2.4.0}/tests/test_config.py +0 -0
- {seren_margin-2.3.0 → seren_margin-2.4.0}/tests/test_exposure_is_wired.py +0 -0
- {seren_margin-2.3.0 → seren_margin-2.4.0}/tests/test_manifest_parity.py +0 -0
- {seren_margin-2.3.0 → seren_margin-2.4.0}/tests/test_mcp_fallback.py +0 -0
- {seren_margin-2.3.0 → seren_margin-2.4.0}/tests/test_mcp_mount.py +0 -0
- {seren_margin-2.3.0 → seren_margin-2.4.0}/tests/test_mcp_tools.py +0 -0
- {seren_margin-2.3.0 → seren_margin-2.4.0}/tests/test_optional_bearer.py +0 -0
- {seren_margin-2.3.0 → seren_margin-2.4.0}/tests/test_reads_stay_private.py +0 -0
- {seren_margin-2.3.0 → seren_margin-2.4.0}/tests/test_search.py +0 -0
- {seren_margin-2.3.0 → seren_margin-2.4.0}/tests/test_smoke.py +0 -0
- {seren_margin-2.3.0 → seren_margin-2.4.0}/tests/test_stats_content_blind.py +0 -0
- {seren_margin-2.3.0 → seren_margin-2.4.0}/tests/test_updates_contract.py +0 -0
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '2.
|
|
22
|
-
__version_tuple__ = version_tuple = (2,
|
|
21
|
+
__version__ = version = '2.4.0'
|
|
22
|
+
__version_tuple__ = version_tuple = (2, 4, 0)
|
|
23
23
|
|
|
24
|
-
__commit_id__ = commit_id = '
|
|
24
|
+
__commit_id__ = commit_id = 'g3a8586044'
|
|
@@ -230,11 +230,23 @@ def create_app(config: Optional[MarginConfig] = None) -> FastAPI:
|
|
|
230
230
|
notes = None
|
|
231
231
|
return {"version": APP_VERSION, "counts": {"notes": notes}}
|
|
232
232
|
|
|
233
|
+
def _margin_check(restored, manifest, snapshot_dir) -> dict:
|
|
234
|
+
"""A rehearsal's look at a restored COPY: opened as a MarginStore
|
|
235
|
+
(schema and migrations run on the copy) and counted. A count and
|
|
236
|
+
nothing else - a rehearsal's report must not say what a note says."""
|
|
237
|
+
import gc
|
|
238
|
+
st = MarginStore(restored["notes"] / cfg.resolved_db_path().name)
|
|
239
|
+
try:
|
|
240
|
+
return {"counts": {"notes": len(st.list_all(limit=1_000_000))}}
|
|
241
|
+
finally:
|
|
242
|
+
del st
|
|
243
|
+
gc.collect() # its connections, so the scratch copy can be removed
|
|
244
|
+
|
|
233
245
|
app.state.stores = StoreKeeper(
|
|
234
246
|
"seren-margin",
|
|
235
247
|
lambda: [Store("notes", "sqlite", str(cfg.resolved_db_path()),
|
|
236
248
|
"the margin: private notes, letters and the dedication")],
|
|
237
|
-
cfg.resolved_backup_dir(), extra=_margin_extra,
|
|
249
|
+
cfg.resolved_backup_dir(), extra=_margin_extra, check=_margin_check,
|
|
238
250
|
keep_daily=cfg.backup.keep_daily, keep_weekly=cfg.backup.keep_weekly,
|
|
239
251
|
log=lambda m: diag(f"[seren-margin] {m}")) if cfg.backup.enabled else None
|
|
240
252
|
add_store_routes(
|
|
@@ -81,3 +81,21 @@ def test_off_and_no_restore_or_delete(tmp_path):
|
|
|
81
81
|
sid = c.post("/stores/snapshot").json()["snapshot"]["id"]
|
|
82
82
|
for path in ("/stores/restore", f"/stores/snapshots/{sid}"):
|
|
83
83
|
assert c.post(path).status_code in (404, 405) and c.delete(path).status_code in (404, 405)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def test_a_rehearsal_counts_the_notes_and_says_nothing_of_them(tmp_path):
|
|
87
|
+
"""A restore's dry run (seren_sinew.stores). It needs no allow_pull: the
|
|
88
|
+
copy never leaves the box and the report is a count."""
|
|
89
|
+
from seren_sinew.stores import pack_snapshot
|
|
90
|
+
with _client(tmp_path) as c:
|
|
91
|
+
c.post("/notes", json={"content": SECRET, "topic": "private"})
|
|
92
|
+
snap = c.post("/stores/snapshot").json()["snapshot"]
|
|
93
|
+
c.post("/notes", json={"content": "written after", "topic": "later"})
|
|
94
|
+
r = c.post(f"/stores/snapshots/{snap['id']}/rehearse")
|
|
95
|
+
assert r.status_code == 200 and SECRET not in r.text, r.text
|
|
96
|
+
rep = r.json()
|
|
97
|
+
assert rep["ok"] and rep["check"] == {"counts": {"notes": 1}} and rep["live_store_touched"] is False, rep
|
|
98
|
+
assert len(c.app.state.store.list_all()) == 2
|
|
99
|
+
r = c.post("/stores/rehearse", content=pack_snapshot(Path(snap["path"])))
|
|
100
|
+
assert r.status_code == 200 and r.json()["ok"] and SECRET not in r.text, "a stash can send it back to be checked"
|
|
101
|
+
assert not list((Path(snap["path"]).parent / ".rehearsal").iterdir())
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|