github.com/lusis/distribution@v2.0.1+incompatible/notifications/sinks_test.go (about) 1 package notifications 2 3 import ( 4 "fmt" 5 "math/rand" 6 "sync" 7 "time" 8 9 "github.com/Sirupsen/logrus" 10 11 "testing" 12 ) 13 14 func TestBroadcaster(t *testing.T) { 15 const nEvents = 1000 16 var sinks []Sink 17 18 for i := 0; i < 10; i++ { 19 sinks = append(sinks, &testSink{}) 20 } 21 22 b := NewBroadcaster(sinks...) 23 24 var block []Event 25 var wg sync.WaitGroup 26 for i := 1; i <= nEvents; i++ { 27 block = append(block, createTestEvent("push", "library/test", "blob")) 28 29 if i%10 == 0 && i > 0 { 30 wg.Add(1) 31 go func(block ...Event) { 32 if err := b.Write(block...); err != nil { 33 t.Fatalf("error writing block of length %d: %v", len(block), err) 34 } 35 wg.Done() 36 }(block...) 37 38 block = nil 39 } 40 } 41 42 wg.Wait() // Wait until writes complete 43 checkClose(t, b) 44 45 // Iterate through the sinks and check that they all have the expected length. 46 for _, sink := range sinks { 47 ts := sink.(*testSink) 48 ts.mu.Lock() 49 defer ts.mu.Unlock() 50 51 if len(ts.events) != nEvents { 52 t.Fatalf("not all events ended up in testsink: len(testSink) == %d, not %d", len(ts.events), nEvents) 53 } 54 55 if !ts.closed { 56 t.Fatalf("sink should have been closed") 57 } 58 } 59 60 } 61 62 func TestEventQueue(t *testing.T) { 63 const nevents = 1000 64 var ts testSink 65 metrics := newSafeMetrics() 66 eq := newEventQueue( 67 // delayed sync simulates destination slower than channel comms 68 &delayedSink{ 69 Sink: &ts, 70 delay: time.Millisecond * 1, 71 }, metrics.eventQueueListener()) 72 73 var wg sync.WaitGroup 74 var block []Event 75 for i := 1; i <= nevents; i++ { 76 block = append(block, createTestEvent("push", "library/test", "blob")) 77 if i%10 == 0 && i > 0 { 78 wg.Add(1) 79 go func(block ...Event) { 80 if err := eq.Write(block...); err != nil { 81 t.Fatalf("error writing event block: %v", err) 82 } 83 wg.Done() 84 }(block...) 85 86 block = nil 87 } 88 } 89 90 wg.Wait() 91 checkClose(t, eq) 92 93 ts.mu.Lock() 94 defer ts.mu.Unlock() 95 metrics.Lock() 96 defer metrics.Unlock() 97 98 if len(ts.events) != nevents { 99 t.Fatalf("events did not make it to the sink: %d != %d", len(ts.events), 1000) 100 } 101 102 if !ts.closed { 103 t.Fatalf("sink should have been closed") 104 } 105 106 if metrics.Events != nevents { 107 t.Fatalf("unexpected ingress count: %d != %d", metrics.Events, nevents) 108 } 109 110 if metrics.Pending != 0 { 111 t.Fatalf("unexpected egress count: %d != %d", metrics.Pending, 0) 112 } 113 } 114 115 func TestRetryingSink(t *testing.T) { 116 117 // Make a sync that fails most of the time, ensuring that all the events 118 // make it through. 119 var ts testSink 120 flaky := &flakySink{ 121 rate: 1.0, // start out always failing. 122 Sink: &ts, 123 } 124 s := newRetryingSink(flaky, 3, 10*time.Millisecond) 125 126 var wg sync.WaitGroup 127 var block []Event 128 for i := 1; i <= 100; i++ { 129 block = append(block, createTestEvent("push", "library/test", "blob")) 130 131 // Above 50, set the failure rate lower 132 if i > 50 { 133 s.mu.Lock() 134 flaky.rate = 0.90 135 s.mu.Unlock() 136 } 137 138 if i%10 == 0 && i > 0 { 139 wg.Add(1) 140 go func(block ...Event) { 141 defer wg.Done() 142 if err := s.Write(block...); err != nil { 143 t.Fatalf("error writing event block: %v", err) 144 } 145 }(block...) 146 147 block = nil 148 } 149 } 150 151 wg.Wait() 152 checkClose(t, s) 153 154 ts.mu.Lock() 155 defer ts.mu.Unlock() 156 157 if len(ts.events) != 100 { 158 t.Fatalf("events not propagated: %d != %d", len(ts.events), 100) 159 } 160 } 161 162 type testSink struct { 163 events []Event 164 mu sync.Mutex 165 closed bool 166 } 167 168 func (ts *testSink) Write(events ...Event) error { 169 ts.mu.Lock() 170 defer ts.mu.Unlock() 171 ts.events = append(ts.events, events...) 172 return nil 173 } 174 175 func (ts *testSink) Close() error { 176 ts.mu.Lock() 177 defer ts.mu.Unlock() 178 ts.closed = true 179 180 logrus.Infof("closing testSink") 181 return nil 182 } 183 184 type delayedSink struct { 185 Sink 186 delay time.Duration 187 } 188 189 func (ds *delayedSink) Write(events ...Event) error { 190 time.Sleep(ds.delay) 191 return ds.Sink.Write(events...) 192 } 193 194 type flakySink struct { 195 Sink 196 rate float64 197 } 198 199 func (fs *flakySink) Write(events ...Event) error { 200 if rand.Float64() < fs.rate { 201 return fmt.Errorf("error writing %d events", len(events)) 202 } 203 204 return fs.Sink.Write(events...) 205 } 206 207 func checkClose(t *testing.T, sink Sink) { 208 if err := sink.Close(); err != nil { 209 t.Fatalf("unexpected error closing: %v", err) 210 } 211 212 // second close should not crash but should return an error. 213 if err := sink.Close(); err == nil { 214 t.Fatalf("no error on double close") 215 } 216 217 // Write after closed should be an error 218 if err := sink.Write([]Event{}...); err == nil { 219 t.Fatalf("write after closed did not have an error") 220 } else if err != ErrSinkClosed { 221 t.Fatalf("error should be ErrSinkClosed") 222 } 223 }