Skip to content

Commit 23af649

Browse files
authored
Merge pull request #165 from projectsyn/tests/compare-to-kapicorp-reclass
Add Python tests which compare against kapicorp-reclass
2 parents 126b6ff + 05711ae commit 23af649

File tree

2 files changed

+142
-29
lines changed

2 files changed

+142
-29
lines changed

tests/test_inventory_class_mapping.py

Lines changed: 7 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
import pytest
33
import sys
44

5+
from test_with_kapicorp_reclass import prune_timestamps, py_reclass_inventory
6+
57

68
@pytest.mark.parametrize(
79
"compose_node_name,class_mappings_match_path",
@@ -26,6 +28,9 @@ def test_inventory_class_mappings(compose_node_name, class_mappings_match_path):
2628
"test.* composed.test",
2729
"production.* composed.production",
2830
"/(test|production)\\/.*/ regex.params regex.\\\\1",
31+
"/^test(?!.*-stg-test).*/ cluster.test",
32+
"/^test.*-stg-test.*/ cluster.staging",
33+
"/.*c$/ class1 class2",
2934
],
3035
"class_mappings_match_path": False,
3136
}
@@ -37,35 +42,8 @@ def test_inventory_class_mappings(compose_node_name, class_mappings_match_path):
3742

3843
inv = r.inventory().as_dict()
3944
assert inv is not None
45+
prune_timestamps(inv)
4046

41-
# delete timestamps from resulting dict to ensure that we don't run into issues when comparing
42-
# two dicts that are rendered at slightly different times
43-
del inv["__reclass__"]["timestamp"]
44-
for n in inv["nodes"].keys():
45-
del inv["nodes"][n]["__reclass__"]["timestamp"]
46-
47-
storage = reclass.get_storage(
48-
"yaml_fs",
49-
"./tests/inventory-class-mapping/nodes",
50-
"./tests/inventory-class-mapping/classes",
51-
config_options["compose_node_name"],
52-
)
53-
class_mappings = config_options.get("class_mappings")
54-
_reclass = reclass.core.Core(
55-
storage, class_mappings, reclass.settings.Settings(config_options)
56-
)
57-
py_inv = _reclass.inventory()
58-
59-
# delete timestamps from resulting dict to ensure that we don't run into issues when comparing
60-
# two dicts that are rendered at slightly different times
61-
del py_inv["__reclass__"]["timestamp"]
62-
for n in py_inv["nodes"].keys():
63-
del py_inv["nodes"][n]["__reclass__"]["timestamp"]
64-
65-
for nodes in py_inv["classes"].values():
66-
nodes.sort()
67-
68-
for nodes in py_inv["applications"].values():
69-
nodes.sort()
47+
py_inv = py_reclass_inventory("./tests/inventory-class-mapping", config_options)
7048

