more otel
This commit is contained in:
1 parent
9620e3bfa6
commit
21f04d696b
88 files changed
+23419
-192
No files matched your search
@@ -73,3 +73,157 @@ func TestTrackerListRespectsLimit(t *testing.T) {
|
||||
t.Fatalf("expected limit to cap results at 2, got %d", len(list))
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrackerFilteredCursorPagination(t *testing.T) {
|
||||
tracker := NewTracker()
|
||||
tracker.Record("span_error", "nginx", "GET /.env", Sample{Timestamp: 300, SourceIP: "203.0.113.10", HTTPRoute: "/.env"})
|
||||
tracker.Record("span_error", "nginx", "GET /.git/HEAD", Sample{Timestamp: 200, SourceIP: "203.0.113.11", HTTPRoute: "/.git/HEAD"})
|
||||
tracker.Record("log_error", "api", "database down", Sample{Timestamp: 100})
|
||||
|
||||
page := tracker.ListFiltered(ListOptions{Service: "nginx", Kind: "span_error", Route: "/.env", Limit: 1})
|
||||
if len(page.Issues) != 1 || page.Issues[0].Title != "GET /.env" {
|
||||
t.Fatalf("unexpected filtered page: %+v", page)
|
||||
}
|
||||
if page.HasMore {
|
||||
t.Error("single matching issue should not have another page")
|
||||
}
|
||||
|
||||
all := tracker.ListFiltered(ListOptions{Service: "nginx", Limit: 1})
|
||||
if len(all.Issues) != 1 || !all.HasMore {
|
||||
t.Fatalf("expected first paginated page with more results, got %+v", all)
|
||||
}
|
||||
next := tracker.ListFiltered(ListOptions{
|
||||
Service: "nginx",
|
||||
AfterLastSeen: all.Issues[0].LastSeen,
|
||||
AfterID: all.Issues[0].ID,
|
||||
Limit: 1,
|
||||
})
|
||||
if len(next.Issues) != 1 || next.Issues[0].Title != "GET /.git/HEAD" || next.HasMore {
|
||||
t.Fatalf("unexpected cursor page: %+v", next)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNormalizeTitleGroupsVariableMessages(t *testing.T) {
|
||||
cases := [][3]string{
|
||||
// same, same, different: [a, b, expectedEqual(a,b)]
|
||||
{"User 123 not found", "User 456 not found", "user <num> not found"},
|
||||
{"order 550e8400-e29b-41d4-a716-446655440000 failed", "order 550e8400-e29b-41d4-a716-446655440001 failed", "order <id> failed"},
|
||||
{"request 0a1b2c3d4e5f6a7b timed out", "request ff11aa22bb33cc44 timed out", "request <id> timed out"},
|
||||
}
|
||||
for _, c := range cases {
|
||||
if got := NormalizeTitle(c[0]); got != c[2] {
|
||||
t.Errorf("NormalizeTitle(%q) = %q, want %q", c[0], got, c[2])
|
||||
}
|
||||
if NormalizeTitle(c[0]) != NormalizeTitle(c[1]) {
|
||||
t.Errorf("expected %q and %q to normalize identically", c[0], c[1])
|
||||
}
|
||||
}
|
||||
// Words spelled from hex letters without digits must survive.
|
||||
if got := NormalizeTitle("bad failed deadbeef badge"); got != "bad failed deadbeef badge" {
|
||||
t.Errorf("word-like hex incorrectly replaced: %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrackerNormalizedFingerprinting(t *testing.T) {
|
||||
tracker := NewTracker()
|
||||
tracker.Record("log_error", "api", "User 123 not found", Sample{Timestamp: 100, Message: "User 123 not found"})
|
||||
tracker.Record("log_error", "api", "User 98765432 not found", Sample{Timestamp: 200, Message: "User 98765432 not found"})
|
||||
|
||||
list := tracker.List(0)
|
||||
if len(list) != 1 {
|
||||
t.Fatalf("expected variable messages to merge into 1 issue, got %d", len(list))
|
||||
}
|
||||
if list[0].Count != 2 {
|
||||
t.Errorf("expected count 2, got %d", list[0].Count)
|
||||
}
|
||||
// The display title is the first-seen raw message.
|
||||
if list[0].Title != "User 123 not found" {
|
||||
t.Errorf("expected first-seen raw title kept, got %q", list[0].Title)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrackerTrendBuckets(t *testing.T) {
|
||||
tracker := NewTracker()
|
||||
// Two occurrences in the same hour.
|
||||
tracker.Record("log_error", "api", "boom", Sample{Timestamp: 100})
|
||||
tracker.Record("log_error", "api", "boom", Sample{Timestamp: 200})
|
||||
|
||||
issue, ok := tracker.Get(tracker.List(1)[0].ID)
|
||||
if !ok {
|
||||
t.Fatal("expected issue to exist")
|
||||
}
|
||||
bucket := HourBucket(100)
|
||||
if issue.Trend[bucket] != 2 {
|
||||
t.Errorf("expected trend[%s]=2, got %d", bucket, issue.Trend[bucket])
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrackerStatusLifecycle(t *testing.T) {
|
||||
tracker := NewTracker()
|
||||
tracker.Record("log_error", "api", "boom", Sample{Timestamp: 100})
|
||||
id := tracker.List(1)[0].ID
|
||||
|
||||
// Invalid status rejected.
|
||||
if _, ok := tracker.SetStatus(id, "bogus"); ok {
|
||||
t.Error("expected invalid status to be rejected")
|
||||
}
|
||||
// Unknown id rejected.
|
||||
if _, ok := tracker.SetStatus("nope", StatusResolved); ok {
|
||||
t.Error("expected unknown id to be rejected")
|
||||
}
|
||||
// Resolve.
|
||||
resolved, ok := tracker.SetStatus(id, StatusResolved)
|
||||
if !ok || resolved.Status != StatusResolved || resolved.ResolvedAt == 0 {
|
||||
t.Fatalf("expected resolved issue, got %+v ok=%v", resolved, ok)
|
||||
}
|
||||
// A new occurrence reopens it (regression).
|
||||
tracker.Record("log_error", "api", "boom", Sample{Timestamp: 200})
|
||||
issue, _ := tracker.Get(id)
|
||||
if issue.Status != StatusOpen || issue.ResolvedAt != 0 {
|
||||
t.Errorf("expected regression to reopen issue, got status=%s resolvedAt=%d", issue.Status, issue.ResolvedAt)
|
||||
}
|
||||
// Ignored survives new occurrences.
|
||||
if _, ok := tracker.SetStatus(id, StatusIgnored); !ok {
|
||||
t.Fatal("expected ignore to succeed")
|
||||
}
|
||||
tracker.Record("log_error", "api", "boom", Sample{Timestamp: 300})
|
||||
issue, _ = tracker.Get(id)
|
||||
if issue.Status != StatusIgnored {
|
||||
t.Errorf("expected ignored state to survive new occurrence, got %s", issue.Status)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrackerPatternsAndSimilarSuggestions(t *testing.T) {
|
||||
tracker := NewTracker()
|
||||
tracker.Record("span_error", "checkout", "HTTP 504: database timeout for order 123", Sample{
|
||||
Timestamp: 100, HTTPRoute: "/orders/123", HTTPStatus: 504, Stacktrace: "db.go:42\nhandler.go:10",
|
||||
})
|
||||
tracker.Record("span_error", "checkout", "HTTP 502: database timeout for order 456", Sample{
|
||||
Timestamp: 200, HTTPRoute: "/orders/456", HTTPStatus: 502, Stacktrace: "db.go:42\nhandler.go:10",
|
||||
})
|
||||
issuesList := tracker.List(0)
|
||||
if len(issuesList) != 2 {
|
||||
t.Fatalf("expected separate exact issues, got %d", len(issuesList))
|
||||
}
|
||||
if issuesList[0].PatternID == "" || issuesList[0].PatternID != issuesList[1].PatternID {
|
||||
t.Fatalf("expected related HTTP variants to share a pattern: %+v", issuesList)
|
||||
}
|
||||
if issuesList[0].Samples[0].NormalizedRoute != "/orders/{id}" {
|
||||
t.Fatalf("expected normalized route, got %q", issuesList[0].Samples[0].NormalizedRoute)
|
||||
}
|
||||
related, ok := tracker.Similar(issuesList[0].ID, 5)
|
||||
if !ok || len(related) != 1 || related[0].Score < .45 {
|
||||
t.Fatalf("expected deterministic related suggestion, got ok=%v related=%+v", ok, related)
|
||||
}
|
||||
if !tracker.SetSimilarityFeedback(issuesList[0].ID, related[0].Issue.ID, "same") {
|
||||
t.Fatal("expected feedback to be accepted")
|
||||
}
|
||||
related, _ = tracker.Similar(issuesList[0].ID, 5)
|
||||
if related[0].Decision != "same" {
|
||||
t.Fatalf("expected feedback decision, got %q", related[0].Decision)
|
||||
}
|
||||
patterns := tracker.Patterns()
|
||||
if len(patterns) != 1 || patterns[0].IssueCount != 2 || patterns[0].OccurrenceCount != 2 {
|
||||
t.Fatalf("expected one two-issue pattern, got %+v", patterns)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user