183 lines
6.9 KiB
Python
183 lines
6.9 KiB
Python
"""
|
||
Tests for from_stands.py — конвертер STANDS YAML → polygon config.
|
||
"""
|
||
|
||
import os
|
||
import tempfile
|
||
|
||
import pytest
|
||
import yaml
|
||
|
||
# Добавляем site/ в path для импорта from_stands
|
||
import sys
|
||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "site"))
|
||
import from_stands
|
||
|
||
|
||
@pytest.fixture
|
||
def dummy_yaml():
|
||
"""Минимальный STANDS YAML для тестов."""
|
||
return {
|
||
"name": "dummy",
|
||
"service_id": 1,
|
||
"service_display_name": "Болванка",
|
||
"service_short_name": "dummy",
|
||
"lifecycle": {
|
||
"suspend_on_destroy_default": True,
|
||
"adopt_existing_on_create_default": False,
|
||
},
|
||
"operations": [
|
||
{
|
||
"name": "create",
|
||
"id": 18,
|
||
"kind": "instance",
|
||
"action": "create",
|
||
"params": [
|
||
{
|
||
"id": 242,
|
||
"code": "resourceRealm",
|
||
"data_type": "string",
|
||
"required": True,
|
||
"default": "dummy",
|
||
"value_list": ["dummy"],
|
||
},
|
||
{
|
||
"id": 198,
|
||
"code": "durationMs",
|
||
"data_type": "integer >= 0",
|
||
"required": True,
|
||
"default": "0",
|
||
},
|
||
],
|
||
},
|
||
{"name": "delete", "id": 71, "kind": "instance", "action": "delete"},
|
||
],
|
||
}
|
||
|
||
|
||
@pytest.fixture
|
||
def pg_yaml():
|
||
"""STANDS YAML с subresources для тестов."""
|
||
return {
|
||
"name": "postgres",
|
||
"service_id": 90,
|
||
"service_display_name": "PostgreSQL",
|
||
"service_short_name": "postgres",
|
||
"lifecycle": {
|
||
"suspend_on_destroy_default": True,
|
||
"adopt_existing_on_create_default": False,
|
||
},
|
||
"operations": [
|
||
{
|
||
"name": "create",
|
||
"id": 19,
|
||
"kind": "instance",
|
||
"action": "create",
|
||
"params": [
|
||
{
|
||
"id": 788,
|
||
"code": "clusterConfiguration",
|
||
"data_type": "map-fixed",
|
||
"required": True,
|
||
"sub_params": [
|
||
{"id": 106, "code": "replicas", "data_type": "integer > 0", "required": True, "default": "1"},
|
||
{"id": 107, "code": "disk", "data_type": "integer > 0", "required": True, "default": "10"},
|
||
],
|
||
},
|
||
],
|
||
},
|
||
{"name": "create_user", "id": 44, "kind": "subresource", "action": "create"},
|
||
{"name": "delete_user", "id": 45, "kind": "subresource", "action": "delete"},
|
||
{"name": "create_database", "id": 46, "kind": "subresource", "action": "create"},
|
||
{"name": "delete_database", "id": 47, "kind": "subresource", "action": "delete"},
|
||
],
|
||
}
|
||
|
||
|
||
class TestConvertService:
|
||
"""Тесты конвертации одного сервиса."""
|
||
|
||
def test_basic_fields(self, dummy_yaml):
|
||
"""Поля верхнего уровня переносятся 1:1."""
|
||
cfg = from_stands.convert_service_from_dict(dummy_yaml)
|
||
assert cfg["name"] == "dummy"
|
||
assert cfg["service_id"] == 1
|
||
assert cfg["service_display_name"] == "Болванка"
|
||
|
||
def test_lifecycle(self, dummy_yaml):
|
||
"""lifecycle конвертируется в camelCase."""
|
||
cfg = from_stands.convert_service_from_dict(dummy_yaml)
|
||
assert cfg["lifecycle"]["suspendOnDestroyDefault"] is True
|
||
assert cfg["lifecycle"]["adoptExistingOnCreateDefault"] is False
|
||
|
||
def test_operations_count(self, dummy_yaml):
|
||
"""Количество операций совпадает."""
|
||
cfg = from_stands.convert_service_from_dict(dummy_yaml)
|
||
assert len(cfg["operations"]) == 2
|
||
|
||
def test_html_unescape(self, dummy_yaml):
|
||
"""HTML entities раскодируются: > → >."""
|
||
cfg = from_stands.convert_service_from_dict(dummy_yaml)
|
||
cfs = {p["svcOperationCfsParamId"]: p for p in cfg["cfsParams"]}
|
||
assert cfs[198]["dataType"] == "integer >= 0"
|
||
assert ">" not in cfs[198]["dataType"]
|
||
|
||
def test_value_list(self, dummy_yaml):
|
||
"""valueList конвертируется."""
|
||
cfg = from_stands.convert_service_from_dict(dummy_yaml)
|
||
cfs = {p["svcOperationCfsParamId"]: p for p in cfg["cfsParams"]}
|
||
assert cfs[242]["valueList"] == ["dummy"]
|
||
|
||
def test_state_params_from_create(self, dummy_yaml):
|
||
"""stateParams генерируются из create-операции."""
|
||
cfg = from_stands.convert_service_from_dict(dummy_yaml)
|
||
assert cfg["stateParams"]["resourceRealm"] == "dummy"
|
||
assert cfg["stateParams"]["durationMs"] == "0"
|
||
|
||
def test_data_descriptor_from_sub_params(self, pg_yaml):
|
||
"""map-fixed/sub_params → dataDescriptor."""
|
||
cfg = from_stands.convert_service_from_dict(pg_yaml)
|
||
cfs = {p["svcOperationCfsParamId"]: p for p in cfg["cfsParams"]}
|
||
dd = cfs[788]["dataDescriptor"]
|
||
assert "replicas" in dd
|
||
assert "disk" in dd
|
||
assert dd["replicas"]["dataType"] == "integer > 0"
|
||
assert dd["replicas"]["defaultValue"] == "1"
|
||
|
||
def test_state_out_subresources(self, pg_yaml):
|
||
"""stateOut генерируется из subresource-операций."""
|
||
cfg = from_stands.convert_service_from_dict(pg_yaml)
|
||
assert "users" in cfg["stateOut"]
|
||
assert "databases" in cfg["stateOut"]
|
||
|
||
def test_cfs_params_by_op(self, dummy_yaml):
|
||
"""cfsParamsByOp связывает opId → список paramId."""
|
||
cfg = from_stands.convert_service_from_dict(dummy_yaml)
|
||
assert 18 in cfg["cfsParamsByOp"]
|
||
assert 242 in cfg["cfsParamsByOp"][18]
|
||
assert 198 in cfg["cfsParamsByOp"][18]
|
||
|
||
|
||
class TestFileRoundtrip:
|
||
"""Тесты записи/чтения YAML."""
|
||
|
||
def test_roundtrip(self, dummy_yaml):
|
||
"""Сгенерированный YAML можно записать и прочитать обратно."""
|
||
with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f:
|
||
fname = f.name
|
||
|
||
try:
|
||
# Записать
|
||
with open(fname, "w") as f:
|
||
cfg = from_stands.convert_service_from_dict(dummy_yaml)
|
||
yaml.dump(cfg, f, allow_unicode=True, default_flow_style=False)
|
||
|
||
# Прочитать
|
||
with open(fname) as f:
|
||
loaded = yaml.safe_load(f)
|
||
|
||
assert loaded["name"] == "dummy"
|
||
assert len(loaded["cfsParams"]) == 2
|
||
finally:
|
||
os.unlink(fname)
|