@@ -204,6 +204,45 @@ func TestTogglePaused(t *testing.T) {
204204 }
205205}
206206
207+ func TestErrorChanIsolation (t * testing.T ) {
208+ // Regression test for issue #287: panic: send on closed channel.
209+ //
210+ // gouroboros closes whichever error channel it is given during connection
211+ // shutdown. Passing the same shared errorChan to every connection means
212+ // the first shutdown closes it, and the second connection panics when it
213+ // tries to send on the now-closed channel.
214+ //
215+ // The fix gives each connection its own connErrChan and relays errors into
216+ // the shared channel. This test verifies that pattern: two simulated
217+ // connection lifecycles must not panic and must relay their errors.
218+
219+ sharedErrChan := make (chan error , 10 )
220+
221+ simulateConnection := func (id int ) {
222+ connErrChan := make (chan error , 10 )
223+ done := make (chan struct {})
224+ go func () {
225+ defer close (done )
226+ for err := range connErrChan {
227+ select {
228+ case sharedErrChan <- err :
229+ default :
230+ }
231+ }
232+ }()
233+ connErrChan <- fmt .Errorf ("error from connection %d" , id )
234+ close (connErrChan ) // gouroboros closes this on shutdown
235+ <- done // wait for relay goroutine to finish
236+ }
237+
238+ simulateConnection (1 )
239+ simulateConnection (2 ) // would panic before the fix
240+
241+ if len (sharedErrChan ) != 2 {
242+ t .Errorf ("expected 2 relayed errors, got %d" , len (sharedErrChan ))
243+ }
244+ }
245+
207246func TestLogBuffer_Write (t * testing.T ) {
208247 lb := & LogBuffer {maxLines : 3 }
209248
0 commit comments