Initial monorepo structure
This commit is contained in:
@@ -0,0 +1,547 @@
|
||||
"""Unit-Tests für das hooks-Modul.
|
||||
|
||||
Tests für:
|
||||
- HookManager: Installation, Deinstallation, Validierung
|
||||
- Read-Only-Schutz: Prüfung gestaged Dateien in geschützten Pfaden
|
||||
- Secret-Verschlüsselungs-Prüfung: Unverschlüsselte Secrets blockieren
|
||||
- Integration mit ContextGuard und SecretEncryptionManager
|
||||
- Hook-Installation bei `init`
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import stat
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
import yaml
|
||||
|
||||
from monorepo.hooks import HookManager, install_hooks, uninstall_hooks
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Fixtures
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def monorepo_root(tmp_path: Path) -> Path:
|
||||
"""Erstellt ein minimales Monorepo mit .git-Verzeichnis und Konfiguration."""
|
||||
root = tmp_path / "monorepo"
|
||||
root.mkdir()
|
||||
|
||||
# Git-Verzeichnis simulieren
|
||||
git_dir = root / ".git"
|
||||
git_dir.mkdir()
|
||||
(git_dir / "hooks").mkdir()
|
||||
|
||||
# Kontextordner
|
||||
for ctx in ["privat", "dhive", "bahn", "shared"]:
|
||||
(root / ctx).mkdir()
|
||||
|
||||
# shared/config
|
||||
config_dir = root / "shared" / "config"
|
||||
config_dir.mkdir(parents=True)
|
||||
|
||||
# repos.yaml mit einem Read-Only-Repo
|
||||
repos_config = {
|
||||
"repos": [
|
||||
{
|
||||
"name": "symphony-spec",
|
||||
"url": "https://github.com/openai/symphony",
|
||||
"mode": "read-only",
|
||||
"target": "shared/references/symphony",
|
||||
"pinned": "v1.0.0",
|
||||
"mechanism": "subtree",
|
||||
},
|
||||
{
|
||||
"name": "db-wissensdatenbank",
|
||||
"url": "https://gitlab.2700.2db.it/team/db-wissen.git",
|
||||
"mode": "upstream",
|
||||
"target": "bahn/db-wissensdatenbank",
|
||||
"pinned": "main",
|
||||
"mechanism": "subtree",
|
||||
},
|
||||
]
|
||||
}
|
||||
with open(config_dir / "repos.yaml", "w", encoding="utf-8") as f:
|
||||
yaml.dump(repos_config, f)
|
||||
|
||||
# access-config.yaml (für ContextGuard)
|
||||
access_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": {
|
||||
"env_file": "shared/.env",
|
||||
"allowed_shared": ["*"],
|
||||
},
|
||||
}
|
||||
}
|
||||
with open(config_dir / "access-config.yaml", "w", encoding="utf-8") as f:
|
||||
yaml.dump(access_config, f)
|
||||
|
||||
# .gitattributes im privat-Kontext (mit git-crypt-Filter)
|
||||
privat_ga = root / "privat" / ".gitattributes"
|
||||
privat_ga.write_text(
|
||||
"# git-crypt Filter für Kontext: privat\n"
|
||||
".env filter=git-crypt-privat diff=git-crypt-privat\n"
|
||||
"*.pem filter=git-crypt-privat diff=git-crypt-privat\n"
|
||||
"*.key filter=git-crypt-privat diff=git-crypt-privat\n"
|
||||
"*token* filter=git-crypt-privat diff=git-crypt-privat\n"
|
||||
"*secret* filter=git-crypt-privat diff=git-crypt-privat\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
return root
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def hook_manager(monorepo_root: Path) -> HookManager:
|
||||
"""Erstellt einen HookManager für das Test-Monorepo."""
|
||||
return HookManager(monorepo_root=monorepo_root)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Tests: Installation und Deinstallation
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestHookInstallation:
|
||||
"""Tests für die Hook-Installation und -Deinstallation."""
|
||||
|
||||
def test_install_creates_hook_file(self, hook_manager: HookManager, monorepo_root: Path):
|
||||
"""Hook-Installation erstellt die pre-commit Datei."""
|
||||
hook_manager.install_hooks()
|
||||
hook_path = monorepo_root / ".git" / "hooks" / "pre-commit"
|
||||
assert hook_path.exists()
|
||||
|
||||
def test_hook_is_executable(self, hook_manager: HookManager, monorepo_root: Path):
|
||||
"""Installierter Hook hat Ausführungsrechte (Unix) bzw. existiert (Windows)."""
|
||||
hook_manager.install_hooks()
|
||||
hook_path = monorepo_root / ".git" / "hooks" / "pre-commit"
|
||||
if os.name != "nt":
|
||||
mode = hook_path.stat().st_mode
|
||||
assert mode & stat.S_IXUSR # Owner execute
|
||||
else:
|
||||
# Windows unterstützt keine Unix-Permissions, nur Existenz prüfen
|
||||
assert hook_path.exists()
|
||||
|
||||
def test_hook_contains_shebang(self, hook_manager: HookManager, monorepo_root: Path):
|
||||
"""Installierter Hook beginnt mit Shebang."""
|
||||
hook_manager.install_hooks()
|
||||
hook_path = monorepo_root / ".git" / "hooks" / "pre-commit"
|
||||
content = hook_path.read_text(encoding="utf-8")
|
||||
assert content.startswith("#!/bin/sh")
|
||||
|
||||
def test_hook_contains_markers(self, hook_manager: HookManager, monorepo_root: Path):
|
||||
"""Installierter Hook enthält Start- und End-Marker."""
|
||||
hook_manager.install_hooks()
|
||||
hook_path = monorepo_root / ".git" / "hooks" / "pre-commit"
|
||||
content = hook_path.read_text(encoding="utf-8")
|
||||
assert "# === Monorepo Integrated Pre-Commit Hook (auto-generiert) ===" in content
|
||||
assert "# === Ende Monorepo Integrated Pre-Commit Hook ===" in content
|
||||
|
||||
def test_hook_contains_readonly_section(self, hook_manager: HookManager, monorepo_root: Path):
|
||||
"""Installierter Hook enthält Read-Only-Schutz-Abschnitt."""
|
||||
hook_manager.install_hooks()
|
||||
hook_path = monorepo_root / ".git" / "hooks" / "pre-commit"
|
||||
content = hook_path.read_text(encoding="utf-8")
|
||||
assert "Read-Only-Repos Schutz" in content
|
||||
assert "shared/references/symphony" in content
|
||||
|
||||
def test_hook_contains_secrets_section(self, hook_manager: HookManager, monorepo_root: Path):
|
||||
"""Installierter Hook enthält Secret-Prüfungs-Abschnitt."""
|
||||
hook_manager.install_hooks()
|
||||
hook_path = monorepo_root / ".git" / "hooks" / "pre-commit"
|
||||
content = hook_path.read_text(encoding="utf-8")
|
||||
assert "Unverschlüsselte Secrets Prüfung" in content
|
||||
assert "check_encrypted" in content
|
||||
assert "is_secret_file" in content
|
||||
|
||||
def test_hook_preserves_existing_content(self, hook_manager: HookManager, monorepo_root: Path):
|
||||
"""Installation bewahrt bestehende Hook-Inhalte."""
|
||||
hook_path = monorepo_root / ".git" / "hooks" / "pre-commit"
|
||||
hook_path.write_text("#!/bin/sh\n# Custom Hook Logic\necho 'custom'\n", encoding="utf-8")
|
||||
|
||||
hook_manager.install_hooks()
|
||||
content = hook_path.read_text(encoding="utf-8")
|
||||
|
||||
# Bestehender Inhalt muss erhalten bleiben
|
||||
assert "# Custom Hook Logic" in content
|
||||
assert "echo 'custom'" in content
|
||||
# Neuer Monorepo-Abschnitt muss vorhanden sein
|
||||
assert "Monorepo Integrated Pre-Commit Hook" in content
|
||||
|
||||
def test_hook_update_replaces_existing_section(self, hook_manager: HookManager, monorepo_root: Path):
|
||||
"""Erneute Installation aktualisiert nur den Monorepo-Abschnitt."""
|
||||
hook_manager.install_hooks()
|
||||
|
||||
# Zweite Installation (simuliert Update)
|
||||
hook_manager.install_hooks()
|
||||
|
||||
hook_path = monorepo_root / ".git" / "hooks" / "pre-commit"
|
||||
content = hook_path.read_text(encoding="utf-8")
|
||||
|
||||
# Sollte genau einmal den Marker enthalten (kein doppelter Abschnitt)
|
||||
assert content.count("# === Monorepo Integrated Pre-Commit Hook") == 1
|
||||
|
||||
def test_uninstall_removes_section(self, hook_manager: HookManager, monorepo_root: Path):
|
||||
"""Deinstallation entfernt den Monorepo-Abschnitt."""
|
||||
hook_manager.install_hooks()
|
||||
hook_manager.uninstall_hooks()
|
||||
|
||||
hook_path = monorepo_root / ".git" / "hooks" / "pre-commit"
|
||||
# Hook-Datei sollte entfernt sein (war nur Monorepo-Inhalt)
|
||||
assert not hook_path.exists()
|
||||
|
||||
def test_uninstall_preserves_custom_content(self, hook_manager: HookManager, monorepo_root: Path):
|
||||
"""Deinstallation bewahrt benutzerdefinierte Hook-Inhalte."""
|
||||
hook_path = monorepo_root / ".git" / "hooks" / "pre-commit"
|
||||
hook_path.write_text("#!/bin/sh\n# Custom Logic\necho 'keep'\n", encoding="utf-8")
|
||||
|
||||
hook_manager.install_hooks()
|
||||
hook_manager.uninstall_hooks()
|
||||
|
||||
assert hook_path.exists()
|
||||
content = hook_path.read_text(encoding="utf-8")
|
||||
assert "# Custom Logic" in content
|
||||
assert "Monorepo Integrated Pre-Commit Hook" not in content
|
||||
|
||||
def test_is_hook_installed(self, hook_manager: HookManager):
|
||||
"""is_hook_installed gibt korrekt True/False zurück."""
|
||||
assert not hook_manager.is_hook_installed()
|
||||
hook_manager.install_hooks()
|
||||
assert hook_manager.is_hook_installed()
|
||||
|
||||
def test_uninstall_when_no_hook(self, hook_manager: HookManager):
|
||||
"""Deinstallation ohne vorhandenen Hook läuft ohne Fehler."""
|
||||
# Sollte keine Exception werfen
|
||||
hook_manager.uninstall_hooks()
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Tests: Read-Only-Schutz
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestReadOnlyProtection:
|
||||
"""Tests für die Read-Only-Repo-Schutzprüfung."""
|
||||
|
||||
def test_readonly_path_is_detected(self, hook_manager: HookManager):
|
||||
"""Gestagte Dateien in Read-Only-Repos werden blockiert."""
|
||||
errors = hook_manager.validate_staged_files([
|
||||
"shared/references/symphony/README.md",
|
||||
])
|
||||
assert len(errors) == 1
|
||||
assert "read-only" in errors[0]
|
||||
assert "symphony" in errors[0]
|
||||
|
||||
def test_readonly_subdirectory_is_protected(self, hook_manager: HookManager):
|
||||
"""Dateien in Unterverzeichnissen von Read-Only-Repos werden blockiert."""
|
||||
errors = hook_manager.validate_staged_files([
|
||||
"shared/references/symphony/src/main.py",
|
||||
])
|
||||
assert len(errors) == 1
|
||||
assert "read-only" in errors[0]
|
||||
|
||||
def test_non_readonly_repo_is_allowed(self, hook_manager: HookManager):
|
||||
"""Dateien in Upstream-Repos werden nicht blockiert (nur Read-Only)."""
|
||||
errors = hook_manager.validate_staged_files([
|
||||
"bahn/db-wissensdatenbank/docs/README.md",
|
||||
])
|
||||
# Upstream-Repos sollten nicht blockiert werden
|
||||
readonly_errors = [e for e in errors if "read-only" in e]
|
||||
assert len(readonly_errors) == 0
|
||||
|
||||
def test_normal_files_are_allowed(self, hook_manager: HookManager):
|
||||
"""Normale Dateien außerhalb von Read-Only-Repos werden durchgelassen."""
|
||||
errors = hook_manager.validate_staged_files([
|
||||
"privat/my-project/src/main.py",
|
||||
"dhive/tooling/config.yaml",
|
||||
])
|
||||
assert len(errors) == 0
|
||||
|
||||
def test_multiple_violations_reported(self, hook_manager: HookManager):
|
||||
"""Mehrere Verstöße werden alle gemeldet."""
|
||||
errors = hook_manager.validate_staged_files([
|
||||
"shared/references/symphony/file1.py",
|
||||
"shared/references/symphony/file2.py",
|
||||
])
|
||||
assert len(errors) == 2
|
||||
|
||||
def test_protected_paths_returned(self, hook_manager: HookManager):
|
||||
"""get_protected_paths gibt die konfigurierten Read-Only-Pfade zurück."""
|
||||
paths = hook_manager.get_protected_paths()
|
||||
assert "shared/references/symphony" in paths
|
||||
# Upstream-Repos sollten NICHT in der geschützten Liste sein
|
||||
assert "bahn/db-wissensdatenbank" not in paths
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Tests: Secret-Verschlüsselungs-Prüfung
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestSecretEncryptionCheck:
|
||||
"""Tests für die Prüfung unverschlüsselter Secrets."""
|
||||
|
||||
def test_env_file_without_encryption_blocked(self, hook_manager: HookManager, monorepo_root: Path):
|
||||
"""Unverschlüsselte .env-Datei wird blockiert (kein Filter konfiguriert)."""
|
||||
# Erstelle eine .env im dhive-Kontext OHNE git-crypt-Filter
|
||||
env_path = monorepo_root / "dhive" / ".env"
|
||||
env_path.write_text("SECRET_KEY=abc123\n", encoding="utf-8")
|
||||
|
||||
errors = hook_manager.validate_staged_files(["dhive/.env"])
|
||||
assert len(errors) == 1
|
||||
assert "nicht verschlüsselt" in errors[0]
|
||||
|
||||
def test_env_file_with_filter_allowed(self, hook_manager: HookManager, monorepo_root: Path):
|
||||
"""Unverschlüsselte .env-Datei mit git-crypt-Filter wird durchgelassen."""
|
||||
# privat/.env hat einen git-crypt-Filter in .gitattributes
|
||||
env_path = monorepo_root / "privat" / ".env"
|
||||
env_path.write_text("MY_SECRET=xyz\n", encoding="utf-8")
|
||||
|
||||
errors = hook_manager.validate_staged_files(["privat/.env"])
|
||||
assert len(errors) == 0
|
||||
|
||||
def test_pem_file_without_filter_blocked(self, hook_manager: HookManager, monorepo_root: Path):
|
||||
"""Unverschlüsselte .pem-Datei ohne Filter wird blockiert."""
|
||||
pem_path = monorepo_root / "dhive" / "cert.pem"
|
||||
pem_path.write_text("-----BEGIN CERTIFICATE-----\n...\n", encoding="utf-8")
|
||||
|
||||
errors = hook_manager.validate_staged_files(["dhive/cert.pem"])
|
||||
assert len(errors) == 1
|
||||
assert "nicht verschlüsselt" in errors[0]
|
||||
|
||||
def test_pem_file_with_filter_allowed(self, hook_manager: HookManager, monorepo_root: Path):
|
||||
"""Unverschlüsselte .pem-Datei mit git-crypt-Filter wird durchgelassen."""
|
||||
pem_path = monorepo_root / "privat" / "server.pem"
|
||||
pem_path.write_text("-----BEGIN RSA PRIVATE KEY-----\n...\n", encoding="utf-8")
|
||||
|
||||
errors = hook_manager.validate_staged_files(["privat/server.pem"])
|
||||
assert len(errors) == 0
|
||||
|
||||
def test_key_file_detected(self, hook_manager: HookManager, monorepo_root: Path):
|
||||
"""*.key-Dateien werden als Secrets erkannt."""
|
||||
key_path = monorepo_root / "dhive" / "private.key"
|
||||
key_path.write_text("key-data\n", encoding="utf-8")
|
||||
|
||||
errors = hook_manager.validate_staged_files(["dhive/private.key"])
|
||||
assert len(errors) == 1
|
||||
|
||||
def test_token_file_detected(self, hook_manager: HookManager, monorepo_root: Path):
|
||||
"""Dateien mit 'token' im Namen werden als Secrets erkannt."""
|
||||
token_path = monorepo_root / "dhive" / "api-token.txt"
|
||||
token_path.write_text("token123\n", encoding="utf-8")
|
||||
|
||||
errors = hook_manager.validate_staged_files(["dhive/api-token.txt"])
|
||||
assert len(errors) == 1
|
||||
|
||||
def test_secret_file_detected(self, hook_manager: HookManager, monorepo_root: Path):
|
||||
"""Dateien mit 'secret' im Namen werden als Secrets erkannt."""
|
||||
secret_path = monorepo_root / "dhive" / "my-secret-config.yaml"
|
||||
secret_path.write_text("data: secret\n", encoding="utf-8")
|
||||
|
||||
errors = hook_manager.validate_staged_files(["dhive/my-secret-config.yaml"])
|
||||
assert len(errors) == 1
|
||||
|
||||
def test_encrypted_file_is_allowed(self, hook_manager: HookManager, monorepo_root: Path):
|
||||
"""Verschlüsselte Dateien (mit git-crypt-Header) werden durchgelassen."""
|
||||
# Simuliere eine git-crypt-verschlüsselte Datei
|
||||
enc_path = monorepo_root / "dhive" / ".env"
|
||||
enc_path.write_bytes(b"\x00GITCRYPT\x00" + b"encrypted-data")
|
||||
|
||||
errors = hook_manager.validate_staged_files(["dhive/.env"])
|
||||
assert len(errors) == 0
|
||||
|
||||
def test_normal_files_not_flagged(self, hook_manager: HookManager, monorepo_root: Path):
|
||||
"""Normale Dateien (nicht Secret-Patterns) werden nicht geprüft."""
|
||||
py_path = monorepo_root / "privat" / "main.py"
|
||||
py_path.write_text("print('hello')\n", encoding="utf-8")
|
||||
|
||||
errors = hook_manager.validate_staged_files(["privat/main.py"])
|
||||
assert len(errors) == 0
|
||||
|
||||
def test_gitattributes_pattern_matching(self, hook_manager: HookManager):
|
||||
"""Pattern-Matching für .gitattributes funktioniert korrekt."""
|
||||
# Interne Methode testen
|
||||
assert hook_manager._pattern_matches_file(".env", ".env")
|
||||
assert hook_manager._pattern_matches_file("*.pem", "server.pem")
|
||||
assert hook_manager._pattern_matches_file("*.key", "private.key")
|
||||
assert hook_manager._pattern_matches_file("*token*", "api-token.txt")
|
||||
assert hook_manager._pattern_matches_file("*secret*", "my-secret-config.yaml")
|
||||
|
||||
# Negative Cases
|
||||
assert not hook_manager._pattern_matches_file(".env", ".envrc")
|
||||
assert not hook_manager._pattern_matches_file("*.pem", "file.txt")
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Tests: Convenience-Funktionen und CLI-Integration
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestConvenienceFunctions:
|
||||
"""Tests für install_hooks/uninstall_hooks Convenience-Funktionen."""
|
||||
|
||||
def test_install_hooks_function(self, monorepo_root: Path):
|
||||
"""install_hooks()-Funktion erstellt den Hook erfolgreich."""
|
||||
install_hooks(monorepo_root)
|
||||
hook_path = monorepo_root / ".git" / "hooks" / "pre-commit"
|
||||
assert hook_path.exists()
|
||||
content = hook_path.read_text(encoding="utf-8")
|
||||
assert "Monorepo Integrated Pre-Commit Hook" in content
|
||||
|
||||
def test_uninstall_hooks_function(self, monorepo_root: Path):
|
||||
"""uninstall_hooks()-Funktion entfernt den Hook."""
|
||||
install_hooks(monorepo_root)
|
||||
uninstall_hooks(monorepo_root)
|
||||
hook_path = monorepo_root / ".git" / "hooks" / "pre-commit"
|
||||
assert not hook_path.exists()
|
||||
|
||||
def test_install_without_access_config(self, tmp_path: Path):
|
||||
"""Installation funktioniert auch ohne access-config.yaml."""
|
||||
root = tmp_path / "bare-mono"
|
||||
root.mkdir()
|
||||
(root / ".git" / "hooks").mkdir(parents=True)
|
||||
(root / "shared" / "config").mkdir(parents=True)
|
||||
|
||||
# Nur repos.yaml, keine access-config.yaml
|
||||
repos = {"repos": []}
|
||||
with open(root / "shared" / "config" / "repos.yaml", "w") as f:
|
||||
yaml.dump(repos, f)
|
||||
|
||||
install_hooks(root)
|
||||
hook_path = root / ".git" / "hooks" / "pre-commit"
|
||||
assert hook_path.exists()
|
||||
|
||||
def test_install_without_repos_yaml(self, tmp_path: Path):
|
||||
"""Installation funktioniert auch ohne repos.yaml (leere Schutzliste)."""
|
||||
root = tmp_path / "bare-mono"
|
||||
root.mkdir()
|
||||
(root / ".git" / "hooks").mkdir(parents=True)
|
||||
(root / "shared" / "config").mkdir(parents=True)
|
||||
|
||||
install_hooks(root)
|
||||
hook_path = root / ".git" / "hooks" / "pre-commit"
|
||||
assert hook_path.exists()
|
||||
content = hook_path.read_text(encoding="utf-8")
|
||||
assert "Keine Read-Only-Repos konfiguriert" in content
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Tests: ContextGuard-Integration
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestContextGuardIntegration:
|
||||
"""Tests für die Integration mit dem ContextGuard."""
|
||||
|
||||
def test_hook_manager_with_context_guard(self, monorepo_root: Path):
|
||||
"""HookManager funktioniert mit eingebundenem ContextGuard."""
|
||||
from monorepo.security import ContextGuard
|
||||
|
||||
guard = ContextGuard(monorepo_root)
|
||||
hm = HookManager(
|
||||
monorepo_root=monorepo_root,
|
||||
context_guard=guard,
|
||||
)
|
||||
|
||||
# Normale Datei sollte kein Problem sein
|
||||
errors = hm.validate_staged_files(["privat/project/main.py"])
|
||||
assert len(errors) == 0
|
||||
|
||||
def test_hook_manager_without_context_guard(self, monorepo_root: Path):
|
||||
"""HookManager funktioniert auch ohne ContextGuard."""
|
||||
hm = HookManager(monorepo_root=monorepo_root)
|
||||
errors = hm.validate_staged_files(["privat/project/main.py"])
|
||||
assert len(errors) == 0
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Tests: SecretEncryptionManager-Integration
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestEncryptionManagerIntegration:
|
||||
"""Tests für die Integration mit dem SecretEncryptionManager."""
|
||||
|
||||
def test_hook_manager_with_encryption_manager(self, monorepo_root: Path):
|
||||
"""HookManager funktioniert mit eingebundenem SecretEncryptionManager."""
|
||||
from monorepo.encryption import SecretEncryptionManager
|
||||
from monorepo.models import MachineContext
|
||||
|
||||
mc = MachineContext(
|
||||
name="test-machine",
|
||||
description="Test",
|
||||
authorized_contexts=["privat", "dhive", "bahn"],
|
||||
key_source="keyring",
|
||||
)
|
||||
enc = SecretEncryptionManager(monorepo_root, mc)
|
||||
|
||||
hm = HookManager(
|
||||
monorepo_root=monorepo_root,
|
||||
encryption_manager=enc,
|
||||
)
|
||||
hm.install_hooks()
|
||||
assert hm.is_hook_installed()
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Tests: is_secret_file Erkennung
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestSecretFileDetection:
|
||||
"""Tests für die _is_secret_file Methode."""
|
||||
|
||||
def test_env_detected(self, hook_manager: HookManager):
|
||||
"""Standard .env wird erkannt."""
|
||||
assert hook_manager._is_secret_file("privat/.env")
|
||||
assert hook_manager._is_secret_file("dhive/.env")
|
||||
assert hook_manager._is_secret_file(".env")
|
||||
|
||||
def test_env_prefix_detected(self, hook_manager: HookManager):
|
||||
""".env.local und ähnliche werden erkannt."""
|
||||
assert hook_manager._is_secret_file("privat/.env.local")
|
||||
assert hook_manager._is_secret_file(".env.production")
|
||||
|
||||
def test_pem_detected(self, hook_manager: HookManager):
|
||||
""".pem-Dateien werden erkannt."""
|
||||
assert hook_manager._is_secret_file("certs/server.pem")
|
||||
assert hook_manager._is_secret_file("privat/my.pem")
|
||||
|
||||
def test_key_detected(self, hook_manager: HookManager):
|
||||
""".key-Dateien werden erkannt."""
|
||||
assert hook_manager._is_secret_file("privat/ssl.key")
|
||||
assert hook_manager._is_secret_file("private.key")
|
||||
|
||||
def test_token_detected(self, hook_manager: HookManager):
|
||||
"""Dateien mit 'token' werden erkannt."""
|
||||
assert hook_manager._is_secret_file("api-token.txt")
|
||||
assert hook_manager._is_secret_file("github_token")
|
||||
assert hook_manager._is_secret_file("TOKEN_FILE")
|
||||
|
||||
def test_secret_detected(self, hook_manager: HookManager):
|
||||
"""Dateien mit 'secret' werden erkannt."""
|
||||
assert hook_manager._is_secret_file("my-secret.yaml")
|
||||
assert hook_manager._is_secret_file("SECRET_CONFIG")
|
||||
|
||||
def test_normal_files_not_detected(self, hook_manager: HookManager):
|
||||
"""Normale Dateien werden nicht als Secrets erkannt."""
|
||||
assert not hook_manager._is_secret_file("main.py")
|
||||
assert not hook_manager._is_secret_file("README.md")
|
||||
assert not hook_manager._is_secret_file("package.json")
|
||||
assert not hook_manager._is_secret_file("config.yaml")
|
||||
Reference in New Issue
Block a user