Add tests. They are quite basic ...
This commit is contained in:
53
agentx/agentx_test.go
Normal file
53
agentx/agentx_test.go
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
// Copyright 2025, IPng Networks GmbH, Pim van Pelt <pim@ipng.ch>
|
||||||
|
|
||||||
|
package agentx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAgentXAddrFlag(t *testing.T) {
|
||||||
|
// Test that the flag is registered with correct default
|
||||||
|
if *AgentXAddr != "localhost:705" {
|
||||||
|
t.Errorf("Expected default AgentX address to be 'localhost:705', got '%s'", *AgentXAddr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAgentXAddrFlagParsing(t *testing.T) {
|
||||||
|
// Save original flag value
|
||||||
|
originalAddr := *AgentXAddr
|
||||||
|
defer func() { *AgentXAddr = originalAddr }()
|
||||||
|
|
||||||
|
// Test Unix socket path
|
||||||
|
testAddr := "/var/run/test.sock"
|
||||||
|
*AgentXAddr = testAddr
|
||||||
|
|
||||||
|
if *AgentXAddr != testAddr {
|
||||||
|
t.Errorf("Expected AgentX address to be '%s', got '%s'", testAddr, *AgentXAddr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test TCP address
|
||||||
|
testAddr = "192.168.1.1:705"
|
||||||
|
*AgentXAddr = testAddr
|
||||||
|
|
||||||
|
if *AgentXAddr != testAddr {
|
||||||
|
t.Errorf("Expected AgentX address to be '%s', got '%s'", testAddr, *AgentXAddr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFlagRegistration(t *testing.T) {
|
||||||
|
// Test that our flag is properly registered
|
||||||
|
f := flag.Lookup("agentx.addr")
|
||||||
|
if f == nil {
|
||||||
|
t.Error("Expected agentx.addr flag to be registered")
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.DefValue != "localhost:705" {
|
||||||
|
t.Errorf("Expected flag default value to be 'localhost:705', got '%s'", f.DefValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.Usage != "Address to connect to (hostname:port or Unix socket path)" {
|
||||||
|
t.Errorf("Unexpected flag usage string: %s", f.Usage)
|
||||||
|
}
|
||||||
|
}
|
30
config/config_test.go
Normal file
30
config/config_test.go
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
// Copyright 2025, IPng Networks GmbH, Pim van Pelt <pim@ipng.ch>
|
||||||
|
|
||||||
|
package config
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestDebugFlagDefault(t *testing.T) {
|
||||||
|
// Test that Debug flag starts as false by default
|
||||||
|
if Debug != false {
|
||||||
|
t.Errorf("Expected Debug to be false by default, got %v", Debug)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDebugFlagSet(t *testing.T) {
|
||||||
|
// Save original value
|
||||||
|
original := Debug
|
||||||
|
defer func() { Debug = original }()
|
||||||
|
|
||||||
|
// Test setting Debug to true
|
||||||
|
Debug = true
|
||||||
|
if Debug != true {
|
||||||
|
t.Errorf("Expected Debug to be true after setting, got %v", Debug)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test setting Debug to false
|
||||||
|
Debug = false
|
||||||
|
if Debug != false {
|
||||||
|
t.Errorf("Expected Debug to be false after setting, got %v", Debug)
|
||||||
|
}
|
||||||
|
}
|
180
ifmib/ifmib_test.go
Normal file
180
ifmib/ifmib_test.go
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
// Copyright 2025, IPng Networks GmbH, Pim van Pelt <pim@ipng.ch>
|
||||||
|
|
||||||
|
package ifmib
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"go.fd.io/govpp/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNewInterfaceMIB(t *testing.T) {
|
||||||
|
mib := NewInterfaceMIB()
|
||||||
|
|
||||||
|
if mib == nil {
|
||||||
|
t.Fatal("NewInterfaceMIB returned nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
if mib.handler == nil {
|
||||||
|
t.Error("Expected handler to be initialized")
|
||||||
|
}
|
||||||
|
|
||||||
|
if mib.stats == nil {
|
||||||
|
t.Error("Expected stats map to be initialized")
|
||||||
|
}
|
||||||
|
|
||||||
|
if mib.descriptions == nil {
|
||||||
|
t.Error("Expected descriptions map to be initialized")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(mib.stats) != 0 {
|
||||||
|
t.Errorf("Expected stats map to be empty, got %d entries", len(mib.stats))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(mib.descriptions) != 0 {
|
||||||
|
t.Errorf("Expected descriptions map to be empty, got %d entries", len(mib.descriptions))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetHandler(t *testing.T) {
|
||||||
|
mib := NewInterfaceMIB()
|
||||||
|
handler := mib.GetHandler()
|
||||||
|
|
||||||
|
if handler == nil {
|
||||||
|
t.Error("GetHandler returned nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
if handler != mib.handler {
|
||||||
|
t.Error("GetHandler returned different handler than expected")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoadVPPConfigValidYAML(t *testing.T) {
|
||||||
|
mib := NewInterfaceMIB()
|
||||||
|
|
||||||
|
// Create a temporary YAML file
|
||||||
|
yamlContent := `interfaces:
|
||||||
|
GigabitEthernet0/0/0:
|
||||||
|
description: 'Test: Interface'
|
||||||
|
sub-interfaces:
|
||||||
|
100:
|
||||||
|
description: 'Test: Sub-interface'
|
||||||
|
loopbacks:
|
||||||
|
loop0:
|
||||||
|
description: 'Test: Loopback'
|
||||||
|
`
|
||||||
|
|
||||||
|
tmpfile, err := os.CreateTemp("", "test_*.yaml")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.Remove(tmpfile.Name())
|
||||||
|
|
||||||
|
if _, err := tmpfile.Write([]byte(yamlContent)); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := tmpfile.Close(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test loading the config
|
||||||
|
err = mib.LoadVPPConfig(tmpfile.Name())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("LoadVPPConfig failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that descriptions were loaded
|
||||||
|
if len(mib.descriptions) != 3 {
|
||||||
|
t.Errorf("Expected 3 descriptions, got %d", len(mib.descriptions))
|
||||||
|
}
|
||||||
|
|
||||||
|
if mib.descriptions["GigabitEthernet0/0/0"] != "Test: Interface" {
|
||||||
|
t.Errorf("Unexpected interface description: %s", mib.descriptions["GigabitEthernet0/0/0"])
|
||||||
|
}
|
||||||
|
|
||||||
|
if mib.descriptions["GigabitEthernet0/0/0.100"] != "Test: Sub-interface" {
|
||||||
|
t.Errorf("Unexpected sub-interface description: %s", mib.descriptions["GigabitEthernet0/0/0.100"])
|
||||||
|
}
|
||||||
|
|
||||||
|
if mib.descriptions["loop0"] != "Test: Loopback" {
|
||||||
|
t.Errorf("Unexpected loopback description: %s", mib.descriptions["loop0"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoadVPPConfigNonExistentFile(t *testing.T) {
|
||||||
|
mib := NewInterfaceMIB()
|
||||||
|
|
||||||
|
err := mib.LoadVPPConfig("/nonexistent/file.yaml")
|
||||||
|
if err == nil {
|
||||||
|
t.Error("Expected error for non-existent file")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoadVPPConfigInvalidYAML(t *testing.T) {
|
||||||
|
mib := NewInterfaceMIB()
|
||||||
|
|
||||||
|
// Create a temporary file with invalid YAML
|
||||||
|
invalidYAML := `interfaces:
|
||||||
|
test: [
|
||||||
|
`
|
||||||
|
|
||||||
|
tmpfile, err := os.CreateTemp("", "invalid_*.yaml")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.Remove(tmpfile.Name())
|
||||||
|
|
||||||
|
if _, err := tmpfile.Write([]byte(invalidYAML)); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := tmpfile.Close(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = mib.LoadVPPConfig(tmpfile.Name())
|
||||||
|
if err == nil {
|
||||||
|
t.Error("Expected error for invalid YAML")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpdateStatsBasic(t *testing.T) {
|
||||||
|
mib := NewInterfaceMIB()
|
||||||
|
|
||||||
|
// Create mock interface stats
|
||||||
|
stats := &api.InterfaceStats{
|
||||||
|
Interfaces: []api.InterfaceCounters{
|
||||||
|
{
|
||||||
|
InterfaceIndex: 0,
|
||||||
|
InterfaceName: "test0",
|
||||||
|
Rx: api.InterfaceCounterCombined{
|
||||||
|
Packets: 100,
|
||||||
|
Bytes: 1000,
|
||||||
|
},
|
||||||
|
Tx: api.InterfaceCounterCombined{
|
||||||
|
Packets: 200,
|
||||||
|
Bytes: 2000,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call UpdateStats (this will test the basic flow without AgentX sessions)
|
||||||
|
mib.UpdateStats(stats)
|
||||||
|
|
||||||
|
// Check that stats were stored
|
||||||
|
if len(mib.stats) != 1 {
|
||||||
|
t.Errorf("Expected 1 interface in stats, got %d", len(mib.stats))
|
||||||
|
}
|
||||||
|
|
||||||
|
if storedStats, exists := mib.stats[0]; !exists {
|
||||||
|
t.Error("Expected interface 0 to be stored in stats")
|
||||||
|
} else {
|
||||||
|
if storedStats.InterfaceName != "test0" {
|
||||||
|
t.Errorf("Expected interface name 'test0', got '%s'", storedStats.InterfaceName)
|
||||||
|
}
|
||||||
|
if storedStats.Rx.Packets != 100 {
|
||||||
|
t.Errorf("Expected RX packets 100, got %d", storedStats.Rx.Packets)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
72
logger/logger_test.go
Normal file
72
logger/logger_test.go
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
// Copyright 2025, IPng Networks GmbH, Pim van Pelt <pim@ipng.ch>
|
||||||
|
|
||||||
|
package logger
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"govpp-snmp-agentx/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPrintf(t *testing.T) {
|
||||||
|
// Capture log output
|
||||||
|
var buf bytes.Buffer
|
||||||
|
log.SetOutput(&buf)
|
||||||
|
defer log.SetOutput(os.Stderr)
|
||||||
|
|
||||||
|
Printf("test message: %s", "hello")
|
||||||
|
|
||||||
|
output := buf.String()
|
||||||
|
if !strings.Contains(output, "logger_test.go:logger.TestPrintf") {
|
||||||
|
t.Errorf("Expected log output to contain caller info, got: %s", output)
|
||||||
|
}
|
||||||
|
if !strings.Contains(output, "test message: hello") {
|
||||||
|
t.Errorf("Expected log output to contain message, got: %s", output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDebugfWithDebugEnabled(t *testing.T) {
|
||||||
|
// Save original debug state
|
||||||
|
originalDebug := config.Debug
|
||||||
|
defer func() { config.Debug = originalDebug }()
|
||||||
|
|
||||||
|
// Enable debug
|
||||||
|
config.Debug = true
|
||||||
|
|
||||||
|
// Capture log output
|
||||||
|
var buf bytes.Buffer
|
||||||
|
log.SetOutput(&buf)
|
||||||
|
defer log.SetOutput(os.Stderr)
|
||||||
|
|
||||||
|
Debugf("debug message: %s", "test")
|
||||||
|
|
||||||
|
output := buf.String()
|
||||||
|
if !strings.Contains(output, "debug message: test") {
|
||||||
|
t.Errorf("Expected debug message to be logged when debug is enabled, got: %s", output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDebugfWithDebugDisabled(t *testing.T) {
|
||||||
|
// Save original debug state
|
||||||
|
originalDebug := config.Debug
|
||||||
|
defer func() { config.Debug = originalDebug }()
|
||||||
|
|
||||||
|
// Disable debug
|
||||||
|
config.Debug = false
|
||||||
|
|
||||||
|
// Capture log output
|
||||||
|
var buf bytes.Buffer
|
||||||
|
log.SetOutput(&buf)
|
||||||
|
defer log.SetOutput(os.Stderr)
|
||||||
|
|
||||||
|
Debugf("debug message: %s", "test")
|
||||||
|
|
||||||
|
output := buf.String()
|
||||||
|
if strings.Contains(output, "debug message: test") {
|
||||||
|
t.Errorf("Expected debug message to NOT be logged when debug is disabled, got: %s", output)
|
||||||
|
}
|
||||||
|
}
|
20
main_test.go
Normal file
20
main_test.go
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
// Copyright 2025, IPng Networks GmbH, Pim van Pelt <pim@ipng.ch>
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMainCompiles(t *testing.T) {
|
||||||
|
// This test simply ensures that main package compiles
|
||||||
|
// More comprehensive integration tests would require mocking VPP and SNMP
|
||||||
|
if os.Getenv("BE_MAIN") == "1" {
|
||||||
|
// This would run main(), but we skip it in tests
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Just test that we can access main package
|
||||||
|
t.Log("Main package compiles successfully")
|
||||||
|
}
|
163
vppstats/stats_test.go
Normal file
163
vppstats/stats_test.go
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
// Copyright 2025, IPng Networks GmbH, Pim van Pelt <pim@ipng.ch>
|
||||||
|
|
||||||
|
package vppstats
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"go.fd.io/govpp/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestVPPStatsFlags(t *testing.T) {
|
||||||
|
// Test default values
|
||||||
|
if *ApiAddr != "/var/run/vpp/api.sock" {
|
||||||
|
t.Errorf("Expected default API address to be '/var/run/vpp/api.sock', got '%s'", *ApiAddr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if *StatsAddr != "/var/run/vpp/stats.sock" {
|
||||||
|
t.Errorf("Expected default stats address to be '/var/run/vpp/stats.sock', got '%s'", *StatsAddr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if *IfIndexOffset != 1000 {
|
||||||
|
t.Errorf("Expected default interface index offset to be 1000, got %d", *IfIndexOffset)
|
||||||
|
}
|
||||||
|
|
||||||
|
if *Period != 10 {
|
||||||
|
t.Errorf("Expected default period to be 10, got %d", *Period)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFlagRegistrations(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
flagName string
|
||||||
|
defValue string
|
||||||
|
}{
|
||||||
|
{"API address", "vppstats.api.addr", "/var/run/vpp/api.sock"},
|
||||||
|
{"Stats address", "vppstats.stats.addr", "/var/run/vpp/stats.sock"},
|
||||||
|
{"Index offset", "vppstats.ifindex-offset", "1000"},
|
||||||
|
{"Period", "vppstats.period", "10"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
f := flag.Lookup(tt.flagName)
|
||||||
|
if f == nil {
|
||||||
|
t.Errorf("Expected %s flag to be registered", tt.flagName)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.DefValue != tt.defValue {
|
||||||
|
t.Errorf("Expected %s flag default value to be '%s', got '%s'",
|
||||||
|
tt.flagName, tt.defValue, f.DefValue)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStatsCallbackType(t *testing.T) {
|
||||||
|
// Test that we can create a valid callback function
|
||||||
|
var called bool
|
||||||
|
var receivedStats *api.InterfaceStats
|
||||||
|
|
||||||
|
callback := func(stats *api.InterfaceStats) {
|
||||||
|
called = true
|
||||||
|
receivedStats = stats
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create mock stats
|
||||||
|
mockStats := &api.InterfaceStats{
|
||||||
|
Interfaces: []api.InterfaceCounters{
|
||||||
|
{
|
||||||
|
InterfaceIndex: 1,
|
||||||
|
InterfaceName: "test",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call the callback
|
||||||
|
callback(mockStats)
|
||||||
|
|
||||||
|
if !called {
|
||||||
|
t.Error("Expected callback to be called")
|
||||||
|
}
|
||||||
|
|
||||||
|
if receivedStats != mockStats {
|
||||||
|
t.Error("Expected callback to receive the same stats object")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(receivedStats.Interfaces) != 1 {
|
||||||
|
t.Errorf("Expected 1 interface, got %d", len(receivedStats.Interfaces))
|
||||||
|
}
|
||||||
|
|
||||||
|
if receivedStats.Interfaces[0].InterfaceName != "test" {
|
||||||
|
t.Errorf("Expected interface name 'test', got '%s'", receivedStats.Interfaces[0].InterfaceName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPeriodConversion(t *testing.T) {
|
||||||
|
// Test that period conversion works correctly
|
||||||
|
originalPeriod := *Period
|
||||||
|
defer func() { *Period = originalPeriod }()
|
||||||
|
|
||||||
|
testPeriods := []struct {
|
||||||
|
input int
|
||||||
|
expected time.Duration
|
||||||
|
}{
|
||||||
|
{1, time.Second},
|
||||||
|
{5, 5 * time.Second},
|
||||||
|
{10, 10 * time.Second},
|
||||||
|
{60, time.Minute},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range testPeriods {
|
||||||
|
t.Run(fmt.Sprintf("period_%d", tt.input), func(t *testing.T) {
|
||||||
|
*Period = tt.input
|
||||||
|
result := time.Duration(*Period) * time.Second
|
||||||
|
|
||||||
|
if result != tt.expected {
|
||||||
|
t.Errorf("Expected period %v, got %v", tt.expected, result)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFlagValues(t *testing.T) {
|
||||||
|
// Save original flag values
|
||||||
|
originalApiAddr := *ApiAddr
|
||||||
|
originalStatsAddr := *StatsAddr
|
||||||
|
originalOffset := *IfIndexOffset
|
||||||
|
originalPeriod := *Period
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
*ApiAddr = originalApiAddr
|
||||||
|
*StatsAddr = originalStatsAddr
|
||||||
|
*IfIndexOffset = originalOffset
|
||||||
|
*Period = originalPeriod
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Test setting flag values
|
||||||
|
*ApiAddr = "/custom/api.sock"
|
||||||
|
*StatsAddr = "/custom/stats.sock"
|
||||||
|
*IfIndexOffset = 2000
|
||||||
|
*Period = 30
|
||||||
|
|
||||||
|
if *ApiAddr != "/custom/api.sock" {
|
||||||
|
t.Errorf("Expected API address to be '/custom/api.sock', got '%s'", *ApiAddr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if *StatsAddr != "/custom/stats.sock" {
|
||||||
|
t.Errorf("Expected stats address to be '/custom/stats.sock', got '%s'", *StatsAddr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if *IfIndexOffset != 2000 {
|
||||||
|
t.Errorf("Expected interface index offset to be 2000, got %d", *IfIndexOffset)
|
||||||
|
}
|
||||||
|
|
||||||
|
if *Period != 30 {
|
||||||
|
t.Errorf("Expected period to be 30, got %d", *Period)
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user