mirror of
https://github.com/hanzoai/otel-collector.git
synced 2026-11-03 02:20:04 +00:00
Modules declare go 1.27.1 and the build images follow. go fix applied the modernizers; omitzero is not applied, since it changes what the JSON says. Co-authored-by: Hanzo Dev <[email protected]>
236 lines
6.6 KiB
Go
236 lines
6.6 KiB
Go
// Copyright The OpenTelemetry Authors
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package o11yawsfirehosereceiver
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/base64"
|
|
"errors"
|
|
"fmt"
|
|
"net"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strconv"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/goccy/go-json"
|
|
|
|
"github.com/hanzoai/otel-collector/receiver/o11yawsfirehosereceiver/internal/metadata"
|
|
"github.com/stretchr/testify/require"
|
|
"go.opentelemetry.io/collector/component"
|
|
"go.opentelemetry.io/collector/component/componenttest"
|
|
"go.opentelemetry.io/collector/config/confignet"
|
|
"go.opentelemetry.io/collector/receiver/receivertest"
|
|
)
|
|
|
|
const (
|
|
testFirehoseRequestID = "firehose-request-id"
|
|
testFirehoseAccessKey = "firehose-access-key"
|
|
)
|
|
|
|
type nopFirehoseConsumer struct {
|
|
err error
|
|
}
|
|
|
|
var _ firehoseConsumer = (*nopFirehoseConsumer)(nil)
|
|
|
|
func newNopFirehoseConsumer(err error) *nopFirehoseConsumer {
|
|
return &nopFirehoseConsumer{err}
|
|
}
|
|
|
|
func (nfc *nopFirehoseConsumer) Consume(context.Context, [][]byte, map[string]string) error {
|
|
return nfc.err
|
|
}
|
|
|
|
func TestStart(t *testing.T) {
|
|
testCases := map[string]struct {
|
|
host component.Host
|
|
wantErr error
|
|
}{
|
|
"WithoutHost": {
|
|
wantErr: errMissingHost,
|
|
},
|
|
"WithHost": {
|
|
host: componenttest.NewNopHost(),
|
|
},
|
|
}
|
|
for name, testCase := range testCases {
|
|
t.Run(name, func(t *testing.T) {
|
|
cfg := createDefaultConfig().(*Config)
|
|
ctx := context.TODO()
|
|
r := testFirehoseReceiver(cfg, nil)
|
|
got := r.Start(ctx, testCase.host)
|
|
require.Equal(t, testCase.wantErr, got)
|
|
if r.server != nil {
|
|
require.NoError(t, r.Shutdown(ctx))
|
|
}
|
|
})
|
|
}
|
|
t.Run("WithPortTaken", func(t *testing.T) {
|
|
listener, err := net.Listen("tcp", "localhost:")
|
|
require.NoError(t, err)
|
|
t.Cleanup(func() {
|
|
require.NoError(t, listener.Close())
|
|
})
|
|
cfg := &Config{
|
|
NetAddr: confignet.AddrConfig{
|
|
Endpoint: listener.Addr().String(),
|
|
Transport: "tcp",
|
|
},
|
|
}
|
|
ctx := context.TODO()
|
|
r := testFirehoseReceiver(cfg, nil)
|
|
got := r.Start(ctx, componenttest.NewNopHost())
|
|
require.Error(t, got)
|
|
if r.server != nil {
|
|
require.NoError(t, r.Shutdown(ctx))
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestFirehoseRequest(t *testing.T) {
|
|
defaultConsumer := newNopFirehoseConsumer(nil)
|
|
firehoseConsumerErr := errors.New("firehose consumer error")
|
|
cfg := &Config{}
|
|
var noRecords []firehoseRecord
|
|
testCases := map[string]struct {
|
|
headers map[string]string
|
|
commonAttributes map[string]string
|
|
body any
|
|
consumer firehoseConsumer
|
|
wantStatusCode int
|
|
wantErr error
|
|
}{
|
|
"WithoutRequestId/Header": {
|
|
headers: map[string]string{
|
|
headerFirehoseRequestID: "",
|
|
},
|
|
body: testFirehoseRequest(testFirehoseRequestID, noRecords),
|
|
wantStatusCode: http.StatusBadRequest,
|
|
wantErr: errInHeaderMissingRequestID,
|
|
},
|
|
"WithoutRequestId/Body": {
|
|
headers: map[string]string{
|
|
headerFirehoseRequestID: testFirehoseRequestID,
|
|
},
|
|
body: testFirehoseRequest("", noRecords),
|
|
wantStatusCode: http.StatusBadRequest,
|
|
wantErr: errInBodyMissingRequestID,
|
|
},
|
|
"WithDifferentRequestIds": {
|
|
headers: map[string]string{
|
|
headerFirehoseRequestID: testFirehoseRequestID,
|
|
},
|
|
body: testFirehoseRequest("otherId", noRecords),
|
|
wantStatusCode: http.StatusBadRequest,
|
|
wantErr: errInBodyDiffRequestID,
|
|
},
|
|
"WithInvalidBody": {
|
|
body: "{ test: ",
|
|
wantStatusCode: http.StatusBadRequest,
|
|
wantErr: errors.New("invalid character '\"' looking for beginning of value"),
|
|
},
|
|
"WithNoRecords": {
|
|
body: testFirehoseRequest(testFirehoseRequestID, noRecords),
|
|
wantStatusCode: http.StatusOK,
|
|
},
|
|
"WithFirehoseConsumerError": {
|
|
body: testFirehoseRequest(testFirehoseRequestID, noRecords),
|
|
consumer: newNopFirehoseConsumer(firehoseConsumerErr),
|
|
wantStatusCode: http.StatusInternalServerError,
|
|
wantErr: firehoseConsumerErr,
|
|
},
|
|
"WithCorruptBase64Records": {
|
|
body: testFirehoseRequest(testFirehoseRequestID, []firehoseRecord{
|
|
{Data: "XXXXXaGVsbG8="},
|
|
}),
|
|
wantStatusCode: http.StatusBadRequest,
|
|
wantErr: fmt.Errorf("unable to base64 decode the record at index 0: %w", base64.CorruptInputError(12)),
|
|
},
|
|
"WithValidRecords": {
|
|
body: testFirehoseRequest(testFirehoseRequestID, []firehoseRecord{
|
|
testFirehoseRecord("test"),
|
|
}),
|
|
wantStatusCode: http.StatusOK,
|
|
},
|
|
"WithValidRecords/CommonAttributes": {
|
|
body: testFirehoseRequest(testFirehoseRequestID, []firehoseRecord{
|
|
testFirehoseRecord("test"),
|
|
}),
|
|
commonAttributes: map[string]string{
|
|
"TestAttribute": "common",
|
|
},
|
|
wantStatusCode: http.StatusOK,
|
|
},
|
|
}
|
|
for name, testCase := range testCases {
|
|
t.Run(name, func(t *testing.T) {
|
|
body, err := json.Marshal(testCase.body)
|
|
require.NoError(t, err)
|
|
|
|
requestBody := bytes.NewBuffer(body)
|
|
|
|
request := httptest.NewRequest(http.MethodPost, "/", requestBody)
|
|
request.Header.Set(headerContentType, "application/json")
|
|
request.Header.Set(headerContentLength, strconv.Itoa(requestBody.Len()))
|
|
request.Header.Set(headerFirehoseRequestID, testFirehoseRequestID)
|
|
if testCase.headers != nil {
|
|
for k, v := range testCase.headers {
|
|
request.Header.Set(k, v)
|
|
}
|
|
}
|
|
if testCase.commonAttributes != nil {
|
|
attrs, err := json.Marshal(firehoseCommonAttributes{
|
|
CommonAttributes: testCase.commonAttributes,
|
|
})
|
|
require.NoError(t, err)
|
|
request.Header.Set(headerFirehoseCommonAttributes, string(attrs))
|
|
}
|
|
|
|
consumer := testCase.consumer
|
|
if consumer == nil {
|
|
consumer = defaultConsumer
|
|
}
|
|
r := testFirehoseReceiver(cfg, consumer)
|
|
|
|
got := httptest.NewRecorder()
|
|
r.ServeHTTP(got, request)
|
|
|
|
require.Equal(t, testCase.wantStatusCode, got.Code)
|
|
var gotResponse firehoseResponse
|
|
require.NoError(t, json.Unmarshal(got.Body.Bytes(), &gotResponse))
|
|
require.Equal(t, request.Header.Get(headerFirehoseRequestID), gotResponse.RequestID)
|
|
if testCase.wantErr != nil {
|
|
require.Equal(t, testCase.wantErr.Error(), gotResponse.ErrorMessage)
|
|
} else {
|
|
require.Empty(t, gotResponse.ErrorMessage)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// testFirehoseReceiver is a convenience function for creating a test firehoseReceiver
|
|
func testFirehoseReceiver(config *Config, consumer firehoseConsumer) *firehoseReceiver {
|
|
return &firehoseReceiver{
|
|
settings: receivertest.NewNopSettings(metadata.Type),
|
|
config: config,
|
|
consumer: consumer,
|
|
}
|
|
}
|
|
|
|
func testFirehoseRequest(requestID string, records []firehoseRecord) firehoseRequest {
|
|
return firehoseRequest{
|
|
RequestID: requestID,
|
|
Timestamp: time.Now().UnixMilli(),
|
|
Records: records,
|
|
}
|
|
}
|
|
|
|
func testFirehoseRecord(data string) firehoseRecord {
|
|
encoded := base64.StdEncoding.EncodeToString([]byte(data))
|
|
return firehoseRecord{Data: encoded}
|
|
}
|