// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1

//go:build !enterprise

package audit

import (
	"testing"

	"github.com/hashicorp/eventlogger"
	"github.com/hashicorp/go-hclog"
	"github.com/hashicorp/vault/sdk/helper/salt"
	"github.com/hashicorp/vault/sdk/logical"
	"github.com/stretchr/testify/require"
)

// TestFileBackend_newFileBackend_fallback ensures that we get the correct errors
// in CE when we try to enable a fileBackend with enterprise options like fallback
// and filter.
func TestFileBackend_newFileBackend_fallback(t *testing.T) {
	t.Parallel()

	tests := map[string]struct {
		backendConfig        *BackendConfig
		isErrorExpected      bool
		expectedErrorMessage string
	}{
		"non-fallback-device-with-filter": {
			backendConfig: &BackendConfig{
				MountPath:  "discard",
				SaltConfig: &salt.Config{},
				SaltView:   &logical.InmemStorage{},
				Logger:     hclog.NewNullLogger(),
				Config: map[string]string{
					"fallback":  "false",
					"file_path": discard,
					"filter":    "mount_type == kv",
				},
			},
			isErrorExpected:      true,
			expectedErrorMessage: "enterprise-only options supplied: invalid configuration",
		},
		"fallback-device-with-filter": {
			backendConfig: &BackendConfig{
				MountPath:  "discard",
				SaltConfig: &salt.Config{},
				SaltView:   &logical.InmemStorage{},
				Logger:     hclog.NewNullLogger(),
				Config: map[string]string{
					"fallback":  "true",
					"file_path": discard,
					"filter":    "mount_type == kv",
				},
			},
			isErrorExpected:      true,
			expectedErrorMessage: "enterprise-only options supplied: invalid configuration",
		},
	}

	for name, tc := range tests {
		name := name
		tc := tc
		t.Run(name, func(t *testing.T) {
			be, err := newFileBackend(tc.backendConfig, &noopHeaderFormatter{})

			if tc.isErrorExpected {
				require.Error(t, err)
				require.EqualError(t, err, tc.expectedErrorMessage)
			} else {
				require.NoError(t, err)
				require.NotNil(t, be)
			}
		})
	}
}

// TestFileBackend_newFileBackend_FilterFormatterSink ensures that when configuring
// a backend in community edition we cannot configure a filter node.
// We can verify that we have formatter and sink nodes added to the backend.
// The order of calls influences the slice of IDs on the Backend.
func TestFileBackend_newFileBackend_FilterFormatterSink(t *testing.T) {
	t.Parallel()

	cfg := map[string]string{
		"file_path": "/tmp/foo",
		"mode":      "0777",
		"format":    "json",
		"filter":    "mount_type == \"kv\"",
	}

	backendConfig := &BackendConfig{
		SaltView:   &logical.InmemStorage{},
		SaltConfig: &salt.Config{},
		Config:     cfg,
		MountPath:  "bar",
		Logger:     hclog.NewNullLogger(),
	}

	b, err := newFileBackend(backendConfig, &noopHeaderFormatter{})
	require.Error(t, err)
	require.EqualError(t, err, "enterprise-only options supplied: invalid configuration")

	// Try without filter option
	delete(cfg, "filter")
	b, err = newFileBackend(backendConfig, &noopHeaderFormatter{})
	require.NoError(t, err)

	require.Len(t, b.nodeIDList, 2)
	require.Len(t, b.nodeMap, 2)

	id := b.nodeIDList[0]
	node := b.nodeMap[id]
	require.Equal(t, eventlogger.NodeTypeFormatter, node.Type())

	id = b.nodeIDList[1]
	node = b.nodeMap[id]
	require.Equal(t, eventlogger.NodeTypeSink, node.Type())
}

// TestBackend_IsFallback ensures that no CE audit device can be a fallback.
func TestBackend_IsFallback(t *testing.T) {
	t.Parallel()

	cfg := &BackendConfig{
		MountPath:  "discard",
		SaltConfig: &salt.Config{},
		SaltView:   &logical.InmemStorage{},
		Logger:     hclog.NewNullLogger(),
		Config: map[string]string{
			"fallback":  "true",
			"file_path": discard,
		},
	}

	be, err := newFileBackend(cfg, &noopHeaderFormatter{})
	require.Error(t, err)
	require.EqualError(t, err, "enterprise-only options supplied: invalid configuration")

	// Remove the option and try again
	delete(cfg.Config, "fallback")

	be, err = newFileBackend(cfg, &noopHeaderFormatter{})
	require.NoError(t, err)
	require.NotNil(t, be)
	require.Equal(t, false, be.IsFallback())
}
