opentelemetry-collector/component/status_test.go

331 lines
8.5 KiB
Go

// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
package component
import (
"fmt"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestNewStatusEvent(t *testing.T) {
statuses := []Status{
StatusStarting,
StatusOK,
StatusRecoverableError,
StatusPermanentError,
StatusFatalError,
StatusStopping,
StatusStopped,
}
for _, status := range statuses {
t.Run(fmt.Sprintf("%s without error", status), func(t *testing.T) {
ev := NewStatusEvent(status)
require.Equal(t, status, ev.Status())
require.Nil(t, ev.Err())
require.False(t, ev.Timestamp().IsZero())
})
}
}
func TestStatusEventsWithError(t *testing.T) {
statusConstructorMap := map[Status]func(error) *StatusEvent{
StatusRecoverableError: NewRecoverableErrorEvent,
StatusPermanentError: NewPermanentErrorEvent,
StatusFatalError: NewFatalErrorEvent,
}
for status, newEvent := range statusConstructorMap {
t.Run(fmt.Sprintf("error status constructor for: %s", status), func(t *testing.T) {
ev := newEvent(assert.AnError)
require.Equal(t, status, ev.Status())
require.Equal(t, assert.AnError, ev.Err())
require.False(t, ev.Timestamp().IsZero())
})
}
}
func TestAggregateStatus(t *testing.T) {
for _, tc := range []struct {
name string
statusMap map[*InstanceID]*StatusEvent
expectedStatus Status
}{
{
name: "aggregate status with fatal is FatalError",
statusMap: map[*InstanceID]*StatusEvent{
{}: NewStatusEvent(StatusStarting),
{}: NewStatusEvent(StatusOK),
{}: NewStatusEvent(StatusFatalError),
{}: NewStatusEvent(StatusRecoverableError),
},
expectedStatus: StatusFatalError,
},
{
name: "aggregate status with permanent is PermanentError",
statusMap: map[*InstanceID]*StatusEvent{
{}: NewStatusEvent(StatusStarting),
{}: NewStatusEvent(StatusOK),
{}: NewStatusEvent(StatusPermanentError),
{}: NewStatusEvent(StatusRecoverableError),
},
expectedStatus: StatusPermanentError,
},
{
name: "aggregate status with stopping is Stopping",
statusMap: map[*InstanceID]*StatusEvent{
{}: NewStatusEvent(StatusStarting),
{}: NewStatusEvent(StatusOK),
{}: NewStatusEvent(StatusRecoverableError),
{}: NewStatusEvent(StatusStopping),
},
expectedStatus: StatusStopping,
},
{
name: "aggregate status with stopped and non-stopped is Stopping",
statusMap: map[*InstanceID]*StatusEvent{
{}: NewStatusEvent(StatusStarting),
{}: NewStatusEvent(StatusOK),
{}: NewStatusEvent(StatusRecoverableError),
{}: NewStatusEvent(StatusStopped),
},
expectedStatus: StatusStopping,
},
{
name: "aggregate status with all stopped is Stopped",
statusMap: map[*InstanceID]*StatusEvent{
{}: NewStatusEvent(StatusStopped),
{}: NewStatusEvent(StatusStopped),
{}: NewStatusEvent(StatusStopped),
},
expectedStatus: StatusStopped,
},
{
name: "aggregate status with recoverable is RecoverableError",
statusMap: map[*InstanceID]*StatusEvent{
{}: NewStatusEvent(StatusStarting),
{}: NewStatusEvent(StatusOK),
{}: NewStatusEvent(StatusRecoverableError),
},
expectedStatus: StatusRecoverableError,
},
{
name: "aggregate status with starting is Starting",
statusMap: map[*InstanceID]*StatusEvent{
{}: NewStatusEvent(StatusStarting),
{}: NewStatusEvent(StatusOK),
},
expectedStatus: StatusStarting,
},
{
name: "aggregate status with all ok is OK",
statusMap: map[*InstanceID]*StatusEvent{
{}: NewStatusEvent(StatusOK),
{}: NewStatusEvent(StatusOK),
{}: NewStatusEvent(StatusOK),
},
expectedStatus: StatusOK,
},
} {
t.Run(tc.name, func(t *testing.T) {
assert.Equal(t, tc.expectedStatus, AggregateStatus(tc.statusMap))
})
}
}
func TestStatusIsError(t *testing.T) {
for _, tc := range []struct {
status Status
isError bool
}{
{
status: StatusStarting,
isError: false,
},
{
status: StatusOK,
isError: false,
},
{
status: StatusRecoverableError,
isError: true,
},
{
status: StatusPermanentError,
isError: true,
},
{
status: StatusFatalError,
isError: true,
},
{
status: StatusStopping,
isError: false,
},
{
status: StatusStopped,
isError: false,
},
} {
name := fmt.Sprintf("StatusIsError(%s) is %t", tc.status, tc.isError)
t.Run(name, func(t *testing.T) {
assert.Equal(t, tc.isError, StatusIsError(tc.status))
})
}
}
func TestAggregateStatusEvent(t *testing.T) {
// maxTime is used to make sure we select the event with the latest timestamp
maxTime := time.Unix(1<<63-62135596801, 999999999)
// latest sets the timestamp for an event to maxTime
latest := func(ev *StatusEvent) *StatusEvent {
ev.timestamp = maxTime
return ev
}
for _, tc := range []struct {
name string
statusMap map[*InstanceID]*StatusEvent
expectedStatus *StatusEvent
}{
{
name: "FatalError - existing event",
statusMap: map[*InstanceID]*StatusEvent{
{}: NewStatusEvent(StatusStarting),
{}: NewStatusEvent(StatusOK),
{}: latest(NewFatalErrorEvent(assert.AnError)),
{}: NewStatusEvent(StatusRecoverableError),
},
expectedStatus: &StatusEvent{
status: StatusFatalError,
timestamp: maxTime,
err: assert.AnError,
},
},
{
name: "FatalError - synthetic event",
statusMap: map[*InstanceID]*StatusEvent{
{}: NewStatusEvent(StatusStarting),
{}: NewStatusEvent(StatusOK),
{}: NewFatalErrorEvent(assert.AnError),
{}: latest(NewStatusEvent(StatusRecoverableError)),
},
expectedStatus: &StatusEvent{
status: StatusFatalError,
timestamp: maxTime,
err: assert.AnError,
},
},
{
name: "PermanentError - existing event",
statusMap: map[*InstanceID]*StatusEvent{
{}: NewStatusEvent(StatusStarting),
{}: NewStatusEvent(StatusOK),
{}: latest(NewPermanentErrorEvent(assert.AnError)),
{}: NewStatusEvent(StatusRecoverableError),
},
expectedStatus: &StatusEvent{
status: StatusPermanentError,
timestamp: maxTime,
err: assert.AnError,
},
},
{
name: "PermanentError - synthetic event",
statusMap: map[*InstanceID]*StatusEvent{
{}: NewStatusEvent(StatusStarting),
{}: NewStatusEvent(StatusOK),
{}: NewPermanentErrorEvent(assert.AnError),
{}: latest(NewStatusEvent(StatusRecoverableError)),
},
expectedStatus: &StatusEvent{
status: StatusPermanentError,
timestamp: maxTime,
err: assert.AnError,
},
},
{
name: "Stopping - existing event",
statusMap: map[*InstanceID]*StatusEvent{
{}: NewStatusEvent(StatusStarting),
{}: NewStatusEvent(StatusOK),
{}: NewStatusEvent(StatusRecoverableError),
{}: latest(NewStatusEvent(StatusStopping)),
},
expectedStatus: &StatusEvent{
status: StatusStopping,
timestamp: maxTime,
},
},
{
name: "Stopping - synthetic event",
statusMap: map[*InstanceID]*StatusEvent{
{}: NewStatusEvent(StatusStarting),
{}: NewStatusEvent(StatusOK),
{}: NewStatusEvent(StatusRecoverableError),
{}: latest(NewStatusEvent(StatusStopped)),
},
expectedStatus: &StatusEvent{
status: StatusStopping,
timestamp: maxTime,
},
},
{
name: "Stopped - existing event",
statusMap: map[*InstanceID]*StatusEvent{
{}: NewStatusEvent(StatusStopped),
{}: latest(NewStatusEvent(StatusStopped)),
{}: NewStatusEvent(StatusStopped),
},
expectedStatus: &StatusEvent{
status: StatusStopped,
timestamp: maxTime,
},
},
{
name: "RecoverableError - existing event",
statusMap: map[*InstanceID]*StatusEvent{
{}: NewStatusEvent(StatusStarting),
{}: NewStatusEvent(StatusOK),
{}: latest(NewRecoverableErrorEvent(assert.AnError)),
},
expectedStatus: &StatusEvent{
status: StatusRecoverableError,
timestamp: maxTime,
err: assert.AnError,
},
},
{
name: "Starting - synthetic event",
statusMap: map[*InstanceID]*StatusEvent{
{}: NewStatusEvent(StatusStarting),
{}: latest(NewStatusEvent(StatusOK)),
},
expectedStatus: &StatusEvent{
status: StatusStarting,
timestamp: maxTime,
},
},
{
name: "OK - existing event",
statusMap: map[*InstanceID]*StatusEvent{
{}: NewStatusEvent(StatusOK),
{}: latest(NewStatusEvent(StatusOK)),
{}: NewStatusEvent(StatusOK),
},
expectedStatus: &StatusEvent{
status: StatusOK,
timestamp: maxTime,
},
},
} {
t.Run(tc.name, func(t *testing.T) {
assert.Equal(t, tc.expectedStatus, AggregateStatusEvent(tc.statusMap))
})
}
}