Merge pull request #384 from christianlouis/copilot/clean-up-redundant-api-endpoints
Remove redundant /env view and /api/diagnostic/settings endpoint
This commit is contained in:
+2
-45
@@ -3,11 +3,10 @@ Diagnostic API endpoints
|
||||
"""
|
||||
|
||||
import logging
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import APIRouter, Depends, Request
|
||||
from fastapi import APIRouter, Request
|
||||
|
||||
from app.auth import get_current_user, require_login
|
||||
from app.auth import require_login
|
||||
from app.config import settings
|
||||
|
||||
# Set up logging
|
||||
@@ -15,48 +14,6 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
CurrentUser = Annotated[dict, Depends(get_current_user)]
|
||||
|
||||
|
||||
@router.get("/diagnostic/settings")
|
||||
@require_login
|
||||
async def diagnostic_settings(request: Request, current_user: CurrentUser):
|
||||
"""
|
||||
API endpoint to dump settings to the log and view basic config information
|
||||
This endpoint doesn't expose sensitive information like passwords or tokens
|
||||
"""
|
||||
from app.utils.config_validator import dump_all_settings
|
||||
|
||||
# Dump full settings to log for admin to see
|
||||
dump_all_settings()
|
||||
|
||||
# Return safe subset of settings for API response
|
||||
safe_settings = {
|
||||
"workdir": settings.workdir,
|
||||
"external_hostname": settings.external_hostname,
|
||||
"configured_services": {
|
||||
"email": bool(getattr(settings, "email_host", None)),
|
||||
"s3": bool(getattr(settings, "s3_bucket_name", None)),
|
||||
"dropbox": bool(getattr(settings, "dropbox_refresh_token", None)),
|
||||
"onedrive": bool(getattr(settings, "onedrive_refresh_token", None)),
|
||||
"nextcloud": bool(getattr(settings, "nextcloud_upload_url", None)),
|
||||
"sftp": bool(getattr(settings, "sftp_host", None)),
|
||||
"paperless": bool(getattr(settings, "paperless_host", None)),
|
||||
"google_drive": bool(getattr(settings, "google_drive_credentials_json", None)),
|
||||
"uptime_kuma": bool(getattr(settings, "uptime_kuma_url", None)),
|
||||
"auth": bool(getattr(settings, "authentik_config_url", None)),
|
||||
"openai": bool(getattr(settings, "openai_api_key", None)),
|
||||
"azure": bool(getattr(settings, "azure_api_key", None) and getattr(settings, "azure_endpoint", None)),
|
||||
},
|
||||
"imap_enabled": bool(getattr(settings, "imap1_host", None) or getattr(settings, "imap2_host", None)),
|
||||
}
|
||||
|
||||
return {
|
||||
"status": "success",
|
||||
"settings": safe_settings,
|
||||
"message": "Full settings have been dumped to application logs",
|
||||
}
|
||||
|
||||
|
||||
@router.post("/diagnostic/test-notification")
|
||||
@require_login
|
||||
|
||||
+1
-25
@@ -8,7 +8,7 @@ from datetime import datetime
|
||||
|
||||
from fastapi import Request
|
||||
|
||||
from app.utils.config_validator import get_provider_status, get_settings_for_display
|
||||
from app.utils.config_validator import get_provider_status
|
||||
from app.views.base import APIRouter, require_login, settings, templates
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -86,27 +86,3 @@ async def status_dashboard(request: Request):
|
||||
"settings": {"notification_urls": notification_urls},
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@router.get("/env")
|
||||
@require_login
|
||||
async def env_debug(request: Request):
|
||||
"""
|
||||
Debug endpoint to view environment variables and settings
|
||||
Uses actual debug setting from config
|
||||
"""
|
||||
# Use the actual debug setting from configuration
|
||||
debug_enabled = settings.debug
|
||||
|
||||
# Get settings data
|
||||
settings_data = get_settings_for_display(show_values=debug_enabled)
|
||||
|
||||
return templates.TemplateResponse(
|
||||
"env_debug.html",
|
||||
{
|
||||
"request": request,
|
||||
"settings": settings_data,
|
||||
"debug_enabled": debug_enabled,
|
||||
"app_version": settings.version,
|
||||
},
|
||||
)
|
||||
|
||||
@@ -60,7 +60,6 @@ All API endpoints are protected with a default rate limit unless explicitly exem
|
||||
- `GET /api/files/{file_id}/metadata` - Get file metadata
|
||||
- `GET /api/files/{file_id}/preview` - Get file preview
|
||||
- `GET /api/files/{file_id}/download` - Download file
|
||||
- `GET /api/diagnostic/settings` - Get settings
|
||||
- `GET /api/logs` - Get logs
|
||||
|
||||
**Rationale**: Read-only operations are less resource-intensive but still need protection against scraping and excessive polling. The default limit of 100 requests per minute allows legitimate applications while preventing abuse.
|
||||
|
||||
@@ -79,9 +79,6 @@
|
||||
<a href="/admin/credentials" class="flex items-center px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">
|
||||
<i class="fas fa-key w-4 mr-2 text-yellow-500"></i> Credentials
|
||||
</a>
|
||||
<a href="/env" class="flex items-center px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">
|
||||
<i class="fas fa-terminal w-4 mr-2 text-gray-500"></i> Environment
|
||||
</a>
|
||||
<a href="/admin/files" class="flex items-center px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">
|
||||
<i class="fas fa-folder-open w-4 mr-2 text-gray-500"></i> File Manager
|
||||
</a>
|
||||
@@ -142,9 +139,6 @@
|
||||
<a href="/admin/credentials" class="block px-3 py-3 rounded-md text-base font-medium text-gray-700 hover:text-gray-900 hover:bg-gray-50">
|
||||
<i class="fas fa-key mr-2 text-yellow-400"></i> Credentials
|
||||
</a>
|
||||
<a href="/env" class="block px-3 py-3 rounded-md text-base font-medium text-gray-700 hover:text-gray-900 hover:bg-gray-50">
|
||||
<i class="fas fa-terminal mr-2 text-gray-400"></i> Environment
|
||||
</a>
|
||||
<a href="/admin/files" class="block px-3 py-3 rounded-md text-base font-medium text-gray-700 hover:text-gray-900 hover:bg-gray-50">
|
||||
<i class="fas fa-folder-open mr-2 text-gray-400"></i> File Manager
|
||||
</a>
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}Environment Configuration{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container mx-auto px-4 py-8">
|
||||
<div class="mb-8">
|
||||
<h1 class="text-3xl font-bold mb-2">Environment Configuration</h1>
|
||||
<p class="text-gray-600">
|
||||
This page displays the current configuration settings for the application. For security reasons,
|
||||
sensitive values like passwords, tokens, and keys may be hidden.
|
||||
</p>
|
||||
<div class="bg-blue-100 border-l-4 border-blue-500 text-blue-700 p-4 my-4" role="alert">
|
||||
<p><strong>Debug Mode:</strong> {{ "Enabled" if debug_enabled else "Disabled" }}</p>
|
||||
<p><strong>App Version:</strong> {{ app_version }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% for category, items in settings.items() %}
|
||||
<div class="mb-8">
|
||||
<h2 class="text-2xl font-semibold mb-4">{{ category }} Configuration</h2>
|
||||
<div class="bg-white shadow overflow-hidden rounded-lg">
|
||||
<table class="min-w-full divide-y divide-gray-200">
|
||||
<thead class="bg-gray-50">
|
||||
<tr>
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Setting</th>
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Value</th>
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white divide-y divide-gray-200">
|
||||
{% for item in items %}
|
||||
<tr>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">
|
||||
{{ item.name }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
||||
{% if item.value is none %}
|
||||
<span class="text-gray-400">NULL</span>
|
||||
{% elif item.value == "" %}
|
||||
<span class="text-gray-400">(empty string)</span>
|
||||
{% elif item.value == "********" %}
|
||||
<span class="text-gray-400">********</span>
|
||||
{% else %}
|
||||
{{ item.value }}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
{% if item.is_configured %}
|
||||
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800">
|
||||
Configured
|
||||
</span>
|
||||
{% else %}
|
||||
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-red-100 text-red-800">
|
||||
Not Configured
|
||||
</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
<div class="mt-8 bg-gray-50 p-4 rounded-lg border border-gray-200">
|
||||
<h3 class="text-lg font-medium text-gray-900">Environment Variables</h3>
|
||||
<p class="text-sm text-gray-600 mt-1">
|
||||
Configuration is loaded from environment variables or .env files.
|
||||
Make sure your environment variables are correctly set.
|
||||
</p>
|
||||
|
||||
<div class="mt-4">
|
||||
<a href="/api/diagnostic/settings" class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
|
||||
View API Diagnostic
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -221,11 +221,11 @@
|
||||
<div class="mt-8 bg-gray-50 p-4 rounded-lg border border-gray-200">
|
||||
<h3 class="text-lg font-medium text-gray-900">Configuration Settings</h3>
|
||||
<p class="text-sm text-gray-600 mt-1">
|
||||
For more detailed configuration settings and environment variables, check the environment debug page.
|
||||
For more detailed configuration settings and environment variables, check the settings page.
|
||||
</p>
|
||||
|
||||
<div class="mt-4">
|
||||
<a href="/env" class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
|
||||
<a href="/settings" class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
|
||||
View Detailed Configuration
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -3,119 +3,6 @@
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
class TestDiagnosticSettings:
|
||||
"""Tests for GET /diagnostic/settings endpoint."""
|
||||
|
||||
@patch("app.utils.config_validator.dump_all_settings")
|
||||
def test_diagnostic_settings_success(self, mock_dump, client: TestClient):
|
||||
"""Test successful diagnostic settings retrieval."""
|
||||
from app.config import settings
|
||||
|
||||
with patch.object(settings, "workdir", "/tmp/test"):
|
||||
with patch.object(settings, "external_hostname", "test-host"):
|
||||
with patch.object(settings, "email_host", "smtp.test.com"):
|
||||
with patch.object(settings, "openai_api_key", "sk-test"):
|
||||
# The endpoint requires login, so we'd need to mock auth
|
||||
# Testing the function logic directly
|
||||
pass
|
||||
|
||||
@patch("app.utils.config_validator.dump_all_settings")
|
||||
def test_diagnostic_settings_logs_to_file(self, mock_dump):
|
||||
"""Test that settings are dumped to logs."""
|
||||
# Endpoint should call dump_all_settings
|
||||
# mock_dump should be called once
|
||||
|
||||
@patch("app.utils.config_validator.dump_all_settings")
|
||||
def test_diagnostic_settings_returns_safe_subset(self, mock_dump):
|
||||
"""Test that only safe settings are returned in response."""
|
||||
from app.config import settings
|
||||
|
||||
with patch.object(settings, "openai_api_key", "sk-secret-key"):
|
||||
# Response should NOT contain the actual API key
|
||||
# Should only return bool indicating it's configured
|
||||
pass
|
||||
|
||||
def test_diagnostic_settings_configured_services_all_false(self):
|
||||
"""Test configured_services when nothing is configured."""
|
||||
from app.config import settings
|
||||
|
||||
with patch.object(settings, "email_host", None):
|
||||
with patch.object(settings, "s3_bucket_name", None):
|
||||
with patch.object(settings, "dropbox_refresh_token", None):
|
||||
with patch.object(settings, "onedrive_refresh_token", None):
|
||||
with patch.object(settings, "nextcloud_upload_url", None):
|
||||
with patch.object(settings, "sftp_host", None):
|
||||
with patch.object(settings, "paperless_host", None):
|
||||
with patch.object(settings, "google_drive_credentials_json", None):
|
||||
with patch.object(settings, "uptime_kuma_url", None):
|
||||
with patch.object(settings, "authentik_config_url", None):
|
||||
with patch.object(settings, "openai_api_key", None):
|
||||
with patch.object(settings, "azure_ai_key", None):
|
||||
# All configured_services should be False
|
||||
pass
|
||||
|
||||
def test_diagnostic_settings_configured_services_all_true(self):
|
||||
"""Test configured_services when all services are configured."""
|
||||
from app.config import settings
|
||||
|
||||
with patch.object(settings, "email_host", "smtp.test.com"):
|
||||
with patch.object(settings, "s3_bucket_name", "test-bucket"):
|
||||
with patch.object(settings, "dropbox_refresh_token", "token"):
|
||||
# All configured_services should be True
|
||||
pass
|
||||
|
||||
def test_diagnostic_settings_imap_enabled_imap1(self):
|
||||
"""Test imap_enabled when imap1_host is configured."""
|
||||
from app.config import settings
|
||||
|
||||
with patch.object(settings, "imap1_host", "imap.test.com"):
|
||||
with patch.object(settings, "imap2_host", None):
|
||||
# imap_enabled should be True
|
||||
pass
|
||||
|
||||
def test_diagnostic_settings_imap_enabled_imap2(self):
|
||||
"""Test imap_enabled when imap2_host is configured."""
|
||||
from app.config import settings
|
||||
|
||||
with patch.object(settings, "imap1_host", None):
|
||||
with patch.object(settings, "imap2_host", "imap2.test.com"):
|
||||
# imap_enabled should be True
|
||||
pass
|
||||
|
||||
def test_diagnostic_settings_imap_disabled(self):
|
||||
"""Test imap_enabled when no IMAP hosts configured."""
|
||||
from app.config import settings
|
||||
|
||||
with patch.object(settings, "imap1_host", None):
|
||||
with patch.object(settings, "imap2_host", None):
|
||||
# imap_enabled should be False
|
||||
pass
|
||||
|
||||
def test_diagnostic_settings_azure_requires_both_settings(self):
|
||||
"""Test Azure configured only when both key and endpoint are set."""
|
||||
from app.config import settings
|
||||
|
||||
# Only key, no endpoint
|
||||
with patch.object(settings, "azure_ai_key", "key"):
|
||||
with patch.object(settings, "azure_endpoint", None):
|
||||
# azure should be False
|
||||
pass
|
||||
|
||||
# Only endpoint, no key
|
||||
with patch.object(settings, "azure_ai_key", None):
|
||||
with patch.object(settings, "azure_endpoint", "https://test.com"):
|
||||
# azure should be False
|
||||
pass
|
||||
|
||||
# Both set
|
||||
with patch.object(settings, "azure_ai_key", "key"):
|
||||
with patch.object(settings, "azure_endpoint", "https://test.com"):
|
||||
# azure should be True
|
||||
pass
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
|
||||
@@ -490,21 +490,6 @@ class TestLicenseRoutes:
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestDiagnosticSettings:
|
||||
"""GET /api/diagnostic/settings - dump settings."""
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_diagnostic_settings_success(self, client):
|
||||
"""Returns safe subset of settings."""
|
||||
with patch("app.utils.config_validator.dump_all_settings"):
|
||||
response = client.get("/api/diagnostic/settings")
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["status"] == "success"
|
||||
assert "settings" in data
|
||||
assert "configured_services" in data["settings"]
|
||||
|
||||
|
||||
class TestDiagnosticTestNotification:
|
||||
"""POST /api/diagnostic/test-notification - send test notification."""
|
||||
|
||||
|
||||
@@ -5,47 +5,6 @@ from unittest.mock import patch
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
class TestDiagnosticSettings:
|
||||
"""Tests for diagnostic settings endpoint."""
|
||||
|
||||
def test_diagnostic_settings_endpoint(self, client):
|
||||
"""Test /api/diagnostic/settings endpoint."""
|
||||
response = client.get("/api/diagnostic/settings")
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["status"] == "success"
|
||||
assert "settings" in data
|
||||
assert "configured_services" in data["settings"]
|
||||
|
||||
def test_diagnostic_settings_has_expected_services(self, client):
|
||||
"""Test that diagnostic settings has expected service keys."""
|
||||
response = client.get("/api/diagnostic/settings")
|
||||
data = response.json()
|
||||
services = data["settings"]["configured_services"]
|
||||
expected_keys = ["email", "s3", "dropbox", "onedrive", "nextcloud", "sftp", "openai", "azure"]
|
||||
for key in expected_keys:
|
||||
assert key in services
|
||||
|
||||
def test_diagnostic_settings_includes_workdir(self, client):
|
||||
"""Test that settings include workdir."""
|
||||
response = client.get("/api/diagnostic/settings")
|
||||
data = response.json()
|
||||
assert "workdir" in data["settings"]
|
||||
|
||||
def test_diagnostic_settings_includes_hostname(self, client):
|
||||
"""Test that settings include external hostname."""
|
||||
response = client.get("/api/diagnostic/settings")
|
||||
data = response.json()
|
||||
assert "external_hostname" in data["settings"]
|
||||
|
||||
def test_diagnostic_settings_includes_imap_status(self, client):
|
||||
"""Test that settings include IMAP enabled status."""
|
||||
response = client.get("/api/diagnostic/settings")
|
||||
data = response.json()
|
||||
assert "imap_enabled" in data["settings"]
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
class TestTestNotification:
|
||||
"""Tests for test notification endpoint."""
|
||||
@@ -120,66 +79,3 @@ class TestTestNotification:
|
||||
|
||||
# Response should have been processed
|
||||
assert response.status_code == 200
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
class TestDiagnosticHelpers:
|
||||
"""Test helper functions in diagnostic module."""
|
||||
|
||||
@patch("app.utils.config_validator.dump_all_settings")
|
||||
@patch("app.api.diagnostic.settings")
|
||||
def test_dump_all_settings_called(self, mock_settings, mock_dump, client):
|
||||
"""Test that dump_all_settings is called."""
|
||||
mock_settings.external_hostname = "test"
|
||||
# Setup minimal mocks for configured services
|
||||
mock_settings.email_host = None
|
||||
mock_settings.s3_bucket_name = None
|
||||
mock_settings.dropbox_refresh_token = None
|
||||
mock_settings.onedrive_refresh_token = None
|
||||
mock_settings.nextcloud_upload_url = None
|
||||
mock_settings.sftp_host = None
|
||||
mock_settings.paperless_host = None
|
||||
mock_settings.google_drive_credentials_json = None
|
||||
mock_settings.uptime_kuma_url = None
|
||||
mock_settings.authentik_config_url = None
|
||||
mock_settings.openai_api_key = None
|
||||
mock_settings.azure_api_key = None
|
||||
mock_settings.azure_endpoint = None
|
||||
mock_settings.imap1_host = None
|
||||
mock_settings.imap2_host = None
|
||||
|
||||
response = client.get("/api/diagnostic/settings")
|
||||
|
||||
# dump_all_settings should have been called
|
||||
mock_dump.assert_called_once()
|
||||
|
||||
@patch("app.api.diagnostic.settings")
|
||||
def test_safe_settings_no_sensitive_data(self, mock_settings, client):
|
||||
"""Test that safe settings don't include sensitive data."""
|
||||
mock_settings.workdir = "/tmp/workdir"
|
||||
mock_settings.external_hostname = "test-host"
|
||||
mock_settings.openai_api_key = "sk-secret-key-12345"
|
||||
mock_settings.aws_secret_access_key = "secret-aws-key"
|
||||
# Setup minimal configured services
|
||||
mock_settings.email_host = None
|
||||
mock_settings.s3_bucket_name = None
|
||||
mock_settings.dropbox_refresh_token = None
|
||||
mock_settings.onedrive_refresh_token = None
|
||||
mock_settings.nextcloud_upload_url = None
|
||||
mock_settings.sftp_host = None
|
||||
mock_settings.paperless_host = None
|
||||
mock_settings.google_drive_credentials_json = None
|
||||
mock_settings.uptime_kuma_url = None
|
||||
mock_settings.authentik_config_url = None
|
||||
mock_settings.azure_api_key = None
|
||||
mock_settings.azure_endpoint = None
|
||||
mock_settings.imap1_host = None
|
||||
mock_settings.imap2_host = None
|
||||
|
||||
response = client.get("/api/diagnostic/settings")
|
||||
data = response.json()
|
||||
|
||||
# Sensitive keys should not be in response
|
||||
response_str = str(data)
|
||||
assert "sk-secret-key" not in response_str
|
||||
assert "secret-aws-key" not in response_str
|
||||
|
||||
@@ -92,7 +92,6 @@ class TestEndpointRegistration:
|
||||
# Test a few known API endpoints to ensure the /api prefix works
|
||||
endpoints_to_check = [
|
||||
("/api/process-url", "post"),
|
||||
("/api/diagnostic/settings", "get"),
|
||||
]
|
||||
|
||||
for endpoint, method in endpoints_to_check:
|
||||
|
||||
@@ -14,11 +14,6 @@ class TestStatusViews:
|
||||
response = client.get("/status")
|
||||
assert response.status_code == 200
|
||||
|
||||
def test_env_debug_page(self, client):
|
||||
"""Test env debug page."""
|
||||
response = client.get("/env")
|
||||
assert response.status_code == 200
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
class TestStatusDashboard:
|
||||
@@ -149,89 +144,6 @@ class TestStatusDashboard:
|
||||
assert context["settings"]["notification_urls"] == ["https://webhook.example.com/notify"]
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
class TestEnvDebug:
|
||||
"""Tests for env_debug function."""
|
||||
|
||||
@patch("app.views.status.get_settings_for_display")
|
||||
@patch("app.views.status.templates")
|
||||
@patch("app.views.status.settings")
|
||||
@pytest.mark.asyncio
|
||||
async def test_env_debug_returns_template(self, mock_settings, mock_templates, mock_get_settings):
|
||||
"""Test env debug returns template response."""
|
||||
from app.views.status import env_debug
|
||||
|
||||
mock_settings.debug = False
|
||||
mock_settings.version = "1.0.0"
|
||||
mock_get_settings.return_value = {"workdir": {"value": "/app/workdir"}}
|
||||
|
||||
mock_request = Mock()
|
||||
|
||||
result = await env_debug(mock_request)
|
||||
|
||||
mock_templates.TemplateResponse.assert_called_once()
|
||||
call_args = mock_templates.TemplateResponse.call_args
|
||||
assert call_args[0][0] == "env_debug.html"
|
||||
|
||||
@patch("app.views.status.get_settings_for_display")
|
||||
@patch("app.views.status.templates")
|
||||
@patch("app.views.status.settings")
|
||||
@pytest.mark.asyncio
|
||||
async def test_env_debug_respects_debug_setting(self, mock_settings, mock_templates, mock_get_settings):
|
||||
"""Test env debug respects debug setting."""
|
||||
from app.views.status import env_debug
|
||||
|
||||
mock_settings.debug = True
|
||||
mock_settings.version = "1.0.0"
|
||||
mock_get_settings.return_value = {}
|
||||
|
||||
mock_request = Mock()
|
||||
|
||||
await env_debug(mock_request)
|
||||
|
||||
# Should call with show_values=True when debug is enabled
|
||||
mock_get_settings.assert_called_once_with(show_values=True)
|
||||
|
||||
@patch("app.views.status.get_settings_for_display")
|
||||
@patch("app.views.status.templates")
|
||||
@patch("app.views.status.settings")
|
||||
@pytest.mark.asyncio
|
||||
async def test_env_debug_hides_values_when_debug_disabled(self, mock_settings, mock_templates, mock_get_settings):
|
||||
"""Test env debug hides values when debug is disabled."""
|
||||
from app.views.status import env_debug
|
||||
|
||||
mock_settings.debug = False
|
||||
mock_settings.version = "1.0.0"
|
||||
mock_get_settings.return_value = {}
|
||||
|
||||
mock_request = Mock()
|
||||
|
||||
await env_debug(mock_request)
|
||||
|
||||
# Should call with show_values=False when debug is disabled
|
||||
mock_get_settings.assert_called_once_with(show_values=False)
|
||||
|
||||
@patch("app.views.status.get_settings_for_display")
|
||||
@patch("app.views.status.templates")
|
||||
@patch("app.views.status.settings")
|
||||
@pytest.mark.asyncio
|
||||
async def test_env_debug_includes_app_version(self, mock_settings, mock_templates, mock_get_settings):
|
||||
"""Test env debug includes app version."""
|
||||
from app.views.status import env_debug
|
||||
|
||||
mock_settings.debug = False
|
||||
mock_settings.version = "1.2.3"
|
||||
mock_get_settings.return_value = {}
|
||||
|
||||
mock_request = Mock()
|
||||
|
||||
await env_debug(mock_request)
|
||||
|
||||
call_args = mock_templates.TemplateResponse.call_args
|
||||
context = call_args[0][1]
|
||||
assert context["app_version"] == "1.2.3"
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
class TestContainerInfoDetection:
|
||||
"""Tests for container information detection logic."""
|
||||
@@ -386,9 +298,3 @@ class TestStatusEndpointsRequireAuth:
|
||||
# Should return 200 or redirect to login
|
||||
response = client.get("/status", follow_redirects=False)
|
||||
assert response.status_code in [200, 302, 401]
|
||||
|
||||
def test_env_debug_requires_login(self, client):
|
||||
"""Test env debug requires authentication."""
|
||||
# Should return 200 or redirect to login
|
||||
response = client.get("/env", follow_redirects=False)
|
||||
assert response.status_code in [200, 302, 401]
|
||||
|
||||
Reference in New Issue
Block a user