@@ -48,6 +48,37 @@ class ActorTestCase < ActiveSupport::TestCase
4848 assert_empty SolidObjects ::Process . all
4949 end
5050
51+ # The helper used to delete instances and let the database cascade remove
52+ # everything else. Where the cascade does not fire, rows survive into the
53+ # next test pointing at an instance that no longer exists, and a test that
54+ # reads them sees another test's data.
55+ test "reset actors clears actor-owned rows without the database cascade" do
56+ skip unless database_family == :sqlite
57+
58+ instance = create_actor_owned_rows
59+ without_foreign_keys do
60+ SolidObjects ::TestHelper . reset_actors!
61+ end
62+
63+ remaining = SolidObjects ::TestHelper . actor_owned_models . reject { |model | model . count . zero? }
64+ assert_empty remaining . map ( &:table_name ) ,
65+ "these tables survived a reset that could not rely on the cascade"
66+ refute_nil instance
67+ end
68+
69+ # A table added later is only covered if the helper is told about it, and the
70+ # cascade would hide the omission on every database that enforces it.
71+ test "every actor-owned table is in the reset list" do
72+ owned = SolidObjects ::Record . connection . tables
73+ . grep ( /\A solid_objects_/ )
74+ . reject { |table | table == "solid_objects_test_domain_records" }
75+ . sort
76+ listed = SolidObjects ::TestHelper . actor_owned_models . map ( &:table_name )
77+
78+ assert_equal owned , ( listed + [ SolidObjects ::Process . table_name ] ) . sort ,
79+ "a Solid Objects table is missing from reset_actors!"
80+ end
81+
5182 test "drain actor messages processes queued work deterministically" do
5283 test_case = ActorTestCase . new ( "unused" )
5384 message_reference = HelperActor . ref ( "async" ) . async ( :increment )
@@ -90,4 +121,90 @@ class ActorTestCase < ActiveSupport::TestCase
90121
91122 assert_includes error . message , "unknown"
92123 end
124+
125+ private
126+
127+ # One row in every actor-owned table, so an omission from the reset list
128+ # shows up as a surviving table rather than as a passing test.
129+ def create_actor_owned_rows
130+ now = Time . current
131+ instance = SolidObjects ::Instance . create! (
132+ actor_type : "reset-probe" ,
133+ actor_id : "one" ,
134+ state : { } ,
135+ state_version : 1
136+ )
137+ message = SolidObjects ::Message . create! (
138+ instance :,
139+ actor_type : instance . actor_type ,
140+ actor_id : instance . actor_id ,
141+ message_name : "noop" ,
142+ message_kind : "async" ,
143+ arguments : { } ,
144+ sequence : 1 ,
145+ max_attempts : 1 ,
146+ request_id : SecureRandom . uuid ,
147+ enqueued_at : now ,
148+ available_at : now
149+ )
150+ SolidObjects ::ReadyMessage . create! ( message :, instance :, sequence : 1 , available_at : now )
151+ SolidObjects ::ClaimedMessage . create! (
152+ message :,
153+ instance :,
154+ activation_generation : 1 ,
155+ claimed_at : now
156+ )
157+ SolidObjects ::Reminder . create! (
158+ instance :,
159+ actor_type : instance . actor_type ,
160+ actor_id : instance . actor_id ,
161+ name : "probe" ,
162+ message_name : "noop" ,
163+ arguments : { } ,
164+ next_run_at : now ,
165+ status : "scheduled"
166+ )
167+ SolidObjects ::Effect . create! (
168+ instance :,
169+ message :,
170+ effect_id : SecureRandom . uuid ,
171+ name : "probe" ,
172+ arguments : { } ,
173+ max_attempts : 1 ,
174+ available_at : now
175+ )
176+ SolidObjects ::Broadcast . create! (
177+ instance :,
178+ message :,
179+ broadcast_id : SecureRandom . uuid ,
180+ observable_name : "probe" ,
181+ value : { } ,
182+ state_version : 1 ,
183+ activation_generation : 1 ,
184+ available_at : now
185+ )
186+ SolidObjects ::DeadLetter . create! (
187+ instance :,
188+ message :,
189+ actor_type : instance . actor_type ,
190+ actor_id : instance . actor_id ,
191+ message_name : "noop" ,
192+ arguments : { } ,
193+ attempts : 1 ,
194+ exception_class : "RuntimeError" ,
195+ exception_message : "probe" ,
196+ backtrace : [ ] ,
197+ first_failed_at : now ,
198+ last_failed_at : now
199+ )
200+ instance
201+ end
202+
203+ def without_foreign_keys
204+ connection = SolidObjects ::Record . connection
205+ connection . execute ( "PRAGMA foreign_keys = OFF" )
206+ yield
207+ ensure
208+ connection . execute ( "PRAGMA foreign_keys = ON" )
209+ end
93210end
0 commit comments