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:
@@ -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.")
|
||||
Reference in New Issue
Block a user