7149
assert inv == py_inv
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
import reclass_rs
2+
import pytest
3+
import sys
4+
5+
6+
def fixup_pyfmt(v: str) -> str:
7+
# NOTE(sg): this is not generally applicable, it only works for the `embedded` parameter for
8+
# node n22 in `tests/inventory`.
9+
r = v.replace("'", '"')
10+
lines = r.splitlines()
11+
for i in range(0, len(lines)):
12+
parts = lines[i].split(": ", 1)
13+
parts[1] = parts[1].replace(" ", "")
14+
lines[i] = ": ".join(parts)
15+
16+
return "\n".join(lines)
17+
18+
19+
def prune_timestamps(inv: dict):
20+
del inv["__reclass__"]["timestamp"]
21+
for n in inv["nodes"].values():
22+
del n["__reclass__"]["timestamp"]
23+
24+
25+
def py_reclass_inventory(inv_path: str, config: dict) -> dict:
26+
import reclass
27+
import reclass.core
28+
29+
storage = reclass.get_storage(
30+
"yaml_fs",
31+
f"{inv_path}/nodes",
32+
f"{inv_path}/classes",
33+
config["compose_node_name"],
34+
)
35+
class_mappings = config.get("class_mappings")
36+
_reclass = reclass.core.Core(
37+
storage, class_mappings, reclass.settings.Settings(config)
38+
)
39+
py_inv = _reclass.inventory()
40+
# remove timestamps so we can compare the whole dicts
41+
prune_timestamps(py_inv)
42+
43+
# ensure that kapicorp-reclass top-level `classes` and `applications` values are sorted lists
44+
for nodes in py_inv["classes"].values():
45+
nodes.sort()
46+
for nodes in py_inv["applications"].values():
47+
nodes.sort()
48+
49+
return py_inv
50+
51+
52+
@pytest.mark.skipif(
53+
sys.platform == "win32", reason="kapicorp-reclass not supported on Windows"
54+
)
55+
def test_inventory_matches_pyreclass():
56+
config_options = {
57+
"compose_node_name": False,
58+
"allow_none_override": True,
59+
"ignore_class_notfound": True,
60+
}
61+
c = reclass_rs.Config.from_dict("./tests/inventory", config_options)
62+
assert c is not None
63+
r = reclass_rs.Reclass.from_config(c)
64+
assert r is not None
65+
66+
inv = r.inventory().as_dict()
67+
assert inv is not None
68+
prune_timestamps(inv)
69+
70+
py_inv = py_reclass_inventory("./tests/inventory", config_options)
71+
# kapicorp-reclass hasn't fixed the applications merge bug yet,
72+
# cf.https://github.com/kapicorp/reclass/pull/9
73+
py_inv["nodes"]["n12"]["applications"].remove("b")
74+
py_inv["applications"]["b"].remove("n12")
75+
76+
# complex values that are embedded in a string via reclass reference are formatted differently,
77+
# we use proper JSON serialization, while kapicorp-reclass just uses Python object notation
78+
# (via string formatting for the value).
79+
for k, v in py_inv["nodes"]["n22"]["parameters"]["embedded"].items():
80+
py_inv["nodes"]["n22"]["parameters"]["embedded"][k] = fixup_pyfmt(v)
81+
82+
assert inv == py_inv
83+
84+
85+
@pytest.mark.parametrize("compose", [False, True])
86+
@pytest.mark.skipif(
87+
sys.platform == "win32", reason="kapicorp-reclass not supported on Windows"
88+
)
89+
def test_inventory_nested_nodes_matches_pyreclass(compose):
90+
config_options = {
91+
"compose_node_name": compose,
92+
"allow_none_override": True,
93+
"ignore_class_notfound": True,
94+
}
95+
c = reclass_rs.Config.from_dict("./tests/inventory-nested-nodes", config_options)
96+
assert c is not None
97+
r = reclass_rs.Reclass.from_config(c)
98+
assert r is not None
99+
100+
inv = r.inventory().as_dict()
101+
assert inv is not None
102+
prune_timestamps(inv)
103+
104+
py_inv = py_reclass_inventory("./tests/inventory-nested-nodes", config_options)
105+
106+
assert inv == py_inv
107+
108+
109+
@pytest.mark.skipif(
110+
sys.platform == "win32", reason="kapicorp-reclass not supported on Windows"
111+
)
112+
def test_inventory_compose_node_name_compat_matches_pyreclass():
113+
config_options = {
114+
"compose_node_name": True,
115+
"allow_none_override": True,
116+
"ignore_class_notfound": True,
117+
# NOTE(sg): We need the compose-node-name-literal-dots Python reclass compatibility mode
118+
# here, since the `compose-node-name` inventory has nodes that behave differently without
119+
# the compatibility mode.
120+
"reclass_rs_compat_flags": ["compose-node-name-literal-dots"],
121+
}
122+
c = reclass_rs.Config.from_dict(
123+
"./tests/inventory-compose-node-name", config_options
124+
)
125+
assert c is not None
126+
r = reclass_rs.Reclass.from_config(c)
127+
assert r is not None
128+
129+
inv = r.inventory().as_dict()
130+
assert inv is not None
131+
prune_timestamps(inv)
132+
133+
py_inv = py_reclass_inventory("./tests/inventory-compose-node-name", config_options)
134+
135+
assert inv == py_inv

0 commit comments

Comments
 (0)