168 lines
4.3 KiB
Go
168 lines
4.3 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"testing"
|
|
"time"
|
|
|
|
pb "git.ipng.ch/ipng/nginx-logtail/proto/logtailpb"
|
|
st "git.ipng.ch/ipng/nginx-logtail/internal/store"
|
|
)
|
|
|
|
func makeStore() *Store {
|
|
return NewStore("test")
|
|
}
|
|
|
|
func ingestN(s *Store, website, prefix, uri, status string, n int) {
|
|
for i := 0; i < n; i++ {
|
|
s.ingest(LogRecord{website, prefix, uri, status})
|
|
}
|
|
}
|
|
|
|
func TestIngestAndRotate(t *testing.T) {
|
|
s := makeStore()
|
|
ingestN(s, "example.com", "1.2.3.0/24", "/", "200", 100)
|
|
ingestN(s, "other.com", "5.6.7.0/24", "/api", "429", 50)
|
|
|
|
s.rotate(time.Now())
|
|
|
|
s.mu.RLock()
|
|
defer s.mu.RUnlock()
|
|
if s.fineFilled != 1 {
|
|
t.Fatalf("fineFilled = %d, want 1", s.fineFilled)
|
|
}
|
|
snap := s.fineRing[(s.fineHead-1+st.FineRingSize)%st.FineRingSize]
|
|
if len(snap.Entries) != 2 {
|
|
t.Fatalf("snapshot has %d entries, want 2", len(snap.Entries))
|
|
}
|
|
if snap.Entries[0].Count != 100 {
|
|
t.Errorf("top entry count = %d, want 100", snap.Entries[0].Count)
|
|
}
|
|
}
|
|
|
|
func TestLiveMapCap(t *testing.T) {
|
|
s := makeStore()
|
|
for i := 0; i < liveMapCap+100; i++ {
|
|
s.ingest(LogRecord{
|
|
Website: fmt.Sprintf("site%d.com", i),
|
|
ClientPrefix: "1.2.3.0/24",
|
|
URI: "/",
|
|
Status: "200",
|
|
})
|
|
}
|
|
if s.liveLen != liveMapCap {
|
|
t.Errorf("liveLen = %d, want %d", s.liveLen, liveMapCap)
|
|
}
|
|
}
|
|
|
|
func TestQueryTopN(t *testing.T) {
|
|
s := makeStore()
|
|
ingestN(s, "busy.com", "1.0.0.0/24", "/", "200", 300)
|
|
ingestN(s, "medium.com", "2.0.0.0/24", "/", "200", 100)
|
|
ingestN(s, "quiet.com", "3.0.0.0/24", "/", "200", 10)
|
|
s.rotate(time.Now())
|
|
|
|
entries := s.QueryTopN(nil, pb.GroupBy_WEBSITE, 2, pb.Window_W1M)
|
|
if len(entries) != 2 {
|
|
t.Fatalf("got %d entries, want 2", len(entries))
|
|
}
|
|
if entries[0].Label != "busy.com" {
|
|
t.Errorf("top entry = %q, want busy.com", entries[0].Label)
|
|
}
|
|
if entries[0].Count != 300 {
|
|
t.Errorf("top count = %d, want 300", entries[0].Count)
|
|
}
|
|
}
|
|
|
|
func TestQueryTopNWithFilter(t *testing.T) {
|
|
s := makeStore()
|
|
ingestN(s, "example.com", "1.0.0.0/24", "/api", "429", 200)
|
|
ingestN(s, "example.com", "2.0.0.0/24", "/api", "200", 500)
|
|
ingestN(s, "other.com", "3.0.0.0/24", "/", "429", 100)
|
|
s.rotate(time.Now())
|
|
|
|
status429 := int32(429)
|
|
entries := s.QueryTopN(&pb.Filter{HttpResponse: &status429}, pb.GroupBy_WEBSITE, 10, pb.Window_W1M)
|
|
if len(entries) != 2 {
|
|
t.Fatalf("got %d entries, want 2", len(entries))
|
|
}
|
|
if entries[0].Label != "example.com" || entries[0].Count != 200 {
|
|
t.Errorf("unexpected top: %+v", entries[0])
|
|
}
|
|
}
|
|
|
|
func TestQueryTrend(t *testing.T) {
|
|
s := makeStore()
|
|
now := time.Now()
|
|
|
|
ingestN(s, "x.com", "1.0.0.0/24", "/", "200", 10)
|
|
s.rotate(now.Add(-2 * time.Minute))
|
|
ingestN(s, "x.com", "1.0.0.0/24", "/", "200", 20)
|
|
s.rotate(now.Add(-1 * time.Minute))
|
|
ingestN(s, "x.com", "1.0.0.0/24", "/", "200", 30)
|
|
s.rotate(now)
|
|
|
|
points := s.QueryTrend(nil, pb.Window_W5M)
|
|
if len(points) != 3 {
|
|
t.Fatalf("got %d points, want 3", len(points))
|
|
}
|
|
if points[0].Count != 10 || points[1].Count != 20 || points[2].Count != 30 {
|
|
t.Errorf("unexpected counts: %v", points)
|
|
}
|
|
}
|
|
|
|
func TestCoarseRingPopulated(t *testing.T) {
|
|
s := makeStore()
|
|
now := time.Now()
|
|
|
|
for i := 0; i < st.CoarseEvery; i++ {
|
|
ingestN(s, "x.com", "1.0.0.0/24", "/", "200", 10)
|
|
s.rotate(now.Add(time.Duration(i) * time.Minute))
|
|
}
|
|
|
|
s.mu.RLock()
|
|
defer s.mu.RUnlock()
|
|
if s.coarseFilled != 1 {
|
|
t.Fatalf("coarseFilled = %d, want 1", s.coarseFilled)
|
|
}
|
|
coarse := s.coarseRing[(s.coarseHead-1+st.CoarseRingSize)%st.CoarseRingSize]
|
|
if len(coarse.Entries) == 0 {
|
|
t.Fatal("coarse snapshot is empty")
|
|
}
|
|
if coarse.Entries[0].Count != 50 {
|
|
t.Errorf("coarse count = %d, want 50", coarse.Entries[0].Count)
|
|
}
|
|
}
|
|
|
|
func TestSubscribeBroadcast(t *testing.T) {
|
|
s := makeStore()
|
|
ch := s.Subscribe()
|
|
|
|
ingestN(s, "x.com", "1.0.0.0/24", "/", "200", 5)
|
|
s.rotate(time.Now())
|
|
|
|
select {
|
|
case snap := <-ch:
|
|
if len(snap.Entries) != 1 {
|
|
t.Errorf("got %d entries, want 1", len(snap.Entries))
|
|
}
|
|
case <-time.After(time.Second):
|
|
t.Fatal("no snapshot received within 1s")
|
|
}
|
|
s.Unsubscribe(ch)
|
|
}
|
|
|
|
func TestTopKOrdering(t *testing.T) {
|
|
m := map[string]int64{"a": 5, "b": 100, "c": 1, "d": 50}
|
|
entries := st.TopKFromMap(m, 3)
|
|
if len(entries) != 3 {
|
|
t.Fatalf("got %d entries, want 3", len(entries))
|
|
}
|
|
if entries[0].Label != "b" || entries[0].Count != 100 {
|
|
t.Errorf("wrong top: %+v", entries[0])
|
|
}
|
|
if entries[1].Label != "d" || entries[1].Count != 50 {
|
|
t.Errorf("wrong second: %+v", entries[1])
|
|
}
|
|
}
|