134 lines
3.5 KiB
Python
134 lines
3.5 KiB
Python
import sqlite3
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from app.config_io import export_document, import_document, parse_document, serialize_document
|
|
from app.models import ServiceCreate, ServiceKind
|
|
from app.store import Store
|
|
|
|
CONFIG = """
|
|
version: 1
|
|
services:
|
|
- name: Database
|
|
kind: tcp
|
|
host: db.local
|
|
port: 5432
|
|
depends_on: []
|
|
- name: API
|
|
kind: http
|
|
url: https://api.local
|
|
depends_on: [Database]
|
|
"""
|
|
|
|
|
|
def test_yaml_import_export_round_trip(tmp_path: Path) -> None:
|
|
store = Store(str(tmp_path / "servicemap.db"))
|
|
result = import_document(store, parse_document(CONFIG, "yaml"), "replace")
|
|
|
|
assert result.created == 2
|
|
services = store.list_services()
|
|
assert len(services) == 2
|
|
api = store.get_service_by_name("API")
|
|
database = store.get_service_by_name("Database")
|
|
assert api is not None and database is not None
|
|
assert api.depends_on == [database.id]
|
|
|
|
exported = export_document(store)
|
|
yaml_text = serialize_document(exported, "yaml")
|
|
reparsed = parse_document(yaml_text, "yaml")
|
|
assert [service.name for service in reparsed.services] == [service.name for service in exported.services]
|
|
|
|
|
|
def test_merge_can_reference_existing_service_not_in_document(tmp_path: Path) -> None:
|
|
store = Store(str(tmp_path / "servicemap.db"))
|
|
database = store.create_service(
|
|
ServiceCreate(name="Database", kind=ServiceKind.TCP, host="db.local", port=5432)
|
|
)
|
|
document = parse_document(
|
|
"""
|
|
version: 1
|
|
services:
|
|
- name: API
|
|
kind: http
|
|
url: https://api.local
|
|
depends_on: [Database]
|
|
""",
|
|
"yaml",
|
|
)
|
|
|
|
result = import_document(store, document, "merge")
|
|
|
|
assert result.created == 1
|
|
assert result.updated == 0
|
|
api = store.get_service_by_name("API")
|
|
assert api is not None
|
|
assert api.depends_on == [database.id]
|
|
|
|
|
|
def test_import_rejects_missing_dependency_and_cycles(tmp_path: Path) -> None:
|
|
store = Store(str(tmp_path / "servicemap.db"))
|
|
missing = parse_document(
|
|
"""
|
|
version: 1
|
|
services:
|
|
- name: API
|
|
kind: http
|
|
url: https://api.local
|
|
depends_on: [Database]
|
|
""",
|
|
"yaml",
|
|
)
|
|
with pytest.raises(ValueError, match="unknown dependencies"):
|
|
import_document(store, missing, "replace")
|
|
|
|
cyclic = """
|
|
version: 1
|
|
services:
|
|
- name: A
|
|
kind: http
|
|
url: https://a.local
|
|
depends_on: [B]
|
|
- name: B
|
|
kind: http
|
|
url: https://b.local
|
|
depends_on: [A]
|
|
"""
|
|
with pytest.raises(ValueError, match="cycle"):
|
|
parse_document(cyclic, "yaml")
|
|
|
|
with pytest.raises(ValueError, match="less than or equal to 1"):
|
|
parse_document("version: 2\nservices: []", "yaml")
|
|
|
|
|
|
def test_replace_import_rolls_back_on_mid_transaction_failure(
|
|
tmp_path: Path,
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
store = Store(str(tmp_path / "servicemap.db"))
|
|
original = store.create_service(
|
|
ServiceCreate(name="Original", kind=ServiceKind.HTTP, url="https://original.local")
|
|
)
|
|
document = parse_document(
|
|
"""
|
|
version: 1
|
|
services:
|
|
- name: First
|
|
kind: http
|
|
url: https://first.local
|
|
depends_on: []
|
|
- name: Second
|
|
kind: http
|
|
url: https://second.local
|
|
depends_on: []
|
|
""",
|
|
"yaml",
|
|
)
|
|
monkeypatch.setattr("app.store.uuid4", lambda: "duplicate-id")
|
|
|
|
with pytest.raises(sqlite3.IntegrityError):
|
|
import_document(store, document, "replace")
|
|
|
|
remaining = store.list_services()
|
|
assert [(service.id, service.name) for service in remaining] == [(original.id, "Original")]
|