Protokoll #28121

ID28121
Zeitstempel2025-12-28 12:53:55.810788
Clientroot
IP145.224.72.140
Modellclaude-sonnet-4-20250514
Statuscompleted
Tokens2,451 (Input: 1,223, Output: 1,228)
Dauer84 ms
Request-Zeit2025-12-28 12:53:55.810788
Response-Zeit2025-12-28 12:53:55.894315

Request

{
    "event": "PreToolUse",
    "tool_name": "Write",
    "tool_input": {
        "file_path": "\/var\/www\/tools\/ki-protokoll\/claude-hook\/quality\/pre_rules_tests.py",
        "content": "#!\/usr\/bin\/env python3\n\"\"\"\nPre-Hook Test Isolation Regeln (BLOCK) - Test Isolation.\n\nP14.x Regeln: Erzwingt Test-Isolation durch Blockieren von Produktions-DB-Zugriff.\n\nPrinzip: \"Tests beeinflussen sich nicht gegenseitig. Globaler Zustand ist kontrolliert.\"\n\nWICHTIG: Diese Regeln gelten NUR für Test-Dateien (\/tests\/, \/Test\/).\n\"\"\"\n\nimport re\nfrom typing import Optional\nfrom .rule_base import block\n\n\n# =============================================================================\n# KONFIGURATION\n# =============================================================================\n\n# Pfade die als Test-Dateien gelten\nTEST_PATHS = [\"\/tests\/\", \"\/Test\/\"]\n\n# Produktions-Datenbanken (ohne _test Suffix)\nPRODUCTION_DATABASES = {\n    \"P14.1\": {\n        \"pattern\": r\"['\\\"]ki_content['\\\"]\",\n        \"exclude_pattern\": r\"['\\\"]ki_content_test['\\\"]\",\n        \"message\": \"Production database 'ki_content' used in tests. Use 'ki_content_test' instead.\",\n    },\n    \"P14.2\": {\n        \"pattern\": r\"['\\\"]ki_dev['\\\"]\",\n        \"exclude_pattern\": r\"['\\\"]ki_dev_test['\\\"]\",\n        \"message\": \"Production database 'ki_dev' used in tests. Use 'ki_dev_test' instead.\",\n    },\n    \"P14.3\": {\n        \"pattern\": r\"['\\\"]ki_protokoll['\\\"]\",\n        \"exclude_pattern\": r\"['\\\"]ki_protokoll_test['\\\"]\",\n        \"message\": \"Production database 'ki_protokoll' used in tests. Use 'ki_protokoll_test' instead.\",\n    },\n}\n\n# Gefährliche Operationen in Tests\nDANGEROUS_TEST_PATTERNS = {\n    \"P14.4\": {\n        \"pattern\": r\"TRUNCATE\\s+TABLE\",\n        \"message\": \"TRUNCATE TABLE in tests. Use test fixtures or transactions instead.\",\n    },\n    \"P14.5\": {\n        \"pattern\": r\"DROP\\s+(?:TABLE|DATABASE)\",\n        \"message\": \"DROP TABLE\/DATABASE in tests. Never drop in tests.\",\n    },\n}\n\n\n# =============================================================================\n# HELPER\n# =============================================================================\n\ndef is_test_file(file_path: str) -> bool:\n    \"\"\"Prüft ob die Datei eine Test-Datei ist.\"\"\"\n    return any(test_path in file_path for test_path in TEST_PATHS)\n\n\n# =============================================================================\n# REGELN\n# =============================================================================\n\ndef p14_production_database(file_path: str, content: str) -> Optional[dict]:\n    \"\"\"P14.1-P14.3: Produktions-Datenbank in Tests blockieren.\"\"\"\n    # Nur für Test-Dateien prüfen\n    if not is_test_file(file_path):\n        return None\n\n    for rule_id, config in PRODUCTION_DATABASES.items():\n        # Suche nach Produktions-DB\n        if re.search(config[\"pattern\"], content):\n            # Prüfe ob es die Test-Variante ist\n            if not re.search(config[\"exclude_pattern\"], content):\n                return block(rule_id, config[\"message\"])\n\n    return None\n\n\ndef p14_dangerous_operations(file_path: str, content: str) -> Optional[dict]:\n    \"\"\"P14.4-P14.5: Gefährliche Operationen in Tests blockieren.\"\"\"\n    # Nur für Test-Dateien prüfen\n    if not is_test_file(file_path):\n        return None\n\n    for rule_id, config in DANGEROUS_TEST_PATTERNS.items():\n        if re.search(config[\"pattern\"], content, re.IGNORECASE):\n            return block(rule_id, config[\"message\"])\n\n    return None\n\n\ndef p14_shared_state(file_path: str, content: str) -> Optional[dict]:\n    \"\"\"P14.6: Statische Properties ohne Reset in Tests.\"\"\"\n    # Nur für Test-Dateien prüfen\n    if not is_test_file(file_path):\n        return None\n\n    # Prüfe auf static Properties die in Tests gesetzt werden\n    # ohne entsprechenden Reset in tearDown\n    static_assignments = re.findall(\n        r\"self::\\$\\w+\\s*=|static::\\$\\w+\\s*=\",\n        content\n    )\n\n    if static_assignments:\n        # Prüfe ob tearDown vorhanden ist\n        has_teardown = bool(re.search(\n            r\"(?:protected|public)\\s+function\\s+tearDown\\s*\\(\",\n            content\n        ))\n\n        if not has_teardown:\n            return block(\n                \"P14.6\",\n                f\"Static property assignment in tests ({len(static_assignments)}x) \"\n                \"without tearDown() method. Add tearDown() to reset static state.\"\n            )\n\n    return None\n\n\n# =============================================================================\n# RULE COLLECTION\n# =============================================================================\n\nRULES = [\n    p14_production_database,\n    p14_dangerous_operations,\n    p14_shared_state,\n]\n"
    }
}

