chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
package github
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/github/github-mcp-server/pkg/observability"
|
||||
"github.com/github/github-mcp-server/pkg/observability/metrics"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// recordingMetrics is a metrics.Metrics implementation that captures emitted
|
||||
// metrics so tests can assert on telemetry. It is safe for concurrent use.
|
||||
type recordingMetrics struct {
|
||||
mu sync.Mutex
|
||||
increments []recordedMetric
|
||||
counters []recordedMetric
|
||||
}
|
||||
|
||||
type recordedMetric struct {
|
||||
key string
|
||||
tags map[string]string
|
||||
value int64
|
||||
}
|
||||
|
||||
func (m *recordingMetrics) Increment(key string, tags map[string]string) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
m.increments = append(m.increments, recordedMetric{key: key, tags: tags, value: 1})
|
||||
}
|
||||
|
||||
func (m *recordingMetrics) Counter(key string, tags map[string]string, value int64) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
m.counters = append(m.counters, recordedMetric{key: key, tags: tags, value: value})
|
||||
}
|
||||
|
||||
func (m *recordingMetrics) Distribution(_ string, _ map[string]string, _ float64) {}
|
||||
func (m *recordingMetrics) DistributionMs(_ string, _ map[string]string, _ time.Duration) {}
|
||||
func (m *recordingMetrics) WithTags(_ map[string]string) metrics.Metrics { return m }
|
||||
|
||||
// counter returns the recorded counter for the given key, or false if absent.
|
||||
func (m *recordingMetrics) counter(key string) (recordedMetric, bool) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
for _, c := range m.counters {
|
||||
if c.key == key {
|
||||
return c, true
|
||||
}
|
||||
}
|
||||
return recordedMetric{}, false
|
||||
}
|
||||
|
||||
// increment returns the recorded increment for the given key, or false if absent.
|
||||
func (m *recordingMetrics) increment(key string) (recordedMetric, bool) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
for _, c := range m.increments {
|
||||
if c.key == key {
|
||||
return c, true
|
||||
}
|
||||
}
|
||||
return recordedMetric{}, false
|
||||
}
|
||||
|
||||
// depsWithRecordingMetrics returns BaseDeps wired with a recording metrics sink
|
||||
// plus the sink for assertions.
|
||||
func depsWithRecordingMetrics(t *testing.T, base BaseDeps) (BaseDeps, *recordingMetrics) {
|
||||
t.Helper()
|
||||
rec := &recordingMetrics{}
|
||||
exporters, err := observability.NewExporters(slog.New(slog.DiscardHandler), rec)
|
||||
require.NoError(t, err)
|
||||
base.Obsv = exporters
|
||||
return base, rec
|
||||
}
|
||||
|
||||
func Test_recordFieldsUsage_Filtered(t *testing.T) {
|
||||
deps, rec := depsWithRecordingMetrics(t, BaseDeps{})
|
||||
|
||||
recordFieldsUsage(context.Background(), deps, "search_code", true, 100, 30)
|
||||
|
||||
call, ok := rec.increment(metricFieldsToolCall)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, "search_code", call.tags["tool"])
|
||||
assert.Equal(t, "true", call.tags["filtered"])
|
||||
|
||||
full, ok := rec.counter(metricFieldsBytesFull)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, int64(100), full.value)
|
||||
assert.Equal(t, "search_code", full.tags["tool"])
|
||||
assert.NotContains(t, full.tags, "filtered")
|
||||
|
||||
sent, ok := rec.counter(metricFieldsBytesSent)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, int64(30), sent.value)
|
||||
}
|
||||
|
||||
func Test_recordFieldsUsage_NotFiltered(t *testing.T) {
|
||||
deps, rec := depsWithRecordingMetrics(t, BaseDeps{})
|
||||
|
||||
recordFieldsUsage(context.Background(), deps, "search_code", false, 100, 100)
|
||||
|
||||
call, ok := rec.increment(metricFieldsToolCall)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, "false", call.tags["filtered"])
|
||||
|
||||
// No byte counters are emitted when the response was not filtered.
|
||||
_, ok = rec.counter(metricFieldsBytesFull)
|
||||
assert.False(t, ok)
|
||||
_, ok = rec.counter(metricFieldsBytesSent)
|
||||
assert.False(t, ok)
|
||||
}
|
||||
|
||||
func Test_recordFieldsUsage_NilExporterDoesNotPanic(t *testing.T) {
|
||||
// BaseDeps with no Obsv falls back to a noop sink rather than panicking.
|
||||
assert.NotPanics(t, func() {
|
||||
recordFieldsUsage(context.Background(), BaseDeps{}, "search_code", true, 100, 30)
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user