Panel jest osobnym klientem HTTP, więc zmiana ścieżki albo metody psuje go po cichu - w kodzie Pythona nic nie przestaje się kompilować. Test buduje aplikację, generuje specyfikację OpenAPI i porównuje ją z listą 22 endpointów w obie strony: - brak oczekiwanego endpointu to błąd - nowy endpoint spoza listy również, żeby rozszerzenie API było świadomą decyzją, a nie skutkiem ubocznym Sprawdzany jest też sam dokument OpenAPI, co wyłapuje błędy w deklaracjach tras i modelach żądań. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
74 lines
2.2 KiB
Python
74 lines
2.2 KiB
Python
"""Przypina powierzchnię API - panel jest osobnym klientem, więc kontrakt musi być stabilny."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import tempfile
|
|
import unittest
|
|
|
|
from zyrarbot.application import Application
|
|
from zyrarbot.settings import Settings
|
|
|
|
EXPECTED_ENDPOINTS = {
|
|
("get", "/api/status"),
|
|
("post", "/api/play"),
|
|
("post", "/api/play_file"),
|
|
("post", "/api/pause"),
|
|
("post", "/api/resume"),
|
|
("post", "/api/skip"),
|
|
("post", "/api/stop"),
|
|
("post", "/api/volume"),
|
|
("post", "/api/loop"),
|
|
("post", "/api/loop_playlist"),
|
|
("post", "/api/loop_cycle"),
|
|
("post", "/api/jump"),
|
|
("post", "/api/remove"),
|
|
("post", "/api/reorder"),
|
|
("post", "/api/queue/plays"),
|
|
("get", "/api/uploads"),
|
|
("post", "/api/upload"),
|
|
("delete", "/api/uploads/{filename}"),
|
|
("post", "/api/connect"),
|
|
("post", "/api/disconnect"),
|
|
("post", "/api/settings/text_channel"),
|
|
("post", "/api/settings/default_plays"),
|
|
}
|
|
|
|
|
|
class ApiSurfaceTest(unittest.TestCase):
|
|
@classmethod
|
|
def setUpClass(cls) -> None:
|
|
cls._workdir = tempfile.TemporaryDirectory()
|
|
settings = Settings(
|
|
discord_token="test",
|
|
config_dir=f"{cls._workdir.name}/config",
|
|
uploads_dir=f"{cls._workdir.name}/uploads",
|
|
static_dir=f"{cls._workdir.name}/static",
|
|
)
|
|
cls.spec = Application(settings).api.openapi()
|
|
|
|
@classmethod
|
|
def tearDownClass(cls) -> None:
|
|
cls._workdir.cleanup()
|
|
|
|
def _declared(self) -> set[tuple[str, str]]:
|
|
return {
|
|
(method, path)
|
|
for path, operations in self.spec["paths"].items()
|
|
for method in operations
|
|
}
|
|
|
|
def test_all_expected_endpoints_are_declared(self):
|
|
self.assertEqual(set(), EXPECTED_ENDPOINTS - self._declared())
|
|
|
|
def test_no_endpoint_added_without_updating_this_test(self):
|
|
# Panel jest odrębnym klientem - nowa trasa powinna trafić tu świadomie.
|
|
self.assertEqual(set(), self._declared() - EXPECTED_ENDPOINTS)
|
|
|
|
def test_openapi_document_is_well_formed(self):
|
|
self.assertTrue(self.spec["openapi"].startswith("3."))
|
|
self.assertIn("ŻyrarBOT", self.spec["info"]["title"])
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|