diff --git a/test/test_model.py b/test/test_model.py index e636f0e..b577819 100644 --- a/test/test_model.py +++ b/test/test_model.py @@ -2481,6 +2481,99 @@ class MainTests(unittest.TestCase): self.assertFalse(hasattr(b2.F1, "xb")) self.assertFalse(hasattr(c2.F1, "xc")) + def test_dict_field_override_with_nested_containers(self): + class App(dm.BaseAppliance): + data: dict[str, list[int]] = {"numbers": [1, 2]} + + app = App(data={"numbers": [9, 8]}) + # Dict must be frozen, lists inside must become tuples + self.assertEqual(type(app.data).__name__, "frozendict") + self.assertIsInstance(app.data["numbers"], tuple) + self.assertEqual(app.data["numbers"], (9, 8)) + + # Wrong type inside list should raise + with self.assertRaises(dm.InvalidFieldValue): + App(data={"numbers": [1, "oops"]}) + + def test_feature_dict_override_with_nested_containers(self): + class App(dm.BaseAppliance): + class F1(dm.BaseFeature): + values: list[int] = [1, 2] + + app = App(F1={"values": [5, 6]}) + self.assertEqual(app.F1.values, (5, 6)) # deepfreeze → tuple + + # Invalid type in list should fail + with self.assertRaises(dm.InvalidFieldValue): + App(F1={"values": [1, "oops"]}) + + def test_initializer_modifies_nested_containers(self): + class App(dm.BaseAppliance): + data: dict[str, list[int]] = {"nums": [1]} + + @classmethod + def __initializer(cls): + # Append into nested list + cls.data["nums"].append(2) + + app = App() + self.assertEqual(app.data["nums"], (1, 2)) # frozen tuple after init + + def test_feature_dict_override_with_unknown_key(self): + class App(dm.BaseAppliance): + class F1(dm.BaseFeature): + a: int = 1 + + # Dict override with unknown field 'zzz' + with self.assertRaises(dm.InvalidFieldValue): + App(F1={"zzz": 42}) + + def test_schema_isolation_across_multiple_feature_overrides(self): + class App(dm.BaseAppliance): + class F1(dm.BaseFeature): + a: int = 1 + + class F1a(App.F1): + a = 10 + + class F1b(App.F1): + a = 20 + + app1 = App(F1=F1a) + self.assertIsInstance(app1.F1, F1a) + self.assertEqual(app1.F1.a, 10) + + app2 = App(F1=F1b) + self.assertIsInstance(app2.F1, F1b) + self.assertEqual(app2.F1.a, 20) + + # Original appliance schema must not be polluted + app3 = App() + self.assertIsInstance(app3.F1, App.F1) + self.assertEqual(app3.F1.a, 1) + + def test_feature_inheritance_with_annotated_fields(self): + from typing_extensions import Annotated + + class App(dm.BaseAppliance): + class F1(dm.BaseFeature): + a: Annotated[int, dm.DABFieldInfo(doc="field a")] = 1 + + # ✅ Subclass override must inherit from parent F1 + class F1Ex(App.F1): + b: str = "ok" + + app = App(F1=F1Ex) + self.assertIsInstance(app.F1, F1Ex) + self.assertEqual((app.F1.a, app.F1.b), (1, "ok")) + + # ❌ Wrong: fresh BaseFeature under same name + with self.assertRaises(dm.InvalidFeatureInheritance): + + class Bad(App): + class F1(dm.BaseFeature): + fail: str = "oops" + # ---------- main ----------