99
1010 "github.com/MontFerret/ferret/v2/pkg/logging"
1111 "github.com/MontFerret/ferret/v2/pkg/module"
12+ ferretnet "github.com/MontFerret/ferret/v2/pkg/net"
1213 "github.com/MontFerret/ferret/v2/pkg/runtime"
1314 "github.com/MontFerret/ferret/v2/pkg/source"
1415 "github.com/MontFerret/ferret/v2/pkg/vm"
@@ -50,10 +51,17 @@ func TestNewRunsCloseHooksWhenHostBuildFails(t *testing.T) {
5051 moduleRegistered bool
5152 closeHookCalled bool
5253 )
54+ client := & recordingHTTPClient {}
5355
5456 mod := testModule {
5557 registerFn : func (boot module.Bootstrap ) error {
5658 moduleRegistered = true
59+ internal , ok := boot .(* bootstrap )
60+ if ! ok {
61+ t .Fatalf ("expected internal bootstrap, got %T" , boot )
62+ }
63+ internal .host .network = mustNewTestNetwork (t , ferretnet .WithHTTPClient (client ))
64+
5765 boot .Hooks ().Engine ().OnClose (func () error {
5866 closeHookCalled = true
5967 return nil
@@ -78,6 +86,10 @@ func TestNewRunsCloseHooksWhenHostBuildFails(t *testing.T) {
7886 if ! closeHookCalled {
7987 t .Fatal ("expected engine close hooks to run on host build failure" )
8088 }
89+
90+ if got := client .idleCloseCount (); got != 1 {
91+ t .Fatalf ("expected host-build-failure cleanup, got %d calls" , got )
92+ }
8193}
8294
8395func TestNewReturnsJoinedErrorWhenInitAndCloseHooksFail (t * testing.T ) {
@@ -115,6 +127,119 @@ func TestNewReturnsJoinedErrorWhenInitAndCloseHooksFail(t *testing.T) {
115127 }
116128}
117129
130+ func TestEngineCloseClosesOwnedNetworkIdleConnections (t * testing.T ) {
131+ t .Parallel ()
132+
133+ client := & recordingHTTPClient {}
134+ eng := mustNewEngine (t )
135+ eng .host .network = mustNewTestNetwork (t , ferretnet .WithHTTPClient (client ))
136+
137+ if err := eng .Close (); err != nil {
138+ t .Fatalf ("close engine: %v" , err )
139+ }
140+
141+ if got := client .idleCloseCount (); got != 1 {
142+ t .Fatalf ("expected one idle-connection cleanup, got %d" , got )
143+ }
144+ }
145+
146+ func TestEngineCloseCleansOwnedNetworkAfterHookFailure (t * testing.T ) {
147+ t .Parallel ()
148+
149+ hookErr := errors .New ("close hook failed" )
150+ client := & recordingHTTPClient {}
151+ eng := mustNewEngine (t , WithEngineCloseHook (func () error {
152+ return hookErr
153+ }))
154+ eng .host .network = mustNewTestNetwork (t , ferretnet .WithHTTPClient (client ))
155+
156+ err := eng .Close ()
157+ if ! errors .Is (err , hookErr ) {
158+ t .Fatalf ("expected close hook error, got %v" , err )
159+ }
160+
161+ if got := client .idleCloseCount (); got != 1 {
162+ t .Fatalf ("expected cleanup after hook failure, got %d calls" , got )
163+ }
164+ }
165+
166+ func TestEngineCloseDoesNotCleanInjectedNetwork (t * testing.T ) {
167+ t .Parallel ()
168+
169+ client := & recordingHTTPClient {}
170+ network := mustNewTestNetwork (t , ferretnet .WithHTTPClient (client ))
171+ eng := mustNewEngine (t , WithNetwork (network ))
172+
173+ if err := eng .Close (); err != nil {
174+ t .Fatalf ("close engine: %v" , err )
175+ }
176+
177+ if got := client .idleCloseCount (); got != 0 {
178+ t .Fatalf ("expected injected network ownership to remain with caller, got %d cleanup calls" , got )
179+ }
180+ }
181+
182+ func TestNewCleansOwnedNetworkOnRegistrationFailure (t * testing.T ) {
183+ t .Parallel ()
184+
185+ registerErr := errors .New ("register failed" )
186+ client := & recordingHTTPClient {}
187+ mod := testModule {
188+ registerFn : func (boot module.Bootstrap ) error {
189+ internal , ok := boot .(* bootstrap )
190+ if ! ok {
191+ t .Fatalf ("expected internal bootstrap, got %T" , boot )
192+ }
193+
194+ internal .host .network = mustNewTestNetwork (t , ferretnet .WithHTTPClient (client ))
195+
196+ return registerErr
197+ },
198+ }
199+
200+ _ , err := New (WithModules (mod ))
201+ if ! errors .Is (err , registerErr ) {
202+ t .Fatalf ("expected registration error, got %v" , err )
203+ }
204+
205+ if got := client .idleCloseCount (); got != 1 {
206+ t .Fatalf ("expected construction-failure cleanup, got %d calls" , got )
207+ }
208+ }
209+
210+ func TestNewCleansOwnedNetworkOnInitFailure (t * testing.T ) {
211+ t .Parallel ()
212+
213+ initErr := errors .New ("init failed" )
214+ client := & recordingHTTPClient {}
215+ mod := testModule {
216+ registerFn : func (boot module.Bootstrap ) error {
217+ internal , ok := boot .(* bootstrap )
218+ if ! ok {
219+ t .Fatalf ("expected internal bootstrap, got %T" , boot )
220+ }
221+
222+ internal .host .network = mustNewTestNetwork (t , ferretnet .WithHTTPClient (client ))
223+
224+ return nil
225+ },
226+ }
227+
228+ _ , err := New (
229+ WithModules (mod ),
230+ WithEngineInitHook (func () error {
231+ return initErr
232+ }),
233+ )
234+ if ! errors .Is (err , initErr ) {
235+ t .Fatalf ("expected init error, got %v" , err )
236+ }
237+
238+ if got := client .idleCloseCount (); got != 1 {
239+ t .Fatalf ("expected init-failure cleanup, got %d calls" , got )
240+ }
241+ }
242+
118243func TestRunClosesPlanWhenSessionCreationFails (t * testing.T ) {
119244 t .Parallel ()
120245
0 commit comments