228 lines
5.6 KiB
Go
228 lines
5.6 KiB
Go
package whois
|
|
|
|
import (
|
|
// "errors"
|
|
"fmt"
|
|
"net"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/influxdata/telegraf"
|
|
"github.com/influxdata/telegraf/config"
|
|
"github.com/influxdata/telegraf/plugins/parsers/influx"
|
|
"github.com/influxdata/telegraf/testutil"
|
|
)
|
|
|
|
// Make sure Whois implements telegraf.Input
|
|
var _ telegraf.Input = &Whois{}
|
|
|
|
func TestInit(t *testing.T) {
|
|
// Setup the plugin
|
|
plugin := &Whois{
|
|
Domains: []string{"example.com", "google.com"},
|
|
Server: "whois.example.org",
|
|
Timeout: config.Duration(5 * time.Second),
|
|
Log: testutil.Logger{},
|
|
}
|
|
|
|
// Test init
|
|
require.NoError(t, plugin.Init())
|
|
}
|
|
|
|
func TestInitFail(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
domains []string
|
|
server string
|
|
timeout config.Duration
|
|
expected string
|
|
}{
|
|
{
|
|
name: "missing domains",
|
|
timeout: config.Duration(5 * time.Second),
|
|
expected: "no domains configured",
|
|
},
|
|
{
|
|
name: "invalid timeout",
|
|
domains: []string{"example.com"},
|
|
timeout: config.Duration(0),
|
|
expected: "timeout has to be greater than zero",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
// Setup the plugin
|
|
plugin := &Whois{
|
|
Domains: tt.domains,
|
|
Server: tt.server,
|
|
Timeout: tt.timeout,
|
|
Log: testutil.Logger{},
|
|
}
|
|
// Test for the expected error message
|
|
require.ErrorContains(t, plugin.Init(), tt.expected)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCases(t *testing.T) {
|
|
// Get all directories in testcases
|
|
folders, err := os.ReadDir("testcases")
|
|
require.NoError(t, err, "failed to read testcases directory")
|
|
|
|
// Prepare the influx parser for expectations
|
|
parser := &influx.Parser{}
|
|
require.NoError(t, parser.Init())
|
|
|
|
for _, f := range folders {
|
|
if !f.IsDir() {
|
|
continue
|
|
}
|
|
|
|
testcasePath := filepath.Join("testcases", f.Name())
|
|
configFilename := filepath.Join(testcasePath, "telegraf.conf")
|
|
expectedFilename := filepath.Join(testcasePath, "expected.out")
|
|
expectedErrorFilename := filepath.Join(testcasePath, "expected.err")
|
|
|
|
// Compare options for metrics
|
|
options := []cmp.Option{
|
|
testutil.IgnoreTime(),
|
|
testutil.SortMetrics(),
|
|
// Ignore `expiry` due to possibility of fail on tests if CI is under high load
|
|
testutil.IgnoreFields("expiry"),
|
|
}
|
|
|
|
t.Run(f.Name(), func(t *testing.T) {
|
|
// Create and start a mock WHOIS server
|
|
mockServer, err := createMockServer(testcasePath)
|
|
require.NoError(t, err, "failed to create mock WHOIS server")
|
|
|
|
mockServerAddr, err := mockServer.start()
|
|
require.NoError(t, err, "failed to start mock WHOIS server")
|
|
defer mockServer.stop() // Ensure cleanup
|
|
|
|
// Read expected output
|
|
var expectedMetrics []telegraf.Metric
|
|
if _, err := os.Stat(expectedFilename); err == nil {
|
|
expectedMetrics, err = testutil.ParseMetricsFromFile(expectedFilename, parser)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
// Read expected errors
|
|
var expectedErrors []string
|
|
if _, err := os.Stat(expectedErrorFilename); err == nil {
|
|
expectedErrors, err = testutil.ParseLinesFromFile(expectedErrorFilename)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
// Load Telegraf plugin config
|
|
cfg := config.NewConfig()
|
|
require.NoError(t, cfg.LoadConfig(configFilename))
|
|
require.Len(t, cfg.Inputs, 1)
|
|
|
|
// Get WHOIS plugin instance
|
|
plugin := cfg.Inputs[0].Input.(*Whois)
|
|
plugin.Server = mockServerAddr
|
|
require.NoError(t, plugin.Init())
|
|
|
|
var acc testutil.Accumulator
|
|
require.NoError(t, plugin.Gather(&acc))
|
|
|
|
var actualErrorMsgs []string
|
|
for _, err := range acc.Errors {
|
|
actualErrorMsgs = append(actualErrorMsgs, err.Error())
|
|
}
|
|
require.ElementsMatch(t, actualErrorMsgs, expectedErrors)
|
|
|
|
// Compare expected metrics
|
|
actualMetrics := acc.GetTelegrafMetrics()
|
|
testutil.RequireMetricsEqual(t, expectedMetrics, actualMetrics, options...)
|
|
})
|
|
}
|
|
}
|
|
|
|
type server struct {
|
|
responses map[string][]byte
|
|
listener net.Listener
|
|
|
|
errors []error
|
|
sync.Mutex
|
|
}
|
|
|
|
func createMockServer(path string) (*server, error) {
|
|
// Read the input data
|
|
matches, err := filepath.Glob(filepath.Join(path, "input_*.txt"))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("matching input files failed: %w", err)
|
|
}
|
|
|
|
responses := make(map[string][]byte, len(matches))
|
|
for _, fn := range matches {
|
|
buf, err := os.ReadFile(fn)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("reading %q failed: %w", fn, err)
|
|
}
|
|
domain := strings.TrimPrefix(filepath.Base(fn), "input_")
|
|
domain = strings.TrimSuffix(domain, ".txt")
|
|
responses[domain] = buf
|
|
}
|
|
return &server{responses: responses}, nil
|
|
}
|
|
|
|
func (s *server) start() (string, error) {
|
|
// Create the listener
|
|
listener, err := net.Listen("tcp", "127.0.0.1:0")
|
|
if err != nil {
|
|
return "", fmt.Errorf("starting server failed: %w", err)
|
|
}
|
|
s.listener = listener
|
|
|
|
addr := listener.Addr().String()
|
|
go func() {
|
|
for {
|
|
conn, err := s.listener.Accept()
|
|
if err != nil {
|
|
return // Stop accepting new connections on shutdown
|
|
}
|
|
|
|
go func(c net.Conn) {
|
|
defer c.Close()
|
|
// Read the requested domain
|
|
buf := make([]byte, 1024)
|
|
n, err := c.Read(buf)
|
|
if err != nil {
|
|
return
|
|
}
|
|
domain := strings.TrimSpace(string(buf[:n]))
|
|
|
|
// Write the response from the input data or an error if the domain cannot be found
|
|
response, found := s.responses[domain]
|
|
if !found {
|
|
response = []byte("ERROR: No data available\n")
|
|
}
|
|
|
|
if _, err := c.Write(response); err != nil {
|
|
s.Lock()
|
|
s.errors = append(s.errors, fmt.Errorf("writing response %q failed: %w", domain, err))
|
|
s.Unlock()
|
|
}
|
|
}(conn)
|
|
}
|
|
}()
|
|
return addr, nil
|
|
}
|
|
|
|
func (s *server) stop() {
|
|
if s.listener != nil {
|
|
s.listener.Close()
|
|
s.listener = nil
|
|
}
|
|
}
|