vault/audit/nodes_test.go
Marc Boudreau d66fdb4dfd
Use non-persistent Salter for logging test message (#22308)
* use non-persistent Salter for logging test message

* adjust tests based on code changes to ProcessManual

* suggestion for log test message fix (#22320)

* clean up test code and fix misnamed elements

---------

Co-authored-by: Peter Wilson <peter.wilson@hashicorp.com>
2023-08-14 15:00:49 +00:00

274 lines
7.1 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package audit
import (
"context"
"testing"
"github.com/hashicorp/vault/helper/namespace"
"github.com/hashicorp/go-uuid"
"github.com/hashicorp/vault/sdk/logical"
"github.com/hashicorp/eventlogger"
"github.com/hashicorp/vault/internal/observability/event"
"github.com/stretchr/testify/require"
)
// TestProcessManual_NilData tests ProcessManual when nil data is supplied.
func TestProcessManual_NilData(t *testing.T) {
t.Parallel()
var ids []eventlogger.NodeID
nodes := make(map[eventlogger.NodeID]eventlogger.Node)
// Formatter node
formatterId, formatterNode := newFormatterNode(t)
ids = append(ids, formatterId)
nodes[formatterId] = formatterNode
// Sink node
sinkId, sinkNode := newSinkNode(t)
ids = append(ids, sinkId)
nodes[sinkId] = sinkNode
err := ProcessManual(namespace.RootContext(context.Background()), nil, ids, nodes)
require.Error(t, err)
require.EqualError(t, err, "data cannot be nil")
}
// TestProcessManual_BadIDs tests ProcessManual when different bad values are
// supplied for the ID parameter.
func TestProcessManual_BadIDs(t *testing.T) {
tests := map[string]struct {
IDs []eventlogger.NodeID
ExpectedErrorMessage string
}{
"nil": {
IDs: nil,
ExpectedErrorMessage: "minimum of 2 ids are required",
},
"one": {
IDs: []eventlogger.NodeID{"1"},
ExpectedErrorMessage: "minimum of 2 ids are required",
},
}
for name, tc := range tests {
name := name
tc := tc
t.Run(name, func(t *testing.T) {
t.Parallel()
nodes := make(map[eventlogger.NodeID]eventlogger.Node)
// Formatter node
formatterId, formatterNode := newFormatterNode(t)
nodes[formatterId] = formatterNode
// Sink node
sinkId, sinkNode := newSinkNode(t)
nodes[sinkId] = sinkNode
// Data
requestId, err := uuid.GenerateUUID()
require.NoError(t, err)
data := newData(requestId)
err = ProcessManual(namespace.RootContext(context.Background()), data, tc.IDs, nodes)
require.Error(t, err)
require.EqualError(t, err, tc.ExpectedErrorMessage)
})
}
}
// TestProcessManual_NoNodes tests ProcessManual when no nodes are supplied.
func TestProcessManual_NoNodes(t *testing.T) {
t.Parallel()
var ids []eventlogger.NodeID
nodes := make(map[eventlogger.NodeID]eventlogger.Node)
// Formatter node
formatterId, _ := newFormatterNode(t)
ids = append(ids, formatterId)
// Sink node
sinkId, _ := newSinkNode(t)
ids = append(ids, sinkId)
// Data
requestId, err := uuid.GenerateUUID()
require.NoError(t, err)
data := newData(requestId)
err = ProcessManual(namespace.RootContext(context.Background()), data, ids, nodes)
require.Error(t, err)
require.EqualError(t, err, "nodes are required")
}
// TestProcessManual_IdNodeMismatch tests ProcessManual when IDs don't match with
// the nodes in the supplied map.
func TestProcessManual_IdNodeMismatch(t *testing.T) {
t.Parallel()
var ids []eventlogger.NodeID
nodes := make(map[eventlogger.NodeID]eventlogger.Node)
// Formatter node
formatterId, formatterNode := newFormatterNode(t)
ids = append(ids, formatterId)
nodes[formatterId] = formatterNode
// Sink node
sinkId, _ := newSinkNode(t)
ids = append(ids, sinkId)
// Data
requestId, err := uuid.GenerateUUID()
require.NoError(t, err)
data := newData(requestId)
err = ProcessManual(namespace.RootContext(context.Background()), data, ids, nodes)
require.Error(t, err)
require.ErrorContains(t, err, "node not found: ")
}
// TestProcessManual_NotEnoughNodes tests ProcessManual when there is only one
// node provided.
func TestProcessManual_NotEnoughNodes(t *testing.T) {
t.Parallel()
var ids []eventlogger.NodeID
nodes := make(map[eventlogger.NodeID]eventlogger.Node)
// Formatter node
formatterId, formatterNode := newFormatterNode(t)
ids = append(ids, formatterId)
nodes[formatterId] = formatterNode
// Data
requestId, err := uuid.GenerateUUID()
require.NoError(t, err)
data := newData(requestId)
err = ProcessManual(namespace.RootContext(context.Background()), data, ids, nodes)
require.Error(t, err)
require.EqualError(t, err, "minimum of 2 ids are required")
}
// TestProcessManual_LastNodeNotSink tests ProcessManual when the last node is
// not a Sink node.
func TestProcessManual_LastNodeNotSink(t *testing.T) {
t.Parallel()
var ids []eventlogger.NodeID
nodes := make(map[eventlogger.NodeID]eventlogger.Node)
// Formatter node
formatterId, formatterNode := newFormatterNode(t)
ids = append(ids, formatterId)
nodes[formatterId] = formatterNode
// Another Formatter node
formatterId, formatterNode = newFormatterNode(t)
ids = append(ids, formatterId)
nodes[formatterId] = formatterNode
// Data
requestId, err := uuid.GenerateUUID()
require.NoError(t, err)
data := newData(requestId)
err = ProcessManual(namespace.RootContext(context.Background()), data, ids, nodes)
require.Error(t, err)
require.EqualError(t, err, "last node must be a sink")
}
// TestProcessManual ensures that the manual processing of a test message works
// as expected with proper inputs.
func TestProcessManual(t *testing.T) {
t.Parallel()
var ids []eventlogger.NodeID
nodes := make(map[eventlogger.NodeID]eventlogger.Node)
// Formatter node
formatterId, formatterNode := newFormatterNode(t)
ids = append(ids, formatterId)
nodes[formatterId] = formatterNode
// Sink node
sinkId, sinkNode := newSinkNode(t)
ids = append(ids, sinkId)
nodes[sinkId] = sinkNode
// Data
requestId, err := uuid.GenerateUUID()
require.NoError(t, err)
data := newData(requestId)
err = ProcessManual(namespace.RootContext(context.Background()), data, ids, nodes)
require.NoError(t, err)
}
// newSinkNode creates a new UUID and NoopSink (sink node).
func newSinkNode(t *testing.T) (eventlogger.NodeID, *event.NoopSink) {
t.Helper()
sinkId, err := event.GenerateNodeID()
require.NoError(t, err)
sinkNode := event.NewNoopSink()
return sinkId, sinkNode
}
// TestFormatter is a trivial implementation of the eventlogger.Node interface
// used as a place-holder for Formatter nodes in tests.
type TestFormatter struct{}
// Process trivially formats the event by storing "test" as a byte slice under
// the test format type.
func (f *TestFormatter) Process(_ context.Context, e *eventlogger.Event) (*eventlogger.Event, error) {
e.FormattedAs("test", []byte("test"))
return e, nil
}
// Reopen does nothing.
func (f *TestFormatter) Reopen() error {
return nil
}
// Type returns the eventlogger.NodeTypeFormatter type.
func (f *TestFormatter) Type() eventlogger.NodeType {
return eventlogger.NodeTypeFormatter
}
// newFormatterNode creates a new TestFormatter (formatter node).
func newFormatterNode(t *testing.T) (eventlogger.NodeID, *TestFormatter) {
nodeId, err := event.GenerateNodeID()
require.NoError(t, err)
node := &TestFormatter{}
return nodeId, node
}
// newData creates a sample logical.LogInput to be used as data for tests.
func newData(id string) *logical.LogInput {
return &logical.LogInput{
Type: "request",
Auth: nil,
Request: &logical.Request{
ID: id,
Operation: "update",
Path: "sys/audit/test",
},
Response: nil,
OuterErr: nil,
}
}