Allow heading tags (h1-h6) in ContentSanitizer so news editor headings render
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
39
services/enhance-worker/tests/test_health.py
Normal file
39
services/enhance-worker/tests/test_health.py
Normal file
@@ -0,0 +1,39 @@
|
||||
from pathlib import Path
|
||||
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from app.config import Settings
|
||||
from app.main import create_app
|
||||
|
||||
|
||||
def make_settings(tmp_path: Path) -> Settings:
|
||||
return Settings(
|
||||
host="127.0.0.1",
|
||||
port=8095,
|
||||
token="secret-token",
|
||||
engine="pillow",
|
||||
device="cpu",
|
||||
max_upload_mb=20,
|
||||
max_input_width=4096,
|
||||
max_input_height=4096,
|
||||
max_output_width=8192,
|
||||
max_output_height=8192,
|
||||
tmp_dir=str(tmp_path / "tmp"),
|
||||
output_dir=str(tmp_path / "output"),
|
||||
result_ttl_minutes=60,
|
||||
model_dir=str(tmp_path / "models"),
|
||||
default_model="realesrgan-x4plus",
|
||||
)
|
||||
|
||||
|
||||
def test_health_returns_ok(tmp_path: Path) -> None:
|
||||
client = TestClient(create_app(make_settings(tmp_path)))
|
||||
|
||||
response = client.get("/health")
|
||||
|
||||
assert response.status_code == 200
|
||||
payload = response.json()
|
||||
assert payload["status"] == "ok"
|
||||
assert payload["service"] == "skinbase-enhance-worker"
|
||||
assert payload["engine"] == "pillow"
|
||||
assert payload["models_loaded"] is True
|
||||
61
services/enhance-worker/tests/test_realesrgan_config.py
Normal file
61
services/enhance-worker/tests/test_realesrgan_config.py
Normal file
@@ -0,0 +1,61 @@
|
||||
from pathlib import Path
|
||||
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from app.config import Settings
|
||||
from app.main import create_app
|
||||
|
||||
|
||||
def test_health_reports_degraded_when_realesrgan_binary_is_missing(tmp_path: Path) -> None:
|
||||
settings = Settings(
|
||||
engine="realesrgan-ncnn",
|
||||
device="vulkan",
|
||||
token="secret-token",
|
||||
tmp_dir=str(tmp_path / "tmp"),
|
||||
output_dir=str(tmp_path / "output"),
|
||||
realesrgan_bin=str(tmp_path / "bin" / "realesrgan-ncnn-vulkan"),
|
||||
realesrgan_model_dir=str(tmp_path / "models"),
|
||||
)
|
||||
(tmp_path / "models").mkdir(parents=True)
|
||||
|
||||
client = TestClient(create_app(settings))
|
||||
response = client.get("/health")
|
||||
|
||||
assert response.status_code == 200
|
||||
payload = response.json()
|
||||
assert payload["status"] == "degraded"
|
||||
assert payload["engine"] == "realesrgan-ncnn"
|
||||
assert payload["realesrgan"]["binary_exists"] is False
|
||||
assert payload["realesrgan"]["available_models"] == []
|
||||
|
||||
|
||||
def test_health_reports_available_models_for_realesrgan(tmp_path: Path) -> None:
|
||||
binary = tmp_path / "bin" / "realesrgan-ncnn-vulkan"
|
||||
binary.parent.mkdir(parents=True)
|
||||
binary.write_text("#!/bin/sh\nexit 0\n", encoding="utf-8")
|
||||
binary.chmod(0o755)
|
||||
|
||||
model_dir = tmp_path / "models"
|
||||
model_dir.mkdir(parents=True)
|
||||
for name in ("realesrgan-x4plus", "realesrgan-x4plus-anime"):
|
||||
(model_dir / f"{name}.param").write_text("param", encoding="utf-8")
|
||||
(model_dir / f"{name}.bin").write_text("bin", encoding="utf-8")
|
||||
|
||||
settings = Settings(
|
||||
engine="realesrgan-ncnn",
|
||||
device="vulkan",
|
||||
token="secret-token",
|
||||
tmp_dir=str(tmp_path / "tmp"),
|
||||
output_dir=str(tmp_path / "output"),
|
||||
realesrgan_bin=str(binary),
|
||||
realesrgan_model_dir=str(model_dir),
|
||||
)
|
||||
|
||||
client = TestClient(create_app(settings))
|
||||
response = client.get("/health")
|
||||
|
||||
assert response.status_code == 200
|
||||
payload = response.json()
|
||||
assert payload["status"] == "ok"
|
||||
assert payload["models_loaded"] is True
|
||||
assert payload["realesrgan"]["available_models"] == ["realesrgan-x4plus", "realesrgan-x4plus-anime"]
|
||||
110
services/enhance-worker/tests/test_realesrgan_engine.py
Normal file
110
services/enhance-worker/tests/test_realesrgan_engine.py
Normal file
@@ -0,0 +1,110 @@
|
||||
from pathlib import Path
|
||||
from subprocess import CompletedProcess
|
||||
|
||||
import pytest
|
||||
from PIL import Image
|
||||
|
||||
from app.config import Settings
|
||||
from app.engines.base import UpscaleEngineUnavailable
|
||||
from app.image_io import DownloadedImage
|
||||
from app.upscaler import build_upscaler
|
||||
|
||||
|
||||
def make_downloaded_image(tmp_path: Path) -> DownloadedImage:
|
||||
source = tmp_path / "source.png"
|
||||
Image.new("RGBA", (12, 8), (25, 50, 75, 255)).save(source, "PNG")
|
||||
|
||||
return DownloadedImage(
|
||||
path=source,
|
||||
width=12,
|
||||
height=8,
|
||||
mime="image/png",
|
||||
filesize=source.stat().st_size,
|
||||
)
|
||||
|
||||
|
||||
def make_runtime_settings(tmp_path: Path) -> Settings:
|
||||
binary = tmp_path / "bin" / "realesrgan-ncnn-vulkan"
|
||||
binary.parent.mkdir(parents=True)
|
||||
binary.write_text("#!/bin/sh\nexit 0\n", encoding="utf-8")
|
||||
binary.chmod(0o755)
|
||||
|
||||
model_dir = tmp_path / "models"
|
||||
model_dir.mkdir(parents=True)
|
||||
(model_dir / "realesrgan-x4plus.param").write_text("param", encoding="utf-8")
|
||||
(model_dir / "realesrgan-x4plus.bin").write_text("bin", encoding="utf-8")
|
||||
|
||||
return Settings(
|
||||
engine="realesrgan-ncnn",
|
||||
device="vulkan",
|
||||
token="secret-token",
|
||||
tmp_dir=str(tmp_path / "tmp"),
|
||||
output_dir=str(tmp_path / "output"),
|
||||
realesrgan_bin=str(binary),
|
||||
realesrgan_model_dir=str(model_dir),
|
||||
realesrgan_anime_model="realesrgan-x4plus-anime",
|
||||
realesrgan_allow_model_fallback=True,
|
||||
)
|
||||
|
||||
|
||||
def test_realesrgan_command_is_built_without_shell_and_2x_records_downsample(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
settings = make_runtime_settings(tmp_path)
|
||||
engine = build_upscaler(settings)
|
||||
downloaded = make_downloaded_image(tmp_path)
|
||||
captured: dict[str, object] = {}
|
||||
|
||||
def fake_run(command, **kwargs):
|
||||
captured["command"] = command
|
||||
captured["kwargs"] = kwargs
|
||||
output_index = command.index("-o") + 1
|
||||
output_path = Path(command[output_index])
|
||||
output_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
Image.new("RGBA", (downloaded.width * 4, downloaded.height * 4), (120, 80, 20, 255)).save(output_path, "PNG")
|
||||
|
||||
return CompletedProcess(command, 0, stdout="ok", stderr="")
|
||||
|
||||
monkeypatch.setattr("app.engines.realesrgan_ncnn_engine.subprocess.run", fake_run)
|
||||
|
||||
result = engine.upscale(downloaded, 2, "illustration", "webp")
|
||||
|
||||
assert result.image.size == (downloaded.width * 2, downloaded.height * 2)
|
||||
assert result.metadata["requested_scale"] == 2
|
||||
assert result.metadata["native_model_scale"] == 4
|
||||
assert result.metadata["post_downsampled"] is True
|
||||
assert result.metadata["model_fallback"] is True
|
||||
assert result.metadata["used_model"] == "realesrgan-x4plus"
|
||||
assert captured["command"][0] == settings.realesrgan_bin
|
||||
assert captured["kwargs"].get("check") is False
|
||||
assert captured["kwargs"].get("shell", False) is False
|
||||
|
||||
|
||||
def test_missing_binary_raises_safe_engine_unavailable(tmp_path: Path) -> None:
|
||||
engine = build_upscaler(
|
||||
Settings(
|
||||
engine="realesrgan-ncnn",
|
||||
token="secret-token",
|
||||
tmp_dir=str(tmp_path / "tmp"),
|
||||
output_dir=str(tmp_path / "output"),
|
||||
realesrgan_bin=str(tmp_path / "bin" / "missing-binary"),
|
||||
realesrgan_model_dir=str(tmp_path / "models"),
|
||||
)
|
||||
)
|
||||
|
||||
with pytest.raises(UpscaleEngineUnavailable, match="Upscale engine is not available"):
|
||||
engine.upscale(make_downloaded_image(tmp_path), 4, "artwork", "webp")
|
||||
|
||||
|
||||
def test_pillow_engine_still_works(tmp_path: Path) -> None:
|
||||
engine = build_upscaler(
|
||||
Settings(
|
||||
engine="pillow",
|
||||
token="secret-token",
|
||||
tmp_dir=str(tmp_path / "tmp"),
|
||||
output_dir=str(tmp_path / "output"),
|
||||
)
|
||||
)
|
||||
|
||||
result = engine.upscale(make_downloaded_image(tmp_path), 2, "standard", "webp")
|
||||
|
||||
assert result.metadata["engine"] == "pillow"
|
||||
assert result.metadata["real_ai_upscale"] is False
|
||||
50
services/enhance-worker/tests/test_realesrgan_validation.py
Normal file
50
services/enhance-worker/tests/test_realesrgan_validation.py
Normal file
@@ -0,0 +1,50 @@
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from app.config import Settings
|
||||
from app.engines.base import UpscaleEngineUnavailable
|
||||
from app.upscaler import build_upscaler
|
||||
|
||||
|
||||
def test_invalid_engine_returns_degraded_health(tmp_path: Path) -> None:
|
||||
upscaler = build_upscaler(
|
||||
Settings(
|
||||
engine="broken-engine",
|
||||
token="secret-token",
|
||||
tmp_dir=str(tmp_path / "tmp"),
|
||||
output_dir=str(tmp_path / "output"),
|
||||
)
|
||||
)
|
||||
|
||||
health = upscaler.health()
|
||||
|
||||
assert health.status == "degraded"
|
||||
assert health.models_loaded is False
|
||||
|
||||
|
||||
def test_missing_model_raises_safe_error_when_fallback_disabled(tmp_path: Path) -> None:
|
||||
binary = tmp_path / "bin" / "realesrgan-ncnn-vulkan"
|
||||
binary.parent.mkdir(parents=True)
|
||||
binary.write_text("#!/bin/sh\nexit 0\n", encoding="utf-8")
|
||||
binary.chmod(0o755)
|
||||
|
||||
model_dir = tmp_path / "models"
|
||||
model_dir.mkdir(parents=True)
|
||||
(model_dir / "realesrgan-x4plus.param").write_text("param", encoding="utf-8")
|
||||
(model_dir / "realesrgan-x4plus.bin").write_text("bin", encoding="utf-8")
|
||||
|
||||
engine = build_upscaler(
|
||||
Settings(
|
||||
engine="realesrgan-ncnn",
|
||||
token="secret-token",
|
||||
tmp_dir=str(tmp_path / "tmp"),
|
||||
output_dir=str(tmp_path / "output"),
|
||||
realesrgan_bin=str(binary),
|
||||
realesrgan_model_dir=str(model_dir),
|
||||
realesrgan_allow_model_fallback=False,
|
||||
)
|
||||
)
|
||||
|
||||
with pytest.raises(UpscaleEngineUnavailable, match="Upscale engine is not available"):
|
||||
engine.resolve_model("illustration")
|
||||
63
services/enhance-worker/tests/test_security.py
Normal file
63
services/enhance-worker/tests/test_security.py
Normal file
@@ -0,0 +1,63 @@
|
||||
from pathlib import Path
|
||||
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from app.config import Settings
|
||||
from app.main import create_app
|
||||
|
||||
|
||||
def make_settings(tmp_path: Path) -> Settings:
|
||||
return Settings(
|
||||
host="127.0.0.1",
|
||||
port=8095,
|
||||
token="secret-token",
|
||||
engine="pillow",
|
||||
device="cpu",
|
||||
max_upload_mb=20,
|
||||
max_input_width=4096,
|
||||
max_input_height=4096,
|
||||
max_output_width=8192,
|
||||
max_output_height=8192,
|
||||
tmp_dir=str(tmp_path / "tmp"),
|
||||
output_dir=str(tmp_path / "output"),
|
||||
result_ttl_minutes=60,
|
||||
model_dir=str(tmp_path / "models"),
|
||||
default_model="realesrgan-x4plus",
|
||||
)
|
||||
|
||||
|
||||
def test_upscale_requires_bearer_token(tmp_path: Path) -> None:
|
||||
client = TestClient(create_app(make_settings(tmp_path)))
|
||||
|
||||
response = client.post(
|
||||
"/v1/upscale",
|
||||
json={
|
||||
"job_id": 1,
|
||||
"source_url": "https://example.com/source.webp",
|
||||
"scale": 2,
|
||||
"mode": "standard",
|
||||
"output_format": "webp",
|
||||
},
|
||||
)
|
||||
|
||||
assert response.status_code == 401
|
||||
assert response.json()["detail"] == "Unauthorized"
|
||||
|
||||
|
||||
def test_upscale_rejects_invalid_bearer_token(tmp_path: Path) -> None:
|
||||
client = TestClient(create_app(make_settings(tmp_path)))
|
||||
|
||||
response = client.post(
|
||||
"/v1/upscale",
|
||||
headers={"Authorization": "Bearer wrong-token"},
|
||||
json={
|
||||
"job_id": 1,
|
||||
"source_url": "https://example.com/source.webp",
|
||||
"scale": 2,
|
||||
"mode": "standard",
|
||||
"output_format": "webp",
|
||||
},
|
||||
)
|
||||
|
||||
assert response.status_code == 401
|
||||
assert response.json()["detail"] == "Unauthorized"
|
||||
98
services/enhance-worker/tests/test_validation.py
Normal file
98
services/enhance-worker/tests/test_validation.py
Normal file
@@ -0,0 +1,98 @@
|
||||
from pathlib import Path
|
||||
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from app.config import Settings
|
||||
from app.main import create_app
|
||||
|
||||
|
||||
def make_settings(tmp_path: Path) -> Settings:
|
||||
return Settings(
|
||||
host="127.0.0.1",
|
||||
port=8095,
|
||||
token="secret-token",
|
||||
engine="pillow",
|
||||
device="cpu",
|
||||
max_upload_mb=20,
|
||||
max_input_width=4096,
|
||||
max_input_height=4096,
|
||||
max_output_width=8192,
|
||||
max_output_height=8192,
|
||||
tmp_dir=str(tmp_path / "tmp"),
|
||||
output_dir=str(tmp_path / "output"),
|
||||
result_ttl_minutes=60,
|
||||
model_dir=str(tmp_path / "models"),
|
||||
default_model="realesrgan-x4plus",
|
||||
)
|
||||
|
||||
|
||||
def test_validation_rejects_invalid_scale(tmp_path: Path) -> None:
|
||||
client = TestClient(create_app(make_settings(tmp_path)))
|
||||
|
||||
response = client.post(
|
||||
"/v1/upscale",
|
||||
headers={"Authorization": "Bearer secret-token"},
|
||||
json={
|
||||
"job_id": 1,
|
||||
"source_url": "https://example.com/source.webp",
|
||||
"scale": 3,
|
||||
"mode": "standard",
|
||||
"output_format": "webp",
|
||||
},
|
||||
)
|
||||
|
||||
assert response.status_code == 422
|
||||
|
||||
|
||||
def test_validation_rejects_invalid_mode(tmp_path: Path) -> None:
|
||||
client = TestClient(create_app(make_settings(tmp_path)))
|
||||
|
||||
response = client.post(
|
||||
"/v1/upscale",
|
||||
headers={"Authorization": "Bearer secret-token"},
|
||||
json={
|
||||
"job_id": 1,
|
||||
"source_url": "https://example.com/source.webp",
|
||||
"scale": 2,
|
||||
"mode": "broken",
|
||||
"output_format": "webp",
|
||||
},
|
||||
)
|
||||
|
||||
assert response.status_code == 422
|
||||
|
||||
|
||||
def test_validation_rejects_invalid_output_format(tmp_path: Path) -> None:
|
||||
client = TestClient(create_app(make_settings(tmp_path)))
|
||||
|
||||
response = client.post(
|
||||
"/v1/upscale",
|
||||
headers={"Authorization": "Bearer secret-token"},
|
||||
json={
|
||||
"job_id": 1,
|
||||
"source_url": "https://example.com/source.webp",
|
||||
"scale": 2,
|
||||
"mode": "standard",
|
||||
"output_format": "gif",
|
||||
},
|
||||
)
|
||||
|
||||
assert response.status_code == 422
|
||||
|
||||
|
||||
def test_validation_rejects_local_file_source_url(tmp_path: Path) -> None:
|
||||
client = TestClient(create_app(make_settings(tmp_path)))
|
||||
|
||||
response = client.post(
|
||||
"/v1/upscale",
|
||||
headers={"Authorization": "Bearer secret-token"},
|
||||
json={
|
||||
"job_id": 1,
|
||||
"source_url": "file:///tmp/source.webp",
|
||||
"scale": 2,
|
||||
"mode": "standard",
|
||||
"output_format": "webp",
|
||||
},
|
||||
)
|
||||
|
||||
assert response.status_code == 422
|
||||
Reference in New Issue
Block a user