From 6a59c8dfd1c7b4fdf7a85906e13eb09c566bc96d Mon Sep 17 00:00:00 2001 From: pyr0ball Date: Sat, 4 Apr 2026 22:59:06 -0700 Subject: [PATCH] fix: row_factory in _household_store; validate household_id from Heimdall; monkeypatch CLOUD_DATA_ROOT in accept test; add KIWI_BASE_URL to compose.cloud.yml --- app/api/endpoints/household.py | 19 +++++++++++++++---- compose.cloud.yml | 1 + tests/test_household.py | 7 ++++++- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/app/api/endpoints/household.py b/app/api/endpoints/household.py index f923761..8e16ff1 100644 --- a/app/api/endpoints/household.py +++ b/app/api/endpoints/household.py @@ -6,6 +6,8 @@ import os import secrets from datetime import datetime, timedelta, timezone +import sqlite3 + import requests from fastapi import APIRouter, Depends, HTTPException @@ -42,11 +44,14 @@ def _require_household_owner(session: CloudUser = Depends(_require_premium)) -> def _household_store(household_id: str) -> Store: - """Open the household DB directly (used during invite acceptance).""" - from pathlib import Path + """Open the household DB directly (used during invite acceptance). + Sets row_factory so dict-style column access works on raw conn queries. + """ db_path = CLOUD_DATA_ROOT / f"household_{household_id}" / "kiwi.db" db_path.parent.mkdir(parents=True, exist_ok=True) - return Store(db_path) + store = Store(db_path) + store.conn.row_factory = sqlite3.Row + return store def _heimdall_post(path: str, body: dict) -> dict: @@ -74,7 +79,13 @@ async def create_household(session: CloudUser = Depends(_require_premium)): if session.household_id: raise HTTPException(status_code=409, detail="You are already in a household.") data = _heimdall_post("/admin/household/create", {"owner_user_id": session.user_id}) - household_id = data.get("household_id", "local-household") + household_id = data.get("household_id") + if not household_id: + # Heimdall returned OK but without a household_id — treat as server error. + # Fall back to a local stub only when HEIMDALL_ADMIN_TOKEN is unset (dev mode). + if HEIMDALL_ADMIN_TOKEN: + raise HTTPException(status_code=500, detail="Heimdall did not return a household_id.") + household_id = "local-household" return HouseholdCreateResponse( household_id=household_id, message="Household created. Share an invite link to add members.", diff --git a/compose.cloud.yml b/compose.cloud.yml index 7c4fcfd..159217b 100644 --- a/compose.cloud.yml +++ b/compose.cloud.yml @@ -13,6 +13,7 @@ services: environment: CLOUD_MODE: "true" CLOUD_DATA_ROOT: /devl/kiwi-cloud-data + KIWI_BASE_URL: https://menagerie.circuitforge.tech/kiwi # DIRECTUS_JWT_SECRET, HEIMDALL_URL, HEIMDALL_ADMIN_TOKEN — set in .env # DEV ONLY: comma-separated IPs that bypass JWT auth (LAN testing without Caddy). # Production deployments must NOT set this. Leave blank or omit entirely. diff --git a/tests/test_household.py b/tests/test_household.py index 91252a0..c71e0df 100644 --- a/tests/test_household.py +++ b/tests/test_household.py @@ -86,8 +86,13 @@ def test_invite_generates_token(): app.dependency_overrides.clear() -def test_accept_invalid_token_returns_404(): +def test_accept_invalid_token_returns_404(tmp_path, monkeypatch): """Accepting a non-existent token returns 404.""" + import app.api.endpoints.household as hh_ep + import app.cloud_session as cs + monkeypatch.setattr(hh_ep, "CLOUD_DATA_ROOT", tmp_path) + monkeypatch.setattr(cs, "CLOUD_DATA_ROOT", tmp_path) + from app.main import app from app.cloud_session import get_session import tempfile, pathlib