Initial monorepo structure
This commit is contained in:
@@ -0,0 +1,548 @@
|
||||
"""Unit-Tests für ContextGuard und AuditLogger-Integration.
|
||||
|
||||
Testet die Zugriffskontrolle zwischen Arbeitskontexten:
|
||||
- Zugriff innerhalb des eigenen Kontexts (erlaubt)
|
||||
- Zugriff auf fremden Kontext (verweigert)
|
||||
- Zugriff auf shared-Bereich (erlaubt gemäß Konfiguration)
|
||||
- Audit-Log-Format und Vollständigkeit
|
||||
- Edge Cases: shared-Kontext, Tool-Ausführung im shared-Kontext
|
||||
|
||||
Requirements: 2.1, 2.3, 2.5, 2.6, 2.7
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
import yaml
|
||||
|
||||
from monorepo.audit import AuditLogger
|
||||
from monorepo.models import SecurityEvent
|
||||
from monorepo.security import ContextGuard
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Fixtures
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def monorepo_root(tmp_path: Path) -> Path:
|
||||
"""Erstellt eine Monorepo-Struktur mit access-config.yaml."""
|
||||
root = tmp_path / "monorepo"
|
||||
root.mkdir()
|
||||
|
||||
# Kontextordner erstellen
|
||||
(root / "privat").mkdir()
|
||||
(root / "dhive").mkdir()
|
||||
(root / "bahn").mkdir()
|
||||
(root / "shared" / "config").mkdir(parents=True)
|
||||
(root / "shared" / "tools").mkdir(parents=True)
|
||||
(root / "shared" / "powers").mkdir(parents=True)
|
||||
(root / "shared" / "knowledge-store").mkdir(parents=True)
|
||||
(root / "shared" / "mcp-servers").mkdir(parents=True)
|
||||
|
||||
# access-config.yaml erstellen
|
||||
config = {
|
||||
"contexts": {
|
||||
"privat": {
|
||||
"env_file": "privat/.env",
|
||||
"allowed_shared": [
|
||||
"shared/tools/",
|
||||
"shared/powers/",
|
||||
"shared/config/",
|
||||
],
|
||||
},
|
||||
"dhive": {
|
||||
"env_file": "dhive/.env",
|
||||
"allowed_shared": [
|
||||
"shared/tools/",
|
||||
"shared/powers/",
|
||||
"shared/config/",
|
||||
],
|
||||
},
|
||||
"bahn": {
|
||||
"env_file": "bahn/.env",
|
||||
"allowed_shared": [
|
||||
"shared/tools/",
|
||||
"shared/powers/",
|
||||
"shared/config/",
|
||||
"shared/knowledge-store/",
|
||||
],
|
||||
},
|
||||
"shared": {
|
||||
"env_file": "shared/.env",
|
||||
"allowed_shared": ["*"],
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
config_path = root / "shared" / "config" / "access-config.yaml"
|
||||
with open(config_path, "w", encoding="utf-8") as f:
|
||||
yaml.dump(config, f)
|
||||
|
||||
return root
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def guard(monorepo_root: Path) -> ContextGuard:
|
||||
"""Erstellt einen ContextGuard mit der Test-Konfiguration."""
|
||||
return ContextGuard(root_path=monorepo_root)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def env_files(monorepo_root: Path) -> Path:
|
||||
"""Erstellt .env-Dateien für alle Kontexte."""
|
||||
for ctx in ("privat", "dhive", "bahn", "shared"):
|
||||
env_path = monorepo_root / ctx / ".env"
|
||||
env_path.write_text(
|
||||
f"# {ctx} secrets\n"
|
||||
f"{ctx.upper()}_API_KEY=secret-{ctx}-123\n"
|
||||
f"{ctx.upper()}_DB_URL=postgresql://localhost/{ctx}\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
return monorepo_root
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Tests: Zugriff innerhalb des eigenen Kontexts (erlaubt)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestOwnContextAccess:
|
||||
"""Teste Zugriff innerhalb des eigenen Kontexts (erlaubt).
|
||||
|
||||
Requirement 2.1, 2.3: Jeder Kontext darf auf eigene Dateien zugreifen.
|
||||
"""
|
||||
|
||||
def test_privat_accesses_own_env(self, guard: ContextGuard, monorepo_root: Path) -> None:
|
||||
"""privat darf auf privat/.env zugreifen."""
|
||||
target = monorepo_root / "privat" / ".env"
|
||||
assert guard.check_access("privat", target) is True
|
||||
|
||||
def test_dhive_accesses_own_env(self, guard: ContextGuard, monorepo_root: Path) -> None:
|
||||
"""dhive darf auf dhive/.env zugreifen."""
|
||||
target = monorepo_root / "dhive" / ".env"
|
||||
assert guard.check_access("dhive", target) is True
|
||||
|
||||
def test_bahn_accesses_own_env(self, guard: ContextGuard, monorepo_root: Path) -> None:
|
||||
"""bahn darf auf bahn/.env zugreifen."""
|
||||
target = monorepo_root / "bahn" / ".env"
|
||||
assert guard.check_access("bahn", target) is True
|
||||
|
||||
def test_privat_accesses_own_project_file(
|
||||
self, guard: ContextGuard, monorepo_root: Path
|
||||
) -> None:
|
||||
"""privat darf auf beliebige Dateien im eigenen Kontext zugreifen."""
|
||||
target = monorepo_root / "privat" / "project-a" / "src" / "main.py"
|
||||
assert guard.check_access("privat", target) is True
|
||||
|
||||
def test_dhive_accesses_own_nested_file(
|
||||
self, guard: ContextGuard, monorepo_root: Path
|
||||
) -> None:
|
||||
"""dhive darf auf tief verschachtelte Dateien im eigenen Kontext zugreifen."""
|
||||
target = monorepo_root / "dhive" / "my-project" / "module" / "secret.key"
|
||||
assert guard.check_access("dhive", target) is True
|
||||
|
||||
def test_shared_accesses_own_files(self, guard: ContextGuard, monorepo_root: Path) -> None:
|
||||
"""shared-Kontext darf auf eigene Dateien zugreifen."""
|
||||
target = monorepo_root / "shared" / ".env"
|
||||
assert guard.check_access("shared", target) is True
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Tests: Zugriff auf fremden Kontext (verweigert)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestForeignContextAccessDenied:
|
||||
"""Teste Zugriff auf fremden Kontext (verweigert).
|
||||
|
||||
Requirement 2.1, 2.3: Secrets eines Arbeitskontexts sind für andere
|
||||
Kontexte nicht lesbar.
|
||||
"""
|
||||
|
||||
def test_privat_cannot_access_dhive_env(
|
||||
self, guard: ContextGuard, monorepo_root: Path
|
||||
) -> None:
|
||||
"""privat darf NICHT auf dhive/.env zugreifen."""
|
||||
target = monorepo_root / "dhive" / ".env"
|
||||
assert guard.check_access("privat", target) is False
|
||||
|
||||
def test_privat_cannot_access_bahn_env(
|
||||
self, guard: ContextGuard, monorepo_root: Path
|
||||
) -> None:
|
||||
"""privat darf NICHT auf bahn/.env zugreifen."""
|
||||
target = monorepo_root / "bahn" / ".env"
|
||||
assert guard.check_access("privat", target) is False
|
||||
|
||||
def test_dhive_cannot_access_privat_env(
|
||||
self, guard: ContextGuard, monorepo_root: Path
|
||||
) -> None:
|
||||
"""dhive darf NICHT auf privat/.env zugreifen."""
|
||||
target = monorepo_root / "privat" / ".env"
|
||||
assert guard.check_access("dhive", target) is False
|
||||
|
||||
def test_dhive_cannot_access_bahn_secrets(
|
||||
self, guard: ContextGuard, monorepo_root: Path
|
||||
) -> None:
|
||||
"""dhive darf NICHT auf bahn/secret.key zugreifen."""
|
||||
target = monorepo_root / "bahn" / "secret.key"
|
||||
assert guard.check_access("dhive", target) is False
|
||||
|
||||
def test_bahn_cannot_access_privat_project(
|
||||
self, guard: ContextGuard, monorepo_root: Path
|
||||
) -> None:
|
||||
"""bahn darf NICHT auf Dateien im privat-Kontext zugreifen."""
|
||||
target = monorepo_root / "privat" / "project-a" / "config.yaml"
|
||||
assert guard.check_access("bahn", target) is False
|
||||
|
||||
def test_bahn_cannot_access_dhive_env(
|
||||
self, guard: ContextGuard, monorepo_root: Path
|
||||
) -> None:
|
||||
"""bahn darf NICHT auf dhive/.env zugreifen."""
|
||||
target = monorepo_root / "dhive" / ".env"
|
||||
assert guard.check_access("bahn", target) is False
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Tests: Zugriff auf shared-Bereich (erlaubt gemäß Konfiguration)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestSharedAreaAccess:
|
||||
"""Teste Zugriff auf shared-Bereich (erlaubt gemäß Konfiguration).
|
||||
|
||||
Requirement 2.6, 2.7: Zugriff auf shared-Ressourcen nur auf explizit
|
||||
freigegebene Pfade.
|
||||
"""
|
||||
|
||||
def test_privat_accesses_shared_tools(
|
||||
self, guard: ContextGuard, monorepo_root: Path
|
||||
) -> None:
|
||||
"""privat darf auf shared/tools/ zugreifen."""
|
||||
target = monorepo_root / "shared" / "tools" / "monorepo-cli" / "src" / "main.py"
|
||||
assert guard.check_access("privat", target) is True
|
||||
|
||||
def test_privat_accesses_shared_powers(
|
||||
self, guard: ContextGuard, monorepo_root: Path
|
||||
) -> None:
|
||||
"""privat darf auf shared/powers/ zugreifen."""
|
||||
target = monorepo_root / "shared" / "powers" / "db-platform" / "power.md"
|
||||
assert guard.check_access("privat", target) is True
|
||||
|
||||
def test_privat_accesses_shared_config(
|
||||
self, guard: ContextGuard, monorepo_root: Path
|
||||
) -> None:
|
||||
"""privat darf auf shared/config/ zugreifen."""
|
||||
target = monorepo_root / "shared" / "config" / "access-config.yaml"
|
||||
assert guard.check_access("privat", target) is True
|
||||
|
||||
def test_privat_cannot_access_shared_knowledge_store(
|
||||
self, guard: ContextGuard, monorepo_root: Path
|
||||
) -> None:
|
||||
"""privat darf NICHT auf shared/knowledge-store/ zugreifen (nicht in allowed_shared)."""
|
||||
target = monorepo_root / "shared" / "knowledge-store" / "index.yaml"
|
||||
assert guard.check_access("privat", target) is False
|
||||
|
||||
def test_privat_cannot_access_shared_mcp_servers(
|
||||
self, guard: ContextGuard, monorepo_root: Path
|
||||
) -> None:
|
||||
"""privat darf NICHT auf shared/mcp-servers/ zugreifen (nicht in allowed_shared)."""
|
||||
target = monorepo_root / "shared" / "mcp-servers" / "config.json"
|
||||
assert guard.check_access("privat", target) is False
|
||||
|
||||
def test_bahn_accesses_shared_knowledge_store(
|
||||
self, guard: ContextGuard, monorepo_root: Path
|
||||
) -> None:
|
||||
"""bahn darf auf shared/knowledge-store/ zugreifen (in bahn's allowed_shared)."""
|
||||
target = monorepo_root / "shared" / "knowledge-store" / "_index.yaml"
|
||||
assert guard.check_access("bahn", target) is True
|
||||
|
||||
def test_dhive_cannot_access_shared_knowledge_store(
|
||||
self, guard: ContextGuard, monorepo_root: Path
|
||||
) -> None:
|
||||
"""dhive darf NICHT auf shared/knowledge-store/ zugreifen."""
|
||||
target = monorepo_root / "shared" / "knowledge-store" / "data.md"
|
||||
assert guard.check_access("dhive", target) is False
|
||||
|
||||
def test_dhive_accesses_shared_tools(
|
||||
self, guard: ContextGuard, monorepo_root: Path
|
||||
) -> None:
|
||||
"""dhive darf auf shared/tools/ zugreifen."""
|
||||
target = monorepo_root / "shared" / "tools" / "script.sh"
|
||||
assert guard.check_access("dhive", target) is True
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Tests: Shared-Kontext darf auf alles zugreifen (Wildcard)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestSharedContextWildcard:
|
||||
"""Edge Case: shared-Kontext mit allowed_shared: ['*'] darf auf alles in shared zugreifen.
|
||||
|
||||
Requirement 2.7: Tools im shared-Bereich brauchen Zugriff auf shared-Ressourcen.
|
||||
"""
|
||||
|
||||
def test_shared_accesses_shared_tools(
|
||||
self, guard: ContextGuard, monorepo_root: Path
|
||||
) -> None:
|
||||
"""shared darf auf shared/tools/ zugreifen."""
|
||||
target = monorepo_root / "shared" / "tools" / "any-tool" / "main.py"
|
||||
assert guard.check_access("shared", target) is True
|
||||
|
||||
def test_shared_accesses_shared_knowledge_store(
|
||||
self, guard: ContextGuard, monorepo_root: Path
|
||||
) -> None:
|
||||
"""shared darf auf shared/knowledge-store/ zugreifen."""
|
||||
target = monorepo_root / "shared" / "knowledge-store" / "deep" / "doc.md"
|
||||
assert guard.check_access("shared", target) is True
|
||||
|
||||
def test_shared_accesses_shared_mcp_servers(
|
||||
self, guard: ContextGuard, monorepo_root: Path
|
||||
) -> None:
|
||||
"""shared darf auf shared/mcp-servers/ zugreifen (Wildcard)."""
|
||||
target = monorepo_root / "shared" / "mcp-servers" / "server.json"
|
||||
assert guard.check_access("shared", target) is True
|
||||
|
||||
def test_shared_cannot_access_privat(
|
||||
self, guard: ContextGuard, monorepo_root: Path
|
||||
) -> None:
|
||||
"""shared darf NICHT auf privat/ zugreifen (Wildcard gilt nur für shared/)."""
|
||||
target = monorepo_root / "privat" / ".env"
|
||||
assert guard.check_access("shared", target) is False
|
||||
|
||||
def test_shared_cannot_access_dhive(
|
||||
self, guard: ContextGuard, monorepo_root: Path
|
||||
) -> None:
|
||||
"""shared darf NICHT auf dhive/ zugreifen."""
|
||||
target = monorepo_root / "dhive" / "project" / "main.py"
|
||||
assert guard.check_access("shared", target) is False
|
||||
|
||||
def test_shared_cannot_access_bahn(
|
||||
self, guard: ContextGuard, monorepo_root: Path
|
||||
) -> None:
|
||||
"""shared darf NICHT auf bahn/ zugreifen."""
|
||||
target = monorepo_root / "bahn" / ".env"
|
||||
assert guard.check_access("shared", target) is False
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Tests: load_env Funktion
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestLoadEnv:
|
||||
"""Teste load_env-Methode: Lädt nur die eigene .env-Datei.
|
||||
|
||||
Requirement 2.2, 2.3: Jeder Kontext hat eine eigene .env-Datei.
|
||||
"""
|
||||
|
||||
def test_load_privat_env(
|
||||
self, guard: ContextGuard, env_files: Path, monorepo_root: Path
|
||||
) -> None:
|
||||
"""load_env lädt korrekt die privat/.env."""
|
||||
env = guard.load_env("privat")
|
||||
assert env["PRIVAT_API_KEY"] == "secret-privat-123"
|
||||
assert env["PRIVAT_DB_URL"] == "postgresql://localhost/privat"
|
||||
|
||||
def test_load_dhive_env(
|
||||
self, guard: ContextGuard, env_files: Path, monorepo_root: Path
|
||||
) -> None:
|
||||
"""load_env lädt korrekt die dhive/.env."""
|
||||
env = guard.load_env("dhive")
|
||||
assert env["DHIVE_API_KEY"] == "secret-dhive-123"
|
||||
|
||||
def test_load_unknown_context_raises(self, guard: ContextGuard) -> None:
|
||||
"""load_env wirft ValueError bei unbekanntem Kontext."""
|
||||
with pytest.raises(ValueError, match="Unbekannter Kontext"):
|
||||
guard.load_env("unknown")
|
||||
|
||||
def test_load_env_missing_file_raises(self, guard: ContextGuard) -> None:
|
||||
"""load_env wirft FileNotFoundError wenn .env-Datei nicht existiert."""
|
||||
with pytest.raises(FileNotFoundError, match=".env-Datei"):
|
||||
guard.load_env("privat")
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Tests: Audit-Log-Format und Vollständigkeit
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestAuditLogFormatAndCompleteness:
|
||||
"""Teste Audit-Log-Format und Vollständigkeit.
|
||||
|
||||
Requirement 2.5: Protokolleintrag muss enthalten:
|
||||
- Zeitstempel
|
||||
- anfragender Kontext
|
||||
- Zielkontext
|
||||
- angefragte Ressource
|
||||
"""
|
||||
|
||||
def test_audit_log_contains_all_required_fields(self, tmp_path: Path) -> None:
|
||||
"""Ein Audit-Eintrag enthält alle Pflichtfelder: Zeitstempel, Kontexte, Ressource."""
|
||||
log_path = tmp_path / "audit.log"
|
||||
logger = AuditLogger(log_path=log_path)
|
||||
|
||||
event = SecurityEvent(
|
||||
timestamp=datetime(2025, 7, 1, 9, 15, 30),
|
||||
requesting_context="dhive",
|
||||
target_context="privat",
|
||||
resource="privat/.env",
|
||||
action="read",
|
||||
outcome="denied",
|
||||
)
|
||||
logger.log_violation(event)
|
||||
|
||||
content = log_path.read_text(encoding="utf-8")
|
||||
|
||||
# Zeitstempel vorhanden (ISO-Format)
|
||||
assert "2025-07-01T09:15:30" in content
|
||||
# Anfragender Kontext vorhanden
|
||||
assert "dhive" in content
|
||||
# Zielkontext vorhanden
|
||||
assert "privat" in content
|
||||
# Ressource vorhanden
|
||||
assert "privat/.env" in content
|
||||
# Aktion vorhanden
|
||||
assert "read" in content
|
||||
# Outcome vorhanden
|
||||
assert "DENIED" in content
|
||||
|
||||
def test_audit_log_format_structure(self, tmp_path: Path) -> None:
|
||||
"""Audit-Log-Einträge folgen dem definierten Format-Schema."""
|
||||
log_path = tmp_path / "audit.log"
|
||||
logger = AuditLogger(log_path=log_path)
|
||||
|
||||
event = SecurityEvent(
|
||||
timestamp=datetime(2025, 3, 20, 12, 0, 0),
|
||||
requesting_context="bahn",
|
||||
target_context="dhive",
|
||||
resource="dhive/.env",
|
||||
action="write",
|
||||
outcome="denied",
|
||||
)
|
||||
logger.log_violation(event)
|
||||
|
||||
line = log_path.read_text(encoding="utf-8").strip()
|
||||
# Format: [ISO-TIMESTAMP] OUTCOME | requesting -> target | action on resource
|
||||
assert line == "[2025-03-20T12:00:00] DENIED | bahn -> dhive | write on dhive/.env"
|
||||
|
||||
def test_audit_log_allowed_event(self, tmp_path: Path) -> None:
|
||||
"""Auch erlaubte Zugriffe können protokolliert werden."""
|
||||
log_path = tmp_path / "audit.log"
|
||||
logger = AuditLogger(log_path=log_path)
|
||||
|
||||
event = SecurityEvent(
|
||||
timestamp=datetime(2025, 5, 10, 8, 30, 0),
|
||||
requesting_context="privat",
|
||||
target_context="shared",
|
||||
resource="shared/tools/script.sh",
|
||||
action="execute",
|
||||
outcome="allowed",
|
||||
)
|
||||
logger.log_violation(event)
|
||||
|
||||
content = log_path.read_text(encoding="utf-8")
|
||||
assert "ALLOWED" in content
|
||||
assert "privat -> shared" in content
|
||||
assert "execute on shared/tools/script.sh" in content
|
||||
|
||||
def test_audit_log_completeness_multiple_events(self, tmp_path: Path) -> None:
|
||||
"""Mehrere Events werden vollständig und geordnet protokolliert."""
|
||||
log_path = tmp_path / "audit.log"
|
||||
logger = AuditLogger(log_path=log_path)
|
||||
|
||||
events = [
|
||||
SecurityEvent(
|
||||
timestamp=datetime(2025, 1, 1, i, 0, 0),
|
||||
requesting_context=f"ctx{i}",
|
||||
target_context="privat",
|
||||
resource=f"privat/file{i}.txt",
|
||||
action="read",
|
||||
outcome="denied",
|
||||
)
|
||||
for i in range(3)
|
||||
]
|
||||
|
||||
for event in events:
|
||||
logger.log_violation(event)
|
||||
|
||||
lines = log_path.read_text(encoding="utf-8").splitlines()
|
||||
assert len(lines) == 3
|
||||
|
||||
# Jede Zeile enthält die korrekten Daten
|
||||
for i, line in enumerate(lines):
|
||||
assert f"ctx{i}" in line
|
||||
assert f"file{i}.txt" in line
|
||||
assert "DENIED" in line
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Tests: ContextGuard Initialisierung und Konfiguration
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestContextGuardInit:
|
||||
"""Teste ContextGuard-Initialisierung und Fehlerbehandlung.
|
||||
|
||||
Requirement 2.6: Zentrale Konfigurationsdatei.
|
||||
"""
|
||||
|
||||
def test_init_with_valid_config(self, monorepo_root: Path) -> None:
|
||||
"""ContextGuard wird mit gültiger Konfiguration korrekt initialisiert."""
|
||||
guard = ContextGuard(root_path=monorepo_root)
|
||||
assert guard.root_path == monorepo_root.resolve()
|
||||
|
||||
def test_init_missing_config_raises(self, tmp_path: Path) -> None:
|
||||
"""ContextGuard wirft FileNotFoundError bei fehlender Konfiguration."""
|
||||
with pytest.raises(FileNotFoundError, match="Access-Konfiguration nicht gefunden"):
|
||||
ContextGuard(root_path=tmp_path)
|
||||
|
||||
def test_init_invalid_yaml_raises(self, tmp_path: Path) -> None:
|
||||
"""ContextGuard wirft ValueError bei ungültigem YAML-Format."""
|
||||
config_dir = tmp_path / "shared" / "config"
|
||||
config_dir.mkdir(parents=True)
|
||||
config_path = config_dir / "access-config.yaml"
|
||||
config_path.write_text("invalid: data\n", encoding="utf-8")
|
||||
|
||||
with pytest.raises(ValueError, match="Ungültiges access-config.yaml-Format"):
|
||||
ContextGuard(root_path=tmp_path)
|
||||
|
||||
def test_get_context_config(self, guard: ContextGuard) -> None:
|
||||
"""get_context_config gibt die korrekte Konfiguration zurück."""
|
||||
config = guard.get_context_config("bahn")
|
||||
assert config["env_file"] == "bahn/.env"
|
||||
assert "shared/knowledge-store/" in config["allowed_shared"]
|
||||
|
||||
def test_get_context_config_unknown_raises(self, guard: ContextGuard) -> None:
|
||||
"""get_context_config wirft ValueError bei unbekanntem Kontext."""
|
||||
with pytest.raises(ValueError, match="Unbekannter Kontext"):
|
||||
guard.get_context_config("nonexistent")
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Tests: Edge Cases – relative Pfade
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestRelativePaths:
|
||||
"""Teste check_access mit relativen Pfaden."""
|
||||
|
||||
def test_relative_path_own_context(self, guard: ContextGuard) -> None:
|
||||
"""Relativer Pfad innerhalb eigenen Kontexts wird erkannt."""
|
||||
target = Path("privat/.env")
|
||||
assert guard.check_access("privat", target) is True
|
||||
|
||||
def test_relative_path_foreign_context(self, guard: ContextGuard) -> None:
|
||||
"""Relativer Pfad zu fremdem Kontext wird verweigert."""
|
||||
target = Path("dhive/.env")
|
||||
assert guard.check_access("privat", target) is False
|
||||
|
||||
def test_relative_path_shared(self, guard: ContextGuard) -> None:
|
||||
"""Relativer Pfad zu erlaubtem shared-Bereich wird akzeptiert."""
|
||||
target = Path("shared/tools/monorepo-cli/main.py")
|
||||
assert guard.check_access("privat", target) is True
|
||||
Reference in New Issue
Block a user