scraper_test.go 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. // Copyright The OpenTelemetry Authors
  2. // SPDX-License-Identifier: Apache-2.0
  3. package couchdbreceiver // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/couchdbreceiver"
  4. import (
  5. "context"
  6. "encoding/json"
  7. "errors"
  8. "os"
  9. "path"
  10. "path/filepath"
  11. "testing"
  12. "github.com/stretchr/testify/assert"
  13. "github.com/stretchr/testify/mock"
  14. "github.com/stretchr/testify/require"
  15. "go.opentelemetry.io/collector/component"
  16. "go.opentelemetry.io/collector/component/componenttest"
  17. "go.opentelemetry.io/collector/config/confighttp"
  18. "go.opentelemetry.io/collector/receiver/receivertest"
  19. "go.opentelemetry.io/collector/receiver/scrapererror"
  20. "go.uber.org/zap"
  21. "go.uber.org/zap/zapcore"
  22. "go.uber.org/zap/zaptest/observer"
  23. "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/golden"
  24. "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest/pmetrictest"
  25. "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/couchdbreceiver/internal/metadata"
  26. )
  27. func TestScrape(t *testing.T) {
  28. f := NewFactory()
  29. cfg := f.CreateDefaultConfig().(*Config)
  30. cfg.Username = "otelu"
  31. cfg.Password = "otelp"
  32. require.NoError(t, component.ValidateConfig(cfg))
  33. t.Run("scrape from couchdb version 2.31", func(t *testing.T) {
  34. mockClient := new(mockClient)
  35. mockClient.On("GetStats", "_local").Return(getStats("response_2.31.json"))
  36. scraper := newCouchdbScraper(receivertest.NewNopCreateSettings(), cfg)
  37. scraper.client = mockClient
  38. actualMetrics, err := scraper.scrape(context.Background())
  39. require.NoError(t, err)
  40. expectedFile := filepath.Join("testdata", "scraper", "expected.yaml")
  41. expectedMetrics, err := golden.ReadMetrics(expectedFile)
  42. require.NoError(t, err)
  43. require.NoError(t, pmetrictest.CompareMetrics(expectedMetrics, actualMetrics,
  44. pmetrictest.IgnoreMetricDataPointsOrder(), pmetrictest.IgnoreStartTimestamp(), pmetrictest.IgnoreTimestamp()))
  45. })
  46. t.Run("scrape from couchdb 3.12", func(t *testing.T) {
  47. mockClient := new(mockClient)
  48. mockClient.On("GetStats", "_local").Return(getStats("response_3.12.json"))
  49. scraper := newCouchdbScraper(receivertest.NewNopCreateSettings(), cfg)
  50. scraper.client = mockClient
  51. actualMetrics, err := scraper.scrape(context.Background())
  52. require.NoError(t, err)
  53. expectedFile := filepath.Join("testdata", "scraper", "expected.yaml")
  54. expectedMetrics, err := golden.ReadMetrics(expectedFile)
  55. require.NoError(t, err)
  56. require.NoError(t, pmetrictest.CompareMetrics(expectedMetrics, actualMetrics,
  57. pmetrictest.IgnoreMetricDataPointsOrder(), pmetrictest.IgnoreStartTimestamp(), pmetrictest.IgnoreTimestamp()))
  58. })
  59. t.Run("scrape returns nothing", func(t *testing.T) {
  60. mockClient := new(mockClient)
  61. mockClient.On("GetStats", "_local").Return(map[string]any{}, nil)
  62. scraper := newCouchdbScraper(receivertest.NewNopCreateSettings(), cfg)
  63. scraper.client = mockClient
  64. metrics, err := scraper.scrape(context.Background())
  65. require.Error(t, err)
  66. assert.Equal(t, 0, metrics.DataPointCount(), "Expected 0 datapoints to be collected")
  67. var partialScrapeErr scrapererror.PartialScrapeError
  68. require.True(t, errors.As(err, &partialScrapeErr), "returned error was not PartialScrapeError")
  69. require.True(t, partialScrapeErr.Failed > 0, "Expected scrape failures, but none were recorded!")
  70. })
  71. t.Run("scrape error: failed to connect to client", func(t *testing.T) {
  72. scraper := newCouchdbScraper(receivertest.NewNopCreateSettings(), cfg)
  73. _, err := scraper.scrape(context.Background())
  74. require.NotNil(t, err)
  75. require.Equal(t, err, errors.New("no client available"))
  76. })
  77. t.Run("scrape error: get stats endpoint error", func(t *testing.T) {
  78. obs, logs := observer.New(zap.ErrorLevel)
  79. settings := receivertest.NewNopCreateSettings()
  80. settings.Logger = zap.New(obs)
  81. mockClient := new(mockClient)
  82. mockClient.On("GetStats", "_local").Return(getStats(""))
  83. scraper := newCouchdbScraper(settings, cfg)
  84. scraper.client = mockClient
  85. _, err := scraper.scrape(context.Background())
  86. require.NotNil(t, err)
  87. require.Equal(t, 1, logs.Len())
  88. require.Equal(t, []observer.LoggedEntry{
  89. {
  90. Entry: zapcore.Entry{Level: zap.ErrorLevel, Message: "Failed to fetch couchdb stats"},
  91. Context: []zapcore.Field{
  92. zap.String("endpoint", cfg.Endpoint),
  93. zap.Error(errors.New("bad response")),
  94. },
  95. },
  96. }, logs.AllUntimed())
  97. })
  98. }
  99. func TestStart(t *testing.T) {
  100. t.Run("start success", func(t *testing.T) {
  101. f := NewFactory()
  102. cfg := f.CreateDefaultConfig().(*Config)
  103. cfg.Username = "otelu"
  104. cfg.Password = "otelp"
  105. require.NoError(t, component.ValidateConfig(cfg))
  106. scraper := newCouchdbScraper(receivertest.NewNopCreateSettings(), cfg)
  107. err := scraper.start(context.Background(), componenttest.NewNopHost())
  108. require.NoError(t, err)
  109. })
  110. t.Run("start fail", func(t *testing.T) {
  111. f := NewFactory()
  112. cfg := f.CreateDefaultConfig().(*Config)
  113. cfg.HTTPClientSettings.TLSSetting.CAFile = "/non/existent"
  114. cfg.Username = "otelu"
  115. cfg.Password = "otelp"
  116. require.NoError(t, component.ValidateConfig(cfg))
  117. scraper := newCouchdbScraper(receivertest.NewNopCreateSettings(), cfg)
  118. err := scraper.start(context.Background(), componenttest.NewNopHost())
  119. require.NotNil(t, err)
  120. })
  121. }
  122. func TestMetricSettings(t *testing.T) {
  123. mockClient := new(mockClient)
  124. mockClient.On("GetStats", "_local").Return(getStats("response_2.31.json"))
  125. mbc := metadata.DefaultMetricsBuilderConfig()
  126. mbc.Metrics = metadata.MetricsConfig{
  127. CouchdbAverageRequestTime: metadata.MetricConfig{Enabled: false},
  128. CouchdbDatabaseOpen: metadata.MetricConfig{Enabled: false},
  129. CouchdbDatabaseOperations: metadata.MetricConfig{Enabled: true},
  130. CouchdbFileDescriptorOpen: metadata.MetricConfig{Enabled: false},
  131. CouchdbHttpdBulkRequests: metadata.MetricConfig{Enabled: false},
  132. CouchdbHttpdRequests: metadata.MetricConfig{Enabled: false},
  133. CouchdbHttpdResponses: metadata.MetricConfig{Enabled: false},
  134. CouchdbHttpdViews: metadata.MetricConfig{Enabled: false},
  135. }
  136. cfg := &Config{
  137. HTTPClientSettings: confighttp.HTTPClientSettings{},
  138. MetricsBuilderConfig: mbc,
  139. }
  140. scraper := newCouchdbScraper(receivertest.NewNopCreateSettings(), cfg)
  141. scraper.client = mockClient
  142. metrics, err := scraper.scrape(context.Background())
  143. require.NoError(t, err)
  144. expected, err := golden.ReadMetrics(filepath.Join("testdata", "scraper", "only_db_ops.yaml"))
  145. require.NoError(t, err)
  146. require.NoError(t, pmetrictest.CompareMetrics(expected, metrics, pmetrictest.IgnoreMetricDataPointsOrder(),
  147. pmetrictest.IgnoreStartTimestamp(), pmetrictest.IgnoreTimestamp()))
  148. require.Equal(t, metrics.MetricCount(), 1)
  149. }
  150. func getStats(filename string) (map[string]any, error) {
  151. var stats map[string]any
  152. if filename == "" {
  153. return nil, errors.New("bad response")
  154. }
  155. if filename == "empty" {
  156. _ = json.Unmarshal([]byte{}, &stats)
  157. return stats, nil
  158. }
  159. body, err := os.ReadFile(path.Join("testdata", "scraper", filename))
  160. if err != nil {
  161. return nil, err
  162. }
  163. err = json.Unmarshal(body, &stats)
  164. if err != nil {
  165. return nil, err
  166. }
  167. return stats, nil
  168. }
  169. // mockClient is an autogenerated mock type for the client type
  170. type mockClient struct {
  171. mock.Mock
  172. }
  173. // Get provides a mock function with given fields: path
  174. func (_m *mockClient) Get(path string) ([]byte, error) {
  175. ret := _m.Called(path)
  176. var r0 []byte
  177. if rf, ok := ret.Get(0).(func(string) []byte); ok {
  178. r0 = rf(path)
  179. } else if ret.Get(0) != nil {
  180. r0 = ret.Get(0).([]byte)
  181. }
  182. var r1 error
  183. if rf, ok := ret.Get(1).(func(string) error); ok {
  184. r1 = rf(path)
  185. } else {
  186. r1 = ret.Error(1)
  187. }
  188. return r0, r1
  189. }
  190. // GetStats provides a mock function with given fields: nodeName
  191. func (_m *mockClient) GetStats(nodeName string) (map[string]any, error) {
  192. ret := _m.Called(nodeName)
  193. var r0 map[string]any
  194. if rf, ok := ret.Get(0).(func(string) map[string]any); ok {
  195. r0 = rf(nodeName)
  196. } else if ret.Get(0) != nil {
  197. r0 = ret.Get(0).(map[string]any)
  198. }
  199. var r1 error
  200. if rf, ok := ret.Get(1).(func(string) error); ok {
  201. r1 = rf(nodeName)
  202. } else {
  203. r1 = ret.Error(1)
  204. }
  205. return r0, r1
  206. }