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 }