diff --git a/.gitignore b/.gitignore index 8501166..10ed8da 100644 --- a/.gitignore +++ b/.gitignore @@ -9,7 +9,6 @@ Thumbs.db # Env .env .env.* -<<<<<<< HEAD *.bak *.swp *.tmp @@ -19,5 +18,3 @@ __pycache__/ *.pyc data/ *.sqlite -======= ->>>>>>> origin/main diff --git a/PR_DESCRIPTION.md b/PR_DESCRIPTION.md index ed054aa..ea491d3 100644 --- a/PR_DESCRIPTION.md +++ b/PR_DESCRIPTION.md @@ -1,4 +1,3 @@ -<<<<<<< HEAD # 🛡️ Final One-Click Installer for Complete Pi-hole Stack ## 🎯 Objectives @@ -157,7 +156,6 @@ tests/test_dns_monitor.py::test_parse_pihole_line_valid PASSED [ 83%] tests/test_dns_monitor.py::test_parse_pihole_line_empty PASSED [ 91%] tests/test_dns_monitor.py::test_parse_pihole_line_invalid PASSED [100%] -============== 12 passed in 0.34s ============== ``` ## 🚀 Quickstart Testing @@ -231,7 +229,6 @@ journalctl -u pihole-suite -f # View logs --- **This PR delivers a complete, production-ready one-click installer that transforms any Debian/Ubuntu system into a comprehensive DNS security and monitoring platform with zero manual configuration required.** -======= ## 🎯 Overview This comprehensive PR implements critical security fixes, repository hygiene improvements, and modern development standards for the Pi-hole Suite project. @@ -296,4 +293,3 @@ This comprehensive PR implements critical security fixes, repository hygiene imp - `.gitignore`: Extended Python artifacts coverage This PR transforms the repository into a production-ready, enterprise-grade codebase with comprehensive security, testing, and quality standards. ->>>>>>> origin/main diff --git a/README.de.md b/README.de.md index ade447e..02f7c16 100644 --- a/README.de.md +++ b/README.de.md @@ -1,4 +1,3 @@ -<<<<<<< HEAD
# 🛡️ Pi-hole + Unbound + NetAlertX @@ -251,7 +250,6 @@ Siehe [CHANGELOG.md](CHANGELOG.md) für Versionshistorie und Updates. [💬 Diskussionen](https://github.com/TimInTech/Pi-hole-Unbound-PiAlert-Setup/discussions)
-======= # Pi-hole + Unbound + NetAlertX — Ein-Klick-Setup @@ -259,4 +257,3 @@ Siehe [CHANGELOG.md](CHANGELOG.md) für Versionshistorie und Updates. - **Issues**: [GitHub Issues](https://github.com/TimInTech/Pi-hole-Unbound-PiAlert-Setup/issues) - **Diskussionen**: [GitHub Discussions](https://github.com/TimInTech/Pi-hole-Unbound-PiAlert-Setup/discussions) - **Dokumentation**: Diese README und Inline-Code-Kommentare ->>>>>>> origin/main diff --git a/api/__init__.py b/api/__init__.py index 1a01c31..57a924d 100644 --- a/api/__init__.py +++ b/api/__init__.py @@ -1,5 +1,2 @@ -<<<<<<< HEAD """FastAPI API module.""" -======= """API package for the Pi-hole suite.""" ->>>>>>> origin/main diff --git a/api/main.py b/api/main.py index c40418c..675f74d 100644 --- a/api/main.py +++ b/api/main.py @@ -1,4 +1,3 @@ -<<<<<<< HEAD """FastAPI app for Pi-hole suite monitoring and management.""" import os @@ -49,7 +48,6 @@ async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]: def get_api_key() -> str: """Get configured API key.""" -======= """FastAPI app exposing Pi-hole suite data.""" import os import sqlite3 @@ -57,15 +55,11 @@ def get_api_key() -> str: def _get_api_key() -> str: ->>>>>>> origin/main return os.getenv("SUITE_API_KEY", "") def get_db() -> Generator[sqlite3.Connection, None, None]: -<<<<<<< HEAD """Database dependency.""" -======= ->>>>>>> origin/main conn = sqlite3.connect(config.DB_PATH) conn.row_factory = sqlite3.Row try: @@ -74,7 +68,6 @@ def get_db() -> Generator[sqlite3.Connection, None, None]: conn.close() -<<<<<<< HEAD def require_api_key(x_api_key: Optional[str] = Header(None)) -> None: """Require valid API key for authentication.""" api_key = get_api_key() @@ -194,7 +187,6 @@ def get_stats(db: sqlite3.Connection = Depends(get_db)) -> StatsResponse: total_devices=device_count, recent_queries=recent_queries ) -======= cur = db.execute( @@ -211,4 +203,3 @@ def get_ip_leases(db=Depends(get_db)): ->>>>>>> origin/main diff --git a/api/schemas.py b/api/schemas.py index 90a458e..08e10f7 100644 --- a/api/schemas.py +++ b/api/schemas.py @@ -1,4 +1,3 @@ -<<<<<<< HEAD """Pydantic schemas for API responses.""" from typing import Optional @@ -44,7 +43,6 @@ class StatsResponse(BaseModel): total_dns_logs: int = Field(..., description="Total DNS log entries") total_devices: int = Field(..., description="Total known devices") recent_queries: int = Field(..., description="Queries in the last hour") -======= """Pydantic models for API request/response validation.""" from typing import Optional from pydantic import BaseModel, Field, field_validator @@ -100,4 +98,3 @@ class DNSLogResponse(BaseModel): class HealthResponse(BaseModel): """Response model for health check.""" ok: bool ->>>>>>> origin/main diff --git a/install.sh b/install.sh index f82ab27..420ea24 100755 --- a/install.sh +++ b/install.sh @@ -2,8 +2,6 @@ set -euo pipefail # Pi-hole + Unbound + NetAlertX + Python Suite One-Click Installer -# ================================================================ -<<<<<<< HEAD # Modern, idempotent installer for complete DNS security stack # # Author: TimInTech @@ -111,7 +109,6 @@ install_packages() { apt-get install -y \ unbound \ unbound-anchor \ -======= # This script installs and configures: # - Unbound DNS resolver on 127.0.0.1:5335 # - Pi-hole using Unbound as upstream @@ -216,7 +213,6 @@ install_packages() { # Install required packages apt-get install -y \ unbound \ ->>>>>>> origin/main ca-certificates \ curl \ dnsutils \ @@ -226,7 +222,6 @@ install_packages() { git \ docker.io \ openssl \ -<<<<<<< HEAD systemd \ sqlite3 @@ -246,7 +241,6 @@ configure_unbound() { # Create Pi-hole configuration log "Creating Unbound configuration..." -======= systemctl log_success "System packages installed" @@ -266,7 +260,6 @@ configure_unbound() { fi # Create Pi-hole specific Unbound configuration ->>>>>>> origin/main cat > /etc/unbound/unbound.conf.d/pi-hole.conf << 'EOF' server: # Basic settings @@ -278,16 +271,13 @@ server: do-udp: yes do-tcp: yes -<<<<<<< HEAD # Performance tuning edns-buffer-size: 1232 prefetch: yes prefetch-key: yes -======= # Performance settings edns-buffer-size: 1232 prefetch: yes ->>>>>>> origin/main num-threads: 2 so-rcvbuf: 1m so-sndbuf: 1m @@ -297,11 +287,8 @@ server: hide-identity: yes hide-version: yes -<<<<<<< HEAD # Security hardening -======= # Security settings ->>>>>>> origin/main harden-glue: yes harden-dnssec-stripped: yes harden-below-nxdomain: yes @@ -311,12 +298,9 @@ server: # Cache settings cache-min-ttl: 60 cache-max-ttl: 86400 -<<<<<<< HEAD msg-cache-size: 50m rrset-cache-size: 100m -======= prefetch-key: yes ->>>>>>> origin/main # DNSSEC trust-anchor-file: /var/lib/unbound/root.key @@ -331,7 +315,6 @@ server: forward-zone: name: "." -<<<<<<< HEAD forward-tls-upstream: yes forward-addr: 9.9.9.9@853#dns.quad9.net forward-addr: 149.112.112.112@853#dns.quad9.net @@ -343,7 +326,6 @@ EOF # Start Unbound systemctl enable --now unbound -======= # Use Quad9 as fallback forward-addr: 9.9.9.9@853#dns.quad9.net forward-addr: 149.112.112.112@853#dns.quad9.net @@ -361,12 +343,10 @@ EOF systemctl restart unbound # Wait for Unbound to start ->>>>>>> origin/main sleep 3 # Test Unbound if dig +short @127.0.0.1 -p $UNBOUND_PORT example.com | grep -q "93.184."; then -<<<<<<< HEAD success "Unbound is responding correctly" else warn "Unbound test failed - continuing anyway" @@ -407,7 +387,6 @@ EOF # Ensure Pi-hole uses Unbound log "Configuring Pi-hole upstream DNS..." -======= log_success "Unbound is working correctly" else log_warning "Unbound test failed, but continuing..." @@ -431,13 +410,11 @@ install_pihole() { log_info "Configuring Pi-hole to use Unbound..." # Set upstream DNS to Unbound ->>>>>>> origin/main if [[ -f /etc/pihole/setupVars.conf ]]; then sed -i "s/^PIHOLE_DNS_1=.*/PIHOLE_DNS_1=127.0.0.1#$UNBOUND_PORT/" /etc/pihole/setupVars.conf sed -i "s/^PIHOLE_DNS_2=.*/PIHOLE_DNS_2=/" /etc/pihole/setupVars.conf fi -<<<<<<< HEAD # Restart Pi-hole pihole restartdns @@ -461,7 +438,6 @@ install_netalertx() { # Run NetAlertX log "Starting NetAlertX container..." -======= # Restart Pi-hole DNS pihole restartdns @@ -485,14 +461,12 @@ install_netalertx() { docker rm netalertx 2>/dev/null || true # Run NetAlertX container ->>>>>>> origin/main docker run -d \ --name netalertx \ --restart unless-stopped \ -p $NETALERTX_PORT:20211 \ -v /opt/netalertx/config:/app/config \ -v /opt/netalertx/db:/app/db \ -<<<<<<< HEAD -e TZ=$(timedatectl show --property=Timezone --value 2>/dev/null || echo "UTC") \ $NETALERTX_IMAGE @@ -538,7 +512,6 @@ EOF cat > /etc/systemd/system/pihole-suite.service << EOF [Unit] Description=Pi-hole Suite (API + monitoring) -======= -e TZ=$(timedatectl show --property=Timezone --value) \ jokobsk/netalertx:latest @@ -568,13 +541,11 @@ setup_python_suite() { cat > /etc/systemd/system/pihole-suite.service << EOF [Unit] Description=Pi-hole Suite (API + workers) ->>>>>>> origin/main After=network.target pihole-FTL.service Wants=pihole-FTL.service [Service] Type=simple -<<<<<<< HEAD User=$install_user Group=$install_user WorkingDirectory=$project_dir @@ -588,7 +559,6 @@ NoNewPrivileges=yes ProtectSystem=full ProtectHome=read-only ReadWritePaths=$project_dir/data -======= User=$INSTALL_USER Group=$INSTALL_USER WorkingDirectory=$PROJECT_DIR @@ -604,7 +574,6 @@ NoNewPrivileges=yes ProtectSystem=strict ProtectHome=yes ReadWritePaths=$PROJECT_DIR/data ->>>>>>> origin/main PrivateTmp=yes PrivateDevices=yes ProtectHostname=yes @@ -615,29 +584,22 @@ ProtectControlGroups=yes RestrictRealtime=yes RestrictSUIDSGID=yes RemoveIPC=yes -<<<<<<< HEAD -======= MemoryDenyWriteExecute=yes ->>>>>>> origin/main [Install] WantedBy=multi-user.target EOF -<<<<<<< HEAD # Enable and start service -======= # Create data directory with proper ownership mkdir -p "$PROJECT_DIR/data" chown -R "$INSTALL_USER:$INSTALL_USER" "$PROJECT_DIR/data" # Enable and start the service ->>>>>>> origin/main systemctl daemon-reload systemctl enable pihole-suite.service systemctl start pihole-suite.service -<<<<<<< HEAD success "Python suite installed and started" log "API Key: $api_key" } @@ -735,7 +697,6 @@ main() { check_system check_ports -======= log_success "Python suite installed and running" } @@ -789,9 +750,7 @@ run_health_checks() { # Display summary show_summary() { echo - echo "==============================================" echo " Installation Complete!" - echo "==============================================" echo echo "🔧 Components installed:" echo " • Unbound DNS resolver: 127.0.0.1:$UNBOUND_PORT" @@ -828,7 +787,6 @@ show_summary() { # Main installation function main() { echo "Pi-hole + Unbound + NetAlertX + Python Suite Installer" - echo "======================================================" echo check_privileges @@ -837,13 +795,11 @@ main() { log_info "Starting installation..." ->>>>>>> origin/main install_packages configure_unbound install_pihole install_netalertx setup_python_suite -<<<<<<< HEAD run_health_checks show_summary @@ -851,7 +807,6 @@ main() { } # Execute main function -======= log_info "Running health checks..." run_health_checks @@ -863,5 +818,4 @@ main() { } # Run main function ->>>>>>> origin/main main "$@" \ No newline at end of file diff --git a/pyalloc/README_DEMO.md b/pyalloc/README_DEMO.md index 890275f..02e1d9e 100644 --- a/pyalloc/README_DEMO.md +++ b/pyalloc/README_DEMO.md @@ -1,6 +1,5 @@ # PyAlloc - Demo IP Allocator -<<<<<<< HEAD ⚠️ **This is a DEMO component only** ⚠️ This directory contains a simple IP address allocator that serves as a proof-of-concept. It is **disabled by default** and not required for the main Pi-hole + Unbound + NetAlertX installation. @@ -32,7 +31,6 @@ For production environments, consider using proper DHCP management tools: - Router/firewall DHCP services This demo allocator is **not suitable for production use**. -======= ⚠️ **This is a demonstration component only** ⚠️ This directory contains a simple IP address allocator that was created as a proof-of-concept. It is **not required** for the main Pi-hole + Unbound + NetAlertX installation. @@ -54,4 +52,3 @@ The pyalloc component is automatically disabled in the one-click installer. If y ## For production use For production environments, consider using proper DHCP management tools or network management systems instead of this demo allocator. ->>>>>>> origin/main diff --git a/pyalloc/__init__.py b/pyalloc/__init__.py index 798d5c2..4cd615e 100644 --- a/pyalloc/__init__.py +++ b/pyalloc/__init__.py @@ -1,5 +1,2 @@ -<<<<<<< HEAD """Demo IP allocator - Not required for main functionality.""" -======= """IP allocation helpers.""" ->>>>>>> origin/main diff --git a/pyalloc/allocator.py b/pyalloc/allocator.py index 812625f..02ce2ab 100644 --- a/pyalloc/allocator.py +++ b/pyalloc/allocator.py @@ -1,22 +1,18 @@ -<<<<<<< HEAD """Simple IP pool allocator - DEMO ONLY.""" import ipaddress import logging import threading from typing import Set -======= """Simple IP pool allocator.""" import ipaddress import logging import threading ->>>>>>> origin/main logger = logging.getLogger(__name__) class IPPool: -<<<<<<< HEAD """Simple IP address pool manager (Demo implementation).""" def __init__(self, network: str = "192.168.1.0/24"): @@ -39,20 +35,17 @@ def allocate(self) -> str: Raises: RuntimeError: If no IPs available """ -======= def __init__(self, network: str): self.network = ipaddress.ip_network(network) self.lock = threading.Lock() self.allocated = set() def allocate(self) -> str: ->>>>>>> origin/main with self.lock: for ip in self.network.hosts(): ip_str = str(ip) if ip_str not in self.allocated: self.allocated.add(ip_str) -<<<<<<< HEAD logger.info(f"Allocated IP: {ip_str}") return ip_str @@ -102,7 +95,6 @@ def get_stats(self) -> dict: "available": total_hosts - allocated_count, "utilization_percent": (allocated_count / total_hosts) * 100 if total_hosts > 0 else 0 } -======= logger.info("Allocated IP %s", ip_str) return ip_str raise RuntimeError("No free IP addresses") @@ -112,4 +104,3 @@ def release(self, ip: str) -> None: if ip in self.allocated: self.allocated.remove(ip) logger.info("Released IP %s", ip) ->>>>>>> origin/main diff --git a/pyalloc/main.py b/pyalloc/main.py index 87a7d72..7cf1aea 100644 --- a/pyalloc/main.py +++ b/pyalloc/main.py @@ -1,22 +1,18 @@ -<<<<<<< HEAD """Demo IP allocator worker - DEMO ONLY.""" import logging import sqlite3 import threading from typing import Optional -======= """Placeholder IP allocator worker.""" import logging import threading import time ->>>>>>> origin/main from .allocator import IPPool logger = logging.getLogger(__name__) -<<<<<<< HEAD # Global state _pool: Optional[IPPool] = None _stop_event = threading.Event() @@ -105,7 +101,6 @@ def stop() -> None: def get_pool() -> Optional[IPPool]: """Get the current IP pool instance (for demo/testing).""" return _pool -======= _pool = None _stop_event = threading.Event() @@ -128,4 +123,3 @@ def _run() -> None: def stop() -> None: _stop_event.set() ->>>>>>> origin/main diff --git a/pyhole/__init__.py b/pyhole/__init__.py index 9c162e2..ca7db5d 100644 --- a/pyhole/__init__.py +++ b/pyhole/__init__.py @@ -1,5 +1,2 @@ -<<<<<<< HEAD """Pi-hole monitoring components.""" -======= """Pi-hole related helpers.""" ->>>>>>> origin/main diff --git a/pyhole/dns_monitor.py b/pyhole/dns_monitor.py index 5af3f98..a98c2c9 100644 --- a/pyhole/dns_monitor.py +++ b/pyhole/dns_monitor.py @@ -1,19 +1,15 @@ -<<<<<<< HEAD """Robust Pi-hole log monitor with log rotation support.""" import logging import re import sqlite3 -======= """Simple Pi-hole log tailer with log rotation support.""" import logging ->>>>>>> origin/main import threading import time from pathlib import Path from typing import Optional, Tuple -<<<<<<< HEAD from shared.shared_config import PIHOLE_LOG_PATH logger = logging.getLogger(__name__) @@ -220,7 +216,6 @@ def stop() -> None: def is_running() -> bool: """Check if the monitor is currently running.""" return _monitor_thread is not None and _monitor_thread.is_alive() -======= logger = logging.getLogger(__name__) PIHOLE_LOG = Path("/var/log/pihole.log") @@ -306,4 +301,3 @@ def start(conn): def stop() -> None: """Stop the DNS monitor.""" _stop_event.set() ->>>>>>> origin/main diff --git a/scripts/bootstrap.py b/scripts/bootstrap.py index b5b296d..ba885db 100755 --- a/scripts/bootstrap.py +++ b/scripts/bootstrap.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -<<<<<<< HEAD """Bootstrap script to check dependencies and initialize the suite.""" import importlib.util @@ -59,7 +58,6 @@ def main(): if __name__ == "__main__": sys.exit(main()) -======= """Check suite dependencies.""" import importlib.util import sys @@ -84,4 +82,3 @@ def main() -> None: if __name__ == "__main__": main() ->>>>>>> origin/main diff --git a/scripts/healthcheck.py b/scripts/healthcheck.py index 3f0ca4d..2cd7cdd 100755 --- a/scripts/healthcheck.py +++ b/scripts/healthcheck.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -<<<<<<< HEAD """Health check script for Pi-hole suite.""" import os @@ -101,7 +100,6 @@ def main(): if __name__ == "__main__": sys.exit(main()) -======= """Verify database connectivity.""" import sqlite3 import sys @@ -121,4 +119,3 @@ def main() -> None: if __name__ == "__main__": main() ->>>>>>> origin/main diff --git a/shared/__init__.py b/shared/__init__.py index 1bd283c..a590b81 100644 --- a/shared/__init__.py +++ b/shared/__init__.py @@ -1,5 +1,2 @@ -<<<<<<< HEAD """Shared utilities and configuration.""" -======= """Shared utilities for the Pi-hole suite.""" ->>>>>>> origin/main diff --git a/shared/db.py b/shared/db.py index c6ab975..3d48fdc 100644 --- a/shared/db.py +++ b/shared/db.py @@ -1,4 +1,3 @@ -<<<<<<< HEAD """SQLite database initialization and helpers.""" import logging @@ -61,7 +60,6 @@ ); CREATE INDEX IF NOT EXISTS idx_stats_timestamp ON system_stats(timestamp); CREATE INDEX IF NOT EXISTS idx_stats_metric ON system_stats(metric_name); -======= """SQLite helpers for the Pi-hole suite.""" import sqlite3 from pathlib import Path @@ -95,12 +93,10 @@ last_seen TEXT ); CREATE INDEX IF NOT EXISTS idx_devices_ip ON devices(ip); ->>>>>>> origin/main """ def init_db() -> sqlite3.Connection: -<<<<<<< HEAD """Initialize the SQLite database with schema.""" try: # Ensure parent directory exists @@ -124,10 +120,8 @@ def get_connection() -> sqlite3.Connection: conn = sqlite3.connect(str(DB_PATH)) conn.row_factory = sqlite3.Row return conn -======= Path(DB_PATH).parent.mkdir(parents=True, exist_ok=True) conn = sqlite3.connect(DB_PATH, check_same_thread=False) conn.executescript(SCHEMA) conn.commit() return conn ->>>>>>> origin/main diff --git a/shared/shared_config.py b/shared/shared_config.py index ad23ae5..d1dfc97 100644 --- a/shared/shared_config.py +++ b/shared/shared_config.py @@ -1,14 +1,10 @@ -<<<<<<< HEAD """Shared configuration for the Pi-hole suite.""" -======= """Shared configuration helpers for the suite.""" ->>>>>>> origin/main import logging import os from pathlib import Path -<<<<<<< HEAD # Configuration from environment INTERFACE = os.getenv("SUITE_INTERFACE", "eth0") API_HOST = os.getenv("SUITE_HOST", "127.0.0.1") @@ -33,7 +29,6 @@ # Module logger logger = logging.getLogger(__name__) logger.info(f"Configuration loaded - Data directory: {DATA_DIR}, Log level: {LOG_LEVEL}") -======= INTERFACE = os.getenv("SUITE_INTERFACE", "eth0") DNS_PORT = int(os.getenv("SUITE_DNS_PORT", "5335")) LOG_LEVEL = os.getenv("SUITE_LOG_LEVEL", "INFO") @@ -44,4 +39,3 @@ DB_PATH = DATA_DIR / "shared.sqlite" logging.basicConfig(level=LOG_LEVEL, format="%(asctime)s [%(levelname)s] %(message)s") ->>>>>>> origin/main diff --git a/start_suite.py b/start_suite.py index ff05276..65e5135 100644 --- a/start_suite.py +++ b/start_suite.py @@ -1,6 +1,5 @@ #!/usr/bin/env python3 """Entry point for the Pi-hole monitoring suite.""" -<<<<<<< HEAD import asyncio import logging @@ -51,7 +50,6 @@ async def run_api() -> None: log_level="info", access_log=True ) -======= import asyncio import os import threading @@ -71,13 +69,11 @@ async def run_api() -> None: async def run_api() -> None: config = uvicorn.Config(api_app, host="127.0.0.1", port=8090, log_level="info") ->>>>>>> origin/main server = uvicorn.Server(config) await server.serve() def main() -> None: -<<<<<<< HEAD """Main application entry point.""" logger.info("Starting Pi-hole Suite...") @@ -125,7 +121,6 @@ def main() -> None: logger.info(f"API Key: {api_key[:8]}...") logger.info("Starting API server...") -======= conn = init_db() @@ -138,12 +133,10 @@ def main() -> None: print("✓ Started with pyalloc demo component") else: print("✓ Started in production mode (pyalloc demo disabled)") ->>>>>>> origin/main try: asyncio.run(run_api()) except KeyboardInterrupt: -<<<<<<< HEAD logger.info("Shutting down...") except Exception as e: logger.error(f"Application error: {e}") @@ -152,10 +145,8 @@ def main() -> None: if __name__ == "__main__": main() -======= print("Shutting down…") if __name__ == "__main__": main() ->>>>>>> origin/main diff --git a/tests/test_api.py b/tests/test_api.py index b42e475..d210021 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -1,5 +1,4 @@ """Test suite for Pi-hole Suite API.""" -<<<<<<< HEAD import os import tempfile @@ -42,7 +41,6 @@ def test_health_endpoint(client, api_headers): data = response.json() assert data["ok"] is True assert "message" in data -======= import pytest from fastapi.testclient import TestClient import os @@ -109,13 +107,11 @@ def test_health_endpoint(client): response = client.get("/health", headers={"X-API-Key": "test-api-key"}) assert response.status_code == 200 assert response.json() == {"ok": True} ->>>>>>> origin/main def test_health_endpoint_no_auth(client): """Test health endpoint without authentication.""" response = client.get("/health") -<<<<<<< HEAD assert response.status_code == 401 # API key validation returns 401 @@ -128,19 +124,16 @@ def test_health_endpoint_bad_auth(client): def test_dns_logs_endpoint(client, api_headers): """Test DNS logs endpoint.""" response = client.get("/dns", headers=api_headers) -======= assert response.status_code == 422 # Unprocessable Entity due to missing required header def test_dns_logs_endpoint(client): """Test DNS logs endpoint.""" response = client.get("/dns", headers={"X-API-Key": "test-api-key"}) ->>>>>>> origin/main assert response.status_code == 200 assert isinstance(response.json(), list) -<<<<<<< HEAD def test_dns_logs_with_limit(client, api_headers): """Test DNS logs endpoint with limit parameter.""" response = client.get("/dns?limit=10", headers=api_headers) @@ -153,29 +146,23 @@ def test_dns_logs_with_limit(client, api_headers): def test_devices_endpoint(client, api_headers): """Test devices endpoint.""" response = client.get("/devices", headers=api_headers) -======= def test_devices_endpoint(client): """Test devices endpoint.""" response = client.get("/devices", headers={"X-API-Key": "test-api-key"}) ->>>>>>> origin/main assert response.status_code == 200 assert isinstance(response.json(), list) -<<<<<<< HEAD def test_leases_endpoint(client, api_headers): """Test IP leases endpoint.""" response = client.get("/leases", headers=api_headers) -======= def test_leases_endpoint(client): """Test IP leases endpoint.""" response = client.get("/leases", headers={"X-API-Key": "test-api-key"}) ->>>>>>> origin/main assert response.status_code == 200 assert isinstance(response.json(), list) -<<<<<<< HEAD def test_stats_endpoint(client, api_headers): """Test statistics endpoint.""" response = client.get("/stats", headers=api_headers) @@ -195,7 +182,6 @@ def test_root_endpoint(client): assert response.status_code == 200 data = response.json() assert "message" in data -======= def test_root_status(): """Test basic application status.""" # This is a placeholder test for basic functionality @@ -225,4 +211,3 @@ def test_device_request_validation(): # Invalid MAC address with pytest.raises(ValueError): DeviceRequest(ip_address="192.168.1.100", status=True, mac_address="invalid-mac") ->>>>>>> origin/main