diff options
Diffstat (limited to 'src/go/collectors/go.d.plugin/modules/couchdb/couchdb_test.go')
-rw-r--r-- | src/go/collectors/go.d.plugin/modules/couchdb/couchdb_test.go | 464 |
1 files changed, 464 insertions, 0 deletions
diff --git a/src/go/collectors/go.d.plugin/modules/couchdb/couchdb_test.go b/src/go/collectors/go.d.plugin/modules/couchdb/couchdb_test.go new file mode 100644 index 000000000..15e7aa0a1 --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/couchdb/couchdb_test.go @@ -0,0 +1,464 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package couchdb + +import ( + "net/http" + "net/http/httptest" + "os" + "testing" + + "github.com/netdata/netdata/go/go.d.plugin/agent/module" + "github.com/netdata/netdata/go/go.d.plugin/pkg/tlscfg" + "github.com/netdata/netdata/go/go.d.plugin/pkg/web" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +var ( + dataConfigJSON, _ = os.ReadFile("testdata/config.json") + dataConfigYAML, _ = os.ReadFile("testdata/config.yaml") + + dataVer311Root, _ = os.ReadFile("testdata/v3.1.1/root.json") + dataVer311ActiveTasks, _ = os.ReadFile("testdata/v3.1.1/active_tasks.json") + dataVer311NodeStats, _ = os.ReadFile("testdata/v3.1.1/node_stats.json") + dataVer311NodeSystem, _ = os.ReadFile("testdata/v3.1.1/node_system.json") + dataVer311DbsInfo, _ = os.ReadFile("testdata/v3.1.1/dbs_info.json") +) + +func Test_testDataIsValid(t *testing.T) { + for name, data := range map[string][]byte{ + "dataConfigJSON": dataConfigJSON, + "dataConfigYAML": dataConfigYAML, + "dataVer311Root": dataVer311Root, + "dataVer311ActiveTasks": dataVer311ActiveTasks, + "dataVer311NodeStats": dataVer311NodeStats, + "dataVer311NodeSystem": dataVer311NodeSystem, + "dataVer311DbsInfo": dataVer311DbsInfo, + } { + require.NotNil(t, data, name) + } +} + +func TestCouchDB_ConfigurationSerialize(t *testing.T) { + module.TestConfigurationSerialize(t, &CouchDB{}, dataConfigJSON, dataConfigYAML) +} + +func TestCouchDB_Init(t *testing.T) { + tests := map[string]struct { + config Config + wantNumOfCharts int + wantFail bool + }{ + "default": { + wantNumOfCharts: numOfCharts( + dbActivityCharts, + httpTrafficBreakdownCharts, + serverOperationsCharts, + erlangStatisticsCharts, + ), + config: New().Config, + }, + "URL not set": { + wantFail: true, + config: Config{ + HTTP: web.HTTP{ + Request: web.Request{URL: ""}, + }}, + }, + "invalid TLSCA": { + wantFail: true, + config: Config{ + HTTP: web.HTTP{ + Client: web.Client{ + TLSConfig: tlscfg.TLSConfig{TLSCA: "testdata/tls"}, + }, + }}, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + es := New() + es.Config = test.config + + if test.wantFail { + assert.Error(t, es.Init()) + } else { + assert.NoError(t, es.Init()) + assert.Equal(t, test.wantNumOfCharts, len(*es.Charts())) + } + }) + } +} + +func TestCouchDB_Check(t *testing.T) { + tests := map[string]struct { + prepare func(*testing.T) (cdb *CouchDB, cleanup func()) + wantFail bool + }{ + "valid data": {prepare: prepareCouchDBValidData}, + "invalid data": {prepare: prepareCouchDBInvalidData, wantFail: true}, + "404": {prepare: prepareCouchDB404, wantFail: true}, + "connection refused": {prepare: prepareCouchDBConnectionRefused, wantFail: true}, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + cdb, cleanup := test.prepare(t) + defer cleanup() + + if test.wantFail { + assert.Error(t, cdb.Check()) + } else { + assert.NoError(t, cdb.Check()) + } + }) + } +} + +func TestCouchDB_Charts(t *testing.T) { + assert.Nil(t, New().Charts()) +} + +func TestCouchDB_Cleanup(t *testing.T) { + assert.NotPanics(t, New().Cleanup) +} + +func TestCouchDB_Collect(t *testing.T) { + tests := map[string]struct { + prepare func() *CouchDB + wantCollected map[string]int64 + checkCharts bool + }{ + "all stats": { + prepare: func() *CouchDB { + cdb := New() + cdb.Config.Databases = "db1 db2" + return cdb + }, + wantCollected: map[string]int64{ + + // node stats + "couch_replicator_jobs_crashed": 1, + "couch_replicator_jobs_pending": 1, + "couch_replicator_jobs_running": 1, + "couchdb_database_reads": 1, + "couchdb_database_writes": 14, + "couchdb_httpd_request_methods_COPY": 1, + "couchdb_httpd_request_methods_DELETE": 1, + "couchdb_httpd_request_methods_GET": 75544, + "couchdb_httpd_request_methods_HEAD": 1, + "couchdb_httpd_request_methods_OPTIONS": 1, + "couchdb_httpd_request_methods_POST": 15, + "couchdb_httpd_request_methods_PUT": 3, + "couchdb_httpd_status_codes_200": 75294, + "couchdb_httpd_status_codes_201": 15, + "couchdb_httpd_status_codes_202": 1, + "couchdb_httpd_status_codes_204": 1, + "couchdb_httpd_status_codes_206": 1, + "couchdb_httpd_status_codes_301": 1, + "couchdb_httpd_status_codes_302": 1, + "couchdb_httpd_status_codes_304": 1, + "couchdb_httpd_status_codes_400": 1, + "couchdb_httpd_status_codes_401": 20, + "couchdb_httpd_status_codes_403": 1, + "couchdb_httpd_status_codes_404": 225, + "couchdb_httpd_status_codes_405": 1, + "couchdb_httpd_status_codes_406": 1, + "couchdb_httpd_status_codes_409": 1, + "couchdb_httpd_status_codes_412": 3, + "couchdb_httpd_status_codes_413": 1, + "couchdb_httpd_status_codes_414": 1, + "couchdb_httpd_status_codes_415": 1, + "couchdb_httpd_status_codes_416": 1, + "couchdb_httpd_status_codes_417": 1, + "couchdb_httpd_status_codes_500": 1, + "couchdb_httpd_status_codes_501": 1, + "couchdb_httpd_status_codes_503": 1, + "couchdb_httpd_status_codes_2xx": 75312, + "couchdb_httpd_status_codes_3xx": 3, + "couchdb_httpd_status_codes_4xx": 258, + "couchdb_httpd_status_codes_5xx": 3, + "couchdb_httpd_view_reads": 1, + "couchdb_open_os_files": 1, + + // node system + "context_switches": 22614499, + "ets_table_count": 116, + "internal_replication_jobs": 1, + "io_input": 49674812, + "io_output": 686400800, + "memory_atom_used": 488328, + "memory_atom": 504433, + "memory_binary": 297696, + "memory_code": 11252688, + "memory_ets": 1579120, + "memory_other": 20427855, + "memory_processes": 9161448, + "os_proc_count": 1, + "peak_msg_queue": 2, + "process_count": 296, + "reductions": 43211228312, + "run_queue": 1, + + // active tasks + "active_tasks_database_compaction": 1, + "active_tasks_indexer": 2, + "active_tasks_replication": 1, + "active_tasks_view_compaction": 1, + + // databases + "db_db1_db_doc_counts": 14, + "db_db1_db_doc_del_counts": 1, + "db_db1_db_sizes_active": 2818, + "db_db1_db_sizes_external": 588, + "db_db1_db_sizes_file": 74115, + + "db_db2_db_doc_counts": 15, + "db_db2_db_doc_del_counts": 1, + "db_db2_db_sizes_active": 1818, + "db_db2_db_sizes_external": 288, + "db_db2_db_sizes_file": 7415, + }, + checkCharts: true, + }, + "wrong node": { + prepare: func() *CouchDB { + cdb := New() + cdb.Config.Node = "bad_node@bad_host" + cdb.Config.Databases = "db1 db2" + return cdb + }, + wantCollected: map[string]int64{ + + // node stats + + // node system + + // active tasks + "active_tasks_database_compaction": 1, + "active_tasks_indexer": 2, + "active_tasks_replication": 1, + "active_tasks_view_compaction": 1, + + // databases + "db_db1_db_doc_counts": 14, + "db_db1_db_doc_del_counts": 1, + "db_db1_db_sizes_active": 2818, + "db_db1_db_sizes_external": 588, + "db_db1_db_sizes_file": 74115, + + "db_db2_db_doc_counts": 15, + "db_db2_db_doc_del_counts": 1, + "db_db2_db_sizes_active": 1818, + "db_db2_db_sizes_external": 288, + "db_db2_db_sizes_file": 7415, + }, + checkCharts: false, + }, + "wrong database": { + prepare: func() *CouchDB { + cdb := New() + cdb.Config.Databases = "bad_db db1 db2" + return cdb + }, + wantCollected: map[string]int64{ + + // node stats + "couch_replicator_jobs_crashed": 1, + "couch_replicator_jobs_pending": 1, + "couch_replicator_jobs_running": 1, + "couchdb_database_reads": 1, + "couchdb_database_writes": 14, + "couchdb_httpd_request_methods_COPY": 1, + "couchdb_httpd_request_methods_DELETE": 1, + "couchdb_httpd_request_methods_GET": 75544, + "couchdb_httpd_request_methods_HEAD": 1, + "couchdb_httpd_request_methods_OPTIONS": 1, + "couchdb_httpd_request_methods_POST": 15, + "couchdb_httpd_request_methods_PUT": 3, + "couchdb_httpd_status_codes_200": 75294, + "couchdb_httpd_status_codes_201": 15, + "couchdb_httpd_status_codes_202": 1, + "couchdb_httpd_status_codes_204": 1, + "couchdb_httpd_status_codes_206": 1, + "couchdb_httpd_status_codes_301": 1, + "couchdb_httpd_status_codes_302": 1, + "couchdb_httpd_status_codes_304": 1, + "couchdb_httpd_status_codes_400": 1, + "couchdb_httpd_status_codes_401": 20, + "couchdb_httpd_status_codes_403": 1, + "couchdb_httpd_status_codes_404": 225, + "couchdb_httpd_status_codes_405": 1, + "couchdb_httpd_status_codes_406": 1, + "couchdb_httpd_status_codes_409": 1, + "couchdb_httpd_status_codes_412": 3, + "couchdb_httpd_status_codes_413": 1, + "couchdb_httpd_status_codes_414": 1, + "couchdb_httpd_status_codes_415": 1, + "couchdb_httpd_status_codes_416": 1, + "couchdb_httpd_status_codes_417": 1, + "couchdb_httpd_status_codes_500": 1, + "couchdb_httpd_status_codes_501": 1, + "couchdb_httpd_status_codes_503": 1, + "couchdb_httpd_status_codes_2xx": 75312, + "couchdb_httpd_status_codes_3xx": 3, + "couchdb_httpd_status_codes_4xx": 258, + "couchdb_httpd_status_codes_5xx": 3, + "couchdb_httpd_view_reads": 1, + "couchdb_open_os_files": 1, + + // node system + "context_switches": 22614499, + "ets_table_count": 116, + "internal_replication_jobs": 1, + "io_input": 49674812, + "io_output": 686400800, + "memory_atom_used": 488328, + "memory_atom": 504433, + "memory_binary": 297696, + "memory_code": 11252688, + "memory_ets": 1579120, + "memory_other": 20427855, + "memory_processes": 9161448, + "os_proc_count": 1, + "peak_msg_queue": 2, + "process_count": 296, + "reductions": 43211228312, + "run_queue": 1, + + // active tasks + "active_tasks_database_compaction": 1, + "active_tasks_indexer": 2, + "active_tasks_replication": 1, + "active_tasks_view_compaction": 1, + + // databases + "db_db1_db_doc_counts": 14, + "db_db1_db_doc_del_counts": 1, + "db_db1_db_sizes_active": 2818, + "db_db1_db_sizes_external": 588, + "db_db1_db_sizes_file": 74115, + + "db_db2_db_doc_counts": 15, + "db_db2_db_doc_del_counts": 1, + "db_db2_db_sizes_active": 1818, + "db_db2_db_sizes_external": 288, + "db_db2_db_sizes_file": 7415, + }, + checkCharts: false, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + cdb, cleanup := prepareCouchDB(t, test.prepare) + defer cleanup() + + var collected map[string]int64 + for i := 0; i < 10; i++ { + collected = cdb.Collect() + } + + assert.Equal(t, test.wantCollected, collected) + if test.checkCharts { + ensureCollectedHasAllChartsDimsVarsIDs(t, cdb, collected) + } + }) + } +} + +func ensureCollectedHasAllChartsDimsVarsIDs(t *testing.T, cdb *CouchDB, collected map[string]int64) { + for _, chart := range *cdb.Charts() { + if chart.Obsolete { + continue + } + for _, dim := range chart.Dims { + _, ok := collected[dim.ID] + assert.Truef(t, ok, "collected metrics has no data for dim '%s' chart '%s'", dim.ID, chart.ID) + } + for _, v := range chart.Vars { + _, ok := collected[v.ID] + assert.Truef(t, ok, "collected metrics has no data for var '%s' chart '%s'", v.ID, chart.ID) + } + } +} + +func prepareCouchDB(t *testing.T, createCDB func() *CouchDB) (cdb *CouchDB, cleanup func()) { + t.Helper() + cdb = createCDB() + srv := prepareCouchDBEndpoint() + cdb.URL = srv.URL + + require.NoError(t, cdb.Init()) + + return cdb, srv.Close +} + +func prepareCouchDBValidData(t *testing.T) (cdb *CouchDB, cleanup func()) { + return prepareCouchDB(t, New) +} + +func prepareCouchDBInvalidData(t *testing.T) (*CouchDB, func()) { + t.Helper() + srv := httptest.NewServer(http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + _, _ = w.Write([]byte("hello and\n goodbye")) + })) + cdb := New() + cdb.URL = srv.URL + require.NoError(t, cdb.Init()) + + return cdb, srv.Close +} + +func prepareCouchDB404(t *testing.T) (*CouchDB, func()) { + t.Helper() + srv := httptest.NewServer(http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNotFound) + })) + cdb := New() + cdb.URL = srv.URL + require.NoError(t, cdb.Init()) + + return cdb, srv.Close +} + +func prepareCouchDBConnectionRefused(t *testing.T) (*CouchDB, func()) { + t.Helper() + cdb := New() + cdb.URL = "http://127.0.0.1:38001" + require.NoError(t, cdb.Init()) + + return cdb, func() {} +} + +func prepareCouchDBEndpoint() *httptest.Server { + return httptest.NewServer(http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + switch r.URL.Path { + case "/_node/_local/_stats": + _, _ = w.Write(dataVer311NodeStats) + case "/_node/_local/_system": + _, _ = w.Write(dataVer311NodeSystem) + case urlPathActiveTasks: + _, _ = w.Write(dataVer311ActiveTasks) + case "/_dbs_info": + _, _ = w.Write(dataVer311DbsInfo) + case "/": + _, _ = w.Write(dataVer311Root) + default: + w.WriteHeader(http.StatusNotFound) + } + })) +} + +func numOfCharts(charts ...Charts) (num int) { + for _, v := range charts { + num += len(v) + } + return num +} |