|
| 1 | +import json |
| 2 | +import logging |
1 | 3 | from pathlib import Path |
2 | 4 |
|
3 | 5 | import yaml |
4 | 6 |
|
| 7 | +from shard_core.data_model.app_meta import InstallationReason, Status |
| 8 | +from shard_core.database import installed_apps as db_installed_apps |
| 9 | +from shard_core.database.connection import db_conn |
| 10 | +from shard_core.service.app_installation.util import write_traefik_dyn_config |
| 11 | +from shard_core.service.app_tools import get_installed_apps_path |
5 | 12 | from shard_core.settings import settings |
6 | 13 |
|
7 | 14 |
|
@@ -46,3 +53,73 @@ async def test_template_is_written(api_client): |
46 | 53 | "immich_http", |
47 | 54 | } |
48 | 55 | assert out_routers_http["filebrowser_http"]["service"] == "filebrowser_http" |
| 56 | + |
| 57 | + |
| 58 | +def _write_app_meta(app_name: str): |
| 59 | + app_path = get_installed_apps_path() / app_name |
| 60 | + app_path.mkdir(parents=True, exist_ok=True) |
| 61 | + (app_path / "app_meta.json").write_text( |
| 62 | + json.dumps( |
| 63 | + { |
| 64 | + "v": "1.0", |
| 65 | + "app_version": "0.1.0", |
| 66 | + "name": app_name, |
| 67 | + "pretty_name": app_name, |
| 68 | + "icon": "icon.svg", |
| 69 | + "entrypoints": [ |
| 70 | + { |
| 71 | + "container_name": app_name, |
| 72 | + "container_port": 80, |
| 73 | + "entrypoint_port": "http", |
| 74 | + } |
| 75 | + ], |
| 76 | + "paths": {"": {"access": "public"}}, |
| 77 | + } |
| 78 | + ) |
| 79 | + ) |
| 80 | + |
| 81 | + |
| 82 | +async def _add_app_to_db(app_name: str, status: Status): |
| 83 | + async with db_conn() as conn: |
| 84 | + await db_installed_apps.insert( |
| 85 | + conn, |
| 86 | + { |
| 87 | + "name": app_name, |
| 88 | + "installation_reason": InstallationReason.CUSTOM.value, |
| 89 | + "status": status.value, |
| 90 | + "last_access": None, |
| 91 | + }, |
| 92 | + ) |
| 93 | + |
| 94 | + |
| 95 | +def _read_traefik_dyn_config() -> dict: |
| 96 | + with open( |
| 97 | + Path(settings().path_root) / "core" / "traefik_dyn" / "traefik_dyn.yml", "r" |
| 98 | + ) as f: |
| 99 | + return yaml.safe_load(f) |
| 100 | + |
| 101 | + |
| 102 | +async def test_app_with_missing_metadata_is_skipped(app_client, memory_logger): |
| 103 | + _write_app_meta("healthy_app") |
| 104 | + await _add_app_to_db("healthy_app", Status.RUNNING) |
| 105 | + await _add_app_to_db("broken_app", Status.RUNNING) |
| 106 | + |
| 107 | + await write_traefik_dyn_config() |
| 108 | + |
| 109 | + routers = _read_traefik_dyn_config()["http"]["routers"] |
| 110 | + assert "healthy_app_http" in routers |
| 111 | + assert "broken_app_http" not in routers |
| 112 | + assert [ |
| 113 | + r |
| 114 | + for r in memory_logger.records |
| 115 | + if r.levelno == logging.WARNING and "broken_app" in r.getMessage() |
| 116 | + ] |
| 117 | + |
| 118 | + |
| 119 | +async def test_uninstalling_app_is_not_routed(app_client): |
| 120 | + _write_app_meta("uninstalling_app") |
| 121 | + await _add_app_to_db("uninstalling_app", Status.UNINSTALLING) |
| 122 | + |
| 123 | + await write_traefik_dyn_config() |
| 124 | + |
| 125 | + assert "uninstalling_app_http" not in _read_traefik_dyn_config()["http"]["routers"] |
0 commit comments