Response

{
    "tool_response": {
        "type": "create",
        "filePath": "\/var\/www\/tools\/ki-protokoll\/claude-hook\/quality\/pre_rules_tests.py",
        "content": "#!\/usr\/bin\/env python3\n\"\"\"\nPre-Hook Test Isolation Regeln (BLOCK) - Test Isolation.\n\nP14.x Regeln: Erzwingt Test-Isolation durch Blockieren von Produktions-DB-Zugriff.\n\nPrinzip: \"Tests beeinflussen sich nicht gegenseitig. Globaler Zustand ist kontrolliert.\"\n\nWICHTIG: Diese Regeln gelten NUR für Test-Dateien (\/tests\/, \/Test\/).\n\"\"\"\n\nimport re\nfrom typing import Optional\nfrom .rule_base import block\n\n\n# =============================================================================\n# KONFIGURATION\n# =============================================================================\n\n# Pfade die als Test-Dateien gelten\nTEST_PATHS = [\"\/tests\/\", \"\/Test\/\"]\n\n# Produktions-Datenbanken (ohne _test Suffix)\nPRODUCTION_DATABASES = {\n    \"P14.1\": {\n        \"pattern\": r\"['\\\"]ki_content['\\\"]\",\n        \"exclude_pattern\": r\"['\\\"]ki_content_test['\\\"]\",\n        \"message\": \"Production database 'ki_content' used in tests. Use 'ki_content_test' instead.\",\n    },\n    \"P14.2\": {\n        \"pattern\": r\"['\\\"]ki_dev['\\\"]\",\n        \"exclude_pattern\": r\"['\\\"]ki_dev_test['\\\"]\",\n        \"message\": \"Production database 'ki_dev' used in tests. Use 'ki_dev_test' instead.\",\n    },\n    \"P14.3\": {\n        \"pattern\": r\"['\\\"]ki_protokoll['\\\"]\",\n        \"exclude_pattern\": r\"['\\\"]ki_protokoll_test['\\\"]\",\n        \"message\": \"Production database 'ki_protokoll' used in tests. Use 'ki_protokoll_test' instead.\",\n    },\n}\n\n# Gefährliche Operationen in Tests\nDANGEROUS_TEST_PATTERNS = {\n    \"P14.4\": {\n        \"pattern\": r\"TRUNCATE\\s+TABLE\",\n        \"message\": \"TRUNCATE TABLE in tests. Use test fixtures or transactions instead.\",\n    },\n    \"P14.5\": {\n        \"pattern\": r\"DROP\\s+(?:TABLE|DATABASE)\",\n        \"message\": \"DROP TABLE\/DATABASE in tests. Never drop in tests.\",\n    },\n}\n\n\n# =============================================================================\n# HELPER\n# =============================================================================\n\ndef is_test_file(file_path: str) -> bool:\n    \"\"\"Prüft ob die Datei eine Test-Datei ist.\"\"\"\n    return any(test_path in file_path for test_path in TEST_PATHS)\n\n\n# =============================================================================\n# REGELN\n# =============================================================================\n\ndef p14_production_database(file_path: str, content: str) -> Optional[dict]:\n    \"\"\"P14.1-P14.3: Produktions-Datenbank in Tests blockieren.\"\"\"\n    # Nur für Test-Dateien prüfen\n    if not is_test_file(file_path):\n        return None\n\n    for rule_id, config in PRODUCTION_DATABASES.items():\n        # Suche nach Produktions-DB\n        if re.search(config[\"pattern\"], content):\n            # Prüfe ob es die Test-Variante ist\n            if not re.search(config[\"exclude_pattern\"], content):\n                return block(rule_id, config[\"message\"])\n\n    return None\n\n\ndef p14_dangerous_operations(file_path: str, content: str) -> Optional[dict]:\n    \"\"\"P14.4-P14.5: Gefährliche Operationen in Tests blockieren.\"\"\"\n    # Nur für Test-Dateien prüfen\n    if not is_test_file(file_path):\n        return None\n\n    for rule_id, config in DANGEROUS_TEST_PATTERNS.items():\n        if re.search(config[\"pattern\"], content, re.IGNORECASE):\n            return block(rule_id, config[\"message\"])\n\n    return None\n\n\ndef p14_shared_state(file_path: str, content: str) -> Optional[dict]:\n    \"\"\"P14.6: Statische Properties ohne Reset in Tests.\"\"\"\n    # Nur für Test-Dateien prüfen\n    if not is_test_file(file_path):\n        return None\n\n    # Prüfe auf static Properties die in Tests gesetzt werden\n    # ohne entsprechenden Reset in tearDown\n    static_assignments = re.findall(\n        r\"self::\\$\\w+\\s*=|static::\\$\\w+\\s*=\",\n        content\n    )\n\n    if static_assignments:\n        # Prüfe ob tearDown vorhanden ist\n        has_teardown = bool(re.search(\n            r\"(?:protected|public)\\s+function\\s+tearDown\\s*\\(\",\n            content\n        ))\n\n        if not has_teardown:\n            return block(\n                \"P14.6\",\n                f\"Static property assignment in tests ({len(static_assignments)}x) \"\n                \"without tearDown() method. Add tearDown() to reset static state.\"\n            )\n\n    return None\n\n\n# =============================================================================\n# RULE COLLECTION\n# =============================================================================\n\nRULES = [\n    p14_production_database,\n    p14_dangerous_operations,\n    p14_shared_state,\n]\n",
        "structuredPatch": [],
        "originalFile": null
    }
}
← Vorheriger Zur Liste Nächster →