From 735e9913cd0b952dd38bf19c910d525fc999840a Mon Sep 17 00:00:00 2001 From: Yingrong Zhao <22300958+VinozzZ@users.noreply.github.com> Date: Wed, 19 Feb 2025 18:03:49 -0500 Subject: [PATCH] add test --- agent/agent.go | 25 +++++--- agent/agent_test.go | 130 ++++++++++++++++++++++++++++++++++++++++ internal/health/mock.go | 33 ++++++++++ 3 files changed, 180 insertions(+), 8 deletions(-) create mode 100644 agent/agent_test.go create mode 100644 internal/health/mock.go diff --git a/agent/agent.go b/agent/agent.go index 2d0ba0c7c4..1c94380c03 100644 --- a/agent/agent.go +++ b/agent/agent.go @@ -193,14 +193,8 @@ func (agent *Agent) healthCheck() { select { case <-agent.ctx.Done(): case <-timer.C: - lastHealth := agent.lastHealth - report := healthMessage(agent.health.IsAlive()) - if report.GetHealthy() { - report.Healthy = agent.health.IsReady() - } - - // report health only if it has changed - if lastHealth == nil || lastHealth.GetHealthy() != report.GetHealthy() { + report := agent.calculateHealth() + if report != nil { agent.lastHealth = report agent.opampClient.SetHealth(report) } @@ -208,6 +202,21 @@ func (agent *Agent) healthCheck() { } } +func (agent *Agent) calculateHealth() *protobufs.ComponentHealth { + lastHealth := agent.lastHealth + report := healthMessage(agent.health.IsAlive()) + if report.GetHealthy() { + report.Healthy = agent.health.IsReady() + } + + // report health only if it has changed + if lastHealth == nil || lastHealth.GetHealthy() != report.GetHealthy() { + return report + } + + return nil +} + func (agent *Agent) composeEffectiveConfig() *protobufs.EffectiveConfig { configYAML, err := config.SerializeToYAML(agent.effectiveConfig) if err != nil { diff --git a/agent/agent_test.go b/agent/agent_test.go new file mode 100644 index 0000000000..8c7f6377e1 --- /dev/null +++ b/agent/agent_test.go @@ -0,0 +1,130 @@ +package agent + +import ( + "context" + "fmt" + "testing" + "time" + + "github.com/honeycombio/refinery/config" + "github.com/honeycombio/refinery/internal/health" + "github.com/honeycombio/refinery/logger" + "github.com/honeycombio/refinery/metrics" + "github.com/open-telemetry/opamp-go/client/types" + "github.com/open-telemetry/opamp-go/protobufs" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestAgentOnMessage_RemoteConfig(t *testing.T) { + var reloadCalled int + cfg := &config.MockConfig{ + GetLoggerLevelVal: config.InfoLevel, + GetSamplerTypeVal: "FakeSamplerType", + GetSamplerTypeName: "FakeSamplerName", + } + cfg.Callbacks = []config.ConfigReloadCallback{ + func(configHash, ruleCfgHash string) { + reloadCalled++ + }, + } + agent := NewAgent(Logger{Logger: &logger.NullLogger{}}, "1.0.0", cfg, &metrics.NullMetrics{}, &health.Health{}) + + testcases := []struct { + name string + configMap map[string]*protobufs.AgentConfigFile + configHash []byte + expectedReloadCount int + status protobufs.RemoteConfigStatuses + }{ + { + name: "empty config map", + configMap: map[string]*protobufs.AgentConfigFile{}, + configHash: []byte{0}, + expectedReloadCount: 0, + }, + { + name: "new refinery config from remote config", + configMap: map[string]*protobufs.AgentConfigFile{ + "refinery_config": { + Body: []byte(`{"Logger":{"Level":"debug"}}`), + ContentType: "text/yaml", + }, + }, + configHash: []byte{1}, + expectedReloadCount: 1, + status: protobufs.RemoteConfigStatuses_RemoteConfigStatuses_APPLIED, + }, + { + name: "new refinery rules from remote config", + configMap: map[string]*protobufs.AgentConfigFile{ + "refinery_config": { + Body: []byte(`{"Logger":{"Level":"debug"}}`), + ContentType: "text/yaml", + }, + "refinery_rules": { + Body: []byte(`{"rules":[{"name":"test","type":"fake"]}`), + }, + }, + configHash: []byte{2}, + expectedReloadCount: 2, + status: protobufs.RemoteConfigStatuses_RemoteConfigStatuses_APPLIED, + }, + { + name: "same remote config should not cause reload", + configMap: map[string]*protobufs.AgentConfigFile{ + "refinery_config": { + Body: []byte(`{"Logger":{"Level":"debug"}}`), + ContentType: "text/yaml", + }, + "refinery_rules": { + Body: []byte(`{"rules":[{"name":"test","type":"fake"]}`), + }, + }, + configHash: []byte{2}, + expectedReloadCount: 2, + status: protobufs.RemoteConfigStatuses_RemoteConfigStatuses_APPLIED, + }, + } + + for _, tc := range testcases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + agent.onMessage(context.Background(), &types.MessageData{ + RemoteConfig: &protobufs.AgentRemoteConfig{ + Config: &protobufs.AgentConfigMap{ + ConfigMap: tc.configMap, + }, + ConfigHash: tc.configHash, + }, + }) + require.Eventually(t, func() bool { + return tc.expectedReloadCount == reloadCalled + }, 1*time.Second, 100*time.Millisecond, fmt.Sprintf("unexpected reload count %d", reloadCalled)) + + require.Equal(t, tc.status, agent.remoteConfigStatus.GetStatus(), fmt.Sprintf("unexpected status %s", agent.remoteConfigStatus.GetStatus())) + if tc.status == protobufs.RemoteConfigStatuses_RemoteConfigStatuses_APPLIED { + assert.Equal(t, tc.configHash, agent.remoteConfigStatus.GetLastRemoteConfigHash()) + assert.Equal(t, tc.configHash, agent.remoteConfig.GetConfigHash()) + assert.Equal(t, tc.configMap, agent.remoteConfig.GetConfig().GetConfigMap()) + } + }) + } + +} + +func TestHealthCheck(t *testing.T) { + healthReporter := &health.MockHealthReporter{} + agent := NewAgent(Logger{Logger: &logger.NullLogger{}}, "1.0.0", &config.MockConfig{}, &metrics.NullMetrics{}, healthReporter) + + // health check should start with false + require.False(t, agent.calculateHealth().Healthy) + + // health check should be false if Refinery is not ready + healthReporter.SetAlive(true) + require.False(t, agent.calculateHealth().Healthy) + + // health check should be true if both alive and ready are true + healthReporter.SetReady(true) + require.True(t, agent.calculateHealth().Healthy) +} diff --git a/internal/health/mock.go b/internal/health/mock.go new file mode 100644 index 0000000000..8c3490f077 --- /dev/null +++ b/internal/health/mock.go @@ -0,0 +1,33 @@ +package health + +import "sync" + +type MockHealthReporter struct { + isAlive bool + isReady bool + mutex sync.Mutex +} + +func (m *MockHealthReporter) SetAlive(isAlive bool) { + m.mutex.Lock() + defer m.mutex.Unlock() + m.isAlive = isAlive +} + +func (m *MockHealthReporter) IsAlive() bool { + m.mutex.Lock() + defer m.mutex.Unlock() + return m.isAlive +} + +func (m *MockHealthReporter) SetReady(isReady bool) { + m.mutex.Lock() + defer m.mutex.Unlock() + m.isReady = isReady +} + +func (m *MockHealthReporter) IsReady() bool { + m.mutex.Lock() + defer m.mutex.Unlock() + return m.isReady +}