aggregator_test.go
Rama master · réplica de lectura del repositorio original.
package aastro
import (
"errors"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/zap"
)
func TestAggregator_MergeStrategy(t *testing.T) {
agg := &defaultAggregator{}
responses := []upstreamResponse{
okResponse(`{"a":1,"b":2}`),
okResponse(`{"b":99,"c":4}`),
}
t.Run("conflict policies", func(t *testing.T) {
tests := []struct {
name string
policy conflictPolicy
expected string
}{
{"overwrite uses last value", conflictPolicyOverwrite, `{"a":1,"b":99,"c":4}`},
{"first preserves earliest value", conflictPolicyFirst, `{"a":1,"b":2,"c":4}`},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := agg.aggregate(nil, responses, aggregation{
strategy: strategyMerge,
conflictPolicy: tt.policy,
}, zap.NewNop())
assert.Empty(t, result.errors)
jsonEqual(t, tt.expected, result.data)
})
}
})
t.Run("returns conflict error when policy is error", func(t *testing.T) {
result := agg.aggregate(nil, responses, aggregation{
strategy: strategyMerge,
conflictPolicy: conflictPolicyError,
}, zap.NewNop())
assert.Nil(t, result.data)
assert.ElementsMatch(t, []ClientError{ClientErrValueConflict}, result.errors)
})
t.Run("returns partial result when best effort and one upstream fails", func(t *testing.T) {
result := agg.aggregate(
nil,
[]upstreamResponse{
okResponse(`{"a":1}`),
errResponse(upstreamTimeout),
},
aggregation{
strategy: strategyMerge,
bestEffort: true,
conflictPolicy: conflictPolicyOverwrite,
},
zap.NewNop(),
)
assert.True(t, result.partial)
assert.ElementsMatch(t, []ClientError{ClientErrUpstreamUnavailable}, result.errors)
jsonEqual(t, `{"a":1}`, result.data)
})
}
func TestAggregator_ArrayStrategy(t *testing.T) {
agg := &defaultAggregator{}
t.Run("aggregates responses into an array", func(t *testing.T) {
result := agg.aggregate(
nil,
[]upstreamResponse{
okResponse(`{"x":1}`),
okResponse(`{"y":2}`),
},
aggregation{strategy: strategyArray},
zap.NewNop(),
)
assert.Empty(t, result.errors)
assert.False(t, result.partial)
jsonEqual(t, `[{"x":1},{"y":2}]`, result.data)
})
t.Run("skips failed upstream when best effort", func(t *testing.T) {
result := agg.aggregate(
nil,
[]upstreamResponse{
okResponse(`{"x":1}`),
errResponse(upstreamBadStatus),
},
aggregation{strategy: strategyArray, bestEffort: true},
zap.NewNop(),
)
assert.True(t, result.partial)
assert.ElementsMatch(t, []ClientError{ClientErrUpstreamError}, result.errors)
jsonEqual(t, `[{"x":1}]`, result.data)
})
}
func TestAggregator_NamespaceStrategy(t *testing.T) {
agg := &defaultAggregator{}
t.Run("groups responses by upstream name", func(t *testing.T) {
result := agg.aggregate(
mockUpstreams("users", "orders"),
[]upstreamResponse{
okResponse(`{"id":1}`),
okResponse(`{"total":99}`),
},
aggregation{strategy: strategyNamespace},
zap.NewNop(),
)
assert.Empty(t, result.errors)
jsonEqual(t, `{"users":{"id":1},"orders":{"total":99}}`, result.data)
})
t.Run("omits failed upstream when best effort", func(t *testing.T) {
result := agg.aggregate(
mockUpstreams("users", "orders"),
[]upstreamResponse{
okResponse(`{"id":1}`),
errResponse(upstreamTimeout),
},
aggregation{strategy: strategyNamespace, bestEffort: true},
zap.NewNop(),
)
assert.True(t, result.partial)
assert.Len(t, result.errors, 1)
var got map[string]any
require.NoError(t, decodeJSONInto(result.data, &got))
assert.Contains(t, got, "users")
assert.NotContains(t, got, "orders")
})
}
func TestMapUpstreamError(t *testing.T) {
tests := []struct {
name string
kind upstreamErrorKind
want ClientError
}{
{"timeout → upstream unavailable", upstreamTimeout, ClientErrUpstreamUnavailable},
{"connection → upstream unavailable", upstreamConnection, ClientErrUpstreamUnavailable},
{"bad status → upstream error", upstreamBadStatus, ClientErrUpstreamError},
{"body too large → upstream body too large", upstreamBodyTooLarge, ClientErrUpstreamBodyTooLarge},
{"internal → internal", upstreamInternal, ClientErrInternal},
{"read error → internal", upstreamReadError, ClientErrInternal},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := mapUpstreamError(&upstreamError{
kind: tt.kind,
err: errors.New("err"),
})
assert.Equal(t, tt.want, got)
})
}
}