You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

325 lines
10 KiB

import pytest
from sqlalchemy import select
from app.core.secret_codec import decrypt_secret_config
from app.models.entities import Backend
@pytest.mark.anyio
async def test_list_backends_returns_default_local_backend(api_client) -> None:
response = await api_client.get("/api/backends")
assert response.status_code == 200
payload = response.json()
assert len(payload["items"]) >= 1
assert payload["items"][0]["name"] == "local-default"
assert payload["items"][0]["type"] == "local_directory"
@pytest.mark.anyio
async def test_create_local_backend(api_client, runtime_paths) -> None:
response = await api_client.post(
"/api/backends",
json={
"name": "archive-local",
"type": "local_directory",
"stability_class": "stable",
"read_priority": 50,
"write_priority": 50,
"config": {
"base_path": runtime_paths["local_storage_dir"] + "/archive",
},
},
)
assert response.status_code == 200
payload = response.json()["backend"]
assert payload["name"] == "archive-local"
assert payload["type"] == "local_directory"
@pytest.mark.anyio
async def test_create_s3_backend_metadata(api_client, session_factory) -> None:
response = await api_client.post(
"/api/backends",
json={
"name": "s3-main",
"type": "s3",
"stability_class": "stable",
"read_priority": 80,
"write_priority": 80,
"config": {
"bucket": "iron-test",
"region": "us-east-1",
"access_key_id": "access-key",
"secret_access_key": "secret-key",
},
},
)
assert response.status_code == 200
payload = response.json()["backend"]
assert payload["name"] == "s3-main"
assert payload["type"] == "s3"
assert payload["config"]["bucket"] == "iron-test"
assert "access_key_id" not in payload["config"]
assert "secret_access_key" not in payload["config"]
assert payload["secret_fields"] == ["access_key_id", "secret_access_key"]
async with session_factory() as session:
backend = (
await session.execute(select(Backend).where(Backend.name == "s3-main"))
).scalar_one()
assert backend.secret_config_json.startswith("fernet:v1:")
assert "access-key" not in backend.secret_config_json
assert "secret-key" not in backend.secret_config_json
@pytest.mark.anyio
async def test_create_webdav_backend_separates_password(api_client) -> None:
response = await api_client.post(
"/api/backends",
json={
"name": "openlist-main",
"type": "webdav",
"stability_class": "stable",
"read_priority": 60,
"write_priority": 40,
"config": {
"endpoint_url": "http://127.0.0.1:5244/dav",
"username": "iron",
"password": "secret-password",
"root_path": "apps/iron",
},
},
)
assert response.status_code == 200
payload = response.json()["backend"]
assert payload["type"] == "webdav"
assert payload["config"]["endpoint_url"] == "http://127.0.0.1:5244/dav"
assert payload["config"]["username"] == "iron"
assert payload["config"]["root_path"] == "apps/iron"
assert "password" not in payload["config"]
assert payload["secret_fields"] == ["password"]
@pytest.mark.anyio
async def test_check_local_backend(api_client) -> None:
list_response = await api_client.get("/api/backends")
backend_id = list_response.json()["items"][0]["id"]
response = await api_client.post(f"/api/backends/{backend_id}/check")
assert response.status_code == 200
assert response.json()["status"] == "healthy"
@pytest.mark.anyio
async def test_check_s3_backend_returns_healthy(api_client, fake_s3) -> None:
create_response = await api_client.post(
"/api/backends",
json={
"name": "s3-main",
"type": "s3",
"stability_class": "stable",
"read_priority": 80,
"write_priority": 80,
"config": {
"bucket": "iron-test",
},
},
)
backend_id = create_response.json()["backend"]["id"]
response = await api_client.post(f"/api/backends/{backend_id}/check")
assert response.status_code == 200
assert response.json()["status"] == "healthy"
@pytest.mark.anyio
async def test_disable_backend(api_client) -> None:
create_response = await api_client.post(
"/api/backends",
json={
"name": "archive-local",
"type": "local_directory",
"stability_class": "stable",
"read_priority": 50,
"write_priority": 50,
"config": {
"base_path": "/tmp/archive-local",
},
},
)
backend_id = create_response.json()["backend"]["id"]
response = await api_client.post(f"/api/backends/{backend_id}/disable")
assert response.status_code == 200
assert response.json()["backend"]["is_enabled"] is False
@pytest.mark.anyio
async def test_enable_backend(api_client) -> None:
create_response = await api_client.post(
"/api/backends",
json={
"name": "archive-local",
"type": "local_directory",
"stability_class": "stable",
"read_priority": 50,
"write_priority": 50,
"config": {
"base_path": "/tmp/archive-local",
},
},
)
backend_id = create_response.json()["backend"]["id"]
await api_client.post(f"/api/backends/{backend_id}/disable")
response = await api_client.post(f"/api/backends/{backend_id}/enable")
assert response.status_code == 200
assert response.json()["backend"]["is_enabled"] is True
@pytest.mark.anyio
async def test_update_s3_backend_preserves_and_replaces_secrets(api_client, session_factory) -> None:
create_response = await api_client.post(
"/api/backends",
json={
"name": "s3-main",
"type": "s3",
"stability_class": "stable",
"read_priority": 80,
"write_priority": 80,
"config": {
"bucket": "iron-test",
"region": "us-east-1",
"access_key_id": "access-key",
"secret_access_key": "secret-key",
},
},
)
backend_id = create_response.json()["backend"]["id"]
update_response = await api_client.patch(
f"/api/backends/{backend_id}",
json={
"name": "s3-archive",
"read_priority": 40,
"write_priority": 45,
"config": {
"bucket": "iron-archive",
"prefix": "cold",
},
},
)
assert update_response.status_code == 200
payload = update_response.json()["backend"]
assert payload["name"] == "s3-archive"
assert payload["read_priority"] == 40
assert payload["write_priority"] == 45
assert payload["config"]["bucket"] == "iron-archive"
assert payload["config"]["prefix"] == "cold"
assert payload["secret_fields"] == ["access_key_id", "secret_access_key"]
async with session_factory() as session:
backend = await session.get(Backend, backend_id)
assert decrypt_secret_config(backend.secret_config_json)["secret_access_key"] == "secret-key"
replace_response = await api_client.patch(
f"/api/backends/{backend_id}",
json={
"config": {
"secret_access_key": "next-secret-key",
},
},
)
assert replace_response.status_code == 200
async with session_factory() as session:
backend = await session.get(Backend, backend_id)
assert decrypt_secret_config(backend.secret_config_json)["secret_access_key"] == "next-secret-key"
@pytest.mark.anyio
async def test_delete_backend_requires_disabled_and_unreferenced(api_client) -> None:
create_response = await api_client.post(
"/api/backends",
json={
"name": "archive-local",
"type": "local_directory",
"stability_class": "stable",
"read_priority": 50,
"write_priority": 50,
"config": {
"base_path": "/tmp/archive-local",
},
},
)
backend_id = create_response.json()["backend"]["id"]
enabled_delete = await api_client.delete(f"/api/backends/{backend_id}")
assert enabled_delete.status_code == 409
assert enabled_delete.json()["error"]["code"] == "backend_delete_conflict"
disable_response = await api_client.post(f"/api/backends/{backend_id}/disable")
assert disable_response.status_code == 200
delete_response = await api_client.delete(f"/api/backends/{backend_id}")
assert delete_response.status_code == 204
@pytest.mark.anyio
async def test_delete_backend_rejects_policy_reference(api_client) -> None:
create_response = await api_client.post(
"/api/backends",
json={
"name": "s3-main",
"type": "s3",
"stability_class": "stable",
"read_priority": 80,
"write_priority": 80,
"config": {"bucket": "iron-test"},
},
)
backend_id = create_response.json()["backend"]["id"]
await api_client.put(
"/api/policies/placement/document",
json={
"require_local": True,
"stable_replica_count": 1,
"opportunistic_replica_count": 0,
"preferred_backend_ids": [backend_id],
"excluded_backend_ids": [],
"max_non_local_size_bytes": None,
},
)
await api_client.post(f"/api/backends/{backend_id}/disable")
delete_response = await api_client.delete(f"/api/backends/{backend_id}")
assert delete_response.status_code == 409
assert delete_response.json()["error"]["code"] == "backend_delete_conflict"
@pytest.mark.anyio
async def test_create_backend_rejects_invalid_config(api_client) -> None:
response = await api_client.post(
"/api/backends",
json={
"name": "bad-s3",
"type": "s3",
"stability_class": "stable",
"read_priority": 10,
"write_priority": 10,
"config": {},
},
)
assert response.status_code == 400
assert response.json()["error"]["code"] == "backend_config_invalid"