diff --git a/Claude outputs/main.py b/Claude outputs/main.py index e3858f5..a8c2856 100644 --- a/Claude outputs/main.py +++ b/Claude outputs/main.py @@ -3,7 +3,7 @@ JARVIS API Backend FastAPI application for JARVIS AI Assistant + Business Automation """ -from fastapi import FastAPI, HTTPException, Depends, Header +from fastapi import FastAPI, HTTPException, Depends, Header, Body from fastapi.middleware.cors import CORSMiddleware from pydantic import BaseModel from typing import Optional @@ -1497,6 +1497,20 @@ async def get_orders(status: Optional[str] = None): logger.error(f"Orders fetch error: {str(e)}") raise HTTPException(status_code=503, detail=f"Orders unavailable: {str(e)}") +@app.get("/api/v1/settings", dependencies=[Depends(require_admin_key)]) +async def get_settings_endpoint(): + return await get_all_settings() + + +@app.put("/api/v1/settings", dependencies=[Depends(require_admin_key)]) +async def update_settings_endpoint(updates: dict = Body(...)): + for key in updates: + if key not in SETTINGS_DEFAULTS: + raise HTTPException(status_code=400, detail=f"Unknown setting: {key}") + for key, value in updates.items(): + await set_setting(key, str(value)) + return await get_all_settings() + @app.get("/api/v1/emails", dependencies=[Depends(require_admin_key)]) async def get_cached_emails(limit: int = 10): """Recently cached emails, populated by the n8n 'Neue E-Mails Cache' workflow""" diff --git a/Claude outputs/tests/test_settings.py b/Claude outputs/tests/test_settings.py index 7c9f8ec..d8f88f7 100644 --- a/Claude outputs/tests/test_settings.py +++ b/Claude outputs/tests/test_settings.py @@ -62,3 +62,54 @@ async def test_set_setting_upserts_value(): assert "INSERT INTO settings" in args[0] assert "ON CONFLICT (key) DO UPDATE" in args[0] assert args[1] == ("company_name", "Neue Firma GmbH") + + +from fastapi.testclient import TestClient + +main.API_KEY_ADMIN = "test-secret" +HEADERS = {"X-Admin-Key": "test-secret"} + + +@pytest.fixture() +def client(): + with TestClient(main.app) as c: + yield c + + +def test_get_settings_endpoint_returns_merged_settings(client): + settings = {"assistant_name": "JARVIS", "company_name": "MBO-Tech-IT", "contact_email": "kontakt@mbo-tech-it.de"} + with patch.object(main, "get_all_settings", new=AsyncMock(return_value=settings)): + response = client.get("/api/v1/settings", headers=HEADERS) + + assert response.status_code == 200 + assert response.json() == settings + + +def test_get_settings_endpoint_requires_admin_key(client): + response = client.get("/api/v1/settings") + assert response.status_code == 401 + + +def test_put_settings_endpoint_updates_and_returns_merged_settings(client): + updated = {"assistant_name": "FRIDAY", "company_name": "MBO-Tech-IT", "contact_email": "kontakt@mbo-tech-it.de"} + with patch.object(main, "set_setting", new=AsyncMock()) as mock_set, patch.object( + main, "get_all_settings", new=AsyncMock(return_value=updated) + ): + response = client.put("/api/v1/settings", headers=HEADERS, json={"assistant_name": "FRIDAY"}) + + assert response.status_code == 200 + assert response.json() == updated + mock_set.assert_called_once_with("assistant_name", "FRIDAY") + + +def test_put_settings_endpoint_rejects_unknown_key(client): + with patch.object(main, "set_setting", new=AsyncMock()) as mock_set: + response = client.put("/api/v1/settings", headers=HEADERS, json={"nonsense_key": "x"}) + + assert response.status_code == 400 + mock_set.assert_not_called() + + +def test_put_settings_endpoint_requires_admin_key(client): + response = client.put("/api/v1/settings", json={"assistant_name": "X"}) + assert response.status_code == 401