fix(api): Fix S3 backup path-style configuration and improve UX.

- Fix critical S3 client configuration bug for path-style addressing
- Add toast notifications for better user feedback on S3 config operations
- Set up Toaster component in root layout for app-wide notifications
- Clean up unused imports in test files

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Elisiário Couto
2025-09-28 22:55:15 +01:00
committed by Elisiário Couto
parent 0122913052
commit 22ec0e36b1
7 changed files with 150 additions and 126 deletions

View File

@@ -1,12 +1,9 @@
"""Tests for backup API endpoints."""
from unittest.mock import AsyncMock, patch
from unittest.mock import patch
import pytest
from leggen.api.models.backup import BackupSettings, S3Config
from leggen.models.config import S3BackupConfig
@pytest.mark.api
class TestBackupAPI:
@@ -16,10 +13,10 @@ class TestBackupAPI:
"""Test getting backup settings with no configuration."""
# Mock empty backup config by updating the config dict
mock_config._config["backup"] = {}
with patch("leggen.utils.config.config", mock_config):
response = api_client.get("/api/v1/backup/settings")
assert response.status_code == 200
data = response.json()
assert data["success"] is True
@@ -39,15 +36,15 @@ class TestBackupAPI:
"enabled": True,
}
}
with patch("leggen.utils.config.config", mock_config):
response = api_client.get("/api/v1/backup/settings")
assert response.status_code == 200
data = response.json()
assert data["success"] is True
assert data["data"]["s3"] is not None
s3_config = data["data"]["s3"]
assert s3_config["access_key_id"] == "***" # Masked
assert s3_config["secret_access_key"] == "***" # Masked
@@ -56,11 +53,13 @@ class TestBackupAPI:
assert s3_config["enabled"] is True
@patch("leggen.services.backup_service.BackupService.test_connection")
def test_update_backup_settings_success(self, mock_test_connection, api_client, mock_config):
def test_update_backup_settings_success(
self, mock_test_connection, api_client, mock_config
):
"""Test successful backup settings update."""
mock_test_connection.return_value = True
mock_config._config["backup"] = {}
request_data = {
"s3": {
"access_key_id": "AKIATEST123",
@@ -72,24 +71,26 @@ class TestBackupAPI:
"enabled": True,
}
}
with patch("leggen.utils.config.config", mock_config):
response = api_client.put("/api/v1/backup/settings", json=request_data)
assert response.status_code == 200
data = response.json()
assert data["success"] is True
assert data["data"]["updated"] is True
# Verify connection test was called
mock_test_connection.assert_called_once()
@patch("leggen.services.backup_service.BackupService.test_connection")
def test_update_backup_settings_connection_failure(self, mock_test_connection, api_client, mock_config):
def test_update_backup_settings_connection_failure(
self, mock_test_connection, api_client, mock_config
):
"""Test backup settings update with connection test failure."""
mock_test_connection.return_value = False
mock_config._config["backup"] = {}
request_data = {
"s3": {
"access_key_id": "AKIATEST123",
@@ -101,10 +102,10 @@ class TestBackupAPI:
"enabled": True,
}
}
with patch("leggen.utils.config.config", mock_config):
response = api_client.put("/api/v1/backup/settings", json=request_data)
assert response.status_code == 400
data = response.json()
assert "S3 connection test failed" in data["detail"]
@@ -113,7 +114,7 @@ class TestBackupAPI:
def test_test_backup_connection_success(self, mock_test_connection, api_client):
"""Test successful backup connection test."""
mock_test_connection.return_value = True
request_data = {
"service": "s3",
"config": {
@@ -124,16 +125,16 @@ class TestBackupAPI:
"endpoint_url": None,
"path_style": False,
"enabled": True,
}
},
}
response = api_client.post("/api/v1/backup/test", json=request_data)
assert response.status_code == 200
data = response.json()
assert data["success"] is True
assert data["data"]["connected"] is True
# Verify connection test was called
mock_test_connection.assert_called_once()
@@ -141,7 +142,7 @@ class TestBackupAPI:
def test_test_backup_connection_failure(self, mock_test_connection, api_client):
"""Test failed backup connection test."""
mock_test_connection.return_value = False
request_data = {
"service": "s3",
"config": {
@@ -152,11 +153,11 @@ class TestBackupAPI:
"endpoint_url": None,
"path_style": False,
"enabled": True,
}
},
}
response = api_client.post("/api/v1/backup/test", json=request_data)
assert response.status_code == 200
data = response.json()
assert data["success"] is False
@@ -173,11 +174,11 @@ class TestBackupAPI:
"endpoint_url": None,
"path_style": False,
"enabled": True,
}
},
}
response = api_client.post("/api/v1/backup/test", json=request_data)
assert response.status_code == 400
data = response.json()
assert "Only 's3' service is supported" in data["detail"]
@@ -197,7 +198,7 @@ class TestBackupAPI:
"size": 512,
},
]
mock_config._config["backup"] = {
"s3": {
"access_key_id": "AKIATEST123",
@@ -207,23 +208,26 @@ class TestBackupAPI:
"enabled": True,
}
}
with patch("leggen.utils.config.config", mock_config):
response = api_client.get("/api/v1/backup/list")
assert response.status_code == 200
data = response.json()
assert data["success"] is True
assert len(data["data"]) == 2
assert data["data"][0]["key"] == "leggen_backups/database_backup_20250101_120000.db"
assert (
data["data"][0]["key"]
== "leggen_backups/database_backup_20250101_120000.db"
)
def test_list_backups_no_config(self, api_client, mock_config):
"""Test backup listing with no configuration."""
mock_config._config["backup"] = {}
with patch("leggen.utils.config.config", mock_config):
response = api_client.get("/api/v1/backup/list")
assert response.status_code == 200
data = response.json()
assert data["success"] is True
@@ -231,13 +235,15 @@ class TestBackupAPI:
@patch("leggen.services.backup_service.BackupService.backup_database")
@patch("leggen.utils.paths.path_manager.get_database_path")
def test_backup_operation_success(self, mock_get_db_path, mock_backup_db, api_client, mock_config):
def test_backup_operation_success(
self, mock_get_db_path, mock_backup_db, api_client, mock_config
):
"""Test successful backup operation."""
from pathlib import Path
mock_get_db_path.return_value = Path("/test/database.db")
mock_backup_db.return_value = True
mock_config._config["backup"] = {
"s3": {
"access_key_id": "AKIATEST123",
@@ -247,30 +253,30 @@ class TestBackupAPI:
"enabled": True,
}
}
request_data = {"operation": "backup"}
with patch("leggen.utils.config.config", mock_config):
response = api_client.post("/api/v1/backup/operation", json=request_data)
assert response.status_code == 200
data = response.json()
assert data["success"] is True
assert data["data"]["operation"] == "backup"
assert data["data"]["completed"] is True
# Verify backup was called
mock_backup_db.assert_called_once()
def test_backup_operation_no_config(self, api_client, mock_config):
"""Test backup operation with no configuration."""
mock_config._config["backup"] = {}
request_data = {"operation": "backup"}
with patch("leggen.utils.config.config", mock_config):
response = api_client.post("/api/v1/backup/operation", json=request_data)
assert response.status_code == 400
data = response.json()
assert "S3 backup is not configured" in data["detail"]
@@ -286,12 +292,12 @@ class TestBackupAPI:
"enabled": True,
}
}
request_data = {"operation": "invalid"}
with patch("leggen.utils.config.config", mock_config):
response = api_client.post("/api/v1/backup/operation", json=request_data)
assert response.status_code == 400
data = response.json()
assert "Invalid operation" in data["detail"]
assert "Invalid operation" in data["detail"]