Add tests. They are quite basic ...

This commit is contained in:
Pim van Pelt
2025-06-11 00:02:04 +02:00
parent cb8acc4c13
commit 0a0e3e7055
6 changed files with 518 additions and 0 deletions

53
agentx/agentx_test.go Normal file
View 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
View 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
View 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
View 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
View 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
View 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)
}
}