Backup automatico script del 2026-09-27 07:00
This commit is contained in:
1 parent
f347badf3b
commit
09dcbbdadb
24 files changed
+1164
-193
No files matched your search
@@ -0,0 +1,115 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Test dei contesti condivisi su directory e DB temporanei (non tocca i dati reali).
|
||||
|
||||
Uso (dentro il container):
|
||||
docker exec loogle-mcp python /srv/scripts/test_shared_contexts.py
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
TMP = tempfile.mkdtemp(prefix="shared-ctx-test-")
|
||||
os.environ["MCP_CONTEXT_ROOT"] = os.path.join(TMP, "context")
|
||||
os.environ["MCP_DB"] = os.path.join(TMP, "test.db")
|
||||
sys.path.insert(0, "/srv")
|
||||
|
||||
from app.db import init_db # noqa: E402
|
||||
from app.context import store # noqa: E402
|
||||
from app.knowledge import indexer # noqa: E402
|
||||
from app.mcp import tools # noqa: E402
|
||||
|
||||
init_db()
|
||||
queued = []
|
||||
indexer.submit_background = lambda label, fn, *a, **k: queued.append((label, fn.__name__, a))
|
||||
failures = 0
|
||||
|
||||
|
||||
def claims(user, admin=False):
|
||||
scope = "context:read context:write" + (" admin" if admin else "")
|
||||
return {"sub": user, "scope": scope}
|
||||
|
||||
|
||||
def call(user, name, args, admin=False):
|
||||
out = tools.call_tool(name, args, claims(user, admin))
|
||||
return json.loads(out["content"][0]["text"])
|
||||
|
||||
|
||||
def check(label, cond):
|
||||
global failures
|
||||
failures += 0 if cond else 1
|
||||
print(f"{'OK ' if cond else 'FAIL'} {label}")
|
||||
|
||||
|
||||
def raises(exc, fn, *a, **k):
|
||||
try:
|
||||
fn(*a, **k)
|
||||
except exc:
|
||||
return True
|
||||
except Exception as other: # noqa: BLE001
|
||||
print(" eccezione inattesa:", type(other).__name__, other)
|
||||
return False
|
||||
|
||||
|
||||
# 1. Progetto personale di daniele, poi condiviso
|
||||
p = call("daniele", "create_project", {"title": "Stampa 3D X2D", "tags": ["stampa-3d"]})
|
||||
pid = p["id"]
|
||||
call("daniele", "save_context", {"project_id": pid, "content": "## Dotazione\n- PLA 4 kg"})
|
||||
check("lucia non vede il progetto personale di daniele",
|
||||
pid not in [x["id"] for x in call("lucia", "list_projects", {})["projects"]])
|
||||
check("lucia non può leggerlo", raises(FileNotFoundError, call, "lucia", "get_project_context", {"project_id": pid}))
|
||||
m = call("daniele", "share_project", {"project_id": pid})
|
||||
check("share_project -> shared/family/owner", m.get("shared") and m.get("shared_with") == "family" and m.get("owner") == "daniele")
|
||||
check("la cartella è passata nello spazio condiviso",
|
||||
os.path.isfile(os.path.join(TMP, "context", "_shared", "projects", pid, "context.md"))
|
||||
and not os.path.exists(os.path.join(TMP, "context", "daniele", "projects", pid)))
|
||||
check("reindicizzazione vettori accodata", any(q[1] == "_move_project_vectors" for q in queued))
|
||||
|
||||
# 2. Tutta la famiglia legge e aggiorna
|
||||
for u in ("lucia", "davide", "luca"):
|
||||
ids = {x["id"]: x for x in call(u, "list_projects", {})["projects"]}
|
||||
check(f"{u} vede il contesto condiviso", pid in ids and ids[pid].get("shared"))
|
||||
ctx = call("lucia", "get_project_context", {"project_id": pid})
|
||||
check("lucia legge la storia (sessione di daniele)", "PLA 4 kg" in ctx["context_md"] and ctx["recent_sessions"])
|
||||
rev = ctx["meta"].get("revision", 0)
|
||||
m = call("lucia", "save_context", {"project_id": pid, "mode": "replace",
|
||||
"content": "## Dotazione\n- PLA 4 kg\n- PETG 1 kg (comprato da Lucia)",
|
||||
"if_revision": rev})
|
||||
check("lucia aggiorna, updated_by=lucia", m.get("updated_by") == "lucia")
|
||||
check("daniele vede la modifica di lucia", "PETG 1 kg" in call("daniele", "get_project_context", {"project_id": pid})["context_md"])
|
||||
sessions = os.listdir(os.path.join(TMP, "context", "_shared", "projects", pid, "sessions"))
|
||||
check("il nome della sessione indica l'autore", any(s.endswith("-lucia.md") for s in sessions))
|
||||
check("conflitto: if_revision superata -> errore e nessuna sovrascrittura",
|
||||
raises(store.ConflictError, store.save_context, "davide", pid, "x", "replace", if_revision=rev)
|
||||
and "PETG 1 kg" in call("davide", "get_project_context", {"project_id": pid})["context_md"])
|
||||
check("indicizzazione del contesto completo accodata", any(q[1] == "index_project_context" for q in queued))
|
||||
|
||||
# 3. Permessi: archiviare / rendere personale
|
||||
check("lucia non può archiviare", raises(PermissionError, call, "lucia", "archive_project", {"project_id": pid}))
|
||||
check("lucia non può riportarlo personale", raises(PermissionError, call, "lucia", "share_project", {"project_id": pid, "shared": False}))
|
||||
check("admin può archiviare", call("davide", "archive_project", {"project_id": pid}, admin=True).get("archived") is True)
|
||||
call("daniele", "archive_project", {"project_id": pid, "archived": False})
|
||||
m = call("daniele", "share_project", {"project_id": pid, "shared": False})
|
||||
check("il proprietario lo riporta personale", not m.get("shared") and "owner" not in m)
|
||||
check("dopo: lucia non lo vede più", pid not in [x["id"] for x in call("lucia", "list_projects", {})["projects"]])
|
||||
check("dopo: daniele lo ha di nuovo personale con la storia",
|
||||
"PETG 1 kg" in call("daniele", "get_project_context", {"project_id": pid})["context_md"])
|
||||
|
||||
# 4. Creazione diretta condivisa, ID unici tra i due spazi, risorse MCP
|
||||
s = call("lucia", "create_project", {"title": "Stampa 3D X2D", "shared": True})
|
||||
check("create_project shared=true con ID diverso dal personale di daniele", s.get("shared") and s["id"] != pid)
|
||||
check("daniele raggiunge sia il suo personale sia il nuovo condiviso",
|
||||
call("daniele", "get_project_context", {"project_id": s["id"]})["meta"].get("shared")
|
||||
and not call("daniele", "get_project_context", {"project_id": pid})["meta"].get("shared"))
|
||||
call("luca", "create_project", {"title": "Omonimo"})
|
||||
call("daniele", "create_project", {"title": "Omonimo"})
|
||||
check("share_project rifiuta un ID usato da un personale di un altro utente",
|
||||
raises(ValueError, call, "daniele", "share_project", {"project_id": "omonimo"}))
|
||||
uris = [r["uri"] for r in tools.list_resources(claims("luca"))]
|
||||
check("risorsa condivisa visibile a luca", f"loogle://context/_shared/{s['id']}" in uris)
|
||||
res = tools.read_resource(f"loogle://context/_shared/{s['id']}", claims("luca"))
|
||||
check("luca legge la risorsa condivisa", res["text"].startswith("# Stampa 3D X2D"))
|
||||
|
||||
print(f"Errori: {failures}")
|
||||
sys.exit(1 if failures else 0)
|
||||