@@ -15,7 +15,12 @@ import (
1515
1616// PublisherSubscriberFactory creates a matched Publisher and Subscriber
1717// pair for testing. The factory is called once per test case.
18- type PublisherSubscriberFactory func (t * testing.T ) (events.Publisher , events.Subscriber )
18+ //
19+ // The returned ready channel is closed (or receives a value) when the
20+ // subscriber is connected and ready to receive notifications. For
21+ // memory.Bus, return a pre-closed channel. For PostgreSQL, use
22+ // SubscriberConfig.ReadySignal.
23+ type PublisherSubscriberFactory func (t * testing.T ) (events.Publisher , events.Subscriber , <- chan struct {})
1924
2025// ContractSuite verifies that any Publisher+Subscriber implementation
2126// satisfies the events library's behavioral contract. Run it against
@@ -25,18 +30,28 @@ type ContractSuite struct {
2530 Factory PublisherSubscriberFactory
2631}
2732
28- func (s * ContractSuite ) TestSinglePublishSubscribe () {
29- pub , sub := s .Factory (s .T ())
30- ch := sub .Subscribe (events .ChannelInputReceived )
33+ // startListening starts sub.Listen and waits for the subscriber to be ready.
34+ func (s * ContractSuite ) startListening (sub events.Subscriber , ready <- chan struct {}) {
3135 ctx := s .T ().Context ()
3236 go sub .Listen (ctx ) //nolint:errcheck
33- time .Sleep (200 * time .Millisecond )
37+ select {
38+ case <- ready :
39+ case <- time .After (5 * time .Second ):
40+ s .Fail ("subscriber did not become ready" )
41+ }
42+ }
43+
44+ func (s * ContractSuite ) TestSinglePublishSubscribe () {
45+ pub , sub , ready := s .Factory (s .T ())
46+ ch := sub .Subscribe (events .ChannelInputReceived )
47+ s .startListening (sub , ready )
3448
3549 expected := events.Notification {
3650 Channel : events .ChannelInputReceived ,
3751 ApplicationID : 42 ,
3852 EpochIndex : 7 ,
3953 }
54+ ctx := s .T ().Context ()
4055 pub .Publish (ctx , expected )
4156
4257 select {
@@ -49,12 +64,11 @@ func (s *ContractSuite) TestSinglePublishSubscribe() {
4964}
5065
5166func (s * ContractSuite ) TestChannelIsolation () {
52- pub , sub := s .Factory (s .T ())
67+ pub , sub , ready := s .Factory (s .T ())
5368 ch := sub .Subscribe (events .ChannelInputReceived )
54- ctx := s .T ().Context ()
55- go sub .Listen (ctx ) //nolint:errcheck
56- time .Sleep (200 * time .Millisecond )
69+ s .startListening (sub , ready )
5770
71+ ctx := s .T ().Context ()
5872 // Publish on a different channel.
5973 pub .Publish (ctx , events.Notification {
6074 Channel : events .ChannelClaimComputed ,
@@ -75,12 +89,11 @@ func (s *ContractSuite) TestChannelIsolation() {
7589}
7690
7791func (s * ContractSuite ) TestBufferOverflowDropsWithoutBlocking () {
78- pub , sub := s .Factory (s .T ())
92+ pub , sub , ready := s .Factory (s .T ())
7993 ch := sub .Subscribe (events .ChannelInputReceived )
80- ctx := s .T ().Context ()
81- go sub .Listen (ctx ) //nolint:errcheck
82- time .Sleep (200 * time .Millisecond )
94+ s .startListening (sub , ready )
8395
96+ ctx := s .T ().Context ()
8497 // Publish more than buffer size (64) without reading.
8598 for i := range 100 {
8699 pub .Publish (ctx , events.Notification {
@@ -99,13 +112,12 @@ func (s *ContractSuite) TestBufferOverflowDropsWithoutBlocking() {
99112}
100113
101114func (s * ContractSuite ) TestMultipleSubscriptions () {
102- pub , sub := s .Factory (s .T ())
115+ pub , sub , ready := s .Factory (s .T ())
103116 ch1 := sub .Subscribe (events .ChannelInputReceived )
104117 ch2 := sub .Subscribe (events .ChannelClaimComputed )
105- ctx := s .T ().Context ()
106- go sub .Listen (ctx ) //nolint:errcheck
107- time .Sleep (200 * time .Millisecond )
118+ s .startListening (sub , ready )
108119
120+ ctx := s .T ().Context ()
109121 pub .Publish (ctx , events.Notification {
110122 Channel : events .ChannelInputReceived ,
111123 ApplicationID : 1 ,
@@ -130,13 +142,12 @@ func (s *ContractSuite) TestMultipleSubscriptions() {
130142}
131143
132144func (s * ContractSuite ) TestSubscribeWithFilterByAppID () {
133- pub , sub := s .Factory (s .T ())
145+ pub , sub , ready := s .Factory (s .T ())
134146 filter := events.SubscriptionFilter {ApplicationIDs : []int64 {42 }}
135147 ch := sub .SubscribeWithFilter (filter , events .ChannelInputReceived )
136- ctx := s .T ().Context ()
137- go sub .Listen (ctx ) //nolint:errcheck
138- time .Sleep (200 * time .Millisecond )
148+ s .startListening (sub , ready )
139149
150+ ctx := s .T ().Context ()
140151 // Publish for a non-matching app.
141152 pub .Publish (ctx , events.Notification {
142153 Channel : events .ChannelInputReceived ,
0 commit comments