Skip to content

Commit 0410837

Browse files
committed
feat(validator): validate v2 container kind/containment and use flat container namespace for relations
1 parent ef35d3f commit 0410837

1 file changed

Lines changed: 62 additions & 10 deletions

File tree

pacta/model/validator.py

Lines changed: 62 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from dataclasses import dataclass
22

3-
from pacta.model.types import ArchitectureModel, Container
3+
from pacta.model.types import ArchitectureModel, Container, ContainerKind
44
from pacta.reporting.types import EngineError
55

66

@@ -47,33 +47,85 @@ def validate(self, model: ArchitectureModel) -> list[EngineError]:
4747
)
4848
)
4949

50-
# containers
51-
for cid, c in model.containers.items():
50+
# containers (flat includes nested)
51+
flat = model.containers_flat
52+
for cid, c in flat.items():
5253
self._validate_container(model, cid, c, errors)
5354

54-
# relations reference known containers
55+
# v2: validate kind and child containment
56+
if model.version >= 2:
57+
self._validate_v2_containers(flat, errors)
58+
59+
# v2: check for duplicate IDs in flat namespace
60+
# (already impossible if _collect_containers works correctly, but guard against
61+
# children whose local IDs collide when dot-qualified)
62+
63+
# relations reference known containers (use flat namespace)
64+
all_ids = set(flat.keys())
5565
for r in model.relations:
56-
if r.from_container not in model.containers:
66+
if r.from_container not in all_ids:
5767
errors.append(
5868
EngineError(
5969
type="config_error",
60-
message="Relation references unknown from_container.",
70+
message=f"Relation references unknown from_container '{r.from_container}'."
71+
f" Available: {sorted(all_ids)}.",
6172
location=None,
62-
details={"from_container": r.from_container},
73+
details={"from_container": r.from_container, "available": sorted(all_ids)},
6374
)
6475
)
65-
if r.to_container not in model.containers:
76+
if r.to_container not in all_ids:
6677
errors.append(
6778
EngineError(
6879
type="config_error",
69-
message="Relation references unknown to_container.",
80+
message=f"Relation references unknown to_container '{r.to_container}'."
81+
f" Available: {sorted(all_ids)}.",
7082
location=None,
71-
details={"to_container": r.to_container},
83+
details={"to_container": r.to_container, "available": sorted(all_ids)},
7284
)
7385
)
7486

7587
return errors
7688

89+
def _validate_v2_containers(
90+
self,
91+
flat: dict[str, Container] | object,
92+
errors: list[EngineError],
93+
) -> None:
94+
for cid, c in flat.items(): # type: ignore[union-attr]
95+
# kind must be valid
96+
if c.kind is not None and c.kind not in ContainerKind:
97+
errors.append(
98+
EngineError(
99+
type="config_error",
100+
message=f"Container '{cid}' has invalid kind '{c.kind}'."
101+
f" Must be one of: {', '.join(k.value for k in ContainerKind)}.",
102+
location=None,
103+
details={"container_id": cid, "kind": str(c.kind)},
104+
)
105+
)
106+
107+
# child code.roots must be sub-paths of parent code.roots
108+
if c.parent is not None and c.code is not None:
109+
parent = flat.get(c.parent) # type: ignore[union-attr]
110+
if parent is not None and parent.code is not None:
111+
parent_roots = parent.code.roots
112+
for root in c.code.roots:
113+
if not any(root.startswith(pr) for pr in parent_roots):
114+
errors.append(
115+
EngineError(
116+
type="config_error",
117+
message=f"Container '{cid}' code root '{root}' is not"
118+
f" under parent '{c.parent}' roots {list(parent_roots)}.",
119+
location=None,
120+
details={
121+
"container_id": cid,
122+
"root": root,
123+
"parent_id": c.parent,
124+
"parent_roots": list(parent_roots),
125+
},
126+
)
127+
)
128+
77129
def _validate_container(
78130
self,
79131
model: ArchitectureModel,

0 commit comments

Comments
 (0)