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