feat(inventory/store): init path_match utility
This commit is contained in:
@@ -9,6 +9,33 @@ from typing import Any
|
||||
from clan_lib.errors import ClanError
|
||||
|
||||
|
||||
def path_match(path: list[str], whitelist_paths: list[list[str]]) -> bool:
|
||||
"""
|
||||
Returns True if path matches any whitelist path with "*" wildcards.
|
||||
|
||||
I.e.:
|
||||
whitelist_paths = [["a.b.*"]]
|
||||
path = ["a", "b", "c"]
|
||||
path_match(path, whitelist_paths) == True
|
||||
|
||||
|
||||
whitelist_paths = ["a.b.c", "a.b.*"]
|
||||
path = ["a", "b", "d"]
|
||||
path_match(path, whitelist_paths) == False
|
||||
"""
|
||||
for wp in whitelist_paths:
|
||||
if len(path) != len(wp):
|
||||
continue
|
||||
match = True
|
||||
for p, w in zip(path, wp, strict=False):
|
||||
if w != "*" and p != w:
|
||||
match = False
|
||||
break
|
||||
if match:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def flatten_data(data: dict, parent_key: str = "", separator: str = ".") -> dict:
|
||||
"""
|
||||
Recursively flattens a nested dictionary structure where keys are joined by the separator.
|
||||
|
||||
@@ -9,10 +9,58 @@ from clan_lib.persist.util import (
|
||||
calc_patches,
|
||||
delete_by_path,
|
||||
determine_writeability,
|
||||
path_match,
|
||||
unmerge_lists,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("path", "whitelist", "expected"),
|
||||
[
|
||||
# Exact matches
|
||||
(["a", "b", "c"], [["a", "b", "c"]], True),
|
||||
(["a", "b"], [["a", "b"]], True),
|
||||
([], [[]], True),
|
||||
# Wildcard matches
|
||||
(["a", "b", "c"], [["a", "*", "c"]], True),
|
||||
(["a", "x", "c"], [["a", "*", "c"]], True),
|
||||
(["a", "b", "c"], [["*", "b", "c"]], True),
|
||||
(["a", "b", "c"], [["a", "b", "*"]], True),
|
||||
(["a", "b", "c"], [["*", "*", "*"]], True),
|
||||
# Multiple patterns - one matches
|
||||
(["a", "b", "c"], [["x", "y", "z"], ["a", "*", "c"]], True),
|
||||
(["x", "y", "z"], [["a", "*", "c"], ["x", "y", "z"]], True),
|
||||
# Length mismatch
|
||||
(["a", "b", "c"], [["a", "b"]], False),
|
||||
(["a", "b"], [["a", "b", "c"]], False),
|
||||
# Non-matching
|
||||
(["a", "b", "c"], [["a", "b", "x"]], False),
|
||||
(["a", "b", "c"], [["a", "x", "x"]], False),
|
||||
(["a", "b", "c"], [["x", "x", "x"]], False),
|
||||
# Empty whitelist
|
||||
(["a"], [], False),
|
||||
# Wildcards and exact mixed
|
||||
(
|
||||
["instances", "inst1", "roles", "roleA", "settings"],
|
||||
[["instances", "*", "roles", "*", "settings"]],
|
||||
True,
|
||||
),
|
||||
# Partial wildcard - length mismatch should fail
|
||||
(
|
||||
["instances", "inst1", "roles", "roleA"],
|
||||
[["instances", "*", "roles", "*", "settings"]],
|
||||
False,
|
||||
),
|
||||
# Empty path, no patterns
|
||||
([], [], False),
|
||||
],
|
||||
)
|
||||
def test_path_match(
|
||||
path: list[str], whitelist: list[list[str]], expected: bool
|
||||
) -> None:
|
||||
assert path_match(path, whitelist) == expected
|
||||
|
||||
|
||||
# --------- Patching tests ---------
|
||||
def test_patch_nested() -> None:
|
||||
orig = {"a": 1, "b": {"a": 2.1, "b": 2.2}, "c": 3}
|
||||
|
||||
Reference in New Issue
Block a user