github.com/angenalZZZ/gofunc@v0.0.0-20210507121333-48ff1be3917b/http/longpoll/longpoll_test.go (about)

     1  package longpoll
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	"net/http"
     8  	"net/http/httptest"
     9  	"testing"
    10  	"time"
    11  )
    12  
    13  type closeNotifierRecorder struct {
    14  	httptest.ResponseRecorder
    15  	CloseNotifier chan bool
    16  }
    17  
    18  // As it turns out, httptest.ResponseRecorder (returned by httptest.NewRecorder)
    19  // does not support CloseNotify, so mock it to avoid panics about not supporting
    20  // the interface
    21  func (cnr *closeNotifierRecorder) CloseNotify() <-chan bool {
    22  	return cnr.CloseNotifier
    23  }
    24  
    25  func NewCloseNotifierRecorder() *closeNotifierRecorder {
    26  	return &closeNotifierRecorder{
    27  		httptest.ResponseRecorder{
    28  			HeaderMap: make(http.Header),
    29  			Body:      new(bytes.Buffer),
    30  			Code:      200,
    31  		},
    32  		make(chan bool, 1),
    33  	}
    34  }
    35  
    36  // DEPRECATED.  Use StartLongpoll or StartLongpoll instead.
    37  func createManager() (*LongpollManager, error) {
    38  	return StartLongpoll(Options{
    39  		LoggingEnabled:            true,
    40  		MaxLongpollTimeoutSeconds: 180,
    41  		MaxEventBufferSize:        250,
    42  		// Original behavior maintained, events are never deleted unless they're
    43  		// pushed out by max buffer size exceeded.
    44  		EventTimeToLiveSeconds:         FOREVER,
    45  		DeleteEventAfterFirstRetrieval: false,
    46  	})
    47  }
    48  
    49  // DEPRECATED.  Use StartLongpoll or StartLongpoll instead.
    50  func createCustomManager(maxTimeoutSeconds, eventBufferSize int,
    51  	loggingEnabled bool) (*LongpollManager, error) {
    52  	return StartLongpoll(Options{
    53  		LoggingEnabled:            loggingEnabled,
    54  		MaxLongpollTimeoutSeconds: maxTimeoutSeconds,
    55  		MaxEventBufferSize:        eventBufferSize,
    56  		// Original behavior maintained, events are never deleted unless they're
    57  		// pushed out by max buffer size exceeded.
    58  		EventTimeToLiveSeconds:         FOREVER,
    59  		DeleteEventAfterFirstRetrieval: false,
    60  	})
    61  }
    62  
    63  func Test_LongpollManager_CreateManager(t *testing.T) {
    64  	manager, err := createManager()
    65  	// Confirm the create call worked, and our manager has the expected values
    66  	if err != nil {
    67  		t.Errorf("Failed to create default LongpollManager.  Error was: %q", err)
    68  	}
    69  	// Channel size defaults to 100
    70  	if cap(manager.eventsIn) != 100 {
    71  		t.Errorf("Unexpected event channel capacity.  Expected: %d, got: %d",
    72  			100, cap(manager.eventsIn))
    73  	}
    74  	if cap(manager.subManager.clientSubscriptions) != 100 {
    75  		t.Errorf("Unexpected client subscription channel capacity.  Expected: %d, got: %d",
    76  			100, cap(manager.subManager.clientSubscriptions))
    77  	}
    78  	if cap(manager.subManager.ClientTimeouts) != 100 {
    79  		t.Errorf("Unexpected client timeout channel capacity.  Expected: %d, got: %d",
    80  			100, cap(manager.subManager.ClientTimeouts))
    81  	}
    82  	// Max event buffer size defaults to 250
    83  	if manager.subManager.MaxEventBufferSize != 250 {
    84  		t.Errorf("Unexpected client timeout channel capacity.  Expected: %d, got: %d",
    85  			250, manager.subManager.MaxEventBufferSize)
    86  	}
    87  	// Don't forget to kill subscription manager's running goroutine
    88  	manager.Shutdown()
    89  }
    90  
    91  func Test_LongpollManager_CreateCustomManager(t *testing.T) {
    92  	manager, err := createCustomManager(360, 700, true)
    93  	// Confirm the create call worked, and our manager has the expected values
    94  	if err != nil {
    95  		t.Errorf("Failed to create default LongpollManager.  Error was: %q", err)
    96  	}
    97  	// Channel size defaults to 100
    98  	if cap(manager.eventsIn) != 100 {
    99  		t.Errorf("Unexpected event channel capacity.  Expected: %d, got: %d",
   100  			100, cap(manager.eventsIn))
   101  	}
   102  	if cap(manager.subManager.clientSubscriptions) != 100 {
   103  		t.Errorf("Unexpected client subscription channel capacity.  Expected: %d, got: %d",
   104  			100, cap(manager.subManager.clientSubscriptions))
   105  	}
   106  	if cap(manager.subManager.ClientTimeouts) != 100 {
   107  		t.Errorf("Unexpected client timeout channel capacity.  Expected: %d, got: %d",
   108  			100, cap(manager.subManager.ClientTimeouts))
   109  	}
   110  	// Max event buffer size set  to 700
   111  	if manager.subManager.MaxEventBufferSize != 700 {
   112  		t.Errorf("Unexpected client timeout channel capacity.  Expected: %d, got: %d",
   113  			700, manager.subManager.MaxEventBufferSize)
   114  	}
   115  	// Don't forget to kill subscription manager's running goroutine
   116  	manager.Shutdown()
   117  }
   118  
   119  func Test_LongpollManager_CreateCustomManager_InvalidArgs(t *testing.T) {
   120  	manager, err := createCustomManager(360, -1, false) // buffer size == -1
   121  	if err == nil {
   122  		t.Errorf("Expected error when creating custom manager with invalid event buffer size ")
   123  	}
   124  	if manager != nil {
   125  		t.Errorf("Expected nil response for manager when create call returned error.")
   126  	}
   127  	manager, err = createCustomManager(-1, 200, false) // timeout == -1
   128  	if err == nil {
   129  		t.Errorf("Expected error when creating custom manager with invalid timeout.")
   130  	}
   131  	if manager != nil {
   132  		t.Errorf("Expected nil response for manager when create call returned error.")
   133  	}
   134  }
   135  
   136  func Test_LongpollManager_Publish(t *testing.T) {
   137  	manager, err := createManager()
   138  	// Confirm the create call worked, and our manager has the expected values
   139  	if err != nil {
   140  		t.Errorf("Failed to create default LongpollManager.  Error was: %q", err)
   141  	}
   142  	if len(manager.eventsIn) != 0 {
   143  		t.Errorf("Expected event channel to be initially empty. Instead len: %d",
   144  			len(manager.eventsIn))
   145  	}
   146  	if len(manager.subManager.SubEventBuffer) != 0 {
   147  		t.Errorf("Expected sub manager's event map to be initially empty. Instead len: %d",
   148  			len(manager.subManager.SubEventBuffer))
   149  	}
   150  	err = manager.Publish("fruits", "apple")
   151  	if err != nil {
   152  		t.Errorf("Unexpected error publishing event: %q", err)
   153  	}
   154  	// SubscriptionManager's goroutine should not have picked up event yet:
   155  	// I *think* this should always work because we don't typically yield
   156  	// execution to other goroutines until an end of a func reached,
   157  	// or a channel/network/sleep call is invoked.
   158  	if len(manager.eventsIn) != 1 {
   159  		t.Errorf("Expected event channel to have 1 event. Instead len: %d",
   160  			len(manager.eventsIn))
   161  	}
   162  	// Allow sub manager's goroutine time to pull from channel.
   163  	// This sleep should cause us to yield and let the other goroutine run
   164  	time.Sleep(50 * time.Millisecond)
   165  	if len(manager.eventsIn) != 0 {
   166  		t.Errorf("Expected event channel to have 0 events. Instead len: %d",
   167  			len(manager.eventsIn))
   168  	}
   169  	// Confirm event wound up in the sub manager's internal map:
   170  	if len(manager.subManager.SubEventBuffer) != 1 {
   171  		t.Errorf("Expected sub manager's event map to have 1 item. Instead len: %d",
   172  			len(manager.subManager.SubEventBuffer))
   173  	}
   174  	buf, found := manager.subManager.SubEventBuffer["fruits"]
   175  	if !found {
   176  		t.Errorf("Failed to find event in sub manager's category-to-eventBuffer map")
   177  	}
   178  	// Double check that the expected max buffer size and capacity were set
   179  	if buf.eventBuffer_ptr.MaxBufferSize != 250 {
   180  		t.Errorf("Expected max buffer size of %d, but got %d.", 250, buf.eventBuffer_ptr.MaxBufferSize)
   181  	}
   182  	if buf.eventBuffer_ptr.List.Len() != 1 {
   183  		t.Errorf("Expected buffer to be 1 item. instead: %d", buf.eventBuffer_ptr.List.Len())
   184  	}
   185  	if buf.eventBuffer_ptr.Front().Value.(*lpEvent).Data != "apple" {
   186  		t.Errorf("Expected event data to be %q, but got %q", "apple",
   187  			buf.eventBuffer_ptr.Front().Value.(*lpEvent).Data)
   188  	}
   189  
   190  	// Publish two more events
   191  	err = manager.Publish("veggies", "potato")
   192  	if err != nil {
   193  		t.Errorf("Unexpected error publishing event: %q", err)
   194  	}
   195  	err = manager.Publish("fruits", "orange")
   196  	if err != nil {
   197  		t.Errorf("Unexpected error publishing event: %q", err)
   198  	}
   199  	// Allow other goroutine a chance to do a channel read
   200  	time.Sleep(50 * time.Millisecond)
   201  
   202  	if len(manager.eventsIn) != 0 {
   203  		t.Errorf("Expected event channel to have 0 events. Instead len: %d",
   204  			len(manager.eventsIn))
   205  	}
   206  	if len(manager.subManager.SubEventBuffer) != 2 {
   207  		t.Errorf("Expected sub manager's event map to have 2 item. Instead len: %d",
   208  			len(manager.subManager.SubEventBuffer))
   209  	}
   210  	buf, found = manager.subManager.SubEventBuffer["fruits"]
   211  	if !found {
   212  		t.Errorf("Failed to find event in sub manager's category-to-eventBuffer map")
   213  	}
   214  	if buf.eventBuffer_ptr.List.Len() != 2 {
   215  		t.Errorf("Expected buffer to be 2 items. instead: %d", buf.eventBuffer_ptr.List.Len())
   216  	}
   217  	if buf.eventBuffer_ptr.Front().Value.(*lpEvent).Data != "orange" {
   218  		t.Errorf("Expected event data to be %q, but got %q", "orange",
   219  			buf.eventBuffer_ptr.Front().Value.(*lpEvent).Data)
   220  	}
   221  	buf, found = manager.subManager.SubEventBuffer["veggies"]
   222  	if !found {
   223  		t.Errorf("Failed to find event in sub manager's category-to-eventBuffer map")
   224  	}
   225  	if buf.eventBuffer_ptr.List.Len() != 1 {
   226  		t.Errorf("Expected buffer to be 1 item. instead: %d", buf.eventBuffer_ptr.List.Len())
   227  	}
   228  	if buf.eventBuffer_ptr.Front().Value.(*lpEvent).Data != "potato" {
   229  		t.Errorf("Expected event data to be %q, but got %q", "potato",
   230  			buf.eventBuffer_ptr.Front().Value.(*lpEvent).Data)
   231  	}
   232  	// Don't forget to kill subscription manager's running goroutine
   233  	manager.Shutdown()
   234  }
   235  
   236  func Test_LongpollManager_Publish_MaxBufferSize(t *testing.T) {
   237  	manager, err := createCustomManager(120, 2, true) // max buffer size 3
   238  	if len(manager.subManager.SubEventBuffer) != 0 {
   239  		t.Errorf("Expected sub manager's event map to be initially empty. Instead len: %d",
   240  			len(manager.subManager.SubEventBuffer))
   241  	}
   242  	err = manager.Publish("fruits", "apple")
   243  	if err != nil {
   244  		t.Errorf("Unexpected error publishing event: %q", err)
   245  	}
   246  	err = manager.Publish("fruits", "banana")
   247  	if err != nil {
   248  		t.Errorf("Unexpected error publishing event: %q", err)
   249  	}
   250  	// yield so other goroutine can do channel reads
   251  	time.Sleep(50 * time.Millisecond)
   252  	if len(manager.eventsIn) != 0 {
   253  		t.Errorf("Expected event channel to have 0 events. Instead len: %d",
   254  			len(manager.eventsIn))
   255  	}
   256  	// Confirm events wound up in the sub manager's internal map:
   257  	// NOTE: only one map entry because both events the same category
   258  	if len(manager.subManager.SubEventBuffer) != 1 {
   259  		t.Errorf("Expected sub manager's event map to have 1 item. Instead len: %d",
   260  			len(manager.subManager.SubEventBuffer))
   261  	}
   262  	buf, found := manager.subManager.SubEventBuffer["fruits"]
   263  	if !found {
   264  		t.Errorf("Failed to find event in sub manager's category-to-eventBuffer map")
   265  	}
   266  	// Double check that the expected max buffer size and capacity were set
   267  	if buf.eventBuffer_ptr.MaxBufferSize != 2 {
   268  		t.Errorf("Expected max buffer size of %d, but got %d.", 2, buf.eventBuffer_ptr.MaxBufferSize)
   269  	}
   270  	if buf.eventBuffer_ptr.List.Len() != 2 {
   271  		t.Errorf("Expected buffer to be 2 items. instead: %d", buf.eventBuffer_ptr.List.Len())
   272  	}
   273  	if buf.eventBuffer_ptr.Front().Value.(*lpEvent).Data != "banana" {
   274  		t.Errorf("Expected event data to be %q, but got %q", "banana",
   275  			buf.eventBuffer_ptr.Front().Value.(*lpEvent).Data)
   276  	}
   277  	if buf.eventBuffer_ptr.Back().Value.(*lpEvent).Data != "apple" {
   278  		t.Errorf("Expected event data to be %q, but got %q", "apple",
   279  			buf.eventBuffer_ptr.Front().Value.(*lpEvent).Data)
   280  	}
   281  
   282  	// Now try and publish another event on the same fruit category,
   283  	// confirm that it works, but the oldest fruit is no longer in buffer
   284  	err = manager.Publish("fruits", "pear")
   285  	if err != nil {
   286  		t.Errorf("Unexpected error publishing event: %q", err)
   287  	}
   288  	// yield so other goroutine can do channel reads
   289  	time.Sleep(50 * time.Millisecond)
   290  	buf, found = manager.subManager.SubEventBuffer["fruits"]
   291  	if !found {
   292  		t.Errorf("Failed to find event in sub manager's category-to-eventBuffer map")
   293  	}
   294  	// Double check that the expected max buffer size and capacity were set
   295  	if buf.eventBuffer_ptr.MaxBufferSize != 2 {
   296  		t.Errorf("Expected max buffer size of %d, but got %d.", 2, buf.eventBuffer_ptr.MaxBufferSize)
   297  	}
   298  	if buf.eventBuffer_ptr.List.Len() != 2 {
   299  		t.Errorf("Expected buffer to be 2 items. instead: %d", buf.eventBuffer_ptr.List.Len())
   300  	}
   301  	if buf.eventBuffer_ptr.Front().Value.(*lpEvent).Data != "pear" {
   302  		t.Errorf("Expected event data to be %q, but got %q", "banana",
   303  			buf.eventBuffer_ptr.Front().Value.(*lpEvent).Data)
   304  	}
   305  	if buf.eventBuffer_ptr.Back().Value.(*lpEvent).Data != "banana" {
   306  		t.Errorf("Expected event data to be %q, but got %q", "apple",
   307  			buf.eventBuffer_ptr.Front().Value.(*lpEvent).Data)
   308  	}
   309  
   310  	// Now confirm publishing on a different category still works
   311  	err = manager.Publish("veggies", "potato")
   312  	if err != nil {
   313  		t.Errorf("Unexpected error publishing event: %q", err)
   314  	}
   315  	// yield so other goroutine can do channel reads
   316  	time.Sleep(50 * time.Millisecond)
   317  	buf, found = manager.subManager.SubEventBuffer["veggies"]
   318  	if !found {
   319  		t.Errorf("Failed to find event in sub manager's category-to-eventBuffer map")
   320  	}
   321  	if buf.eventBuffer_ptr.List.Len() != 1 {
   322  		t.Errorf("Expected buffer to be 1 item. instead: %d", buf.eventBuffer_ptr.List.Len())
   323  	}
   324  	if buf.eventBuffer_ptr.Front().Value.(*lpEvent).Data != "potato" {
   325  		t.Errorf("Expected event data to be %q, but got %q", "potato",
   326  			buf.eventBuffer_ptr.Front().Value.(*lpEvent).Data)
   327  	}
   328  	// Don't forget to kill subscription manager's running goroutine
   329  	manager.Shutdown()
   330  }
   331  
   332  func Test_LongpollManager_Publish_InvalidArgs(t *testing.T) {
   333  	manager, err := createManager()
   334  	// Confirm the create call worked, and our manager has the expected values
   335  	if err != nil {
   336  		t.Errorf("Failed to create default LongpollManager.  Error was: %q", err)
   337  	}
   338  	// You must provide a category:
   339  	err = manager.Publish("", "apple")
   340  	if err == nil {
   341  		t.Errorf("Expected calls to Publish with blank category would fail.")
   342  	}
   343  	// category can't be longer than 1024:
   344  	// So 1024 len category should work:
   345  	tooLong := ""
   346  	for i := 0; i < 1024; i++ {
   347  		tooLong += "a"
   348  	}
   349  	err = manager.Publish(tooLong, "apple")
   350  	if err != nil {
   351  		t.Errorf("Expected calls to Publish with 1024 len category not to fail, but got: %q.", err)
   352  	}
   353  	// But now that we're at 1025, we're boned.
   354  	tooLong += "a"
   355  	err = manager.Publish(tooLong, "apple")
   356  	if err == nil {
   357  		t.Errorf("Expected calls to Publish with blank category would fail.")
   358  	}
   359  }
   360  
   361  func Test_LongpollManager_Shutdown(t *testing.T) {
   362  	manager, err := createManager()
   363  	if err != nil {
   364  		t.Errorf("Failed to create default LongpollManager.  Error was: %q", err)
   365  	}
   366  	manager.Shutdown()
   367  	// Confirm the shutdown signal channel was closed (this is now the
   368  	// goroutines are notified to quit)
   369  	select {
   370  	case _, isOpen := <-manager.subManager.Quit:
   371  		if isOpen {
   372  			t.Errorf("Expected channel to be closed, instead it was still open")
   373  		}
   374  	default:
   375  		t.Errorf("Expected channel close, instead got no activity.")
   376  	}
   377  }
   378  
   379  func Test_LongpollManager_newclientSubscription(t *testing.T) {
   380  	subTime := time.Date(2015, 11, 7, 11, 33, 4, 0, time.UTC)
   381  	sub, err := newclientSubscription("colors", subTime)
   382  	if err != nil {
   383  		t.Errorf("Unexpected error when creating new client subscription: %q", err)
   384  	}
   385  	if sub.clientCategoryPair.SubscriptionCategory != "colors" {
   386  		t.Errorf("Unexpected sub category, expected: %q. got: %q", "colors",
   387  			sub.clientCategoryPair.SubscriptionCategory)
   388  	}
   389  	if sub.LastEventTime != subTime {
   390  		t.Errorf("Unexpected sub last event time, expected: %q. got: %q", subTime,
   391  			sub.LastEventTime)
   392  	}
   393  	if cap(sub.Events) != 1 {
   394  		t.Errorf("Unexpected event channel capacity. expected: %q. got: %q", 1,
   395  			cap(sub.Events))
   396  	}
   397  }
   398  
   399  func ajaxHandler(handlerFunc func(w http.ResponseWriter, r *http.Request)) http.Handler {
   400  	return http.HandlerFunc(handlerFunc)
   401  }
   402  
   403  func Test_LongpollManager_WebClient_InvalidRequests(t *testing.T) {
   404  	manager, _ := createCustomManager(120, 100, true)
   405  	subscriptionHandler := ajaxHandler(manager.SubscriptionHandler)
   406  
   407  	// Empty request, this is going to result in an JSON error object:
   408  	req, _ := http.NewRequest("GET", "", nil)
   409  	w := httptest.NewRecorder()
   410  	subscriptionHandler.ServeHTTP(w, req)
   411  	if w.Code != http.StatusOK {
   412  		t.Errorf("SubscriptionHandler didn't return %v", http.StatusOK)
   413  	}
   414  	// Also note how it says "1-120", so our custom timeout arg of 120 was
   415  	// used
   416  	if w.Body.String() != "{\"error\": \"Invalid timeout arg.  Must be 1-120.\"}" {
   417  		t.Errorf("Unexpected response: %q", w.Body.String())
   418  	}
   419  
   420  	// Invalid timeout, not a number
   421  	req, _ = http.NewRequest("GET", "?timeout=adf&category=veggies", nil)
   422  	w = httptest.NewRecorder()
   423  	subscriptionHandler.ServeHTTP(w, req)
   424  	if w.Code != http.StatusOK {
   425  		t.Errorf("SubscriptionHandler didn't return %v", http.StatusOK)
   426  	}
   427  	if w.Body.String() != "{\"error\": \"Invalid timeout arg.  Must be 1-120.\"}" {
   428  		t.Errorf("Unexpected response: %q", w.Body.String())
   429  	}
   430  
   431  	// Invalid timeout, too small
   432  	req, _ = http.NewRequest("GET", "?timeout=0&category=veggies", nil)
   433  	w = httptest.NewRecorder()
   434  	subscriptionHandler.ServeHTTP(w, req)
   435  	if w.Code != http.StatusOK {
   436  		t.Errorf("SubscriptionHandler didn't return %v", http.StatusOK)
   437  	}
   438  	if w.Body.String() != "{\"error\": \"Invalid timeout arg.  Must be 1-120.\"}" {
   439  		t.Errorf("Unexpected response: %q", w.Body.String())
   440  	}
   441  
   442  	// Invalid timeout, too big
   443  	req, _ = http.NewRequest("GET", "?timeout=121&category=veggies", nil)
   444  	w = httptest.NewRecorder()
   445  	subscriptionHandler.ServeHTTP(w, req)
   446  	if w.Code != http.StatusOK {
   447  		t.Errorf("SubscriptionHandler didn't return %v", http.StatusOK)
   448  	}
   449  	if w.Body.String() != "{\"error\": \"Invalid timeout arg.  Must be 1-120.\"}" {
   450  		t.Errorf("Unexpected response: %q", w.Body.String())
   451  	}
   452  
   453  	// Valid timeout, but missing category:
   454  	req, _ = http.NewRequest("GET", "?timeout=30", nil)
   455  	w = httptest.NewRecorder()
   456  	subscriptionHandler.ServeHTTP(w, req)
   457  	if w.Code != http.StatusOK {
   458  		t.Errorf("SubscriptionHandler didn't return %v", http.StatusOK)
   459  	}
   460  	if w.Body.String() != "{\"error\": \"Invalid subscription category, must be 1-1024 characters long.\"}" {
   461  		t.Errorf("Unexpected response: %q", w.Body.String())
   462  	}
   463  
   464  	// Valid timeout, but category is too small
   465  	req, _ = http.NewRequest("GET", "?timeout=30&category=", nil)
   466  	w = httptest.NewRecorder()
   467  	subscriptionHandler.ServeHTTP(w, req)
   468  	if w.Code != http.StatusOK {
   469  		t.Errorf("SubscriptionHandler didn't return %v", http.StatusOK)
   470  	}
   471  	if w.Body.String() != "{\"error\": \"Invalid subscription category, must be 1-1024 characters long.\"}" {
   472  		t.Errorf("Unexpected response: %q", w.Body.String())
   473  	}
   474  
   475  	// Valid timeout, but category too long
   476  	tooLong := ""
   477  	for i := 0; i < 1025; i++ {
   478  		tooLong += "a"
   479  	} // 1025 chars long
   480  	req, _ = http.NewRequest("GET", "?timeout=30&category="+tooLong, nil)
   481  	w = httptest.NewRecorder()
   482  	subscriptionHandler.ServeHTTP(w, req)
   483  	if w.Code != http.StatusOK {
   484  		t.Errorf("SubscriptionHandler didn't return %v", http.StatusOK)
   485  	}
   486  	if w.Body.String() != "{\"error\": \"Invalid subscription category, must be 1-1024 characters long.\"}" {
   487  		t.Errorf("Unexpected response: %q", w.Body.String())
   488  	}
   489  
   490  	// Valid timeout, valid category, but invalid since_time
   491  	req, _ = http.NewRequest("GET", "?timeout=30&category=foobar&since_time=asdf", nil)
   492  	w = httptest.NewRecorder()
   493  	subscriptionHandler.ServeHTTP(w, req)
   494  	if w.Code != http.StatusOK {
   495  		t.Errorf("SubscriptionHandler didn't return %v", http.StatusOK)
   496  	}
   497  	if w.Body.String() != "{\"error\": \"Invalid last_event_time arg.\"}" {
   498  		t.Errorf("Unexpected response: %q", w.Body.String())
   499  	}
   500  
   501  	// Don't forget to kill our pubsub manager's run goroutine
   502  	manager.Shutdown()
   503  }
   504  
   505  func Test_LongpollManager_WebClient_NoEventsSoTimeout(t *testing.T) {
   506  	manager, _ := createCustomManager(120, 100, true)
   507  	subscriptionHandler := ajaxHandler(manager.SubscriptionHandler)
   508  
   509  	// Valid request, but we don't have any events published,
   510  	// so this will wait 2 seconds (because timeout param = 2)
   511  	// and then come back wtih a timeout response
   512  	req, _ := http.NewRequest("GET", "?timeout=2&category=veggies", nil)
   513  	w := NewCloseNotifierRecorder()
   514  	subscriptionHandler.ServeHTTP(w, req)
   515  	if w.Code != http.StatusOK {
   516  		t.Errorf("SubscriptionHandler didn't return %v", http.StatusOK)
   517  	}
   518  	approxTimeoutTime := time.Now().UnixNano() / int64(time.Millisecond)
   519  	var timeoutResp timeoutResponse
   520  	if err := json.Unmarshal(w.Body.Bytes(), &timeoutResp); err != nil {
   521  		t.Errorf("Failed to decode json: %q", err)
   522  	}
   523  	if timeoutResp.TimeoutMessage != "no events before timeout" {
   524  		t.Errorf("Unexpected timeout message: %q", timeoutResp.TimeoutMessage)
   525  	}
   526  	if timeoutResp.Timestamp < (approxTimeoutTime-100) ||
   527  		timeoutResp.Timestamp > approxTimeoutTime {
   528  		t.Errorf("Unexpected timeout timestamp.  Expected: %q, got: %q",
   529  			approxTimeoutTime, timeoutResp.Timestamp)
   530  	}
   531  
   532  	// Don't forget to kill our pubsub manager's run goroutine
   533  	manager.Shutdown()
   534  }
   535  
   536  func Test_LongpollManager_WebClient_Disconnect_RemoveClientSub(t *testing.T) {
   537  	manager, _ := createCustomManager(120, 100, true)
   538  	subscriptionHandler := ajaxHandler(manager.SubscriptionHandler)
   539  	if _, found := manager.subManager.ClientSubChannels["veggies"]; found {
   540  		t.Errorf("Expected client sub channel not to exist yet ")
   541  	}
   542  	// This request has timeout of 5 seconds, but we're going to simulate a
   543  	// disconnect in 2 seconds which is earlier.
   544  	req, _ := http.NewRequest("GET", "?timeout=5&category=veggies", nil)
   545  	w := NewCloseNotifierRecorder()
   546  	// As of go 1.7, calls to t.Error, t.Fatal, after a test exits causes a panic.
   547  	// Before, these were suppressed.  So as it turns out, this test's goroutine
   548  	// that spawns here was outliving the test.  Now let's make the test body
   549  	// explicitly wait for it.
   550  	// see thread here: https://github.com/golang/go/issues/15976
   551  	goroutine_done := make(chan bool)
   552  	go func() {
   553  		time.Sleep(time.Duration(250) * time.Millisecond)
   554  		// confirm subscription entry exists, with one client
   555  		if _, found := manager.subManager.ClientSubChannels["veggies"]; !found {
   556  			t.Errorf("Expected client sub channel to exist")
   557  		}
   558  		if val, _ := manager.subManager.ClientSubChannels["veggies"]; len(val) != 1 {
   559  			t.Errorf("Expected sub channel to have one client subscribed")
   560  		}
   561  		time.Sleep(time.Duration(1) * time.Second)
   562  		w.CloseNotifier <- true
   563  		time.Sleep(time.Duration(1) * time.Second)
   564  		// Confirm that the subscription entry no longer exists.
   565  		// Before, this test asserted that the entry ('veggie' key in the map)
   566  		// existed, but the value listed no clients.  But code was changed to auto
   567  		// remove subscription keys in this map if there are no clients listening.
   568  		// Since these asserts were erroneously firing after the test body exited
   569  		// (and this is undefined behavior and pre go 1.7 it's simply ignored),
   570  		// this bad assertion was never failing when it should have.
   571  		// Once the test body was forced to wait for it's spawned goroutine to exit,
   572  		// the old assertion started failing.
   573  		// This test is now updated to assert that the key "veggies" no longer
   574  		// exists since we're explicitly removing map entries when the value is
   575  		// an empty container.
   576  		if _, found := manager.subManager.ClientSubChannels["veggies"]; found {
   577  			t.Errorf("Expected client sub channel to be auto removed when 0 clients.")
   578  		}
   579  		goroutine_done <- true
   580  	}()
   581  
   582  	subscriptionHandler.ServeHTTP(w, req)
   583  
   584  	// causes test body to block until the above spawned goroutine finishes.
   585  	// Otherwise this will panic in go 1.7 and later :)
   586  	<-goroutine_done
   587  
   588  	// Don't forget to kill our pubsub manager's run goroutine
   589  	manager.Shutdown()
   590  }
   591  
   592  func Test_LongpollManager_WebClient_Disconnect_TerminateHttp(t *testing.T) {
   593  	manager, _ := createCustomManager(120, 100, true)
   594  	testChannel := make(chan int, 2)
   595  	webValue := 7
   596  	goroutineValue := 13
   597  	subscriptionHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   598  		manager.SubscriptionHandler(w, r)
   599  		testChannel <- webValue
   600  	})
   601  	if _, found := manager.subManager.ClientSubChannels["veggies"]; found {
   602  		t.Errorf("Expected client sub channel not to exist yet ")
   603  	}
   604  	// This request has timeout of 5 seconds, but we're going to simulate a
   605  	// disconnect in 1 second which is earlier.
   606  	// If the wrapping subscription handler gets to the end, it will publish
   607  	// the number 7 on our test channel. Have a goroutine publish a different
   608  	// value at some time after the disconnect, but before the timeout
   609  	// and confirm that the disconnect forced the subscription handler
   610  	// to return early and thus we get the expected published value.
   611  	req, _ := http.NewRequest("GET", "?timeout=5&category=veggies", nil)
   612  	w := NewCloseNotifierRecorder()
   613  
   614  	go func() {
   615  		time.Sleep(time.Duration(3) * time.Second)
   616  		testChannel <- goroutineValue
   617  	}()
   618  
   619  	go func() {
   620  		time.Sleep(time.Duration(250) * time.Millisecond)
   621  		// confirm subscription entry exists, with one client
   622  		if _, found := manager.subManager.ClientSubChannels["veggies"]; !found {
   623  			t.Errorf("Expected client sub channel to exist")
   624  		}
   625  		if val, _ := manager.subManager.ClientSubChannels["veggies"]; len(val) != 1 {
   626  			t.Errorf("Expected sub channel to have one client subscribed")
   627  		}
   628  		time.Sleep(time.Duration(1) * time.Second)
   629  		w.CloseNotifier <- true
   630  		time.Sleep(time.Duration(2) * time.Second)
   631  		// Confirm that our test channel has the value from our web handler and
   632  		// not from our goroutine
   633  		select {
   634  		case val := <-testChannel:
   635  			if val != webValue {
   636  				t.Errorf("Expected to get channel send from http handler before the goroutine.")
   637  			}
   638  		}
   639  	}()
   640  
   641  	subscriptionHandler.ServeHTTP(w, req)
   642  	// Don't forget to kill our pubsub manager's run goroutine
   643  	manager.Shutdown()
   644  }
   645  
   646  func Test_LongpollManager_WebClient_HasEvents(t *testing.T) {
   647  	manager, _ := createCustomManager(120, 100, true)
   648  	subscriptionHandler := ajaxHandler(manager.SubscriptionHandler)
   649  
   650  	// Valid request, but we don't have any events published,
   651  	// so this will wait for a publish or timeout (in this case we'll get
   652  	// something)
   653  	req, _ := http.NewRequest("GET", "?timeout=30&category=veggies", nil)
   654  	w := NewCloseNotifierRecorder()
   655  	// Publish two events, only the second is for our subscription category
   656  	// Note how these events occur after the client subscribed
   657  	// if they occurred before, since we don't provide a since_time url param
   658  	// we'd default to now and skip those events.
   659  	startTime := time.Now()
   660  	go func() {
   661  		time.Sleep(1500 * time.Millisecond)
   662  		manager.Publish("fruits", "peach")
   663  		time.Sleep(1000 * time.Millisecond)
   664  		manager.Publish("veggies", "corn")
   665  	}()
   666  	subscriptionHandler.ServeHTTP(w, req)
   667  
   668  	// Confirm we got the correct event
   669  	if w.Code != http.StatusOK {
   670  		t.Errorf("SubscriptionHandler didn't return %v", http.StatusOK)
   671  	}
   672  	var eventResponse eventResponse
   673  	if err := json.Unmarshal(w.Body.Bytes(), &eventResponse); err != nil {
   674  		t.Errorf("Failed to decode json: %q", err)
   675  	}
   676  	if len(*eventResponse.Events) != 1 {
   677  		t.Errorf("Unexpected number of events.  Expected: %d, got: %d", 1, len(*eventResponse.Events))
   678  	}
   679  	if (*eventResponse.Events)[0].Category != "veggies" {
   680  		t.Errorf("Unexpected category.  Expected: %q, got: %q", "veggies", (*eventResponse.Events)[0].Category)
   681  	}
   682  	if (*eventResponse.Events)[0].Data != "corn" {
   683  		t.Errorf("Unexpected data.  Expected: %q, got: %q", "corn", (*eventResponse.Events)[0].Data)
   684  	}
   685  
   686  	// Make a new subscription request.
   687  	// Note how since there's no since_time url param, we default to now,
   688  	// and thus don't see the previous event from our last http request
   689  	req, _ = http.NewRequest("GET", "?timeout=2&category=veggies", nil)
   690  	w = NewCloseNotifierRecorder()
   691  	subscriptionHandler.ServeHTTP(w, req)
   692  	if w.Code != http.StatusOK {
   693  		t.Errorf("SubscriptionHandler didn't return %v", http.StatusOK)
   694  	}
   695  	approxTimeoutTime := time.Now().UnixNano() / int64(time.Millisecond)
   696  	var timeoutResp timeoutResponse
   697  	if err := json.Unmarshal(w.Body.Bytes(), &timeoutResp); err != nil {
   698  		t.Errorf("Failed to decode json: %q", err)
   699  	}
   700  	if timeoutResp.TimeoutMessage != "no events before timeout" {
   701  		t.Errorf("Unexpected timeout message: %q", timeoutResp.TimeoutMessage)
   702  	}
   703  	if timeoutResp.Timestamp < (approxTimeoutTime-100) ||
   704  		timeoutResp.Timestamp > approxTimeoutTime {
   705  		t.Errorf("Unexpected timeout timestamp.  Expected: %q, got: %q",
   706  			approxTimeoutTime, timeoutResp.Timestamp)
   707  	}
   708  
   709  	// Now ask for events since the start of our test, which will include
   710  	// our previously seen event
   711  	req, _ = http.NewRequest("GET", fmt.Sprintf("?timeout=2&category=veggies&since_time=%d",
   712  		startTime.UnixNano()/int64(time.Millisecond)), nil)
   713  	w = NewCloseNotifierRecorder()
   714  	subscriptionHandler.ServeHTTP(w, req)
   715  	if w.Code != http.StatusOK {
   716  		t.Errorf("SubscriptionHandler didn't return %v", http.StatusOK)
   717  	}
   718  	if err := json.Unmarshal(w.Body.Bytes(), &eventResponse); err != nil {
   719  		t.Errorf("Failed to decode json: %q", err)
   720  	}
   721  	if len(*eventResponse.Events) != 1 {
   722  		t.Errorf("Unexpected number of events.  Expected: %d, got: %d", 1, len(*eventResponse.Events))
   723  	}
   724  	if (*eventResponse.Events)[0].Category != "veggies" {
   725  		t.Errorf("Unexpected category.  Expected: %q, got: %q", "veggies", (*eventResponse.Events)[0].Category)
   726  	}
   727  	if (*eventResponse.Events)[0].Data != "corn" {
   728  		t.Errorf("Unexpected data.  Expected: %q, got: %q", "corn", (*eventResponse.Events)[0].Data)
   729  	}
   730  
   731  	firstEventTime := (*eventResponse.Events)[0].Timestamp
   732  	manager.Publish("veggies", "carrot")
   733  	time.Sleep(50 * time.Millisecond) // allow yield for goroutine channel reads
   734  
   735  	// Now ask for any events since our first one, and confirm we get the second
   736  	// 'veggie' category event
   737  	req, _ = http.NewRequest("GET", fmt.Sprintf("?timeout=2&category=veggies&since_time=%d", firstEventTime), nil)
   738  	w = NewCloseNotifierRecorder()
   739  	subscriptionHandler.ServeHTTP(w, req)
   740  	if w.Code != http.StatusOK {
   741  		t.Errorf("SubscriptionHandler didn't return %v", http.StatusOK)
   742  	}
   743  	if err := json.Unmarshal(w.Body.Bytes(), &eventResponse); err != nil {
   744  		t.Errorf("Failed to decode json: %q", err)
   745  	}
   746  	if len(*eventResponse.Events) != 1 {
   747  		t.Errorf("Unexpected number of events.  Expected: %d, got: %d", 1, len(*eventResponse.Events))
   748  	}
   749  	if (*eventResponse.Events)[0].Category != "veggies" {
   750  		t.Errorf("Unexpected category.  Expected: %q, got: %q", "veggies", (*eventResponse.Events)[0].Category)
   751  	}
   752  	if (*eventResponse.Events)[0].Data != "carrot" {
   753  		t.Errorf("Unexpected data.  Expected: %q, got: %q", "carrot", (*eventResponse.Events)[0].Data)
   754  	}
   755  
   756  	// Confirm we get both events when asking for any events since start of test run
   757  	req, _ = http.NewRequest("GET", fmt.Sprintf("?timeout=2&category=veggies&since_time=%d",
   758  		startTime.UnixNano()/int64(time.Millisecond)), nil)
   759  	w = NewCloseNotifierRecorder()
   760  	subscriptionHandler.ServeHTTP(w, req)
   761  	if w.Code != http.StatusOK {
   762  		t.Errorf("SubscriptionHandler didn't return %v", http.StatusOK)
   763  	}
   764  	if err := json.Unmarshal(w.Body.Bytes(), &eventResponse); err != nil {
   765  		t.Errorf("Failed to decode json: %q", err)
   766  	}
   767  	if len(*eventResponse.Events) != 2 {
   768  		t.Errorf("Unexpected number of events.  Expected: %d, got: %d", 2, len(*eventResponse.Events))
   769  	}
   770  	if (*eventResponse.Events)[0].Data != "corn" {
   771  		t.Errorf("Unexpected data.  Expected: %q, got: %q", "corn", (*eventResponse.Events)[0].Data)
   772  	}
   773  	if (*eventResponse.Events)[1].Data != "carrot" {
   774  		t.Errorf("Unexpected data.  Expected: %q, got: %q", "carrot", (*eventResponse.Events)[0].Data)
   775  	}
   776  
   777  	// Don't forget to kill our pubsub manager's run goroutine
   778  	manager.Shutdown()
   779  }
   780  
   781  func Test_LongpollManager_WebClient_HasBufferedEvents(t *testing.T) {
   782  	// Test behavior where clients can see events that happened before
   783  	// they started their longpoll by accessing events in the
   784  	// subscriptionManager's eventBuffer containers.
   785  	// Of course, clients only see this if they request events with a
   786  	// 'since_time' argument of a time earlier than the events occurred.
   787  	manager, _ := createCustomManager(120, 100, true)
   788  	subscriptionHandler := ajaxHandler(manager.SubscriptionHandler)
   789  
   790  	startTime := time.Now()
   791  	time.Sleep(500 * time.Millisecond)
   792  	manager.Publish("veggies", "broccoli")
   793  	time.Sleep(500 * time.Millisecond)
   794  	manager.Publish("veggies", "corn")
   795  	time.Sleep(500 * time.Millisecond)
   796  
   797  	// This request clearly takes place after the two events were published.
   798  	// But we ask for any events since the start of this test case
   799  	req, _ := http.NewRequest("GET", fmt.Sprintf("?timeout=2&category=veggies&since_time=%d",
   800  		startTime.UnixNano()/int64(time.Millisecond)), nil)
   801  	w := NewCloseNotifierRecorder()
   802  	subscriptionHandler.ServeHTTP(w, req)
   803  
   804  	// Confirm we got the correct event
   805  	if w.Code != http.StatusOK {
   806  		t.Errorf("SubscriptionHandler didn't return %v", http.StatusOK)
   807  	}
   808  	var eventResponse eventResponse
   809  	if err := json.Unmarshal(w.Body.Bytes(), &eventResponse); err != nil {
   810  		t.Errorf("Failed to decode json: %q", err)
   811  	}
   812  	if len(*eventResponse.Events) != 2 {
   813  		t.Errorf("Unexpected number of events.  Expected: %d, got: %d", 2, len(*eventResponse.Events))
   814  	}
   815  	if (*eventResponse.Events)[0].Category != "veggies" {
   816  		t.Errorf("Unexpected category.  Expected: %q, got: %q", "veggies", (*eventResponse.Events)[0].Category)
   817  	}
   818  	if (*eventResponse.Events)[0].Data != "broccoli" {
   819  		t.Errorf("Unexpected data.  Expected: %q, got: %q", "broccoli", (*eventResponse.Events)[0].Data)
   820  	}
   821  	if (*eventResponse.Events)[1].Category != "veggies" {
   822  		t.Errorf("Unexpected category.  Expected: %q, got: %q", "veggies", (*eventResponse.Events)[1].Category)
   823  	}
   824  	if (*eventResponse.Events)[1].Data != "corn" {
   825  		t.Errorf("Unexpected data.  Expected: %q, got: %q", "corn", (*eventResponse.Events)[1].Data)
   826  	}
   827  
   828  	// Don't forget to kill our pubsub manager's run goroutine
   829  	manager.Shutdown()
   830  
   831  }
   832  
   833  func Test_LongpollManager_makeTimeoutResponse(t *testing.T) {
   834  	now := time.Now()
   835  	timeoutResp := makeTimeoutResponse(now)
   836  	timeoutTime := now.UnixNano() / int64(time.Millisecond)
   837  	if timeoutResp.TimeoutMessage != "no events before timeout" {
   838  		t.Errorf("Unexpected timeout message: %q", timeoutResp.TimeoutMessage)
   839  	}
   840  	if timeoutResp.Timestamp != timeoutTime {
   841  		t.Errorf("Unexpected timeout timestamp.  Expected: %q, got: %q",
   842  			timeoutTime, timeoutResp.Timestamp)
   843  	}
   844  }
   845  
   846  func Test_LongpollManager_StartLongpoll_Options(t *testing.T) {
   847  	// Error cases due to invalid options:
   848  	if _, err := StartLongpoll(Options{
   849  		LoggingEnabled:            true,
   850  		MaxLongpollTimeoutSeconds: 120,
   851  		MaxEventBufferSize:        -1,
   852  		EventTimeToLiveSeconds:    1,
   853  	}); err == nil {
   854  		t.Errorf("Expected error when passing MaxEventBufferSize that was < 0")
   855  	}
   856  	if _, err := StartLongpoll(Options{
   857  		LoggingEnabled:            true,
   858  		MaxLongpollTimeoutSeconds: -1,
   859  		MaxEventBufferSize:        100,
   860  		EventTimeToLiveSeconds:    1,
   861  	}); err == nil {
   862  		t.Errorf("Expected error when passing MaxLongpollTimeoutSeconds that was < 0")
   863  	}
   864  	if _, err := StartLongpoll(Options{
   865  		LoggingEnabled:            true,
   866  		MaxLongpollTimeoutSeconds: 120,
   867  		MaxEventBufferSize:        100,
   868  		EventTimeToLiveSeconds:    -1,
   869  	}); err == nil {
   870  		t.Errorf("Expected error when passing EventTimeToLiveSeconds that was < 0")
   871  	}
   872  	// Confirm valid options work
   873  	// actual TTL
   874  	if manager, err := StartLongpoll(Options{
   875  		LoggingEnabled:            true,
   876  		MaxLongpollTimeoutSeconds: 120,
   877  		MaxEventBufferSize:        100,
   878  		EventTimeToLiveSeconds:    30,
   879  	}); err != nil {
   880  		t.Errorf("Unxpected error when calling StartLongpoll with valid options")
   881  	} else {
   882  		manager.Shutdown()
   883  	}
   884  
   885  	// Forever
   886  	if manager, err := StartLongpoll(Options{
   887  		LoggingEnabled:            true,
   888  		MaxLongpollTimeoutSeconds: 120,
   889  		MaxEventBufferSize:        100,
   890  		EventTimeToLiveSeconds:    FOREVER,
   891  	}); err != nil {
   892  		t.Errorf("Unxpected error when calling StartLongpoll with valid options")
   893  	} else {
   894  		manager.Shutdown()
   895  	}
   896  	// Confirm zero TTL converts to forever
   897  	if manager, err := StartLongpoll(Options{
   898  		LoggingEnabled:            true,
   899  		MaxLongpollTimeoutSeconds: 120,
   900  		MaxEventBufferSize:        100,
   901  		EventTimeToLiveSeconds:    0,
   902  	}); err != nil {
   903  		t.Errorf("Unxpected error when calling StartLongpoll with valid options")
   904  	} else {
   905  		if manager.subManager.EventTimeToLiveSeconds != FOREVER {
   906  			t.Errorf("Expected default of FOREVER when EventTimeToLiveSeconds is 0.  instead: %d",
   907  				manager.subManager.EventTimeToLiveSeconds)
   908  		}
   909  		manager.Shutdown()
   910  	}
   911  	// Confirm defaults for options set to zero value
   912  	// either explicitly like so:
   913  	if manager, err := StartLongpoll(Options{
   914  		LoggingEnabled:                 false,
   915  		MaxLongpollTimeoutSeconds:      0,
   916  		MaxEventBufferSize:             0,
   917  		EventTimeToLiveSeconds:         0,
   918  		DeleteEventAfterFirstRetrieval: false,
   919  	}); err != nil {
   920  		t.Errorf("Unxpected error when calling StartLongpoll with valid options")
   921  	} else {
   922  		if manager.subManager.EventTimeToLiveSeconds != FOREVER {
   923  			t.Errorf("Expected default of FOREVER when EventTimeToLiveSeconds is 0.  instead: %d",
   924  				manager.subManager.EventTimeToLiveSeconds)
   925  		}
   926  		if manager.subManager.MaxLongpollTimeoutSeconds != 120 {
   927  			t.Errorf("Expected default of 120 when MaxLongpollTimeoutSeconds is 0.  instead: %d",
   928  				manager.subManager.MaxLongpollTimeoutSeconds)
   929  		}
   930  		if manager.subManager.MaxEventBufferSize != 250 {
   931  			t.Errorf("Expected default of 250 when MaxEventBufferSize is 0.  instead: %d",
   932  				manager.subManager.MaxEventBufferSize)
   933  		}
   934  		if manager.subManager.LoggingEnabled != false {
   935  			t.Errorf("Expected default of false when LoggingEnabled is left out.  instead: %t",
   936  				manager.subManager.LoggingEnabled)
   937  		}
   938  		if manager.subManager.DeleteEventAfterFirstRetrieval != false {
   939  			t.Errorf("Expected default of false when DeleteEventAfterFirstRetrieval is left out.  instead: %t",
   940  				manager.subManager.DeleteEventAfterFirstRetrieval)
   941  		}
   942  		manager.Shutdown()
   943  	}
   944  
   945  	// or implicitly by never defining them:
   946  	if manager, err := StartLongpoll(Options{}); err != nil {
   947  		t.Errorf("Unxpected error when calling StartLongpoll with valid options")
   948  	} else {
   949  		if manager.subManager.EventTimeToLiveSeconds != FOREVER {
   950  			t.Errorf("Expected default of FOREVER when EventTimeToLiveSeconds is 0.  instead: %d",
   951  				manager.subManager.EventTimeToLiveSeconds)
   952  		}
   953  		if manager.subManager.MaxLongpollTimeoutSeconds != 120 {
   954  			t.Errorf("Expected default of 120 when MaxLongpollTimeoutSeconds is 0.  instead: %d",
   955  				manager.subManager.MaxLongpollTimeoutSeconds)
   956  		}
   957  		if manager.subManager.MaxEventBufferSize != 250 {
   958  			t.Errorf("Expected default of 250 when MaxEventBufferSize is 0.  instead: %d",
   959  				manager.subManager.MaxEventBufferSize)
   960  		}
   961  		if manager.subManager.LoggingEnabled != false {
   962  			t.Errorf("Expected default of false when LoggingEnabled is left out.  instead: %t",
   963  				manager.subManager.LoggingEnabled)
   964  		}
   965  		if manager.subManager.DeleteEventAfterFirstRetrieval != false {
   966  			t.Errorf("Expected default of false when DeleteEventAfterFirstRetrieval is left out.  instead: %t",
   967  				manager.subManager.DeleteEventAfterFirstRetrieval)
   968  		}
   969  		manager.Shutdown()
   970  	}
   971  
   972  }
   973  
   974  func Test_LongpollManager_EventExpiration(t *testing.T) {
   975  	manager, _ := StartLongpoll(Options{
   976  		LoggingEnabled:            true,
   977  		MaxLongpollTimeoutSeconds: 120,
   978  		MaxEventBufferSize:        100,
   979  		EventTimeToLiveSeconds:    1,
   980  	})
   981  	sm := manager.subManager
   982  	if len(sm.SubEventBuffer) != 0 {
   983  		t.Errorf("Unexpected category-to-buffer map size.  was: %d, expected %d",
   984  			len(sm.SubEventBuffer), 0)
   985  	}
   986  	if sm.bufferPriorityQueue.Len() != 0 {
   987  		t.Errorf("Unexpected heap size.  was: %d, expected: %d", sm.bufferPriorityQueue.Len(), 0)
   988  	}
   989  	manager.Publish("fruit", "apple")
   990  	time.Sleep(10 * time.Millisecond)
   991  	manager.Publish("veggie", "corn")
   992  	time.Sleep(750 * time.Millisecond)
   993  	manager.Publish("fruit", "orange")
   994  	// Allow sub manager's goroutine time to pull from channel.
   995  	// This sleep should cause us to yield and let the other goroutine run
   996  	time.Sleep(50 * time.Millisecond)
   997  	// Only ~800ms has went by, nothing should be expired out yet, so confirm
   998  	// all data is there
   999  	if len(sm.SubEventBuffer) != 2 {
  1000  		t.Errorf("Unexpected category-to-buffer map size.  was: %d, expected %d",
  1001  			len(sm.SubEventBuffer), 2)
  1002  	}
  1003  	fruit_buffer, fruit_found := sm.SubEventBuffer["fruit"]
  1004  	veggie_buffer, veggies_found := sm.SubEventBuffer["veggie"]
  1005  	if !fruit_found || !veggies_found {
  1006  		t.Errorf("failed to find fruit and veggie category event buffers")
  1007  	}
  1008  	if fruit_buffer.eventBuffer_ptr.List.Len() != 2 {
  1009  		t.Errorf("Unexpected number of fruit events.  was: %d, expected %d",
  1010  			fruit_buffer.eventBuffer_ptr.List.Len(), 2)
  1011  	}
  1012  	if veggie_buffer.eventBuffer_ptr.List.Len() != 1 {
  1013  		t.Errorf("Unexpected number of veggie events.  was: %d, expected %d",
  1014  			veggie_buffer.eventBuffer_ptr.List.Len(), 1)
  1015  	}
  1016  	if sm.bufferPriorityQueue.Len() != 2 {
  1017  		t.Errorf("Unexpected heap size.  was: %d, expected: %d", sm.bufferPriorityQueue.Len(), 2)
  1018  	}
  1019  	// Confirm top of heap is the veggie category since veggie is the category
  1020  	// with the oldest last-event (even tho fruit was started first, it has a
  1021  	// more recent event published on it--the heap sorts categories by how old
  1022  	// each categories most recent event is)
  1023  	if priority, peakErr := sm.bufferPriorityQueue.peakTopPriority(); peakErr != nil {
  1024  		t.Errorf("Unexpected error checking top priority: %v", peakErr)
  1025  	} else {
  1026  		if priority != veggie_buffer.eventBuffer_ptr.List.Front().Value.(*lpEvent).Timestamp {
  1027  			t.Errorf("Expected priority to be: %d, was: %d", priority,
  1028  				veggie_buffer.eventBuffer_ptr.List.Front().Value.(*lpEvent).Timestamp)
  1029  		}
  1030  	}
  1031  	// Now wait long enough for the first two published events to expire
  1032  	time.Sleep(220 * time.Millisecond)
  1033  	// NOTE how nothing was removed yet, because we do lazy-eval and only remove
  1034  	// expired stuff when activity occurs (but we do do a periodic check in
  1035  	// addition, but thats every 3 min by default.)
  1036  	if len(sm.SubEventBuffer) != 2 {
  1037  		t.Errorf("Unexpected category-to-buffer map size.  was: %d, expected %d",
  1038  			len(sm.SubEventBuffer), 2)
  1039  	}
  1040  	fruit_buffer, fruit_found = sm.SubEventBuffer["fruit"]
  1041  	veggie_buffer, veggies_found = sm.SubEventBuffer["veggie"]
  1042  	if !fruit_found || !veggies_found {
  1043  		t.Errorf("failed to find fruit and veggie category event buffers")
  1044  	}
  1045  	if fruit_buffer.eventBuffer_ptr.List.Len() != 2 {
  1046  		t.Errorf("Unexpected number of fruit events.  was: %d, expected %d",
  1047  			fruit_buffer.eventBuffer_ptr.List.Len(), 2)
  1048  	}
  1049  	if veggie_buffer.eventBuffer_ptr.List.Len() != 1 {
  1050  		t.Errorf("Unexpected number of veggie events.  was: %d, expected %d",
  1051  			veggie_buffer.eventBuffer_ptr.List.Len(), 1)
  1052  	}
  1053  	if sm.bufferPriorityQueue.Len() != 2 {
  1054  		t.Errorf("Unexpected heap size.  was: %d, expected: %d", sm.bufferPriorityQueue.Len(), 2)
  1055  	}
  1056  	if priority, peakErr := sm.bufferPriorityQueue.peakTopPriority(); peakErr != nil {
  1057  		t.Errorf("Unexpected error checking top priority: %v", peakErr)
  1058  	} else {
  1059  		if priority != veggie_buffer.eventBuffer_ptr.List.Front().Value.(*lpEvent).Timestamp {
  1060  			t.Errorf("Expected priority to be: %d, was: %d", priority,
  1061  				veggie_buffer.eventBuffer_ptr.List.Front().Value.(*lpEvent).Timestamp)
  1062  		}
  1063  	}
  1064  	// Force the fruit event to be expired out by introducing activity on the
  1065  	// fruit category.  (In this case, a new event)
  1066  	manager.Publish("fruit", "pear")
  1067  	// Allow sub manager's goroutine time to pull from channel.
  1068  	// This sleep should cause us to yield and let the other goroutine run
  1069  	time.Sleep(50 * time.Millisecond)
  1070  	if len(sm.SubEventBuffer) != 2 {
  1071  		t.Errorf("Unexpected category-to-buffer map size.  was: %d, expected %d",
  1072  			len(sm.SubEventBuffer), 2)
  1073  	}
  1074  	fruit_buffer, fruit_found = sm.SubEventBuffer["fruit"]
  1075  	veggie_buffer, veggies_found = sm.SubEventBuffer["veggie"]
  1076  	if !fruit_found || !veggies_found {
  1077  		t.Errorf("failed to find fruit and veggie category event buffers")
  1078  	}
  1079  	// NOTE: fruit buffer has still only 2 events, not 3, because the one was
  1080  	// expired out
  1081  	if fruit_buffer.eventBuffer_ptr.List.Len() != 2 {
  1082  		t.Errorf("Unexpected number of fruit events.  was: %d, expected %d",
  1083  			fruit_buffer.eventBuffer_ptr.List.Len(), 2)
  1084  	}
  1085  	if veggie_buffer.eventBuffer_ptr.List.Len() != 1 {
  1086  		t.Errorf("Unexpected number of veggie events.  was: %d, expected %d",
  1087  			veggie_buffer.eventBuffer_ptr.List.Len(), 1)
  1088  	}
  1089  	if sm.bufferPriorityQueue.Len() != 2 {
  1090  		t.Errorf("Unexpected heap size.  was: %d, expected: %d", sm.bufferPriorityQueue.Len(), 2)
  1091  	}
  1092  	// veggie buffer is still the oldest-newest-event category
  1093  	if priority, peakErr := sm.bufferPriorityQueue.peakTopPriority(); peakErr != nil {
  1094  		t.Errorf("Unexpected error checking top priority: %v", peakErr)
  1095  	} else {
  1096  		if priority != veggie_buffer.eventBuffer_ptr.List.Front().Value.(*lpEvent).Timestamp {
  1097  			t.Errorf("Expected priority to be: %d, was: %d", priority,
  1098  				veggie_buffer.eventBuffer_ptr.List.Front().Value.(*lpEvent).Timestamp)
  1099  		}
  1100  	}
  1101  	// Force the veggie event to be expired out by introducing activity on the
  1102  	// veggie category.  (In this case, a client requests a longpoll)
  1103  	// also confirm longpoll doesn't return the now-expired events, even if
  1104  	// client using a really old since param
  1105  	subscriptionHandler := ajaxHandler(manager.SubscriptionHandler)
  1106  	req, _ := http.NewRequest("GET", "?timeout=2&category=veggie", nil)
  1107  	w := NewCloseNotifierRecorder()
  1108  	subscriptionHandler.ServeHTTP(w, req)
  1109  	if w.Code != http.StatusOK {
  1110  		t.Errorf("SubscriptionHandler didn't return %v", http.StatusOK)
  1111  	}
  1112  	approxTimeoutTime := time.Now().UnixNano() / int64(time.Millisecond)
  1113  	var timeoutResp timeoutResponse
  1114  	if err := json.Unmarshal(w.Body.Bytes(), &timeoutResp); err != nil {
  1115  		t.Errorf("Failed to decode json: %q", err)
  1116  	}
  1117  	if timeoutResp.TimeoutMessage != "no events before timeout" {
  1118  		t.Errorf("Unexpected timeout message: %q", timeoutResp.TimeoutMessage)
  1119  	}
  1120  	if timeoutResp.Timestamp < (approxTimeoutTime-100) ||
  1121  		timeoutResp.Timestamp > approxTimeoutTime {
  1122  		t.Errorf("Unexpected timeout timestamp.  Expected: %q, got: %q",
  1123  			approxTimeoutTime, timeoutResp.Timestamp)
  1124  	}
  1125  	// Now confirm veggie category removed--only one category: fruit
  1126  	// and in that category, only 2 events left
  1127  	if len(sm.SubEventBuffer) != 1 {
  1128  		t.Errorf("Unexpected category-to-buffer map size.  was: %d, expected %d",
  1129  			len(sm.SubEventBuffer), 1)
  1130  	}
  1131  	fruit_buffer, fruit_found = sm.SubEventBuffer["fruit"]
  1132  	veggie_buffer, veggies_found = sm.SubEventBuffer["veggie"]
  1133  
  1134  	if !fruit_found || veggies_found {
  1135  		t.Errorf("Fruit should be found, veggies should not.")
  1136  	}
  1137  	// NOTE: fruit buffer has still has last two published fruit events.
  1138  	if fruit_buffer.eventBuffer_ptr.List.Len() != 2 {
  1139  		t.Errorf("Unexpected number of fruit events.  was: %d, expected %d",
  1140  			fruit_buffer.eventBuffer_ptr.List.Len(), 2)
  1141  	}
  1142  	if sm.bufferPriorityQueue.Len() != 1 {
  1143  		t.Errorf("Unexpected heap size.  was: %d, expected: %d", sm.bufferPriorityQueue.Len(), 1)
  1144  	}
  1145  	// fruit buffer is now the oldest most-recent-event-time buffer (and the only one)
  1146  	if priority, peakErr := sm.bufferPriorityQueue.peakTopPriority(); peakErr != nil {
  1147  		t.Errorf("Unexpected error checking top priority: %v", peakErr)
  1148  	} else {
  1149  		if priority != fruit_buffer.eventBuffer_ptr.List.Front().Value.(*lpEvent).Timestamp {
  1150  			t.Errorf("Expected priority to be: %d, was: %d", priority,
  1151  				fruit_buffer.eventBuffer_ptr.List.Front().Value.(*lpEvent).Timestamp)
  1152  		}
  1153  	}
  1154  	// Now force the expire check on the last two fruit events.
  1155  	// Enough time has elapsed that everything should be gone by now.
  1156  	req, _ = http.NewRequest("GET", "?timeout=1&category=fruit", nil)
  1157  	w = NewCloseNotifierRecorder()
  1158  	subscriptionHandler.ServeHTTP(w, req)
  1159  	if w.Code != http.StatusOK {
  1160  		t.Errorf("SubscriptionHandler didn't return %v", http.StatusOK)
  1161  	}
  1162  	approxTimeoutTime = time.Now().UnixNano() / int64(time.Millisecond)
  1163  	if err := json.Unmarshal(w.Body.Bytes(), &timeoutResp); err != nil {
  1164  		t.Errorf("Failed to decode json: %q", err)
  1165  	}
  1166  	if timeoutResp.TimeoutMessage != "no events before timeout" {
  1167  		t.Errorf("Unexpected timeout message: %q", timeoutResp.TimeoutMessage)
  1168  	}
  1169  	if timeoutResp.Timestamp < (approxTimeoutTime-100) ||
  1170  		timeoutResp.Timestamp > approxTimeoutTime {
  1171  		t.Errorf("Unexpected timeout timestamp.  Expected: %q, got: %q",
  1172  			approxTimeoutTime, timeoutResp.Timestamp)
  1173  	}
  1174  	// Now confirm veggie category removed--only one category: fruit
  1175  	// and in that category, only 2 events left
  1176  	if len(sm.SubEventBuffer) != 0 {
  1177  		t.Errorf("Unexpected category-to-buffer map size.  was: %d, expected %d",
  1178  			len(sm.SubEventBuffer), 0)
  1179  	}
  1180  	_, fruit_found = sm.SubEventBuffer["fruit"]
  1181  	_, veggies_found = sm.SubEventBuffer["veggie"]
  1182  
  1183  	if fruit_found || veggies_found {
  1184  		t.Errorf("Both fruit and veggie buffers should no longer exist in map.")
  1185  	}
  1186  	if sm.bufferPriorityQueue.Len() != 0 {
  1187  		t.Errorf("Unexpected heap size.  was: %d, expected: %d", sm.bufferPriorityQueue.Len(), 0)
  1188  	}
  1189  	// fruit buffer is now the oldest most-recent-event-time buffer (and the only one)
  1190  	if priority, peakErr := sm.bufferPriorityQueue.peakTopPriority(); peakErr == nil {
  1191  		t.Errorf("Expected error when peaking at top of empty queue.")
  1192  	} else {
  1193  		if priority != -1 {
  1194  			t.Errorf("Expected priority to be the -1 error value, instead it was: %d", priority)
  1195  		}
  1196  	}
  1197  	manager.Shutdown()
  1198  }
  1199  
  1200  // Shared by multiple tests with manager configured different ways:
  1201  func deleteOnFetchTest(manager *LongpollManager, t *testing.T) {
  1202  	sm := manager.subManager
  1203  	if len(sm.SubEventBuffer) != 0 {
  1204  		t.Errorf("Unexpected category-to-buffer map size.  was: %d, expected %d",
  1205  			len(sm.SubEventBuffer), 0)
  1206  	}
  1207  	if sm.EventTimeToLiveSeconds != FOREVER && sm.bufferPriorityQueue.Len() != 0 {
  1208  		t.Errorf("Unexpected heap size.  was: %d, expected: %d", sm.bufferPriorityQueue.Len(), 0)
  1209  	}
  1210  	manager.Publish("fruit", "apple")
  1211  	time.Sleep(10 * time.Millisecond)
  1212  	manager.Publish("veggie", "corn")
  1213  	time.Sleep(1150 * time.Millisecond)
  1214  	manager.Publish("fruit", "orange")
  1215  	time.Sleep(10 * time.Millisecond)
  1216  	manager.Publish("veggie", "carrot")
  1217  	// small wait so yield occurs and other goroutine gets a chance
  1218  	time.Sleep(50 * time.Millisecond)
  1219  	// Confirm expected state:
  1220  	if len(sm.SubEventBuffer) != 2 {
  1221  		t.Errorf("Unexpected category-to-buffer map size.  was: %d, expected %d",
  1222  			len(sm.SubEventBuffer), 2)
  1223  	}
  1224  	fruit_buffer, fruit_found := sm.SubEventBuffer["fruit"]
  1225  	veggie_buffer, veggies_found := sm.SubEventBuffer["veggie"]
  1226  	if !fruit_found || !veggies_found {
  1227  		t.Errorf("failed to find fruit and veggie category event buffers")
  1228  	}
  1229  	if fruit_buffer.eventBuffer_ptr.List.Len() != 2 {
  1230  		t.Errorf("Unexpected number of fruit events.  was: %d, expected %d",
  1231  			fruit_buffer.eventBuffer_ptr.List.Len(), 2)
  1232  	}
  1233  	if veggie_buffer.eventBuffer_ptr.List.Len() != 2 {
  1234  		t.Errorf("Unexpected number of veggie events.  was: %d, expected %d",
  1235  			veggie_buffer.eventBuffer_ptr.List.Len(), 2)
  1236  	}
  1237  	if sm.EventTimeToLiveSeconds != FOREVER && sm.bufferPriorityQueue.Len() != 2 {
  1238  		t.Errorf("Unexpected heap size.  was: %d, expected: %d", sm.bufferPriorityQueue.Len(), 2)
  1239  	}
  1240  	// Confirm top of heap is the fruit category since fruit is the category
  1241  	// with the oldest last-event
  1242  	var priority_before_removal int64
  1243  	// Only check heap if we're using it.  When no TTL, heap is not used.
  1244  	if sm.EventTimeToLiveSeconds != FOREVER {
  1245  		if priority, peakErr := sm.bufferPriorityQueue.peakTopPriority(); peakErr != nil {
  1246  			t.Errorf("Unexpected error checking top priority: %v", peakErr)
  1247  		} else {
  1248  			if priority != fruit_buffer.eventBuffer_ptr.List.Front().Value.(*lpEvent).Timestamp {
  1249  				t.Errorf("Expected priority to be: %d, was: %d", priority,
  1250  					fruit_buffer.eventBuffer_ptr.List.Front().Value.(*lpEvent).Timestamp)
  1251  			}
  1252  			priority_before_removal = priority
  1253  		}
  1254  	}
  1255  
  1256  	// Now let's do a longpoll on the fruit category asking for events less
  1257  	// than 1s old, confirm the most recent fruit (orange) was removed
  1258  	since_time := time.Now().Add(-1*time.Second).UnixNano() / int64(time.Millisecond)
  1259  	subscriptionHandler := ajaxHandler(manager.SubscriptionHandler)
  1260  	req, _ := http.NewRequest("GET", fmt.Sprintf("?timeout=1&category=fruit&since_time=%d", since_time), nil)
  1261  	w := NewCloseNotifierRecorder()
  1262  	subscriptionHandler.ServeHTTP(w, req)
  1263  	if w.Code != http.StatusOK {
  1264  		t.Errorf("SubscriptionHandler didn't return %v", http.StatusOK)
  1265  	}
  1266  	var eventResponse eventResponse
  1267  	if err := json.Unmarshal(w.Body.Bytes(), &eventResponse); err != nil {
  1268  		t.Errorf("Failed to decode json: %q", err)
  1269  	}
  1270  	if len(*eventResponse.Events) != 1 {
  1271  		t.Errorf("Unexpected number of events.  Expected: %d, got: %d", 1, len(*eventResponse.Events))
  1272  	}
  1273  	if (*eventResponse.Events)[0].Category != "fruit" {
  1274  		t.Errorf("Unexpected category.  Expected: %q, got: %q", "fruit", (*eventResponse.Events)[0].Category)
  1275  	}
  1276  	if (*eventResponse.Events)[0].Data != "orange" {
  1277  		t.Errorf("Unexpected data.  Expected: %q, got: %q", "orange", (*eventResponse.Events)[0].Data)
  1278  	}
  1279  	// Also confirm that orange is now gone out of the buffer
  1280  	if len(sm.SubEventBuffer) != 2 {
  1281  		t.Errorf("Unexpected category-to-buffer map size.  was: %d, expected %d",
  1282  			len(sm.SubEventBuffer), 2)
  1283  	}
  1284  	fruit_buffer, fruit_found = sm.SubEventBuffer["fruit"]
  1285  	veggie_buffer, veggies_found = sm.SubEventBuffer["veggie"]
  1286  	if !fruit_found || !veggies_found {
  1287  		t.Errorf("failed to find fruit and veggie category event buffers")
  1288  	}
  1289  	if fruit_buffer.eventBuffer_ptr.List.Len() != 1 {
  1290  		t.Errorf("Unexpected number of fruit events.  was: %d, expected %d",
  1291  			fruit_buffer.eventBuffer_ptr.List.Len(), 1)
  1292  	}
  1293  	if fruit_buffer.eventBuffer_ptr.List.Front().Value.(*lpEvent).Data.(string) != "apple" {
  1294  		t.Errorf("Unexpected event left in fruit buffer.  was: %s, expected: %s.",
  1295  			fruit_buffer.eventBuffer_ptr.List.Front().Value.(*lpEvent).Data.(string), "apple")
  1296  	}
  1297  	if veggie_buffer.eventBuffer_ptr.List.Len() != 2 {
  1298  		t.Errorf("Unexpected number of veggie events.  was: %d, expected %d",
  1299  			veggie_buffer.eventBuffer_ptr.List.Len(), 2)
  1300  	}
  1301  	if sm.EventTimeToLiveSeconds != FOREVER && sm.bufferPriorityQueue.Len() != 2 {
  1302  		t.Errorf("Unexpected heap size.  was: %d, expected: %d", sm.bufferPriorityQueue.Len(), 2)
  1303  	}
  1304  	if sm.EventTimeToLiveSeconds != FOREVER {
  1305  		// NOTE: the heap priority doesn't change when an event is removed due
  1306  		// to the DeleteEventAfterFirstRetrieval setting.  This is by design because
  1307  		// it is complicated to know what to update the priority to, and it doesn't
  1308  		// harm or break the other behavior to skip updating it, the worst that
  1309  		// happens is a frivolous expiration check that removes nothing.
  1310  		if priority, peakErr := sm.bufferPriorityQueue.peakTopPriority(); peakErr != nil {
  1311  			t.Errorf("Unexpected error checking top priority: %v", peakErr)
  1312  		} else {
  1313  			if priority != priority_before_removal {
  1314  				t.Errorf("Expected priority to be: %d, was: %d", priority,
  1315  					priority_before_removal)
  1316  			}
  1317  		}
  1318  	}
  1319  
  1320  	// Now request all veggie events (since beginning of time), confirm all
  1321  	// veggies removed
  1322  	since_time = time.Now().Add(-60*time.Second).UnixNano() / int64(time.Millisecond)
  1323  	subscriptionHandler = ajaxHandler(manager.SubscriptionHandler)
  1324  	req, _ = http.NewRequest("GET", fmt.Sprintf("?timeout=1&category=veggie&since_time=%d", since_time), nil)
  1325  	w = NewCloseNotifierRecorder()
  1326  	subscriptionHandler.ServeHTTP(w, req)
  1327  	if w.Code != http.StatusOK {
  1328  		t.Errorf("SubscriptionHandler didn't return %v", http.StatusOK)
  1329  	}
  1330  	if err := json.Unmarshal(w.Body.Bytes(), &eventResponse); err != nil {
  1331  		t.Errorf("Failed to decode json: %q", err)
  1332  	}
  1333  	if len(*eventResponse.Events) != 2 {
  1334  		t.Errorf("Unexpected number of events.  Expected: %d, got: %d", 2, len(*eventResponse.Events))
  1335  	}
  1336  	if (*eventResponse.Events)[0].Category != "veggie" {
  1337  		t.Errorf("Unexpected category.  Expected: %q, got: %q", "veggie", (*eventResponse.Events)[0].Category)
  1338  	}
  1339  	if (*eventResponse.Events)[0].Data != "corn" {
  1340  		t.Errorf("Unexpected data.  Expected: %q, got: %q", "corn", (*eventResponse.Events)[0].Data)
  1341  	}
  1342  	if (*eventResponse.Events)[1].Category != "veggie" {
  1343  		t.Errorf("Unexpected category.  Expected: %q, got: %q", "veggie", (*eventResponse.Events)[0].Category)
  1344  	}
  1345  	if (*eventResponse.Events)[1].Data != "carrot" {
  1346  		t.Errorf("Unexpected data.  Expected: %q, got: %q", "carrot", (*eventResponse.Events)[0].Data)
  1347  	}
  1348  	time.Sleep(50 * time.Millisecond)
  1349  	if len(sm.SubEventBuffer) != 1 {
  1350  		t.Errorf("Unexpected category-to-buffer map size.  was: %d, expected %d",
  1351  			len(sm.SubEventBuffer), 1)
  1352  	}
  1353  	fruit_buffer, fruit_found = sm.SubEventBuffer["fruit"]
  1354  	veggie_buffer, veggies_found = sm.SubEventBuffer["veggie"]
  1355  	if !fruit_found || veggies_found {
  1356  		t.Errorf("expected fruit to be found but not veggies")
  1357  	}
  1358  
  1359  	if fruit_buffer.eventBuffer_ptr.List.Len() != 1 {
  1360  		t.Errorf("Unexpected number of fruit events.  was: %d, expected %d",
  1361  			fruit_buffer.eventBuffer_ptr.List.Len(), 1)
  1362  	}
  1363  	if fruit_buffer.eventBuffer_ptr.List.Front().Value.(*lpEvent).Data.(string) != "apple" {
  1364  		t.Errorf("Unexpected event left in fruit buffer.  was: %s, expected: %s.",
  1365  			fruit_buffer.eventBuffer_ptr.List.Front().Value.(*lpEvent).Data.(string), "apple")
  1366  	}
  1367  	if sm.EventTimeToLiveSeconds != FOREVER {
  1368  		// Heap still not changed for same reasons as before
  1369  		if priority, peakErr := sm.bufferPriorityQueue.peakTopPriority(); peakErr != nil {
  1370  			t.Errorf("Unexpected error checking top priority: %v", peakErr)
  1371  		} else {
  1372  			if priority != priority_before_removal {
  1373  				t.Errorf("Expected priority to be: %d, was: %d", priority,
  1374  					priority_before_removal)
  1375  			}
  1376  		}
  1377  	}
  1378  }
  1379  
  1380  func Test_LongpollManager_DeleteOnFetch(t *testing.T) {
  1381  	manager, _ := StartLongpoll(Options{
  1382  		LoggingEnabled:                 true,
  1383  		MaxLongpollTimeoutSeconds:      120,
  1384  		MaxEventBufferSize:             100,
  1385  		EventTimeToLiveSeconds:         60,
  1386  		DeleteEventAfterFirstRetrieval: true,
  1387  	})
  1388  	deleteOnFetchTest(manager, t)
  1389  }
  1390  
  1391  func Test_LongpollManager_DeleteOnFetch_ForeverTTL(t *testing.T) {
  1392  	manager, _ := StartLongpoll(Options{
  1393  		LoggingEnabled:                 true,
  1394  		MaxLongpollTimeoutSeconds:      120,
  1395  		MaxEventBufferSize:             100,
  1396  		EventTimeToLiveSeconds:         FOREVER,
  1397  		DeleteEventAfterFirstRetrieval: true,
  1398  	})
  1399  	deleteOnFetchTest(manager, t)
  1400  }
  1401  
  1402  func Test_LongpollManager_DeleteOnFetch_SkipBuffering(t *testing.T) {
  1403  	// Publish an event while a client is in the middle of a longpoll and
  1404  	// confirm that the event was received by the client and never buffered
  1405  	// on the server.
  1406  	manager, _ := StartLongpoll(Options{
  1407  		LoggingEnabled:                 true,
  1408  		MaxLongpollTimeoutSeconds:      120,
  1409  		MaxEventBufferSize:             100,
  1410  		EventTimeToLiveSeconds:         60 * 10,
  1411  		DeleteEventAfterFirstRetrieval: true,
  1412  	})
  1413  	sm := manager.subManager
  1414  	subscriptionHandler := ajaxHandler(manager.SubscriptionHandler)
  1415  
  1416  	if len(sm.SubEventBuffer) != 0 {
  1417  		t.Errorf("Unexpected category-to-buffer map size.  was: %d, expected %d",
  1418  			len(sm.SubEventBuffer), 0)
  1419  	}
  1420  	if sm.EventTimeToLiveSeconds != FOREVER && sm.bufferPriorityQueue.Len() != 0 {
  1421  		t.Errorf("Unexpected heap size.  was: %d, expected: %d", sm.bufferPriorityQueue.Len(), 0)
  1422  	}
  1423  
  1424  	// Valid request, but we don't have any events published,
  1425  	// so this will wait until a publish or a timeout, in this case we'll get
  1426  	// an event.
  1427  	req, _ := http.NewRequest("GET", "?timeout=30&category=fruit", nil)
  1428  	w := NewCloseNotifierRecorder()
  1429  	// Publish two events, only the second is for our subscription category
  1430  	// Note how these events occur after the client subscribed
  1431  	// if they occurred before, since we don't provide a since_time url param
  1432  	// we'd default to now and skip those events.
  1433  	go func() {
  1434  		time.Sleep(1500 * time.Millisecond)
  1435  		manager.Publish("fruit", "peach")
  1436  	}()
  1437  	subscriptionHandler.ServeHTTP(w, req)
  1438  	// Confirm we got the correct event
  1439  	if w.Code != http.StatusOK {
  1440  		t.Errorf("SubscriptionHandler didn't return %v", http.StatusOK)
  1441  	}
  1442  	var eventResponse eventResponse
  1443  	if err := json.Unmarshal(w.Body.Bytes(), &eventResponse); err != nil {
  1444  		t.Errorf("Failed to decode json: %q", err)
  1445  	}
  1446  	if len(*eventResponse.Events) != 1 {
  1447  		t.Errorf("Unexpected number of events.  Expected: %d, got: %d", 1, len(*eventResponse.Events))
  1448  	}
  1449  	if (*eventResponse.Events)[0].Category != "fruit" {
  1450  		t.Errorf("Unexpected category.  Expected: %q, got: %q", "fruit", (*eventResponse.Events)[0].Category)
  1451  	}
  1452  	if (*eventResponse.Events)[0].Data != "peach" {
  1453  		t.Errorf("Unexpected data.  Expected: %q, got: %q", "peach", (*eventResponse.Events)[0].Data)
  1454  	}
  1455  	// Ensure nothing was buffered:
  1456  	if len(sm.SubEventBuffer) != 0 {
  1457  		t.Errorf("Unexpected category-to-buffer map size.  was: %d, expected %d",
  1458  			len(sm.SubEventBuffer), 0)
  1459  	}
  1460  	if sm.EventTimeToLiveSeconds != FOREVER && sm.bufferPriorityQueue.Len() != 0 {
  1461  		t.Errorf("Unexpected heap size.  was: %d, expected: %d", sm.bufferPriorityQueue.Len(), 0)
  1462  	}
  1463  	// Don't forget to kill our pubsub manager's run goroutine
  1464  	manager.Shutdown()
  1465  }
  1466  
  1467  func Test_LongpollManager_PurgingOldCategories(t *testing.T) {
  1468  	// Confirm that old categories get removed by purge.
  1469  	// After any activity we check to see if it's time to call purge.
  1470  	manager, _ := StartLongpoll(Options{
  1471  		LoggingEnabled:            true,
  1472  		MaxLongpollTimeoutSeconds: 120,
  1473  		MaxEventBufferSize:        100,
  1474  		EventTimeToLiveSeconds:    1,
  1475  	})
  1476  	sm := manager.subManager
  1477  	sm.staleCategoryPurgePeriodSeconds = 2
  1478  	if len(sm.SubEventBuffer) != 0 {
  1479  		t.Errorf("Unexpected category-to-buffer map size.  was: %d, expected %d",
  1480  			len(sm.SubEventBuffer), 0)
  1481  	}
  1482  	if sm.bufferPriorityQueue.Len() != 0 {
  1483  		t.Errorf("Unexpected heap size.  was: %d, expected: %d", sm.bufferPriorityQueue.Len(), 0)
  1484  	}
  1485  	manager.Publish("fruit", "apple")
  1486  	time.Sleep(10 * time.Millisecond)
  1487  	manager.Publish("fruit", "orange")
  1488  	time.Sleep(2000 * time.Millisecond)
  1489  	// It's now been over 2s since both fruit events published, and since our
  1490  	// TTL setting is 1s, these are both expired.
  1491  	// But confirm purge didn't happen yet even tho 2s (the purge period) has
  1492  	// elapsed.
  1493  	if len(sm.SubEventBuffer) != 1 {
  1494  		t.Errorf("Unexpected category-to-buffer map size.  was: %d, expected %d",
  1495  			len(sm.SubEventBuffer), 1)
  1496  	}
  1497  	fruit_buffer, fruit_found := sm.SubEventBuffer["fruit"]
  1498  	veggie_buffer, veggies_found := sm.SubEventBuffer["veggie"]
  1499  	if !fruit_found || veggies_found {
  1500  		t.Errorf("should find fruit but not veggie buffer")
  1501  	}
  1502  	if fruit_buffer.eventBuffer_ptr.List.Len() != 2 {
  1503  		t.Errorf("Unexpected number of fruit events.  was: %d, expected %d",
  1504  			fruit_buffer.eventBuffer_ptr.List.Len(), 2)
  1505  	}
  1506  	if sm.bufferPriorityQueue.Len() != 1 {
  1507  		t.Errorf("Unexpected heap size.  was: %d, expected: %d", sm.bufferPriorityQueue.Len(), 1)
  1508  	}
  1509  
  1510  	// Publish an event which will force us to check if elapsed time is greater
  1511  	// than purge period and kick off a purge.
  1512  	manager.Publish("veggie", "corn")
  1513  	time.Sleep(50 * time.Millisecond)
  1514  
  1515  	// Confirm fruit events were destroyed
  1516  	if len(sm.SubEventBuffer) != 1 {
  1517  		t.Errorf("Unexpected category-to-buffer map size.  was: %d, expected %d",
  1518  			len(sm.SubEventBuffer), 1)
  1519  	}
  1520  	fruit_buffer, fruit_found = sm.SubEventBuffer["fruit"]
  1521  	veggie_buffer, veggies_found = sm.SubEventBuffer["veggie"]
  1522  	if fruit_found || !veggies_found {
  1523  		t.Errorf("Expected to find veggies but not fruit buffer.")
  1524  	}
  1525  	if veggie_buffer.eventBuffer_ptr.List.Len() != 1 {
  1526  		t.Errorf("Unexpected number of veggie events.  was: %d, expected %d",
  1527  			veggie_buffer.eventBuffer_ptr.List.Len(), 1)
  1528  	}
  1529  	if sm.bufferPriorityQueue.Len() != 1 {
  1530  		t.Errorf("Unexpected heap size.  was: %d, expected: %d", sm.bufferPriorityQueue.Len(), 1)
  1531  	}
  1532  }
  1533  
  1534  func Test_LongpollManager_PurgingOldCategories_Inactivity(t *testing.T) {
  1535  	// Confirm that old categories get removed by purge even if there is no
  1536  	// activity going on.  This purge is accomplished via the periodic
  1537  	// check regardless of activity.
  1538  	manager, _ := StartLongpoll(Options{
  1539  		LoggingEnabled:            true,
  1540  		MaxLongpollTimeoutSeconds: 120,
  1541  		MaxEventBufferSize:        100,
  1542  		EventTimeToLiveSeconds:    1,
  1543  	})
  1544  	sm := manager.subManager
  1545  	sm.staleCategoryPurgePeriodSeconds = 2
  1546  	if len(sm.SubEventBuffer) != 0 {
  1547  		t.Errorf("Unexpected category-to-buffer map size.  was: %d, expected %d",
  1548  			len(sm.SubEventBuffer), 0)
  1549  	}
  1550  	if sm.bufferPriorityQueue.Len() != 0 {
  1551  		t.Errorf("Unexpected heap size.  was: %d, expected: %d", sm.bufferPriorityQueue.Len(), 0)
  1552  	}
  1553  	manager.Publish("fruit", "apple")
  1554  	time.Sleep(10 * time.Millisecond)
  1555  	manager.Publish("fruit", "orange")
  1556  	time.Sleep(2000 * time.Millisecond)
  1557  	// It's now been over 2s since both fruit events published, and since our
  1558  	// TTL setting is 1s, these are both expired.
  1559  	// But confirm purge didn't happen yet even tho 2s (the purge period) has
  1560  	// elapsed.
  1561  	if len(sm.SubEventBuffer) != 1 {
  1562  		t.Errorf("Unexpected category-to-buffer map size.  was: %d, expected %d",
  1563  			len(sm.SubEventBuffer), 1)
  1564  	}
  1565  	fruit_buffer, fruit_found := sm.SubEventBuffer["fruit"]
  1566  	if !fruit_found {
  1567  		t.Errorf("should find fruit but not veggie buffer")
  1568  	}
  1569  	if fruit_buffer.eventBuffer_ptr.List.Len() != 2 {
  1570  		t.Errorf("Unexpected number of fruit events.  was: %d, expected %d",
  1571  			fruit_buffer.eventBuffer_ptr.List.Len(), 2)
  1572  	}
  1573  	if sm.bufferPriorityQueue.Len() != 1 {
  1574  		t.Errorf("Unexpected heap size.  was: %d, expected: %d", sm.bufferPriorityQueue.Len(), 1)
  1575  	}
  1576  	// Wait until we do our purge check due to inactivity, then confirm
  1577  	// data was cleared out.
  1578  	time.Sleep(3100 * time.Millisecond)
  1579  
  1580  	// Confirm fruit events were destroyed
  1581  	if len(sm.SubEventBuffer) != 0 {
  1582  		t.Errorf("Unexpected category-to-buffer map size.  was: %d, expected %d",
  1583  			len(sm.SubEventBuffer), 0)
  1584  	}
  1585  	fruit_buffer, fruit_found = sm.SubEventBuffer["fruit"]
  1586  	if fruit_found {
  1587  		t.Errorf("Expected to not find fruit buffer.")
  1588  	}
  1589  	if sm.bufferPriorityQueue.Len() != 0 {
  1590  		t.Errorf("Unexpected heap size.  was: %d, expected: %d", sm.bufferPriorityQueue.Len(), 0)
  1591  	}
  1592  }