github.com/pyroscope-io/pyroscope@v0.37.3-0.20230725203016-5f6947968bd0/pkg/analytics/analytics_test.go (about)

     1  //go:build !windows && !race
     2  // +build !windows,!race
     3  
     4  package analytics
     5  
     6  import (
     7  	"encoding/json"
     8  	"fmt"
     9  	"html"
    10  	"io"
    11  	"net/http"
    12  	"net/http/httptest"
    13  	"sync"
    14  	"time"
    15  
    16  	. "github.com/onsi/ginkgo/v2"
    17  	. "github.com/onsi/gomega"
    18  	"github.com/prometheus/client_golang/prometheus"
    19  	"github.com/pyroscope-io/pyroscope/pkg/health"
    20  	"github.com/sirupsen/logrus"
    21  
    22  	"github.com/pyroscope-io/pyroscope/pkg/config"
    23  	"github.com/pyroscope-io/pyroscope/pkg/storage"
    24  	"github.com/pyroscope-io/pyroscope/pkg/testing"
    25  )
    26  
    27  const durThreshold = 30 * time.Millisecond
    28  
    29  type mockStatsProvider struct {
    30  	stats map[string]int
    31  }
    32  
    33  func (m *mockStatsProvider) Stats() map[string]int {
    34  	if m.stats != nil {
    35  		return m.stats
    36  	}
    37  	return map[string]int{}
    38  }
    39  
    40  func (*mockStatsProvider) AppsCount() int { return 0 }
    41  
    42  var _ = Describe("analytics", func() {
    43  	gracePeriod = 100 * time.Millisecond
    44  	oldMetricsUploadFrequency = 200 * time.Millisecond
    45  	snapshotFrequency = 200 * time.Millisecond
    46  
    47  	testing.WithConfig(func(cfg **config.Config) {
    48  		Describe("NewService", func() {
    49  			It("works as expected", func() {
    50  				done := make(chan interface{})
    51  				go func() {
    52  					defer GinkgoRecover()
    53  
    54  					wg := sync.WaitGroup{}
    55  					wg.Add(3)
    56  					timestamps := []time.Time{}
    57  					myHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    58  						timestamps = append(timestamps, time.Now())
    59  						bytes, err := io.ReadAll(r.Body)
    60  						Expect(err).ToNot(HaveOccurred())
    61  
    62  						v := make(map[string]interface{})
    63  						err = json.Unmarshal(bytes, &v)
    64  						Expect(err).ToNot(HaveOccurred())
    65  
    66  						fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path))
    67  						wg.Done()
    68  					})
    69  
    70  					httpServer := httptest.NewServer(myHandler)
    71  					defer httpServer.Close()
    72  					host = httpServer.URL
    73  
    74  					s, err := storage.New(
    75  						storage.NewConfig(&(*cfg).Server),
    76  						logrus.StandardLogger(),
    77  						prometheus.NewRegistry(),
    78  						new(health.Controller),
    79  						storage.NoopApplicationMetadataService{},
    80  					)
    81  					Expect(err).ToNot(HaveOccurred())
    82  
    83  					analytics := NewService(&(*cfg).Server, s, &mockStatsProvider{})
    84  
    85  					startTime := time.Now()
    86  					go analytics.Start()
    87  					wg.Wait()
    88  					analytics.Stop()
    89  					Expect(timestamps).To(ConsistOf(
    90  						BeTemporally("~", startTime.Add(100*time.Millisecond), durThreshold),
    91  						BeTemporally("~", startTime.Add(300*time.Millisecond), durThreshold),
    92  						BeTemporally("~", startTime.Add(500*time.Millisecond), durThreshold),
    93  					))
    94  					close(done)
    95  				}()
    96  				Eventually(done, 2).Should(BeClosed())
    97  			})
    98  			It("cumilative metrics should persist on service stop", func() {
    99  				done := make(chan interface{})
   100  				go func() {
   101  					defer GinkgoRecover()
   102  
   103  					wg := sync.WaitGroup{}
   104  					v := make(map[string]interface{})
   105  					myHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   106  						bytes, err := io.ReadAll(r.Body)
   107  						Expect(err).ToNot(HaveOccurred())
   108  						err = json.Unmarshal(bytes, &v)
   109  						Expect(err).ToNot(HaveOccurred())
   110  						w.WriteHeader(http.StatusOK)
   111  						wg.Done()
   112  					})
   113  
   114  					httpServer := httptest.NewServer(myHandler)
   115  					defer httpServer.Close()
   116  					host = httpServer.URL
   117  
   118  					s, err := storage.New(
   119  						storage.NewConfig(&(*cfg).Server),
   120  						logrus.StandardLogger(),
   121  						prometheus.NewRegistry(),
   122  						new(health.Controller),
   123  						storage.NoopApplicationMetadataService{},
   124  					)
   125  					Expect(err).ToNot(HaveOccurred())
   126  
   127  					stats := map[string]int{
   128  						"diff":       1,
   129  						"ingest":     1,
   130  						"comparison": 1,
   131  					}
   132  
   133  					mockProvider := mockStatsProvider{stats: stats}
   134  
   135  					for i := 0; i < 2; i = i + 1 {
   136  						wg.Add(1)
   137  						analytics := NewService(&(*cfg).Server, s, &mockProvider)
   138  						go analytics.Start()
   139  						wg.Wait()
   140  						analytics.Stop()
   141  					}
   142  
   143  					Expect(v["controller_diff"]).To(BeEquivalentTo(2))
   144  					Expect(v["controller_ingest"]).To(BeEquivalentTo(2))
   145  					Expect(v["controller_comparison"]).To(BeEquivalentTo(2))
   146  					Expect(v["analytics_persistence"]).To(BeTrue())
   147  
   148  					close(done)
   149  				}()
   150  				Eventually(done, 2).Should(BeClosed())
   151  			})
   152  		})
   153  	})
   154  })