@@ -55,38 +55,24 @@ class WindowSnapshot:
55
55
panes : list [PaneSnapshot ] = dataclasses .field (default_factory = list )
56
56
57
57
58
- def test_basic_functionality () -> None :
59
- """Test that the base class is mutable but the snapshot is not."""
60
- # Create a regular mutable pane
61
- pane = BasePane (pane_id = "pane123" , width = 80 , height = 24 )
62
-
63
- # Should be mutable
64
- pane .width = 100
65
- assert pane .width == 100
66
- pane .resize (120 , 30 )
67
- assert pane .width == 120
68
- assert pane .height == 30
69
-
70
- # Create a frozen snapshot with our decorator
71
- snapshot = PaneSnapshot (
72
- pane_id = pane .pane_id ,
73
- width = pane .width ,
74
- height = pane .height ,
75
- captured_content = ["Line1" , "Line2" ],
58
+ # Core behavior tests
59
+ # ------------------
60
+
61
+ def test_snapshot_initialization () -> None :
62
+ """Test proper initialization of fields in a frozen dataclass."""
63
+ pane = PaneSnapshot (
64
+ pane_id = "pane123" ,
65
+ width = 80 ,
66
+ height = 24 ,
67
+ captured_content = ["Line1" , "Line2" ]
76
68
)
77
-
78
- # Test type checking
79
- assert isinstance (snapshot , PaneSnapshot )
80
-
81
- # Should maintain the inheritance relationship
82
- assert isinstance (snapshot , BasePane )
83
-
69
+
84
70
# Values should be correctly assigned
85
- assert snapshot .pane_id == pane . pane_id
86
- assert snapshot .width == pane . width
87
- assert snapshot .height == pane . height
88
- assert snapshot .captured_content == ["Line1" , "Line2" ]
89
- assert isinstance (snapshot .created_at , datetime )
71
+ assert pane .pane_id == "pane123"
72
+ assert pane .width == 80
73
+ assert pane .height == 24
74
+ assert pane .captured_content == ["Line1" , "Line2" ]
75
+ assert isinstance (pane .created_at , datetime )
90
76
91
77
92
78
def test_immutability () -> None :
@@ -96,33 +82,82 @@ def test_immutability() -> None:
96
82
)
97
83
98
84
# Attempting to modify a field should raise AttributeError
99
- with pytest .raises (AttributeError ) as excinfo :
85
+ with pytest .raises (AttributeError , match = "immutable.*cannot modify field 'width'" ) :
100
86
snapshot .width = 200 # type: ignore
101
- assert "immutable" in str (excinfo .value )
102
87
103
88
# Attempting to add a new field should raise AttributeError
104
- with pytest .raises (AttributeError ) as excinfo :
89
+ with pytest .raises (AttributeError , match = "immutable.*cannot modify field 'new_field'" ) :
105
90
snapshot .new_field = "value" # type: ignore
106
- assert "immutable" in str (excinfo .value )
107
91
108
92
# Attempting to delete a field should raise AttributeError
109
- with pytest .raises (AttributeError ) as excinfo :
93
+ with pytest .raises (AttributeError , match = "immutable.*cannot delete field 'width'" ) :
110
94
del snapshot .width
111
- assert "immutable" in str (excinfo .value )
112
95
113
96
# Calling a method that tries to modify state should fail
114
- # Use separate variable for the NotImplementedError exception info
115
- with pytest .raises (NotImplementedError ) as resize_excinfo :
97
+ with pytest .raises (NotImplementedError , match = "immutable" ):
116
98
snapshot .resize (200 , 50 )
117
- assert "immutable" in str (resize_excinfo .value )
118
99
119
100
120
- def test_nested_references () -> None :
121
- """Test that nested structures work properly."""
101
+ def test_inheritance () -> None :
102
+ """Test that frozen classes correctly inherit from mutable base classes."""
103
+ # Create instances of both classes
104
+ base_pane = BasePane (pane_id = "base1" , width = 80 , height = 24 )
105
+ snapshot = PaneSnapshot (pane_id = "snap1" , width = 80 , height = 24 )
106
+
107
+ # Verify inheritance relationship
108
+ assert isinstance (snapshot , BasePane )
109
+ assert isinstance (snapshot , PaneSnapshot )
110
+
111
+ # Base class remains mutable
112
+ base_pane .width = 100
113
+ assert base_pane .width == 100
114
+
115
+ # Derived class is immutable
116
+ with pytest .raises (AttributeError , match = "immutable" ):
117
+ snapshot .width = 100
118
+
119
+
120
+ # Edge case tests
121
+ # --------------
122
+
123
+ def test_internal_attributes () -> None :
124
+ """Test that internal attributes (starting with _) can be modified."""
125
+ snapshot = PaneSnapshot (
126
+ pane_id = "pane123" ,
127
+ width = 80 ,
128
+ height = 24 ,
129
+ )
130
+
131
+ # Should be able to set internal attributes
132
+ snapshot ._internal_cache = {"test" : "value" } # type: ignore
133
+ assert snapshot ._internal_cache == {"test" : "value" } # type: ignore
134
+
135
+
136
+ def test_nested_mutability_leak () -> None :
137
+ """Test the known limitation that nested mutable fields can still be modified."""
138
+ # Create a frozen dataclass with a mutable field
139
+ snapshot = PaneSnapshot (
140
+ pane_id = "pane123" ,
141
+ width = 80 ,
142
+ height = 24 ,
143
+ captured_content = ["initial" ]
144
+ )
145
+
146
+ # Can't reassign the field itself
147
+ with pytest .raises (AttributeError , match = "immutable" ):
148
+ snapshot .captured_content = ["new" ] # type: ignore
149
+
150
+ # But we can modify its contents (limitation of Python immutability)
151
+ snapshot .captured_content .append ("mutated" )
152
+ assert "mutated" in snapshot .captured_content
153
+ assert snapshot .captured_content == ["initial" , "mutated" ]
154
+
155
+
156
+ def test_bidirectional_references () -> None :
157
+ """Test that nested structures with bidirectional references work properly."""
122
158
# Create temporary panes (will be re-created with the window)
123
159
temp_panes : list [PaneSnapshot ] = []
124
160
125
- # We need to create objects in a specific order to handle bi-directional references
126
161
# First, create a window with an empty panes list
127
162
window = WindowSnapshot (window_id = "win1" , name = "Test Window" , panes = temp_panes )
128
163
@@ -131,8 +166,6 @@ def test_nested_references() -> None:
131
166
pane2 = PaneSnapshot (pane_id = "pane2" , width = 80 , height = 24 , parent_window = window )
132
167
133
168
# Update the panes list before it gets frozen
134
- # This is a bit of a hack, but that's how you'd need to handle bi-directional
135
- # references with immutable objects in real code
136
169
temp_panes .append (pane1 )
137
170
temp_panes .append (pane2 )
138
171
@@ -142,26 +175,11 @@ def test_nested_references() -> None:
142
175
assert pane1 in window .panes
143
176
assert pane2 in window .panes
144
177
145
- # Can't test by trying to reassign since we'll hit a type error first
146
- # But we can still modify the contents of lists (limitation of Python)
147
- # Let's verify this limitation exists:
178
+ # Can still modify the contents of mutable collections
148
179
pane3 = PaneSnapshot (pane_id = "pane3" , width = 100 , height = 30 )
149
180
window .panes .append (pane3 )
150
181
assert len (window .panes ) == 3 # Successfully modified
151
182
152
183
# This is a "leaky abstraction" in Python's immutability model
153
184
# In real code, consider using immutable collections (tuple, frozenset)
154
185
# or deep freezing containers
155
-
156
-
157
- def test_internal_attributes () -> None :
158
- """Test that internal attributes (starting with _) can be modified."""
159
- snapshot = PaneSnapshot (
160
- pane_id = "pane123" ,
161
- width = 80 ,
162
- height = 24 ,
163
- )
164
-
165
- # Should be able to set internal attributes
166
- snapshot ._internal_cache = {"test" : "value" } # type: ignore
167
- assert snapshot ._internal_cache == {"test" : "value" } # type: ignore
0 commit comments