2023-08-22 15:15:04 +02:00
|
|
|
import unittest
|
|
|
|
from typing import Set
|
|
|
|
|
|
|
|
from osc.util.models import *
|
|
|
|
from osc.util.models import get_origin
|
|
|
|
|
|
|
|
|
|
|
|
class TestTyping(unittest.TestCase):
|
|
|
|
def test_get_origin_list(self):
|
|
|
|
typ = get_origin(list)
|
|
|
|
self.assertEqual(typ, None)
|
|
|
|
|
|
|
|
def test_get_origin_list_str(self):
|
|
|
|
typ = get_origin(List[str])
|
|
|
|
self.assertEqual(typ, list)
|
|
|
|
|
|
|
|
|
|
|
|
class TestNotSet(unittest.TestCase):
|
|
|
|
def test_repr(self):
|
|
|
|
self.assertEqual(repr(NotSet), "NotSet")
|
|
|
|
|
|
|
|
def test_bool(self):
|
|
|
|
self.assertEqual(bool(NotSet), False)
|
|
|
|
|
|
|
|
|
|
|
|
class Test(unittest.TestCase):
|
2024-01-04 08:39:06 +01:00
|
|
|
def test_dict(self):
|
|
|
|
class TestSubmodel(BaseModel):
|
|
|
|
text: str = Field(default="default")
|
|
|
|
|
2023-08-22 15:15:04 +02:00
|
|
|
class TestModel(BaseModel):
|
|
|
|
a: str = Field(default="default")
|
|
|
|
b: Optional[str] = Field(default=None)
|
2024-01-04 08:39:06 +01:00
|
|
|
sub: Optional[List[TestSubmodel]] = Field(default=None)
|
2023-08-22 15:15:04 +02:00
|
|
|
|
|
|
|
m = TestModel()
|
2024-01-04 08:39:06 +01:00
|
|
|
self.assertEqual(m.dict(), {"a": "default", "b": None, "sub": None})
|
2023-08-22 15:15:04 +02:00
|
|
|
|
2024-01-04 08:39:06 +01:00
|
|
|
m.b = "B"
|
|
|
|
m.sub = [{"text": "one"}, {"text": "two"}]
|
|
|
|
self.assertEqual(m.dict(), {"a": "default", "b": "B", "sub": [{"text": "one"}, {"text": "two"}]})
|
2023-08-22 15:15:04 +02:00
|
|
|
|
|
|
|
def test_unknown_fields(self):
|
|
|
|
class TestModel(BaseModel):
|
|
|
|
pass
|
|
|
|
|
|
|
|
self.assertRaises(TypeError, TestModel, does_not_exist=None)
|
|
|
|
|
|
|
|
def test_uninitialized(self):
|
|
|
|
class TestModel(BaseModel):
|
|
|
|
field: str = Field()
|
|
|
|
|
|
|
|
self.assertRaises(TypeError, TestModel)
|
|
|
|
|
|
|
|
def test_invalid_type(self):
|
|
|
|
class TestModel(BaseModel):
|
|
|
|
field: Optional[str] = Field()
|
|
|
|
|
|
|
|
m = TestModel()
|
|
|
|
self.assertRaises(TypeError, setattr, m.field, [])
|
|
|
|
|
|
|
|
def test_unsupported_type(self):
|
|
|
|
class TestModel(BaseModel):
|
|
|
|
field: Set[str] = Field(default=None)
|
|
|
|
|
|
|
|
self.assertRaises(TypeError, TestModel)
|
|
|
|
|
2023-12-01 16:20:55 +01:00
|
|
|
def test_lazy_default(self):
|
|
|
|
class TestModel(BaseModel):
|
|
|
|
field: List[str] = Field(default=lambda: ["string"])
|
|
|
|
|
|
|
|
m = TestModel()
|
|
|
|
self.assertEqual(m.field, ["string"])
|
|
|
|
|
|
|
|
def test_lazy_default_invalid_type(self):
|
|
|
|
class TestModel(BaseModel):
|
|
|
|
field: List[str] = Field(default=lambda: None)
|
|
|
|
|
|
|
|
self.assertRaises(TypeError, TestModel)
|
|
|
|
|
2023-08-22 15:15:04 +02:00
|
|
|
def test_is_set(self):
|
|
|
|
class TestModel(BaseModel):
|
|
|
|
field: Optional[str] = Field()
|
|
|
|
|
|
|
|
m = TestModel()
|
|
|
|
|
2024-01-03 21:21:40 +01:00
|
|
|
self.assertNotIn("field", m._values)
|
2023-08-22 15:15:04 +02:00
|
|
|
self.assertEqual(m.field, None)
|
2024-01-03 21:21:40 +01:00
|
|
|
|
2023-08-22 15:15:04 +02:00
|
|
|
m.field = "text"
|
2024-01-03 21:21:40 +01:00
|
|
|
|
|
|
|
self.assertIn("field", m._values)
|
2023-08-22 15:15:04 +02:00
|
|
|
self.assertEqual(m.field, "text")
|
|
|
|
|
|
|
|
def test_str(self):
|
|
|
|
class TestModel(BaseModel):
|
|
|
|
field: str = Field(default="default")
|
|
|
|
|
|
|
|
m = TestModel()
|
|
|
|
|
|
|
|
field = m.__fields__["field"]
|
|
|
|
self.assertEqual(field.is_model, False)
|
|
|
|
self.assertEqual(field.is_optional, False)
|
|
|
|
self.assertEqual(field.origin_type, str)
|
|
|
|
|
|
|
|
self.assertEqual(m.field, "default")
|
|
|
|
m.field = "text"
|
|
|
|
self.assertEqual(m.field, "text")
|
|
|
|
|
|
|
|
def test_optional_str(self):
|
|
|
|
class TestModel(BaseModel):
|
|
|
|
field: Optional[str] = Field()
|
|
|
|
|
|
|
|
m = TestModel()
|
|
|
|
|
|
|
|
field = m.__fields__["field"]
|
|
|
|
self.assertEqual(field.is_model, False)
|
|
|
|
self.assertEqual(field.is_optional, True)
|
|
|
|
self.assertEqual(field.origin_type, str)
|
|
|
|
|
|
|
|
self.assertEqual(m.field, None)
|
|
|
|
m.field = "text"
|
|
|
|
self.assertEqual(m.field, "text")
|
|
|
|
|
|
|
|
def test_int(self):
|
|
|
|
class TestModel(BaseModel):
|
|
|
|
field: int = Field(default=0)
|
|
|
|
|
|
|
|
m = TestModel()
|
|
|
|
|
|
|
|
field = m.__fields__["field"]
|
|
|
|
self.assertEqual(field.is_model, False)
|
|
|
|
self.assertEqual(field.is_optional, False)
|
|
|
|
self.assertEqual(field.origin_type, int)
|
|
|
|
|
|
|
|
self.assertEqual(m.field, 0)
|
|
|
|
m.field = 1
|
|
|
|
self.assertEqual(m.field, 1)
|
|
|
|
|
|
|
|
def test_optional_int(self):
|
|
|
|
class TestModel(BaseModel):
|
|
|
|
field: Optional[int] = Field()
|
|
|
|
|
|
|
|
m = TestModel()
|
|
|
|
|
|
|
|
field = m.__fields__["field"]
|
|
|
|
self.assertEqual(field.is_model, False)
|
|
|
|
self.assertEqual(field.is_optional, True)
|
|
|
|
self.assertEqual(field.origin_type, int)
|
|
|
|
|
|
|
|
self.assertEqual(m.field, None)
|
|
|
|
m.field = 1
|
|
|
|
self.assertEqual(m.field, 1)
|
|
|
|
|
|
|
|
def test_submodel(self):
|
|
|
|
class TestSubmodel(BaseModel):
|
|
|
|
text: str = Field(default="default")
|
|
|
|
|
|
|
|
class TestModel(BaseModel):
|
|
|
|
field: TestSubmodel = Field(default={})
|
|
|
|
|
|
|
|
m = TestModel()
|
|
|
|
|
|
|
|
field = m.__fields__["field"]
|
|
|
|
self.assertEqual(field.is_model, True)
|
|
|
|
self.assertEqual(field.is_optional, False)
|
|
|
|
self.assertEqual(field.origin_type, TestSubmodel)
|
|
|
|
|
|
|
|
m = TestModel(field=TestSubmodel())
|
|
|
|
self.assertEqual(m.field.text, "default")
|
|
|
|
|
|
|
|
m = TestModel(field={"text": "text"})
|
|
|
|
self.assertEqual(m.field.text, "text")
|
|
|
|
|
|
|
|
def test_optional_submodel(self):
|
|
|
|
class TestSubmodel(BaseModel):
|
|
|
|
text: str = Field(default="default")
|
|
|
|
|
|
|
|
class TestModel(BaseModel):
|
|
|
|
field: Optional[TestSubmodel] = Field(default=None)
|
|
|
|
|
|
|
|
m = TestModel()
|
|
|
|
|
|
|
|
field = m.__fields__["field"]
|
|
|
|
self.assertEqual(field.is_model, True)
|
|
|
|
self.assertEqual(field.is_optional, True)
|
|
|
|
self.assertEqual(field.origin_type, TestSubmodel)
|
|
|
|
self.assertEqual(m.field, None)
|
2024-01-23 20:05:28 +01:00
|
|
|
m.dict()
|
2023-08-22 15:15:04 +02:00
|
|
|
|
|
|
|
m = TestModel(field=TestSubmodel())
|
|
|
|
self.assertIsInstance(m.field, TestSubmodel)
|
|
|
|
self.assertEqual(m.field.text, "default")
|
2024-01-23 20:05:28 +01:00
|
|
|
m.dict()
|
2023-08-22 15:15:04 +02:00
|
|
|
|
|
|
|
m = TestModel(field={"text": "text"})
|
|
|
|
self.assertNotEqual(m.field, None)
|
|
|
|
self.assertEqual(m.field.text, "text")
|
2024-01-23 20:05:28 +01:00
|
|
|
m.dict()
|
2023-08-22 15:15:04 +02:00
|
|
|
|
2024-01-23 20:05:38 +01:00
|
|
|
def test_list_submodels(self):
|
|
|
|
class TestSubmodel(BaseModel):
|
|
|
|
text: str = Field(default="default")
|
|
|
|
|
|
|
|
class TestModel(BaseModel):
|
|
|
|
field: List[TestSubmodel] = Field(default=[])
|
|
|
|
|
|
|
|
m = TestModel()
|
|
|
|
|
|
|
|
field = m.__fields__["field"]
|
|
|
|
self.assertEqual(field.is_model, False)
|
|
|
|
self.assertEqual(field.is_model_list, True)
|
|
|
|
self.assertEqual(field.is_optional, False)
|
|
|
|
self.assertEqual(field.origin_type, list)
|
|
|
|
m.dict()
|
|
|
|
|
|
|
|
m = TestModel(field=[TestSubmodel()])
|
|
|
|
self.assertEqual(m.field[0].text, "default")
|
|
|
|
m.dict()
|
|
|
|
|
|
|
|
m = TestModel(field=[{"text": "text"}])
|
|
|
|
self.assertEqual(m.field[0].text, "text")
|
|
|
|
m.dict()
|
|
|
|
|
|
|
|
self.assertRaises(TypeError, getattr(m, "field"))
|
|
|
|
|
|
|
|
def test_optional_list_submodels(self):
|
|
|
|
class TestSubmodel(BaseModel):
|
|
|
|
text: str = Field(default="default")
|
|
|
|
|
|
|
|
class TestModel(BaseModel):
|
|
|
|
field: Optional[List[TestSubmodel]] = Field(default=[])
|
|
|
|
|
|
|
|
m = TestModel()
|
|
|
|
|
|
|
|
field = m.__fields__["field"]
|
|
|
|
self.assertEqual(field.is_model, False)
|
|
|
|
self.assertEqual(field.is_model_list, True)
|
|
|
|
self.assertEqual(field.is_optional, True)
|
|
|
|
self.assertEqual(field.origin_type, list)
|
|
|
|
m.dict()
|
|
|
|
|
|
|
|
m = TestModel(field=[TestSubmodel()])
|
|
|
|
self.assertEqual(m.field[0].text, "default")
|
|
|
|
m.dict()
|
|
|
|
|
|
|
|
m = TestModel(field=[{"text": "text"}])
|
|
|
|
self.assertEqual(m.field[0].text, "text")
|
|
|
|
m.dict()
|
|
|
|
|
|
|
|
m.field = None
|
|
|
|
self.assertEqual(m.field, None)
|
|
|
|
m.dict()
|
|
|
|
|
2024-01-23 20:05:17 +01:00
|
|
|
def test_enum(self):
|
|
|
|
class Numbers(Enum):
|
|
|
|
one = "one"
|
|
|
|
two = "two"
|
|
|
|
|
|
|
|
class TestModel(BaseModel):
|
|
|
|
field: Optional[Numbers] = Field(default=None)
|
|
|
|
|
|
|
|
m = TestModel()
|
|
|
|
field = m.__fields__["field"]
|
|
|
|
self.assertEqual(field.is_model, False)
|
|
|
|
self.assertEqual(field.is_optional, True)
|
|
|
|
self.assertEqual(field.origin_type, Numbers)
|
|
|
|
self.assertEqual(m.field, None)
|
|
|
|
|
|
|
|
m.field = "one"
|
|
|
|
self.assertEqual(m.field, "one")
|
|
|
|
|
|
|
|
self.assertRaises(ValueError, setattr, m, "field", "does-not-exist")
|
|
|
|
|
2023-08-22 15:15:04 +02:00
|
|
|
def test_parent(self):
|
|
|
|
class ParentModel(BaseModel):
|
|
|
|
field: str = Field(default="text")
|
|
|
|
|
|
|
|
class ChildModel(BaseModel):
|
|
|
|
field: str = Field(default=FromParent("field"))
|
|
|
|
field2: str = Field(default=FromParent("field"))
|
|
|
|
|
|
|
|
p = ParentModel()
|
|
|
|
c = ChildModel(_parent=p)
|
|
|
|
self.assertEqual(p.field, "text")
|
|
|
|
self.assertEqual(c.field, "text")
|
|
|
|
self.assertEqual(c.field2, "text")
|
|
|
|
|
|
|
|
c.field = "new-text"
|
|
|
|
self.assertEqual(p.field, "text")
|
|
|
|
self.assertEqual(c.field, "new-text")
|
|
|
|
self.assertEqual(c.field2, "text")
|
|
|
|
|
2024-03-02 19:55:33 +01:00
|
|
|
def test_parent_fallback(self):
|
|
|
|
class SubModel(BaseModel):
|
|
|
|
field: str = Field(default=FromParent("field", fallback="submodel-fallback"))
|
|
|
|
|
|
|
|
class Model(BaseModel):
|
|
|
|
field: str = Field(default=FromParent("field", fallback="model-fallback"))
|
|
|
|
sub: Optional[SubModel] = Field()
|
|
|
|
sub_list: Optional[List[SubModel]] = Field()
|
|
|
|
|
|
|
|
m = Model()
|
|
|
|
s = SubModel(_parent=m)
|
|
|
|
m.sub = s
|
|
|
|
self.assertEqual(m.field, "model-fallback")
|
|
|
|
self.assertEqual(m.sub.field, "model-fallback")
|
|
|
|
|
|
|
|
m = Model(sub={})
|
|
|
|
self.assertEqual(m.field, "model-fallback")
|
|
|
|
self.assertEqual(m.sub.field, "model-fallback")
|
|
|
|
|
|
|
|
m = Model(sub=SubModel())
|
|
|
|
self.assertEqual(m.field, "model-fallback")
|
|
|
|
self.assertEqual(m.sub.field, "model-fallback")
|
|
|
|
|
|
|
|
m = Model()
|
|
|
|
s = SubModel(_parent=m)
|
|
|
|
m.sub_list = [s]
|
|
|
|
self.assertEqual(m.field, "model-fallback")
|
|
|
|
self.assertEqual(m.sub_list[0].field, "model-fallback")
|
|
|
|
|
|
|
|
m = Model(sub_list=[{}])
|
|
|
|
self.assertEqual(m.field, "model-fallback")
|
|
|
|
self.assertEqual(m.sub_list[0].field, "model-fallback")
|
|
|
|
|
|
|
|
m = Model(sub_list=[SubModel()])
|
|
|
|
self.assertEqual(m.field, "model-fallback")
|
|
|
|
self.assertEqual(m.sub_list[0].field, "model-fallback")
|
|
|
|
|
|
|
|
m = Model()
|
|
|
|
m.sub_list = []
|
|
|
|
m.sub_list.append({})
|
|
|
|
self.assertEqual(m.field, "model-fallback")
|
|
|
|
self.assertEqual(m.sub_list[0].field, "model-fallback")
|
|
|
|
|
2024-01-24 09:49:23 +01:00
|
|
|
def test_get_callback(self):
|
|
|
|
class Model(BaseModel):
|
|
|
|
quiet: bool = Field(
|
|
|
|
default=False,
|
|
|
|
)
|
|
|
|
verbose: bool = Field(
|
|
|
|
default=False,
|
|
|
|
# return False if ``quiet`` is True; return the actual value otherwise
|
|
|
|
get_callback=lambda obj, value: False if obj.quiet else value,
|
|
|
|
)
|
|
|
|
|
|
|
|
m = Model()
|
|
|
|
self.assertEqual(m.quiet, False)
|
|
|
|
self.assertEqual(m.verbose, False)
|
|
|
|
|
|
|
|
m.quiet = True
|
|
|
|
m.verbose = True
|
|
|
|
self.assertEqual(m.quiet, True)
|
|
|
|
self.assertEqual(m.verbose, False)
|
|
|
|
|
|
|
|
m.quiet = False
|
|
|
|
m.verbose = True
|
|
|
|
self.assertEqual(m.quiet, False)
|
|
|
|
self.assertEqual(m.verbose, True)
|
|
|
|
|
2024-02-13 09:40:53 +01:00
|
|
|
def test_has_changed(self):
|
|
|
|
class TestSubmodel(BaseModel):
|
|
|
|
text: str = Field(default="default")
|
|
|
|
|
|
|
|
class TestModel(BaseModel):
|
|
|
|
field: Optional[List[TestSubmodel]] = Field(default=[])
|
|
|
|
|
|
|
|
m = TestModel()
|
|
|
|
self.assertFalse(m.has_changed())
|
|
|
|
|
|
|
|
# a new instance of empty list
|
|
|
|
m.field = []
|
|
|
|
self.assertFalse(m.has_changed())
|
|
|
|
|
|
|
|
m.field = [{"text": "one"}, {"text": "two"}]
|
|
|
|
self.assertTrue(m.has_changed())
|
|
|
|
|
|
|
|
m.do_snapshot()
|
|
|
|
|
|
|
|
# a new instance of list with new instances of objects with the same data
|
|
|
|
m.field = [{"text": "one"}, {"text": "two"}]
|
|
|
|
self.assertFalse(m.has_changed())
|
|
|
|
|
2024-02-14 14:00:06 +01:00
|
|
|
def test_append_dict(self):
|
|
|
|
class TestSubmodel(BaseModel):
|
|
|
|
text: str = Field(default="default")
|
|
|
|
|
|
|
|
class TestModel(BaseModel):
|
|
|
|
field: Optional[List[TestSubmodel]] = Field(default=[])
|
|
|
|
|
|
|
|
m = TestModel()
|
|
|
|
m.field.append({"text": "value"})
|
|
|
|
# dict is converted to object next time the field is retrieved
|
|
|
|
self.assertIsInstance(m.field[0], BaseModel)
|
|
|
|
self.assertEqual(m.field[0].text, "value")
|
|
|
|
|
2024-03-04 13:52:57 +01:00
|
|
|
def test_ordering(self):
|
|
|
|
class TestSubmodel(BaseModel):
|
|
|
|
txt: Optional[str] = Field()
|
|
|
|
|
|
|
|
class TestModel(BaseModel):
|
|
|
|
num: Optional[int] = Field()
|
|
|
|
txt: Optional[str] = Field()
|
|
|
|
sub: Optional[TestSubmodel] = Field()
|
|
|
|
dct: Optional[Dict[str, TestSubmodel]] = Field()
|
|
|
|
|
|
|
|
m1 = TestModel()
|
|
|
|
m2 = TestModel()
|
|
|
|
self.assertEqual(m1, m2)
|
|
|
|
|
|
|
|
m1 = TestModel(num=1)
|
|
|
|
m2 = TestModel(num=2)
|
|
|
|
self.assertNotEqual(m1, m2)
|
|
|
|
self.assertLess(m1, m2)
|
|
|
|
self.assertGreater(m2, m1)
|
|
|
|
|
|
|
|
m1 = TestModel(txt="a")
|
|
|
|
m2 = TestModel(txt="b")
|
|
|
|
self.assertNotEqual(m1, m2)
|
|
|
|
self.assertLess(m1, m2)
|
|
|
|
self.assertGreater(m2, m1)
|
|
|
|
|
|
|
|
m1 = TestModel(sub={})
|
|
|
|
m2 = TestModel(sub={})
|
|
|
|
self.assertEqual(m1, m2)
|
|
|
|
|
|
|
|
m1 = TestModel(sub={"txt": "a"})
|
|
|
|
m2 = TestModel(sub={"txt": "b"})
|
|
|
|
self.assertNotEqual(m1, m2)
|
|
|
|
self.assertLess(m1, m2)
|
|
|
|
self.assertGreater(m2, m1)
|
|
|
|
|
|
|
|
m1 = TestModel(dct={})
|
|
|
|
m2 = TestModel(dct={})
|
|
|
|
self.assertEqual(m1, m2)
|
|
|
|
|
|
|
|
m1 = TestModel(dct={"a": TestSubmodel()})
|
|
|
|
m2 = TestModel(dct={"b": TestSubmodel()})
|
|
|
|
self.assertNotEqual(m1, m2)
|
|
|
|
self.assertLess(m1, m2)
|
|
|
|
self.assertGreater(m2, m1)
|
|
|
|
|
|
|
|
# dict ordering doesn't matter
|
|
|
|
m1 = TestModel(dct={"a": TestSubmodel(), "b": TestSubmodel()})
|
|
|
|
m2 = TestModel(dct={"b": TestSubmodel(), "a": TestSubmodel()})
|
|
|
|
self.assertEqual(m1, m2)
|
|
|
|
|
2023-08-22 15:15:04 +02:00
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
unittest.main()
|