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 })