feature (events): add TestWithoutEventing(t) (#2137)
This commit is contained in:
6 files changed
+171
-5
No files matched your search
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -373,6 +374,11 @@ type TestControllerOpts struct {
|
||||
// If true, the controller will not be started
|
||||
DisableAutoStart bool
|
||||
|
||||
// DisableEventing, if true the test controller will not create events
|
||||
// You must not run the test in parallel (no calls to t.Parallel) since the
|
||||
// this option relies on modifying the system wide default eventer.
|
||||
DisableEventing bool
|
||||
|
||||
// DisableAuthorizationFailures will still cause authz checks to be
|
||||
// performed but they won't cause 403 Forbidden. Useful for API-level
|
||||
// testing to avoid a lot of faff.
|
||||
@@ -543,8 +549,32 @@ func TestControllerConfig(t testing.TB, ctx context.Context, tc *TestController,
|
||||
}
|
||||
opts.Config.Controller.SchedulerRunJobInterval = opts.SchedulerRunJobInterval
|
||||
|
||||
if err := tc.b.SetupEventing(tc.b.Logger, tc.b.StderrLock, opts.Config.Controller.Name, base.WithEventerConfig(opts.Config.Eventing)); err != nil {
|
||||
t.Fatal(err)
|
||||
switch {
|
||||
case opts.DisableEventing:
|
||||
opts.Config.Eventing = &event.EventerConfig{
|
||||
AuditEnabled: false,
|
||||
ObservationsEnabled: false,
|
||||
SysEventsEnabled: false,
|
||||
}
|
||||
testLogger := hclog.New(&hclog.LoggerOptions{
|
||||
Mutex: tc.b.StderrLock,
|
||||
Output: io.Discard,
|
||||
})
|
||||
e, err := event.NewEventer(
|
||||
testLogger,
|
||||
tc.b.StderrLock,
|
||||
opts.Config.Controller.Name,
|
||||
*opts.Config.Eventing,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
tc.b.Eventer = e
|
||||
event.TestWithoutEventing(t) // this ensures the sys eventer will also stop eventing
|
||||
default:
|
||||
if err := tc.b.SetupEventing(tc.b.Logger, tc.b.StderrLock, opts.Config.Controller.Name, base.WithEventerConfig(opts.Config.Eventing)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize status grace period
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_TestController(t *testing.T) {
|
||||
@@ -17,4 +22,44 @@ func Test_TestController(t *testing.T) {
|
||||
defer tc1.Shutdown()
|
||||
defer tc2.Shutdown()
|
||||
})
|
||||
t.Run("controller-without-eventing", func(t *testing.T) {
|
||||
const op = "Test_TestWithoutEventing"
|
||||
assert := assert.New(t)
|
||||
|
||||
// this isn't the best solution for capturing stdout but it works for now...
|
||||
captureFn := func(fn func()) string {
|
||||
old := os.Stdout
|
||||
defer func() {
|
||||
os.Stderr = old
|
||||
}()
|
||||
|
||||
r, w, _ := os.Pipe()
|
||||
os.Stderr = w
|
||||
|
||||
{
|
||||
fn()
|
||||
}
|
||||
|
||||
outC := make(chan string)
|
||||
// copy the output in a separate goroutine so writing to stderr can't block indefinitely
|
||||
go func() {
|
||||
var buf bytes.Buffer
|
||||
io.Copy(&buf, r)
|
||||
outC <- buf.String()
|
||||
}()
|
||||
|
||||
// back to normal state
|
||||
w.Close()
|
||||
return <-outC
|
||||
}
|
||||
|
||||
assert.Empty(captureFn(func() {
|
||||
tc := NewTestController(t, &TestControllerOpts{DisableEventing: true})
|
||||
defer tc.Shutdown()
|
||||
}))
|
||||
assert.NotEmpty(captureFn(func() {
|
||||
tc := NewTestController(t, nil)
|
||||
defer tc.Shutdown()
|
||||
}))
|
||||
})
|
||||
}
|
||||
@@ -170,7 +170,8 @@ func NewAuditEncryptFilter(opt ...Option) (*encrypt.Filter, error) {
|
||||
}
|
||||
|
||||
// NewEventer creates a new Eventer using the config. Supports options:
|
||||
// WithNow, WithSerializationLock, WithBroker, WithAuditWrapper
|
||||
// WithNow, WithSerializationLock, WithBroker, WithAuditWrapper,
|
||||
// WithNoDefaultSink
|
||||
func NewEventer(log hclog.Logger, serializationLock *sync.Mutex, serverName string, c EventerConfig, opt ...Option) (*Eventer, error) {
|
||||
const op = "event.NewEventer"
|
||||
if log == nil {
|
||||
@@ -183,9 +184,11 @@ func NewEventer(log hclog.Logger, serializationLock *sync.Mutex, serverName stri
|
||||
return nil, fmt.Errorf("%s: missing server name: %w", op, ErrInvalidParameter)
|
||||
}
|
||||
|
||||
opts := getOpts(opt...)
|
||||
|
||||
// if there are no sinks in config, then we'll default to just one stderr
|
||||
// sink.
|
||||
if len(c.Sinks) == 0 {
|
||||
if len(c.Sinks) == 0 && !opts.withNoDefaultSink {
|
||||
c.Sinks = append(c.Sinks, DefaultSink())
|
||||
}
|
||||
|
||||
@@ -195,7 +198,6 @@ func NewEventer(log hclog.Logger, serializationLock *sync.Mutex, serverName stri
|
||||
|
||||
var auditPipelines, observationPipelines, errPipelines, sysPipelines []pipeline
|
||||
|
||||
opts := getOpts(opt...)
|
||||
var b broker
|
||||
switch {
|
||||
case opts.withBroker != nil:
|
||||
|
||||
@@ -54,6 +54,7 @@ type options struct {
|
||||
withObservationSink bool // test only option
|
||||
withSysSink bool // test only option
|
||||
withSinkFormat SinkFormat // test only option
|
||||
withNoDefaultSink bool // test only option
|
||||
}
|
||||
|
||||
func getDefaultOptions() options {
|
||||
|
||||
@@ -4,16 +4,41 @@ import (
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
|
||||
"github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/groups"
|
||||
"github.com/hashicorp/eventlogger"
|
||||
"github.com/hashicorp/go-hclog"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/protobuf/types/known/wrapperspb"
|
||||
)
|
||||
|
||||
// TestWithoutEventing allows the caller to "disable" all eventing for a test.
|
||||
// You must not run the test in parallel (no calls to t.Parallel) since the
|
||||
// function relies on modifying the system wide default eventer.
|
||||
func TestWithoutEventing(t testing.TB) *Eventer {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
testConfig := EventerConfig{
|
||||
AuditEnabled: false,
|
||||
ObservationsEnabled: false,
|
||||
SysEventsEnabled: false,
|
||||
}
|
||||
testLock := &sync.Mutex{}
|
||||
testLogger := hclog.New(&hclog.LoggerOptions{
|
||||
Mutex: testLock,
|
||||
Output: ioutil.Discard,
|
||||
})
|
||||
testEventer, err := NewEventer(testLogger, testLock, "TestWithoutEventing", testConfig, withNoDefaultSink(t))
|
||||
require.NoError(err)
|
||||
|
||||
require.NoError(InitSysEventer(testLogger, testLock, "TestWithoutEventing", WithEventer(testEventer)))
|
||||
return testEventer
|
||||
}
|
||||
|
||||
// TestGetEventerConfig is a test accessor for the eventer's config
|
||||
func TestGetEventerConfig(t testing.TB, e *Eventer) EventerConfig {
|
||||
t.Helper()
|
||||
@@ -231,6 +256,14 @@ func TestWithSysSink(t testing.TB) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// withNoDefaultSink is an unexported test option
|
||||
func withNoDefaultSink(t testing.TB) Option {
|
||||
t.Helper()
|
||||
return func(o *options) {
|
||||
o.withNoDefaultSink = true
|
||||
}
|
||||
}
|
||||
|
||||
// testWithSinkFormat is an unexported and a test option
|
||||
func testWithSinkFormat(t testing.TB, fmt SinkFormat) Option {
|
||||
t.Helper()
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
package event_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/observability/event"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_TestWithoutEventing(t *testing.T) {
|
||||
const op = "Test_TestWithoutEventing"
|
||||
assert := assert.New(t)
|
||||
|
||||
// this isn't the best solution for capturing stdout but it works for now...
|
||||
captureFn := func(fn func()) string {
|
||||
old := os.Stdout
|
||||
defer func() {
|
||||
os.Stderr = old
|
||||
}()
|
||||
|
||||
r, w, _ := os.Pipe()
|
||||
os.Stderr = w
|
||||
|
||||
{
|
||||
fn()
|
||||
}
|
||||
|
||||
outC := make(chan string)
|
||||
// copy the output in a separate goroutine so writing to stderr can't block indefinitely
|
||||
go func() {
|
||||
var buf bytes.Buffer
|
||||
io.Copy(&buf, r)
|
||||
outC <- buf.String()
|
||||
}()
|
||||
|
||||
// back to normal state
|
||||
w.Close()
|
||||
return <-outC
|
||||
}
|
||||
|
||||
assert.NotEmpty(captureFn(func() {
|
||||
fmt.Fprintln(os.Stderr, "not-empty")
|
||||
}))
|
||||
|
||||
assert.Empty(captureFn(func() {
|
||||
testCtx := context.Background()
|
||||
event.TestWithoutEventing(t)
|
||||
event.WriteSysEvent(testCtx, op, "test-event")
|
||||
}))
|
||||
}
|
||||
Reference in new issue
Block a user