Browse Source

test: cover cipy.common, fix some bad assumptions

Sam Jaffe 4 weeks ago
parent
commit
6e84d66b29
2 changed files with 65 additions and 4 deletions
  1. 8 4
      src/cipy/common.py
  2. 57 0
      tests/common_test.py

+ 8 - 4
src/cipy/common.py

@@ -1,7 +1,8 @@
 """Common objects in the CI hierarchy"""
+import re
 
 from dataclasses import dataclass
-from typing import Annotated, Self, final
+from typing import ClassVar, Final, Self, final
 
 from pydantic import BaseModel, Field
 
@@ -16,19 +17,22 @@ class Outputs(BaseModel):
     @final
     def validated(self) -> Self:
         """Validate this output object, affirm that it is properly constructed"""
-        return self.model_validate(self, extra="forbid")
+        return self.model_validate(vars(self), extra="forbid")
 
 
 @dataclass
 class Ref:
     """Annotation class describing a reference into Context or another place"""
 
-    path: list[Annotated[str, Field(pattern="\\w*(_\\w*)*")]]
+    path: list[str]
+    PATTERN: Final[ClassVar[re.Pattern]] = re.compile("[A-Za-z][\\W_]*(_[\\W_]*)*")
 
     def __init__(self, pathstr: str) -> None:
         self.path = pathstr.split(".")
-        if not self.path:
+        if len(self.path) <= 1:
             raise ValueError("References must be of the form A.B.C etc.")
+        if any(not Ref.PATTERN.match(t) for t in self.path):
+            raise ValueError("All path elements in a References must be")
 
     def __repr__(self) -> str:
         return "Ref({})".format(".".join(self.path))

+ 57 - 0
tests/common_test.py

@@ -0,0 +1,57 @@
+import pytest
+
+from pydantic import ValidationError
+
+import cipy
+import cipy.common
+
+
+class Outputs(cipy.common.Outputs):
+    a: int = cipy.required()
+
+
+def test_outputs_can_be_constructed_in_illegal_state() -> None:
+    output = Outputs()
+
+
+def test_outputs_forbids_extras() -> None:
+    output = cipy.common.Outputs()
+    output.__dict__["A"] = 1
+
+    with pytest.raises(ValidationError):
+        output.validated()
+
+
+def test_outputs_validated_throws_on_unset_attr() -> None:
+    output = Outputs()
+
+    with pytest.raises(ValidationError):
+        output.validated()
+
+
+def test_outputs_subclass_can_be_constructed() -> None:
+    output = Outputs(a=1).validated()
+
+
+def test_ref_cannot_be_empty_ctor() -> None:
+    with pytest.raises(TypeError):
+        cipy.common.Ref()  # type: ignore[call-arg]
+
+
+def test_ref_cannot_be_empty_str() -> None:
+    with pytest.raises(ValueError):
+        cipy.common.Ref("")
+
+
+def test_ref_cannot_be_single_token() -> None:
+    with pytest.raises(ValueError):
+        cipy.common.Ref("A")
+
+
+def test_ref_cannot_be_two_tokens() -> None:
+    cipy.common.Ref("A.B")
+
+
+def test_ref_path_is_pattern_validated() -> None:
+    with pytest.raises(ValueError):
+        cipy.common.Ref("A._B")