github.com/npaton/distribution@v2.3.1-rc.0+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  }