feat: implement observability and metrics (Task 22)
- Add metrics package tracking cache hit-rate, API quota, circuit breaker trips, search time - Integrate metrics with cache layer, yandex client, and API handlers - Add /metrics HTTP endpoint for Prometheus-compatible metrics exposure - Write tests for metrics functionality across cache, yandex, and API handlers - Update test files to support new metrics infrastructure
This commit is contained in:
35
internal/cache/store_test.go
vendored
35
internal/cache/store_test.go
vendored
@@ -5,6 +5,8 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/go-redis/redis/v8"
|
||||
|
||||
"trip-planner/internal/metrics"
|
||||
)
|
||||
|
||||
// TestCacheGetSet tests basic Get and Set operations.
|
||||
@@ -12,7 +14,7 @@ func TestCacheGetSet(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
client := NewRedisClient(redis.NewClient(&redis.Options{
|
||||
Addr: "localhost:6379",
|
||||
}))
|
||||
}), metrics.New())
|
||||
|
||||
// Test Set
|
||||
key := &CacheKey{Kind: "city", Code: "c146"}
|
||||
@@ -81,7 +83,7 @@ func TestCacheAsideGetOrSet(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
client := NewRedisClient(redis.NewClient(&redis.Options{
|
||||
Addr: "localhost:6379",
|
||||
}))
|
||||
}), metrics.New())
|
||||
|
||||
fetchCallCount := 0
|
||||
fetchFunc := func() ([]byte, error) {
|
||||
@@ -91,7 +93,7 @@ func TestCacheAsideGetOrSet(t *testing.T) {
|
||||
|
||||
// First call: cache miss, should fetch from backend
|
||||
key := &CacheKey{Kind: "station", Code: "s9600213"}
|
||||
data, err := NewCacheAside(client).GetOrSetFuncPattern(ctx, key, fetchFunc, CityTTL)
|
||||
data, err := NewCacheAside(client, metrics.New()).GetOrSetFuncPattern(ctx, key, fetchFunc, CityTTL)
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error on cache miss, got: %v", err)
|
||||
}
|
||||
@@ -104,7 +106,7 @@ func TestCacheAsideGetOrSet(t *testing.T) {
|
||||
|
||||
// Second call: cache hit, should not fetch from backend
|
||||
fetchCallCount = 0
|
||||
data, err = NewCacheAside(client).GetOrSetFuncPattern(ctx, key, fetchFunc, CityTTL)
|
||||
data, err = NewCacheAside(client, metrics.New()).GetOrSetFuncPattern(ctx, key, fetchFunc, CityTTL)
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error on cache hit, got: %v", err)
|
||||
}
|
||||
@@ -121,7 +123,7 @@ func TestCacheAsideGetCity(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
client := NewRedisClient(redis.NewClient(&redis.Options{
|
||||
Addr: "localhost:6379",
|
||||
}))
|
||||
}), metrics.New())
|
||||
|
||||
fetchCallCount := 0
|
||||
fetchFunc := func() ([]byte, error) {
|
||||
@@ -130,7 +132,7 @@ func TestCacheAsideGetCity(t *testing.T) {
|
||||
}
|
||||
|
||||
key := &CacheKey{Kind: "city", Code: "c146"}
|
||||
data, err := NewCacheAside(client).GetCity(ctx, key, fetchFunc)
|
||||
data, err := NewCacheAside(client, metrics.New()).GetCity(ctx, key, fetchFunc)
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error on cache miss for city, got: %v", err)
|
||||
}
|
||||
@@ -143,10 +145,13 @@ func TestCacheAsideGetCity(t *testing.T) {
|
||||
|
||||
// Second call: cache hit
|
||||
fetchCallCount = 0
|
||||
data, err = NewCacheAside(client).GetCity(ctx, key, fetchFunc)
|
||||
data, err = NewCacheAside(client, metrics.New()).GetCity(ctx, key, fetchFunc)
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error on cache hit for city, got: %v", err)
|
||||
}
|
||||
if string(data) != `{"code":"c146","title":"Simferopol"}` {
|
||||
t.Errorf("expected %s, got %s", `{"code":"c146","title":"Simferopol"}`, string(data))
|
||||
}
|
||||
if fetchCallCount != 0 {
|
||||
t.Errorf("expected 0 fetch calls on cache hit, got %d", fetchCallCount)
|
||||
}
|
||||
@@ -157,7 +162,7 @@ func TestCacheAsideGetSearch(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
client := NewRedisClient(redis.NewClient(&redis.Options{
|
||||
Addr: "localhost:6379",
|
||||
}))
|
||||
}), metrics.New())
|
||||
|
||||
fetchNearTerm := func() ([]byte, error) {
|
||||
return []byte(`{"near_term":true}`), nil
|
||||
@@ -172,7 +177,7 @@ func TestCacheAsideGetSearch(t *testing.T) {
|
||||
farKey := &CacheKey{Kind: "search", From: "c146", To: "c213", Date: "2026-09-15"}
|
||||
|
||||
// Near-term: should use SearchNearTermTTL (3 hours)
|
||||
data, err := NewCacheAside(client).GetSearch(ctx, nearKey, fetchNearTerm, false)
|
||||
data, err := NewCacheAside(client, metrics.New()).GetSearch(ctx, nearKey, fetchNearTerm, false)
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error on near-term search cache miss, got: %v", err)
|
||||
}
|
||||
@@ -181,7 +186,7 @@ func TestCacheAsideGetSearch(t *testing.T) {
|
||||
}
|
||||
|
||||
// Far-term: should use SearchFarTermTTL (7 days)
|
||||
data, err = NewCacheAside(client).GetSearch(ctx, farKey, fetchFarTerm, true)
|
||||
data, err = NewCacheAside(client, metrics.New()).GetSearch(ctx, farKey, fetchFarTerm, true)
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error on far-term search cache miss, got: %v", err)
|
||||
}
|
||||
@@ -195,7 +200,7 @@ func TestCacheInvalidate(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
client := NewRedisClient(redis.NewClient(&redis.Options{
|
||||
Addr: "localhost:6379",
|
||||
}))
|
||||
}), metrics.New())
|
||||
|
||||
// Set up some keys
|
||||
cityKey := &CacheKey{Kind: "city", Code: "c146"}
|
||||
@@ -208,20 +213,20 @@ func TestCacheInvalidate(t *testing.T) {
|
||||
client.Set(ctx, searchKey, []byte(`{"search":true}`), SearchNearTermTTL)
|
||||
|
||||
// Invalidate city
|
||||
err := NewCacheAside(client).InvalidateCity(ctx, cityKey)
|
||||
err := NewCacheAside(client, metrics.New()).InvalidateCity(ctx, cityKey)
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error invalidating city, got: %v", err)
|
||||
}
|
||||
|
||||
// Invalidate station
|
||||
err = NewCacheAside(client).InvalidateStation(ctx, stationKey)
|
||||
err = NewCacheAside(client, metrics.New()).InvalidateStation(ctx, stationKey)
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error invalidating station, got: %v", err)
|
||||
}
|
||||
|
||||
// Invalidate search
|
||||
err = NewCacheAside(client).InvalidateSearch(ctx, searchKey)
|
||||
err = NewCacheAside(client, metrics.New()).InvalidateSearch(ctx, searchKey)
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error invalidating search, got: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user