test: cover remaining cloud fallback safeguards
This commit is contained in:
@@ -15,6 +15,7 @@ key-pw.xml
|
||||
secrets.local.xml
|
||||
*.local.xml
|
||||
config/javis.toml
|
||||
/javis.toml
|
||||
config/*secret*.xml
|
||||
!config/*.example.xml
|
||||
|
||||
|
||||
+10
-5
@@ -170,10 +170,15 @@ Letzter bestätigter Projektstand:
|
||||
- uv-Lock und `uv sync --dev`: bestanden
|
||||
- Python in `.venv`: 3.12.13
|
||||
- Ruff in `.venv`: 0.16.0
|
||||
- Unit-Tests: 60 bestanden; Startskript-Sicherheitsregeln zusätzlich abgedeckt
|
||||
- Unit-Tests: 62 bestanden; Timeout und fehlendes Modell fallen nachweislich lokal zurück
|
||||
- PowerShell-Syntax des Startskripts: erfolgreich geparst
|
||||
- Ruff Lint: bestanden
|
||||
- Ruff Formatprüfung: bestanden
|
||||
- lokaler nachrichtenfreier CLI-Smoke: bestanden; Nullkostenmodus aktiv,
|
||||
Gemini nicht konfiguriert, Schlüssel nicht vorhanden, 0 Cloudanfragen
|
||||
- Obsidian-Vault unverändert: 1 Markdown-, 11 Gesamtdateien, letzter
|
||||
Schreibzeitpunkt weiterhin 30.07.2026 15:42:31
|
||||
- Rootserver und Ubuntu-Laptop wurden nicht kontaktiert
|
||||
|
||||
Lokaler Modell-Smoke-Test:
|
||||
|
||||
@@ -257,10 +262,10 @@ Abnahmestatus:
|
||||
|
||||
Auf dem bestehenden Feature-Branch als nächstes einzelnes Arbeitspaket:
|
||||
|
||||
1. geforderte Resttests ergänzen, insbesondere Timeout und bereinigte Fehler
|
||||
2. vollständige Qualitäts-, Gitignore-, Secret- und Außenbereichsprüfung ausführen
|
||||
3. Abschlussdokumentation aktualisieren, Feature-Branch pushen und vor Pascals
|
||||
manuellem Live-Test stoppen; nicht nach `main` mergen
|
||||
1. veraltete Architektur-, Sicherheits-, Changelog- und Nächste-Sitzung-Texte
|
||||
auf den tatsächlich implementierten Stand bringen
|
||||
2. Feature-Branch pushen und vor Pascals manuellem Live-Test stoppen
|
||||
3. nicht nach `main` mergen
|
||||
|
||||
Keinen API-Key in Chat, Git, Obsidian, Logs oder Dokumentation übernehmen.
|
||||
|
||||
|
||||
@@ -60,7 +60,8 @@ Stand: lokaler Textchat auf `main`, 30.07.2026.
|
||||
- `/provider`, `/privacy` und `/status` geben keine Inhalte oder Keywerte aus
|
||||
- sicheres `scripts/start-javis.ps1` mit relativer Repositoryprüfung,
|
||||
Loopback-Ollama und überschreibbaren externen Pfaden
|
||||
- 60 Unit-Tests aktuell erfolgreich; PowerShell-Syntax, Ruff-Format und Lint sauber
|
||||
- 62 Unit-Tests aktuell erfolgreich; PowerShell-Syntax, Lockfile, Sync,
|
||||
Strukturprüfung, Secret-Scan, Gitignore, Ruff-Format und Lint sauber
|
||||
|
||||
## Nicht implementiert
|
||||
|
||||
@@ -85,6 +86,5 @@ Living-Mind-Frontend und automatische Ollama-Prozessverwaltung.
|
||||
Ollama wird bewusst manuell in einem eigenen Terminal gestartet. Antworten werden
|
||||
noch nicht gestreamt; Sitzungen haben noch keine Titel, Suche oder Löschfunktion.
|
||||
`qwen3:8b` ist nicht automatisch für die GTX 1050 des späteren Zielhosts geeignet.
|
||||
Abschließende Resttests, Qualitätsprüfung und Feature-Branch-Push fehlen noch.
|
||||
Es wurde kein echter API-Key angefordert, angezeigt oder konfiguriert und keine
|
||||
Cloudanfrage ausgeführt.
|
||||
Nur Abschlussdokumentation und Feature-Branch-Push fehlen. Es wurde kein echter
|
||||
API-Key angefordert, angezeigt oder konfiguriert und keine Cloudanfrage ausgeführt.
|
||||
|
||||
@@ -14,6 +14,7 @@ from javis.providers.base import (
|
||||
InvalidProviderResponseError,
|
||||
MissingApiKeyError,
|
||||
ProviderRateLimitError,
|
||||
ProviderTimeoutError,
|
||||
)
|
||||
from javis.providers.gemini import GeminiProvider
|
||||
|
||||
@@ -140,6 +141,13 @@ class GeminiProviderTests(unittest.TestCase):
|
||||
with self.assertRaises(CloudNetworkError):
|
||||
provider.chat([ChatMessage("user", "Was ist Python?")])
|
||||
|
||||
def test_timeout_is_a_distinct_fallback_signal(self) -> None:
|
||||
request = httpx.Request("POST", "https://example.invalid")
|
||||
provider, _ = self._provider(httpx.ReadTimeout("timeout", request=request))
|
||||
|
||||
with self.assertRaises(ProviderTimeoutError):
|
||||
provider.chat([ChatMessage("user", "Was ist Python?")])
|
||||
|
||||
def test_empty_response_is_rejected(self) -> None:
|
||||
provider, _ = self._provider(_response(" "))
|
||||
|
||||
|
||||
@@ -8,11 +8,13 @@ from javis.core.provider_router import HybridProvider
|
||||
from javis.memory.usage_store import ProviderEvent, SQLiteUsageStore
|
||||
from javis.providers.base import (
|
||||
ChatMessage,
|
||||
CloudModelUnavailableError,
|
||||
CloudNetworkError,
|
||||
InvalidApiKeyError,
|
||||
LocalModelProvider,
|
||||
MissingApiKeyError,
|
||||
ProviderRateLimitError,
|
||||
ProviderTimeoutError,
|
||||
ProviderUsage,
|
||||
)
|
||||
from javis.security.privacy import CloudPolicy, PrivacyRouter
|
||||
@@ -167,6 +169,20 @@ class HybridProviderTests(unittest.TestCase):
|
||||
self.assertEqual(answer, "Lokal")
|
||||
self.assertTrue(router.last_route.fallback)
|
||||
|
||||
def test_timeout_and_missing_model_fall_back(self) -> None:
|
||||
for error in (
|
||||
ProviderTimeoutError("timeout"),
|
||||
CloudModelUnavailableError("missing model"),
|
||||
):
|
||||
with self.subTest(error=type(error).__name__):
|
||||
self.cloud.error = error
|
||||
router = self._router()
|
||||
|
||||
answer = router.chat([ChatMessage("user", "Wie funktioniert Python?")])
|
||||
|
||||
self.assertEqual(answer, "Lokal")
|
||||
self.assertTrue(router.last_route.fallback)
|
||||
|
||||
def test_missing_key_falls_back(self) -> None:
|
||||
def missing_key() -> _RecordingProvider:
|
||||
raise MissingApiKeyError("missing")
|
||||
|
||||
Reference in New Issue
Block a user