chore: finalize v1.0.0 — cleanup, unified errors, date validation

- Move integration tests to tests/; fix .gitignore to scope root-only
- Remove tracked debug artefacts (probe2_stderr/stdout.txt)
- __init__.py: version via importlib.metadata; export create_server in __all__
- server.py: unified JSON error format {"error":"..."} for all tools
- server.py: date validation (YYYY-MM-DD) for all meal-plan tools
- server.py: clear_list reports partial failures (failed_count, failed_ids)
- server.py: -> str annotations on get_circles, get_tasks, get_activities
- server.py: document TODO:94 as known limitation (no name in sortingIndexByTaskList)
- server.py: date validation also added to get_meal_plan
- Add LICENSE (MIT, Marcus van Elst)
- Add CHANGELOG.md (Keep a Changelog, v0.1.0–v1.0.0)
- README.md: restructured by use case; 🔒 marks write tools
- CLAUDE.md: update to v1.0.0 state; condense roadmap history
- SPEC.md: add version stamp
- pyproject.toml: version 1.0.0 (single source of truth)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-17 15:30:26 +02:00
parent 0b56ea92bc
commit bdf9e3c59e
13 changed files with 599 additions and 336 deletions
+98
View File
@@ -0,0 +1,98 @@
"""Integration test for emoji + color in get_lists / create_list (v0.5.1)."""
import sys, json
sys.path.insert(0, "src")
import keyring
class _K:
def get_password(self, s, k): return {"email": "marcus@gecheckt.de", "password": "Lasdas1234"}.get(k)
keyring.get_password = _K().get_password
from mcp_familywall.server import create_list, delete_list, get_lists
errors = []
def log(msg):
sys.stdout.buffer.write((str(msg) + "\n").encode("utf-8"))
def check(label, result, **expect):
try:
obj = json.loads(result)
except Exception:
errors.append(f"FAIL [{label}]: not JSON -- {result!r}")
return None
for k, v in expect.items():
if obj.get(k) != v:
errors.append(f"FAIL [{label}]: {k}={obj.get(k)!r}, expected {v!r}")
return None
log(f"OK [{label}]: {dict((k, obj.get(k)) for k in expect)}")
return obj
# --- Test 1: create with emoji + color ---
r1 = create_list("__test_emoji__", "TODOS", emoji="\U0001F331", color="#E53935")
log(f"create with emoji+color = {r1}")
o1 = check("create_emoji_color", r1, created=True, emoji="\U0001F331", color="#E53935")
id1 = o1["id"] if o1 else None
# --- Test 2: create without emoji/color ---
r2 = create_list("__test_plain__", "SHOPPING_LIST")
log(f"create plain = {r2}")
o2 = check("create_plain", r2, created=True, emoji=None, color=None)
id2 = o2["id"] if o2 else None
# --- Test 3: get_lists shows emoji + color ---
lists_json = get_lists()
try:
all_lists = json.loads(lists_json)
by_id = {l["id"]: l for l in all_lists}
if id1 and id1 in by_id:
l = by_id[id1]
if l.get("emoji") == "\U0001F331" and l.get("color") == "#E53935":
log(f"OK [get_lists emoji+color]: emoji={l['emoji']!r} color={l['color']!r}")
else:
errors.append(f"FAIL [get_lists emoji+color]: emoji={l.get('emoji')!r} color={l.get('color')!r}")
elif id1:
errors.append(f"FAIL [get_lists emoji+color]: {id1} not in lists")
if id2 and id2 in by_id:
l = by_id[id2]
if l.get("emoji") is None and l.get("color") is None:
log(f"OK [get_lists plain]: emoji={l.get('emoji')!r} color={l.get('color')!r}")
else:
errors.append(f"FAIL [get_lists plain]: emoji={l.get('emoji')!r} color={l.get('color')!r}")
elif id2:
errors.append(f"FAIL [get_lists plain]: {id2} not in lists")
# Test 4: system lists have emoji=None (API returns ""), color=None (absent)
sys_lists = [l for l in all_lists if "SYS-CAT" in l.get("name", "") or
l.get("name") in ("Aufgaben", "Einkaufsliste")]
for sl in sys_lists:
if sl.get("emoji") is None and sl.get("color") is None:
log(f"OK [get_lists sys emoji/color=null]: {sl.get('name')}")
else:
errors.append(f"FAIL [get_lists sys emoji/color]: name={sl.get('name')!r} emoji={sl.get('emoji')!r} color={sl.get('color')!r}")
except Exception as e:
errors.append(f"FAIL [get_lists parse]: {e}")
# --- Cleanup ---
for lid, label in [(id1, "emoji"), (id2, "plain")]:
if lid:
r = delete_list(lid)
try:
obj = json.loads(r)
if obj.get("deleted"):
log(f"OK [cleanup {label}]: deleted {lid}")
else:
errors.append(f"FAIL [cleanup {label}]: {r}")
except Exception:
errors.append(f"FAIL [cleanup {label}]: {r}")
# --- Summary ---
if errors:
log("\nFAILURES:")
for e in errors:
log(" " + e)
sys.exit(1)
else:
log("\nAll tests passed.")