github.com/grafana/synthetic-monitoring-agent@v0.64.2/internal/testhelper/testhelper.go (about)

     1  package testhelper
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"io"
     7  	"os"
     8  	"path/filepath"
     9  	"runtime"
    10  	"strings"
    11  	"testing"
    12  	"time"
    13  
    14  	"github.com/grafana/synthetic-monitoring-agent/internal/model"
    15  	sm "github.com/grafana/synthetic-monitoring-agent/pkg/pb/synthetic_monitoring"
    16  	"github.com/rs/zerolog"
    17  	"github.com/stretchr/testify/require"
    18  )
    19  
    20  func Context(ctx context.Context, t *testing.T) (context.Context, context.CancelFunc) {
    21  	deadline, found := t.Deadline()
    22  	if !found {
    23  		deadline = time.Now().Add(30 * time.Second)
    24  	}
    25  
    26  	return context.WithDeadline(ctx, deadline)
    27  }
    28  
    29  func Logger(t *testing.T) zerolog.Logger {
    30  	logger := zerolog.New(zerolog.NewTestWriter(t)).Level(zerolog.ErrorLevel)
    31  	if testing.Verbose() {
    32  		logger = logger.Level(zerolog.DebugLevel)
    33  	}
    34  
    35  	return logger.With().Caller().Timestamp().Logger()
    36  }
    37  
    38  // NewTestLogger creates a simple logger that discards output for use in tests
    39  // where you don't need to see the log output.
    40  func NewTestLogger() zerolog.Logger {
    41  	return zerolog.New(io.Discard)
    42  }
    43  
    44  func MustReadFile(t *testing.T, filename string) []byte {
    45  	t.Helper()
    46  
    47  	data, err := os.ReadFile(filename)
    48  	require.NoError(t, err)
    49  	require.NotEmpty(t, data)
    50  
    51  	return data
    52  }
    53  
    54  func ModuleDir(t *testing.T) string {
    55  	t.Helper()
    56  
    57  	_, filename, _, ok := runtime.Caller(1)
    58  	if !ok {
    59  		// uh?
    60  		return ""
    61  	}
    62  
    63  	dir := filepath.Dir(filename)
    64  	for dir != "/" {
    65  		gomod := filepath.Join(dir, "go.mod")
    66  
    67  		_, err := os.Stat(gomod)
    68  		switch {
    69  		case err == nil:
    70  			return dir
    71  		case os.IsNotExist(err):
    72  			dir = filepath.Join(dir, "..")
    73  			continue
    74  		default:
    75  			panic(err)
    76  		}
    77  	}
    78  
    79  	return dir
    80  }
    81  
    82  func K6Paths(t *testing.T) []string {
    83  	t.Helper()
    84  
    85  	dir := filepath.Join(ModuleDir(t), "dist", runtime.GOOS+"-"+runtime.GOARCH)
    86  	entries, err := os.ReadDir(dir)
    87  	require.NoErrorf(t, err, "reading k6 binaries directory %s", dir)
    88  
    89  	var paths []string
    90  
    91  	for _, entry := range entries {
    92  		if entry.IsDir() || !strings.Contains(entry.Name(), "k6") {
    93  			continue
    94  		}
    95  
    96  		info, err := entry.Info()
    97  		if err != nil || info.Mode().Perm()&0o111 == 0 {
    98  			continue
    99  		}
   100  
   101  		paths = append(paths, filepath.Join(dir, entry.Name()))
   102  	}
   103  
   104  	require.NotEmptyf(t, paths, "no k6 binaries found in %s", dir)
   105  
   106  	return paths
   107  }
   108  
   109  // NoopSecretStore is a test implementation of the SecretProvider interface
   110  // that returns an empty secret store. Use this in tests when you need a
   111  // secret store but don't care about the actual secrets.
   112  type NoopSecretStore struct{}
   113  
   114  func (n NoopSecretStore) GetSecretCredentials(ctx context.Context, tenantID model.GlobalID) (*sm.SecretStore, error) {
   115  	return &sm.SecretStore{}, nil
   116  }
   117  
   118  func (n NoopSecretStore) GetSecretValue(ctx context.Context, tenantID model.GlobalID, secretKey string) (string, error) {
   119  	return "", nil
   120  }
   121  
   122  func (n NoopSecretStore) IsProtocolSecretsEnabled() bool {
   123  	return false
   124  }
   125  
   126  // TestSecretStore is a test implementation of the SecretProvider interface
   127  // that returns a mock secret store with test credentials. Use this in tests
   128  // when you need to test behavior that depends on having actual secret values.
   129  type TestSecretStore struct{}
   130  
   131  func (s TestSecretStore) GetSecretCredentials(ctx context.Context, tenantId model.GlobalID) (*sm.SecretStore, error) {
   132  	if tenantId == 0 {
   133  		return nil, errors.New("invalid tenant ID")
   134  	}
   135  
   136  	return &sm.SecretStore{
   137  		Url:   "http://example.com",
   138  		Token: "test-token",
   139  	}, nil
   140  }
   141  
   142  func (s TestSecretStore) GetSecretValue(ctx context.Context, tenantID model.GlobalID, secretKey string) (string, error) {
   143  	if tenantID == 0 {
   144  		return "", errors.New("invalid tenant ID")
   145  	}
   146  
   147  	// For testing purposes, return a mock secret value
   148  	return "test-secret-value", nil
   149  }
   150  
   151  func (s TestSecretStore) IsProtocolSecretsEnabled() bool {
   152  	return true
   153  }
   154  
   155  // MockSecretProvider is a flexible mock implementation of the SecretProvider interface
   156  // that allows you to specify custom secret values and behaviors for testing.
   157  type MockSecretProvider struct {
   158  	secrets               map[string]string
   159  	getSecretValueFunc    func(ctx context.Context, tenantID model.GlobalID, secretKey string) (string, error)
   160  	enableProtocolSecrets bool
   161  }
   162  
   163  // NewMockSecretProvider creates a new MockSecretProvider with the given secrets map.
   164  func NewMockSecretProvider(secrets map[string]string) *MockSecretProvider {
   165  	return &MockSecretProvider{
   166  		secrets:               secrets,
   167  		enableProtocolSecrets: false, // Default to false for testing
   168  	}
   169  }
   170  
   171  // NewMockSecretProviderWithFunc creates a new MockSecretProvider with a custom function.
   172  func NewMockSecretProviderWithFunc(fn func(ctx context.Context, tenantID model.GlobalID, secretKey string) (string, error)) *MockSecretProvider {
   173  	return &MockSecretProvider{
   174  		getSecretValueFunc:    fn,
   175  		enableProtocolSecrets: false, // Default to false for testing
   176  	}
   177  }
   178  
   179  func (m *MockSecretProvider) GetSecretCredentials(ctx context.Context, tenantID model.GlobalID) (*sm.SecretStore, error) {
   180  	return &sm.SecretStore{
   181  		Url:   "https://mock-gsm.example.com",
   182  		Token: "mock-token",
   183  	}, nil
   184  }
   185  
   186  func (m *MockSecretProvider) GetSecretValue(ctx context.Context, tenantID model.GlobalID, secretKey string) (string, error) {
   187  	if m.getSecretValueFunc != nil {
   188  		return m.getSecretValueFunc(ctx, tenantID, secretKey)
   189  	}
   190  
   191  	if value, exists := m.secrets[secretKey]; exists {
   192  		return value, nil
   193  	}
   194  
   195  	return "", errors.New("secret not found")
   196  }
   197  
   198  func (m *MockSecretProvider) IsProtocolSecretsEnabled() bool {
   199  	return m.enableProtocolSecrets
   200  }
   201  
   202  // UpdateCapabilities updates the probe capabilities for testing
   203  func (m *MockSecretProvider) UpdateCapabilities(probeCapabilities *sm.Probe_Capabilities) {
   204  	m.enableProtocolSecrets = false
   205  	if probeCapabilities != nil {
   206  		m.enableProtocolSecrets = probeCapabilities.EnableProtocolSecrets
   207  	}
   208  }
   209  
   210  // CommonTestSetup returns commonly used test values for context, logger, and tenant ID
   211  func CommonTestSetup() (context.Context, zerolog.Logger, model.GlobalID) {
   212  	return context.Background(), NewTestLogger(), model.GlobalID(123)
   213  }